From 1fdb864b4bdb9b7c224353743287667751e11596 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 10 Dec 2025 16:36:59 +0100 Subject: [PATCH 01/24] Startet implementation of an adaption-class to simplify the addition of different adaptation styles --- .../t8_forest_adapt/t8_forest_adapt.cxx | 76 +++++++++ .../t8_forest_adapt/t8_forest_adapt.hxx | 148 ++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx create mode 100644 src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx new file mode 100644 index 0000000000..f981227cd5 --- /dev/null +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx @@ -0,0 +1,76 @@ +/* + 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_forest_adapt/t8_forest_adapt.cxx + * Implementation of the adaptation routine to refine and coarsen a forest of trees. + */ + + #include + + + + void + t8_forest_adapt::basic_adaptation::adapt() + { + T8_ASSERT (forest != nullptr); + + if (profiling) { + profile_adaptation(); + } + T8_ASSERT (forest_from != nullptr); + + collect_adapt_actions(); + + t8_locidx_t el_offset = 0; + const t8_locidx_t num_trees = t8_forest_get_num_trees (forest_from); + + for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { + t8_tree_t tree = t8_forest_get_tree (forest, ltree_id); + const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); + t8_element_array_t elements = &tree->leaf_elements; + const t8_element_array_t tree_elements_from = &tree_from->leaf_elements; + const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (tree_elements_from); + T8_ASSERT (num_el_from == t8_forest_get_tree_num_leaf_elements (forest_from, ltree_id)); + const t8_eclass_t tree_class = tree_from->tree_class; + /* Continue only if tree_from is not empty */ + if (num_el_from < 0){ + const t8_element_t *first_element_from = t8_element_array_index_locidx (tree_elements_from, 0); + t8_locidx_t curr_size_elements_from = scheme->element_get_num_siblings (tree_class, first_element_from); + t8_locidx_t el_considered = 0; + std::vector elements_from; + + while (el_considered < num_el_from) { + const t8_locidx_t num_siblings = scheme->element_get_num_siblings (tree_class, t8_element_array_index_locidx (tree_elements_from, el_considered)); + if (num_siblings > curr_size_elements_from) { + elements_from.resize (num_siblings); + curr_size_elements_from = num_siblings; + } + const bool is_family = family_check (tree_elements_from, elements_from, el_considered, scheme, tree_class); + const adapt_action action = adapt_actions[el_offset + el_considered]; + + } + } + el_offset += num_el_from; + } + + } \ No newline at end of file diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx new file mode 100644 index 0000000000..eb9ce99aa9 --- /dev/null +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -0,0 +1,148 @@ +/* + 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_forest_adapt.hxx + * Definition of a C++ class for the adaptation routine to provide a flexible way of implementing + * different kinds of adaptation strategies. + */ + +#ifndef T8_FOREST_ADAPT_HXX +#define T8_FOREST_ADAPT_HXX + +#include +#include +/** + * Namespace for adaptation related classes and functions. + */ +namespace t8_forest_adapt{ + /** The action to be taken on an element during adaptation. + * COARSEN: The element should be coarsened. + * KEEP: The element should remain as is. + * REFINE: The element should be refined. + */ + enum int8_t AdaptAction { + COARSEN = -1, + KEEP = 0, + REFINE = 1 + }; + + /** + * Callback function type for element adaptation. + */ + using element_callback = std::function; + +class basic_adaptation { + public: + /** Constructor for basic_adaptation class. + * \param [in] forest_in The forest to be adapted. + * \param [in] callback_in The callback function to determine adaptation actions. + */ + basic_adaptation (t8_forest_t forest, t8_forest_t forest_from, element_callback callback_in) + : forest (forest), forest_from (forest_from), callback (callback_in) + { + T8_ASSERT (forest != nullptr); + T8_ASSERT (callback); + if (forest_from != nullptr) { + t8_forest_ref (forest_from); + } + T8_ASSERT (forest != nullptr); + if (forest != nullptr) { + t8_forest_ref (forest); + } + } + + /** Destructor for basic_adaptation class. */ + ~basic_adaptation () { + if (forest_from != nullptr) { + t8_forest_unref (forest_from); + } + if (forest != nullptr) { + t8_forest_unref (forest); + } + } + + /** Perform the adaptation process on the forest. */ + void adapt(); + + element_callback callback; /**< The callback function to determine adaptation actions. */ + private: + /** + * Profile the adaptation process. + */ + inline void + profile_adaptation(){ + T8_ASSERT(forest->profile != nullptr); + forest->profile->adapt_runtime = -sc_MPI_Wtime(); + } + + /** + * Collect adaptation actions for all elements in the source forest. + */ + inline void + collect_adapt_actions(){ + t8_locidx_t el_offset = 0; + const t8_locidx_t num_trees = t8_forest_get_num_trees (forest_from); + const t8_locidx_t local_num_elements = t8_forest_get_num_local_elements (forest_from); + adapt_actions.resize (local_num_elements); + + const t8_scheme *scheme = t8_forest_get_scheme (forest_from); + + /* For each element get the adaptation action */ + for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { + const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); + const t8_eclass_t tree_class = tree_from->tree_class; + const t8_element_array_t elements_from = &tree_from->leaf_elements; + const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (elements_from); + for (t8_locidx_t i = 0; i < num_el_from; i++) { + const t8_element_t *element = (t8_element_t *) t8_element_array_get_element (elements_from, i); + adapt_actions[el_offset + i] = element_callback (forest_from, ltree_id, element, scheme); + } + el_offset += num_el_from; + } + }; + + inline bool + family_check(const t8_element_array_t *telements_from, const t8_locidx_t offset, const t8_scheme *scheme, const t8_eclass_t tree_class){ + const int num_siblings = scheme->element_get_num_siblings (tree_class, t8_element_array_index_locidx (telements_from, offset)); + t8_element_t **elements_from = T8_ALLOC (t8_element_t *, num_siblings); + for (int isibling = 0; isibling < num_siblings; isibling++) { + elements_from[isibling] = (t8_element_t *) t8_element_array_index_locidx_mutable (telements_from, offset + (t8_locidx_t )isibling); + if (scheme->element_get_child_id (tree_class, elements_from[isibling]) != isibling) { + T8_FREE (elements_from); + return false; + } + } + const bool is_family = scheme->elements_are_family (tree_class, elements_from); + T8_FREE (elements_from); + return is_family; + } + + t8_forest_t forest; /**< The target forest */ + t8_forest_t forest_from; /**< The source forest to adapt from. */ + std::vector adapt_actions; /**< The adaptation actions for each element in the source forest. */ + bool profiling = false; /**< Flag to indicate if profiling is enabled. */ +} + +}; + +#endif /* T8_FOREST_ADAPT_HXX */ \ No newline at end of file From 5c65516f9b7292ef03793e2b6714789332690e25 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Fri, 12 Dec 2025 14:00:40 +0100 Subject: [PATCH 02/24] Started Implementation of the manipulation-functions a templated function to describe how the new forest is based on the old forest depending on the return value of the adaptation cb --- .../t8_forest_adapt/t8_forest_adapt.cxx | 43 ++++++++-- .../t8_forest_adapt/t8_forest_adapt.hxx | 80 +++++++++++++++++-- 2 files changed, 108 insertions(+), 15 deletions(-) diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx index f981227cd5..9893750a12 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx @@ -25,7 +25,7 @@ * Implementation of the adaptation routine to refine and coarsen a forest of trees. */ - #include +#include @@ -45,10 +45,13 @@ const t8_locidx_t num_trees = t8_forest_get_num_trees (forest_from); for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { + /* get the trees from both forests. */ t8_tree_t tree = t8_forest_get_tree (forest, ltree_id); const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); + /* get the leaf arrays from both forests */ t8_element_array_t elements = &tree->leaf_elements; const t8_element_array_t tree_elements_from = &tree_from->leaf_elements; + /* Get the number of elements in the source tree */ const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (tree_elements_from); T8_ASSERT (num_el_from == t8_forest_get_tree_num_leaf_elements (forest_from, ltree_id)); const t8_eclass_t tree_class = tree_from->tree_class; @@ -56,21 +59,47 @@ if (num_el_from < 0){ const t8_element_t *first_element_from = t8_element_array_index_locidx (tree_elements_from, 0); t8_locidx_t curr_size_elements_from = scheme->element_get_num_siblings (tree_class, first_element_from); + /* index of the elements in source tree */ t8_locidx_t el_considered = 0; - std::vector elements_from; + /* index of the elements in target tree */ + t8_locidx_t el_inserted = 0; + std::vector elements_temp; while (el_considered < num_el_from) { const t8_locidx_t num_siblings = scheme->element_get_num_siblings (tree_class, t8_element_array_index_locidx (tree_elements_from, el_considered)); if (num_siblings > curr_size_elements_from) { - elements_from.resize (num_siblings); + elements_temp.resize (num_siblings); curr_size_elements_from = num_siblings; } - const bool is_family = family_check (tree_elements_from, elements_from, el_considered, scheme, tree_class); - const adapt_action action = adapt_actions[el_offset + el_considered]; - + for (int isibling = 0; isibling < num_siblings && el_considered + isibling < num_el_from; isibling++) { + elements_temp[isibling] = (t8_element_t *) t8_element_array_index_locidx_mutable (tree_elements_from, el_considered + (t8_locidx_t )isibling); + if (scheme->element_get_child_id (tree_class, elements_temp[isibling]) != isibling) { + break; + } + } + const bool is_family = family_check (tree_elements_from, elements_temp, el_considered, scheme, tree_class); + adapt_action action = adapt_actions[el_offset + el_considered]; + + if (!is_family && action == COARSEN) { + action = KEEP; + } + /* Check that all siblings want to be coarsened */ + if (is_family && action == COARSEN) { + const auto start = adapt_actions.begin() + static_cast(el_offset + el_considered); + const auto end = start + static_cast(num_siblings); + if (!std::all_of(start, end, [](const adapt_action &a){ return a == COARSEN; })) { + action = KEEP; + } + } + + el_inserted += manipulate_elements (elements, tree_elements_from,el_considered ); + el_considered++; } } + tree->elements_offset = el_offset; el_offset += num_el_from; - } + + + } } \ No newline at end of file diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index eb9ce99aa9..28e33039fe 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -39,7 +39,7 @@ namespace t8_forest_adapt{ * KEEP: The element should remain as is. * REFINE: The element should be refined. */ - enum int8_t AdaptAction { + enum adapt_action { COARSEN = -1, KEEP = 0, REFINE = 1 @@ -48,9 +48,76 @@ namespace t8_forest_adapt{ /** * Callback function type for element adaptation. */ - using element_callback = std::function; + using element_callback = std::function; + + /** * Function to manipulate elements based on the specified adaptation action. + * \tparam action The adaptation action to be performed. + * \param [in, out] elements The element array to be modified. + * \param [in] elements_from The source element array. + * \param [in] scheme The element scheme. + * \param [in] tree_class The eclass of the tree used by the scheme + * \param [in] elements_index The index in the target element array. + * \param [in] elements_from_index The index in the source element array. + * \return The number of elements created in the target array. + */ + template + int manipulate_elements (t8_element_array_t *elements, + const t8_element_array_t *elements_from, + const t8_scheme *scheme, + const t8_eclass_t tree_class + const t8_locidx_t elements_index, + const t8_locidx_t elements_from_index); + + template <> + int manipulate_elements (t8_element_array_t *elements, + const t8_element_array_t *elements_from, + const t8_scheme *scheme, + const t8_eclass_t tree_class + const t8_locidx_t elements_index, + const t8_locidx_t elements_from_index) + { + t8_element_t *element = t8_element_array_push (elements); + scheme->element_copy (tree_class, elements_from[elements_from_index], element); + return 1; + }; + template <> + int manipulate_elements (t8_element_array_t *elements, + const t8_element_array_t *elements_from, + const t8_scheme *scheme, + const t8_eclass_t tree_class, + const t8_locidx_t elements_index, + const t8_locidx_t elements_from_index) + { + t8_element_t *element = t8_element_array_push (elements); + T8_ASSERT (scheme->element_get_level (tree_class, elements_from[elements_from_index]) > 0); + scheme->element_get_parent (tree_class, elements_from[elements_from_index], element); + + /* Hier eventuell noch was mit num_children = num_siblings*/ + return 1; + }; + + template <> + int manipulate_elements (t8_element_array_t *elements, + const t8_element_array_t *elements_from, + const t8_scheme *scheme, + const t8_eclass_t tree_class, + const t8_locidx_t elements_index, + const t8_locidx_t elements_from_index) + { + const int num_children = scheme->element_get_num_children (tree_class, elements_from[elements_from_index]); + /* CONTINUE WORK HERE */ + (void) t8_element_array_push_count (elements, num_children); + for (int ichildren = 0; ichildren < num_children; ichildren++) { + elements[ichildren + elements_index] = t8_element_array_index_locidx_mutable (elements, *el_inserted + ichildren); + } + return num_children; + }; + + + /** * Class implementing a basic adaptation strategy for a forest of trees. + */ class basic_adaptation { public: /** Constructor for basic_adaptation class. @@ -122,24 +189,21 @@ class basic_adaptation { }; inline bool - family_check(const t8_element_array_t *telements_from, const t8_locidx_t offset, const t8_scheme *scheme, const t8_eclass_t tree_class){ + family_check(const t8_element_array_t *tree_elements_from, std::vector &elements_from, const t8_locidx_t offset, const t8_scheme *scheme, const t8_eclass_t tree_class){ const int num_siblings = scheme->element_get_num_siblings (tree_class, t8_element_array_index_locidx (telements_from, offset)); - t8_element_t **elements_from = T8_ALLOC (t8_element_t *, num_siblings); for (int isibling = 0; isibling < num_siblings; isibling++) { elements_from[isibling] = (t8_element_t *) t8_element_array_index_locidx_mutable (telements_from, offset + (t8_locidx_t )isibling); if (scheme->element_get_child_id (tree_class, elements_from[isibling]) != isibling) { - T8_FREE (elements_from); return false; } } const bool is_family = scheme->elements_are_family (tree_class, elements_from); - T8_FREE (elements_from); return is_family; } t8_forest_t forest; /**< The target forest */ t8_forest_t forest_from; /**< The source forest to adapt from. */ - std::vector adapt_actions; /**< The adaptation actions for each element in the source forest. */ + std::vector adapt_actions; /**< The adaptation actions for each element in the source forest. */ bool profiling = false; /**< Flag to indicate if profiling is enabled. */ } From 60e93a10030095e22868721e9e012838d5c0ffeb Mon Sep 17 00:00:00 2001 From: David Knapp Date: Mon, 15 Dec 2025 13:53:27 +0100 Subject: [PATCH 03/24] fix compiler errors --- src/CMakeLists.txt | 1 + .../t8_forest_adapt/t8_forest_adapt.cxx | 39 +++++++--- .../t8_forest_adapt/t8_forest_adapt.hxx | 74 +++++++++++-------- .../t8_default_hex/t8_default_hex.cxx | 2 +- .../t8_default_hex/t8_default_hex.hxx | 2 +- src/t8_schemes/t8_scheme.cxx | 2 +- src/t8_schemes/t8_scheme.h | 2 +- src/t8_schemes/t8_scheme.hxx | 2 +- 8 files changed, 81 insertions(+), 43 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7f1f44b9dd..10fff05fdd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -171,6 +171,7 @@ target_sources( T8 PRIVATE t8_data/t8_shmem.c t8_data/t8_containers.cxx t8_forest/t8_forest_adapt.cxx + t8_forest/t8_forest_adapt/t8_forest_adapt.cxx t8_forest/t8_forest_partition.cxx t8_forest/t8_forest.cxx t8_forest/t8_forest_private.cxx diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx index 9893750a12..1a299c5807 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx @@ -28,9 +28,8 @@ #include - void - t8_forest_adapt::basic_adaptation::adapt() + t8_forest_adapt_namespace::basic_adaptation::adapt() { T8_ASSERT (forest != nullptr); @@ -41,20 +40,23 @@ collect_adapt_actions(); + /* Offset per tree in the source forest */ t8_locidx_t el_offset = 0; - const t8_locidx_t num_trees = t8_forest_get_num_trees (forest_from); + const t8_locidx_t num_trees = t8_forest_get_num_local_trees (forest_from); + /* Get the scheme used by the forest */ + const t8_scheme *scheme = t8_forest_get_scheme (forest_from); for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { /* get the trees from both forests. */ t8_tree_t tree = t8_forest_get_tree (forest, ltree_id); const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); /* get the leaf arrays from both forests */ - t8_element_array_t elements = &tree->leaf_elements; - const t8_element_array_t tree_elements_from = &tree_from->leaf_elements; + t8_element_array_t *elements = &tree->leaf_elements; + const t8_element_array_t *tree_elements_from = &tree_from->leaf_elements; /* Get the number of elements in the source tree */ const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (tree_elements_from); T8_ASSERT (num_el_from == t8_forest_get_tree_num_leaf_elements (forest_from, ltree_id)); - const t8_eclass_t tree_class = tree_from->tree_class; + const t8_eclass_t tree_class = tree_from->eclass; /* Continue only if tree_from is not empty */ if (num_el_from < 0){ const t8_element_t *first_element_from = t8_element_array_index_locidx (tree_elements_from, 0); @@ -63,7 +65,7 @@ t8_locidx_t el_considered = 0; /* index of the elements in target tree */ t8_locidx_t el_inserted = 0; - std::vector elements_temp; + std::vector elements_temp; while (el_considered < num_el_from) { const t8_locidx_t num_siblings = scheme->element_get_num_siblings (tree_class, t8_element_array_index_locidx (tree_elements_from, el_considered)); @@ -72,7 +74,7 @@ curr_size_elements_from = num_siblings; } for (int isibling = 0; isibling < num_siblings && el_considered + isibling < num_el_from; isibling++) { - elements_temp[isibling] = (t8_element_t *) t8_element_array_index_locidx_mutable (tree_elements_from, el_considered + (t8_locidx_t )isibling); + elements_temp[isibling] = (const t8_element_t *) t8_element_array_index_locidx (tree_elements_from, el_considered + (t8_locidx_t )isibling); if (scheme->element_get_child_id (tree_class, elements_temp[isibling]) != isibling) { break; } @@ -92,7 +94,26 @@ } } - el_inserted += manipulate_elements (elements, tree_elements_from,el_considered ); + switch (action) { + case COARSEN: + el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, + el_inserted, el_offset + el_considered); + break; + case KEEP: + el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, + el_inserted, el_offset + el_considered); + break; + case REFINE: + el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, + el_inserted, el_offset + el_considered); + break; + default: + { + t8_errorf ("Unknown adapt action.\n"); + SC_ABORT_NOT_REACHED (); + break; + } + } el_considered++; } } diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index 28e33039fe..fc4cf5cd36 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -30,10 +30,17 @@ #include #include +#include +#include + +#include + /** * Namespace for adaptation related classes and functions. */ -namespace t8_forest_adapt{ + + /* TODO rename to t8_forest_adapt as soon as it is not used as function name anymore. */ +namespace t8_forest_adapt_namespace { /** The action to be taken on an element during adaptation. * COARSEN: The element should be coarsened. * KEEP: The element should remain as is. @@ -62,56 +69,61 @@ namespace t8_forest_adapt{ * \return The number of elements created in the target array. */ template - int manipulate_elements (t8_element_array_t *elements, - const t8_element_array_t *elements_from, + t8_locidx_t manipulate_elements (t8_element_array_t *elements, + const t8_element_array_t *const elements_from, const t8_scheme *scheme, - const t8_eclass_t tree_class + const t8_eclass_t tree_class, const t8_locidx_t elements_index, const t8_locidx_t elements_from_index); template <> - int manipulate_elements (t8_element_array_t *elements, - const t8_element_array_t *elements_from, + t8_locidx_t manipulate_elements (t8_element_array_t *elements, + const t8_element_array_t *const elements_from, const t8_scheme *scheme, - const t8_eclass_t tree_class + const t8_eclass_t tree_class, const t8_locidx_t elements_index, const t8_locidx_t elements_from_index) { t8_element_t *element = t8_element_array_push (elements); - scheme->element_copy (tree_class, elements_from[elements_from_index], element); + const t8_element_t * element_from = t8_element_array_index_locidx (elements, elements_index); + scheme->element_copy (tree_class, element_from, element); return 1; }; template <> - int manipulate_elements (t8_element_array_t *elements, - const t8_element_array_t *elements_from, + t8_locidx_t manipulate_elements (t8_element_array_t *elements, + const t8_element_array_t *const elements_from, const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t elements_index, const t8_locidx_t elements_from_index) { t8_element_t *element = t8_element_array_push (elements); - T8_ASSERT (scheme->element_get_level (tree_class, elements_from[elements_from_index]) > 0); - scheme->element_get_parent (tree_class, elements_from[elements_from_index], element); + const t8_element_t * element_from = t8_element_array_index_locidx (elements, elements_index); + T8_ASSERT (scheme->element_get_level (tree_class, element_from) > 0); + scheme->element_get_parent (tree_class, element_from, element); /* Hier eventuell noch was mit num_children = num_siblings*/ return 1; }; template <> - int manipulate_elements (t8_element_array_t *elements, - const t8_element_array_t *elements_from, + t8_locidx_t manipulate_elements (t8_element_array_t *elements, + const t8_element_array_t *const elements_from, const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t elements_index, const t8_locidx_t elements_from_index) { - const int num_children = scheme->element_get_num_children (tree_class, elements_from[elements_from_index]); + const t8_element_t * element_from = t8_element_array_index_locidx (elements_from, elements_from_index); + const int num_children = scheme->element_get_num_children (tree_class, element_from); /* CONTINUE WORK HERE */ (void) t8_element_array_push_count (elements, num_children); + std::vector children(num_children); for (int ichildren = 0; ichildren < num_children; ichildren++) { - elements[ichildren + elements_index] = t8_element_array_index_locidx_mutable (elements, *el_inserted + ichildren); + children[ichildren] = t8_element_array_index_locidx_mutable (elements, elements_index + ichildren); } + scheme->element_get_children (tree_class, element_from, num_children, children.data()); return num_children; }; @@ -141,10 +153,10 @@ class basic_adaptation { /** Destructor for basic_adaptation class. */ ~basic_adaptation () { if (forest_from != nullptr) { - t8_forest_unref (forest_from); + t8_forest_unref (&forest_from); } if (forest != nullptr) { - t8_forest_unref (forest); + t8_forest_unref (&forest); } } @@ -168,8 +180,8 @@ class basic_adaptation { inline void collect_adapt_actions(){ t8_locidx_t el_offset = 0; - const t8_locidx_t num_trees = t8_forest_get_num_trees (forest_from); - const t8_locidx_t local_num_elements = t8_forest_get_num_local_elements (forest_from); + const t8_locidx_t num_trees = t8_forest_get_num_local_trees (forest_from); + const t8_locidx_t local_num_elements = t8_forest_get_local_num_leaf_elements (forest_from); adapt_actions.resize (local_num_elements); const t8_scheme *scheme = t8_forest_get_scheme (forest_from); @@ -177,27 +189,31 @@ class basic_adaptation { /* For each element get the adaptation action */ for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); - const t8_eclass_t tree_class = tree_from->tree_class; - const t8_element_array_t elements_from = &tree_from->leaf_elements; + const t8_eclass_t tree_class = tree_from->eclass; + const t8_element_array_t *elements_from = &tree_from->leaf_elements; const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (elements_from); for (t8_locidx_t i = 0; i < num_el_from; i++) { - const t8_element_t *element = (t8_element_t *) t8_element_array_get_element (elements_from, i); - adapt_actions[el_offset + i] = element_callback (forest_from, ltree_id, element, scheme); + const t8_element_t * element = t8_element_array_index_locidx (elements_from, i); + adapt_actions[el_offset + i] = callback (forest_from, ltree_id, element, scheme, tree_class); } el_offset += num_el_from; } }; inline bool - family_check(const t8_element_array_t *tree_elements_from, std::vector &elements_from, const t8_locidx_t offset, const t8_scheme *scheme, const t8_eclass_t tree_class){ - const int num_siblings = scheme->element_get_num_siblings (tree_class, t8_element_array_index_locidx (telements_from, offset)); + family_check(const t8_element_array_t *tree_elements_from, std::vector &elements_from, const t8_locidx_t offset, const t8_scheme *scheme, const t8_eclass_t tree_class){ + const int num_siblings = scheme->element_get_num_siblings (tree_class, elements_from[offset]); for (int isibling = 0; isibling < num_siblings; isibling++) { - elements_from[isibling] = (t8_element_t *) t8_element_array_index_locidx_mutable (telements_from, offset + (t8_locidx_t )isibling); + elements_from[isibling] = (const t8_element_t *) t8_element_array_index_locidx (tree_elements_from, offset + (t8_locidx_t )isibling); if (scheme->element_get_child_id (tree_class, elements_from[isibling]) != isibling) { return false; } } - const bool is_family = scheme->elements_are_family (tree_class, elements_from); + /* elements_are_family expects t8_element_t *const *; build a non-const pointer array */ + std::vector children_nonconst(num_siblings); + for (int i = 0; i < num_siblings; ++i) + children_nonconst[i] = const_cast(elements_from[i]); + const bool is_family = scheme->elements_are_family (tree_class, children_nonconst.data()); return is_family; } @@ -205,7 +221,7 @@ class basic_adaptation { t8_forest_t forest_from; /**< The source forest to adapt from. */ std::vector adapt_actions; /**< The adaptation actions for each element in the source forest. */ bool profiling = false; /**< Flag to indicate if profiling is enabled. */ -} +}; }; diff --git a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx index a63ab50dfd..8f2af4711b 100644 --- a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx +++ b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx @@ -639,6 +639,7 @@ t8_default_scheme_hex::element_is_valid (const t8_element_t *element) const && T8_QUAD_GET_TDIM ((const p8est_quadrant_t *) element) == 3; } +#endif void t8_default_scheme_hex::element_to_string (const t8_element_t *elem, char *debug_string, const int string_size) const { @@ -647,7 +648,6 @@ t8_default_scheme_hex::element_to_string (const t8_element_t *elem, char *debug_ p8est_quadrant_t *hex = (p8est_quadrant_t *) elem; snprintf (debug_string, string_size, "x: %i, y: %i, z: %i, level: %i", hex->x, hex->y, hex->z, hex->level); } -#endif void t8_default_scheme_hex::set_to_root (t8_element_t *elem) const diff --git a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx index 11715252ae..f86b5d2d7e 100644 --- a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx +++ b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx @@ -574,6 +574,7 @@ class t8_default_scheme_hex: public t8_default_scheme_commonelement_debug_print (tree_class, elem); } +#endif void t8_element_to_string (const t8_scheme_c *scheme, const t8_eclass_t tree_class, const t8_element_t *elem, char *debug_string, const int string_size) @@ -378,7 +379,6 @@ t8_element_to_string (const t8_scheme_c *scheme, const t8_eclass_t tree_class, c return scheme->element_to_string (tree_class, elem, debug_string, string_size); } -#endif void t8_element_new (const t8_scheme_c *scheme, const t8_eclass_t tree_class, const int length, t8_element_t **elems) diff --git a/src/t8_schemes/t8_scheme.h b/src/t8_schemes/t8_scheme.h index 324d6e368f..fc6da52564 100644 --- a/src/t8_schemes/t8_scheme.h +++ b/src/t8_schemes/t8_scheme.h @@ -721,6 +721,7 @@ t8_element_is_valid (const t8_scheme_c *scheme, const t8_eclass_t tree_class, co void t8_element_debug_print (const t8_scheme_c *scheme, const t8_eclass_t tree_class, const t8_element_t *element); +#endif /** * \brief Fill a string with readable information about the element * @@ -733,7 +734,6 @@ t8_element_debug_print (const t8_scheme_c *scheme, const t8_eclass_t tree_class, void t8_element_to_string (const t8_scheme_c *scheme, const t8_eclass_t tree_class, const t8_element_t *element, char *debug_string, const int string_size); -#endif /** Allocate memory for an array of elements of a given class and initialize them. * \param [in] scheme The scheme of the forest. diff --git a/src/t8_schemes/t8_scheme.hxx b/src/t8_schemes/t8_scheme.hxx index 1bbd23087b..1854fe07eb 100644 --- a/src/t8_schemes/t8_scheme.hxx +++ b/src/t8_schemes/t8_scheme.hxx @@ -991,6 +991,7 @@ class t8_scheme { eclass_schemes[tree_class]); }; + #endif /** * Fill a string with readable information about the element * \param [in] tree_class The eclass of the current tree. @@ -1005,7 +1006,6 @@ class t8_scheme { return std::visit ([&] (auto &&scheme) { return scheme.element_to_string (element, debug_string, string_size); }, eclass_schemes[tree_class]); }; -#endif /** Allocate memory for \a length many elements of a given class and initialize them, * and put pointers to the elements in the provided array. From 99c16c2c7aa59c962c9e309494b3445b19faf52e Mon Sep 17 00:00:00 2001 From: David Knapp Date: Mon, 15 Dec 2025 14:03:57 +0100 Subject: [PATCH 04/24] fix macro changes --- src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx | 2 +- src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx | 2 +- src/t8_schemes/t8_scheme.cxx | 2 +- src/t8_schemes/t8_scheme.h | 2 +- src/t8_schemes/t8_scheme.hxx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx index 8f2af4711b..eea846f1ef 100644 --- a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx +++ b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx @@ -638,8 +638,8 @@ t8_default_scheme_hex::element_is_valid (const t8_element_t *element) const return p8est_quadrant_is_extended ((const p8est_quadrant_t *) element) && T8_QUAD_GET_TDIM ((const p8est_quadrant_t *) element) == 3; } - #endif + void t8_default_scheme_hex::element_to_string (const t8_element_t *elem, char *debug_string, const int string_size) const { diff --git a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx index f86b5d2d7e..11715252ae 100644 --- a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx +++ b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx @@ -574,7 +574,6 @@ class t8_default_scheme_hex: public t8_default_scheme_commonelement_debug_print (tree_class, elem); } -#endif void t8_element_to_string (const t8_scheme_c *scheme, const t8_eclass_t tree_class, const t8_element_t *elem, char *debug_string, const int string_size) @@ -383,6 +382,7 @@ t8_element_to_string (const t8_scheme_c *scheme, const t8_eclass_t tree_class, c return scheme->element_to_string (tree_class, elem, debug_string, string_size); } +#endif void t8_element_new (const t8_scheme_c *scheme, const t8_eclass_t tree_class, const int length, t8_element_t **elems) diff --git a/src/t8_schemes/t8_scheme.h b/src/t8_schemes/t8_scheme.h index 3b36bc4f4a..130c3287eb 100644 --- a/src/t8_schemes/t8_scheme.h +++ b/src/t8_schemes/t8_scheme.h @@ -736,7 +736,6 @@ t8_element_is_valid (const t8_scheme_c *scheme, const t8_eclass_t tree_class, co void t8_element_debug_print (const t8_scheme_c *scheme, const t8_eclass_t tree_class, const t8_element_t *element); -#endif /** * \brief Fill a string with readable information about the element * @@ -749,6 +748,7 @@ t8_element_debug_print (const t8_scheme_c *scheme, const t8_eclass_t tree_class, void t8_element_to_string (const t8_scheme_c *scheme, const t8_eclass_t tree_class, const t8_element_t *element, char *debug_string, const int string_size); +#endif /** Allocate memory for an array of elements of a given class and initialize them. * \param [in] scheme The scheme of the forest. diff --git a/src/t8_schemes/t8_scheme.hxx b/src/t8_schemes/t8_scheme.hxx index 1854fe07eb..1bbd23087b 100644 --- a/src/t8_schemes/t8_scheme.hxx +++ b/src/t8_schemes/t8_scheme.hxx @@ -991,7 +991,6 @@ class t8_scheme { eclass_schemes[tree_class]); }; - #endif /** * Fill a string with readable information about the element * \param [in] tree_class The eclass of the current tree. @@ -1006,6 +1005,7 @@ class t8_scheme { return std::visit ([&] (auto &&scheme) { return scheme.element_to_string (element, debug_string, string_size); }, eclass_schemes[tree_class]); }; +#endif /** Allocate memory for \a length many elements of a given class and initialize them, * and put pointers to the elements in the provided array. From a7878222a6e9d97973e9c513657096e14b05dc72 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Mon, 15 Dec 2025 14:04:54 +0100 Subject: [PATCH 05/24] fix last macro change --- src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx index eea846f1ef..b9b2af379e 100644 --- a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx +++ b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx @@ -638,7 +638,7 @@ t8_default_scheme_hex::element_is_valid (const t8_element_t *element) const return p8est_quadrant_is_extended ((const p8est_quadrant_t *) element) && T8_QUAD_GET_TDIM ((const p8est_quadrant_t *) element) == 3; } -#endif + void t8_default_scheme_hex::element_to_string (const t8_element_t *elem, char *debug_string, const int string_size) const @@ -648,6 +648,7 @@ t8_default_scheme_hex::element_to_string (const t8_element_t *elem, char *debug_ p8est_quadrant_t *hex = (p8est_quadrant_t *) elem; snprintf (debug_string, string_size, "x: %i, y: %i, z: %i, level: %i", hex->x, hex->y, hex->z, hex->level); } +#endif void t8_default_scheme_hex::set_to_root (t8_element_t *elem) const From b96d5cd0e33f03b9ce1fa9dc7f4501ea62234296 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Mon, 15 Dec 2025 14:05:19 +0100 Subject: [PATCH 06/24] fix empty space --- src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx index b9b2af379e..a63ab50dfd 100644 --- a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx +++ b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx @@ -639,7 +639,6 @@ t8_default_scheme_hex::element_is_valid (const t8_element_t *element) const && T8_QUAD_GET_TDIM ((const p8est_quadrant_t *) element) == 3; } - void t8_default_scheme_hex::element_to_string (const t8_element_t *elem, char *debug_string, const int string_size) const { From a299e52150aa429fdcd91f9d142908a8c7d9a6a0 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 11:57:03 +0100 Subject: [PATCH 07/24] change to concepts --- .../t8_forest_adapt/t8_forest_adapt.cxx | 159 ++++---- .../t8_forest_adapt/t8_forest_adapt.hxx | 342 ++++++++++-------- 2 files changed, 254 insertions(+), 247 deletions(-) diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx index 1a299c5807..d2a50c5e81 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx @@ -27,100 +27,71 @@ #include - - void - t8_forest_adapt_namespace::basic_adaptation::adapt() - { - T8_ASSERT (forest != nullptr); - - if (profiling) { - profile_adaptation(); - } - T8_ASSERT (forest_from != nullptr); - - collect_adapt_actions(); - - /* Offset per tree in the source forest */ - t8_locidx_t el_offset = 0; - const t8_locidx_t num_trees = t8_forest_get_num_local_trees (forest_from); - /* Get the scheme used by the forest */ - const t8_scheme *scheme = t8_forest_get_scheme (forest_from); - - for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { - /* get the trees from both forests. */ - t8_tree_t tree = t8_forest_get_tree (forest, ltree_id); - const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); - /* get the leaf arrays from both forests */ - t8_element_array_t *elements = &tree->leaf_elements; - const t8_element_array_t *tree_elements_from = &tree_from->leaf_elements; - /* Get the number of elements in the source tree */ - const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (tree_elements_from); - T8_ASSERT (num_el_from == t8_forest_get_tree_num_leaf_elements (forest_from, ltree_id)); - const t8_eclass_t tree_class = tree_from->eclass; - /* Continue only if tree_from is not empty */ - if (num_el_from < 0){ - const t8_element_t *first_element_from = t8_element_array_index_locidx (tree_elements_from, 0); - t8_locidx_t curr_size_elements_from = scheme->element_get_num_siblings (tree_class, first_element_from); - /* index of the elements in source tree */ - t8_locidx_t el_considered = 0; - /* index of the elements in target tree */ - t8_locidx_t el_inserted = 0; - std::vector elements_temp; - - while (el_considered < num_el_from) { - const t8_locidx_t num_siblings = scheme->element_get_num_siblings (tree_class, t8_element_array_index_locidx (tree_elements_from, el_considered)); - if (num_siblings > curr_size_elements_from) { - elements_temp.resize (num_siblings); - curr_size_elements_from = num_siblings; - } - for (int isibling = 0; isibling < num_siblings && el_considered + isibling < num_el_from; isibling++) { - elements_temp[isibling] = (const t8_element_t *) t8_element_array_index_locidx (tree_elements_from, el_considered + (t8_locidx_t )isibling); - if (scheme->element_get_child_id (tree_class, elements_temp[isibling]) != isibling) { - break; - } - } - const bool is_family = family_check (tree_elements_from, elements_temp, el_considered, scheme, tree_class); - adapt_action action = adapt_actions[el_offset + el_considered]; - - if (!is_family && action == COARSEN) { - action = KEEP; - } - /* Check that all siblings want to be coarsened */ - if (is_family && action == COARSEN) { - const auto start = adapt_actions.begin() + static_cast(el_offset + el_considered); - const auto end = start + static_cast(num_siblings); - if (!std::all_of(start, end, [](const adapt_action &a){ return a == COARSEN; })) { - action = KEEP; - } - } - - switch (action) { - case COARSEN: - el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, - el_inserted, el_offset + el_considered); - break; - case KEEP: - el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, - el_inserted, el_offset + el_considered); - break; - case REFINE: - el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, - el_inserted, el_offset + el_considered); - break; - default: - { - t8_errorf ("Unknown adapt action.\n"); - SC_ABORT_NOT_REACHED (); - break; - } - } - el_considered++; - } +void +t8_forest_adapt_namespace::adaptor::adapt () +{ + T8_ASSERT (forest != nullptr); + + if (profiling) { + profile_adaptation (); + } + T8_ASSERT (forest_from != nullptr); + + TCollect::collect_adapt_actions (forest_from, adapt_actions, callback); + + /* Offset per tree in the source forest */ + t8_locidx_t el_offset = 0; + const t8_locidx_t num_trees = t8_forest_get_num_local_trees (forest_from); + /* Get the scheme used by the forest */ + const t8_scheme *scheme = t8_forest_get_scheme (forest_from); + + for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { + /* get the trees from both forests. */ + t8_tree_t tree = t8_forest_get_tree (forest, ltree_id); + const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); + /* get the leaf arrays from both forests */ + t8_element_array_t *elements = &tree->leaf_elements; + const t8_element_array_t *tree_elements_from = &tree_from->leaf_elements; + /* Get the number of elements in the source tree */ + const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (tree_elements_from); + T8_ASSERT (num_el_from == t8_forest_get_tree_num_leaf_elements (forest_from, ltree_id)); + const t8_eclass_t tree_class = tree_from->eclass; + /* Continue only if tree_from is not empty */ + if (num_el_from < 0) { + const t8_element_t *first_element_from = t8_element_array_index_locidx (tree_elements_from, 0); + t8_locidx_t curr_size_elements_from = scheme->element_get_num_siblings (tree_class, first_element_from); + /* index of the elements in source tree */ + t8_locidx_t el_considered = 0; + /* index of the elements in target tree */ + t8_locidx_t el_inserted = 0; + std::vector elements_temp; + + while (el_considered < num_el_from) { + const t8_locidx_t num_siblings = scheme->element_get_num_siblings ( + tree_class, t8_element_array_index_locidx (tree_elements_from, el_considered)); + if (num_siblings > curr_size_elements_from) { + elements_temp.resize (num_siblings); + curr_size_elements_from = num_siblings; + } + for (int isibling = 0; isibling < num_siblings && el_considered + isibling < num_el_from; isibling++) { + elements_temp[isibling] = (const t8_element_t *) t8_element_array_index_locidx ( + tree_elements_from, el_considered + (t8_locidx_t) isibling); + if (scheme->element_get_child_id (tree_class, elements_temp[isibling]) != isibling) { + break; + } } - tree->elements_offset = el_offset; - el_offset += num_el_from; - - } + const bool is_family + = TFamily::family_check (tree_elements_from, elements_temp, el_considered, scheme, tree_class); + const adapt_action action = adapt_actions[el_offset + el_considered]; - } \ No newline at end of file + /* manipulator step*/ + TManipulate::element_manipulator (elements, tree_elements_from, scheme, tree_class, el_considered, el_inserted, + action, is_family); + el_considered++; + } + } + tree->elements_offset = el_offset; + el_offset += num_el_from; + } +} diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index fc4cf5cd36..9b999d87cf 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -33,196 +33,232 @@ #include #include +#include #include +#include /** * Namespace for adaptation related classes and functions. */ - /* TODO rename to t8_forest_adapt as soon as it is not used as function name anymore. */ -namespace t8_forest_adapt_namespace { - /** The action to be taken on an element during adaptation. +/* TODO rename to t8_forest_adapt as soon as it is not used as function name anymore. */ +namespace t8_forest_adapt_namespace +{ +/** The action to be taken on an element during adaptation. * COARSEN: The element should be coarsened. * KEEP: The element should remain as is. * REFINE: The element should be refined. + * Can be extended (e.g., for special refinement types). */ - enum adapt_action { - COARSEN = -1, - KEEP = 0, - REFINE = 1 - }; +class adapt_action { + public: + static const int COARSEN = -1; + static const int KEEP = 0; + static const int REFINE = 1; +}; - /** +/** * Callback function type for element adaptation. */ - using element_callback = std::function; - - /** * Function to manipulate elements based on the specified adaptation action. - * \tparam action The adaptation action to be performed. - * \param [in, out] elements The element array to be modified. - * \param [in] elements_from The source element array. - * \param [in] scheme The element scheme. - * \param [in] tree_class The eclass of the tree used by the scheme - * \param [in] elements_index The index in the target element array. - * \param [in] elements_from_index The index in the source element array. - * \return The number of elements created in the target array. - */ - template - t8_locidx_t manipulate_elements (t8_element_array_t *elements, - const t8_element_array_t *const elements_from, - const t8_scheme *scheme, - const t8_eclass_t tree_class, - const t8_locidx_t elements_index, - const t8_locidx_t elements_from_index); - - template <> - t8_locidx_t manipulate_elements (t8_element_array_t *elements, - const t8_element_array_t *const elements_from, - const t8_scheme *scheme, - const t8_eclass_t tree_class, - const t8_locidx_t elements_index, - const t8_locidx_t elements_from_index) - { - t8_element_t *element = t8_element_array_push (elements); - const t8_element_t * element_from = t8_element_array_index_locidx (elements, elements_index); - scheme->element_copy (tree_class, element_from, element); - return 1; - }; +using element_callback + = std::function; - template <> - t8_locidx_t manipulate_elements (t8_element_array_t *elements, - const t8_element_array_t *const elements_from, - const t8_scheme *scheme, - const t8_eclass_t tree_class, - const t8_locidx_t elements_index, - const t8_locidx_t elements_from_index) - { - t8_element_t *element = t8_element_array_push (elements); - const t8_element_t * element_from = t8_element_array_index_locidx (elements, elements_index); - T8_ASSERT (scheme->element_get_level (tree_class, element_from) > 0); - scheme->element_get_parent (tree_class, element_from, element); +using batched_element_callback + = std::function &action)>; - /* Hier eventuell noch was mit num_children = num_siblings*/ - return 1; - }; +template +concept has_element_callback_collect + = requires (T a, const t8_forest_t forest_from, std::vector &adapt_actions, element_callback cb) { + { + a.collect_adapt_actions (forest_from, adapt_actions, cb) + } -> std::same_as; + }; - template <> - t8_locidx_t manipulate_elements (t8_element_array_t *elements, - const t8_element_array_t *const elements_from, - const t8_scheme *scheme, - const t8_eclass_t tree_class, - const t8_locidx_t elements_index, - const t8_locidx_t elements_from_index) +template +concept has_batched_callback_collect = requires ( + T a, const t8_forest_t forest_from, std::vector &adapt_actions, batched_element_callback cb) { { - const t8_element_t * element_from = t8_element_array_index_locidx (elements_from, elements_from_index); - const int num_children = scheme->element_get_num_children (tree_class, element_from); - /* CONTINUE WORK HERE */ - (void) t8_element_array_push_count (elements, num_children); - std::vector children(num_children); - for (int ichildren = 0; ichildren < num_children; ichildren++) { - children[ichildren] = t8_element_array_index_locidx_mutable (elements, elements_index + ichildren); - } - scheme->element_get_children (tree_class, element_from, num_children, children.data()); - return num_children; - }; + a.collect_adapt_actions (forest_from, adapt_actions, cb) + } -> std::same_as; +}; +template +concept adapt_actions_collectable = has_element_callback_collect || has_batched_callback_collect; - /** * Class implementing a basic adaptation strategy for a forest of trees. - */ -class basic_adaptation { - public: - /** Constructor for basic_adaptation class. - * \param [in] forest_in The forest to be adapted. - * \param [in] callback_in The callback function to determine adaptation actions. - */ - basic_adaptation (t8_forest_t forest, t8_forest_t forest_from, element_callback callback_in) - : forest (forest), forest_from (forest_from), callback (callback_in) - { - T8_ASSERT (forest != nullptr); - T8_ASSERT (callback); - if (forest_from != nullptr) { - t8_forest_ref (forest_from); - } - T8_ASSERT (forest != nullptr); - if (forest != nullptr) { - t8_forest_ref (forest); - } - } - - /** Destructor for basic_adaptation class. */ - ~basic_adaptation () { - if (forest_from != nullptr) { - t8_forest_unref (&forest_from); - } - if (forest != nullptr) { - t8_forest_unref (&forest); - } - } +template +concept family_checkable + = requires (T a, const t8_element_array_t *tree_elements_from, std::vector &elements_from, + const t8_locidx_t offset, const t8_scheme *scheme, const t8_eclass_t tree_class) { + { + a.family_check (tree_elements_from, elements_from, offset, scheme, tree_class) + } -> std::convertible_to; + }; - /** Perform the adaptation process on the forest. */ - void adapt(); +template +concept element_manipulatable = requires ( + T a, t8_element_array_t *elements, const t8_element_array_t *elements_from, const t8_scheme *scheme, + const t8_eclass_t tree_class, const t8_locidx_t &el_considered, t8_locidx_t &el_inserted, const adapt_action action) { + { + a.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_inserted, action) + } -> std::same_as; +}; - element_callback callback; /**< The callback function to determine adaptation actions. */ - private: - /** - * Profile the adaptation process. - */ - inline void - profile_adaptation(){ - T8_ASSERT(forest->profile != nullptr); - forest->profile->adapt_runtime = -sc_MPI_Wtime(); - } +struct adapt_collector +{ + void + collect_adapt_actions (const t8_forest_t forest_from, std::vector &adapt_actions, + element_callback callback) + { + T8_ASSERT (forest_from != nullptr); - /** - * Collect adaptation actions for all elements in the source forest. - */ - inline void - collect_adapt_actions(){ - t8_locidx_t el_offset = 0; - const t8_locidx_t num_trees = t8_forest_get_num_local_trees (forest_from); - const t8_locidx_t local_num_elements = t8_forest_get_local_num_leaf_elements (forest_from); - adapt_actions.resize (local_num_elements); + t8_locidx_t el_offset = 0; + const t8_locidx_t num_trees = t8_forest_get_num_local_trees (forest_from); + const t8_locidx_t local_num_elements = t8_forest_get_local_num_leaf_elements (forest_from); + adapt_actions.resize (static_cast (local_num_elements)); + for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { + const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); + const t8_element_array_t *tree_elements_from = &tree_from->leaf_elements; + const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (tree_elements_from); + T8_ASSERT (num_el_from == t8_forest_get_tree_num_leaf_elements (forest_from, ltree_id)); + const t8_eclass_t tree_class = tree_from->eclass; const t8_scheme *scheme = t8_forest_get_scheme (forest_from); - /* For each element get the adaptation action */ - for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { - const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); - const t8_eclass_t tree_class = tree_from->eclass; - const t8_element_array_t *elements_from = &tree_from->leaf_elements; - const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (elements_from); - for (t8_locidx_t i = 0; i < num_el_from; i++) { - const t8_element_t * element = t8_element_array_index_locidx (elements_from, i); - adapt_actions[el_offset + i] = callback (forest_from, ltree_id, element, scheme, tree_class); - } - el_offset += num_el_from; + for (t8_locidx_t el_considered = 0; el_considered < num_el_from; el_considered++) { + const t8_element_t *element_from = t8_element_array_index_locidx (tree_elements_from, el_considered); + adapt_actions[el_offset + el_considered] = callback (forest_from, ltree_id, element_from, scheme, tree_class); } - }; + el_offset += num_el_from; + } + }; - inline bool - family_check(const t8_element_array_t *tree_elements_from, std::vector &elements_from, const t8_locidx_t offset, const t8_scheme *scheme, const t8_eclass_t tree_class){ + /** Standard family checker implementation. s*/ + struct family_checker + { + bool + family_check (const t8_element_array_t *tree_elements_from, std::vector &elements_from, + const t8_locidx_t offset, const t8_scheme *scheme, const t8_eclass_t tree_class) + { const int num_siblings = scheme->element_get_num_siblings (tree_class, elements_from[offset]); for (int isibling = 0; isibling < num_siblings; isibling++) { - elements_from[isibling] = (const t8_element_t *) t8_element_array_index_locidx (tree_elements_from, offset + (t8_locidx_t )isibling); + elements_from[isibling] + = (const t8_element_t *) t8_element_array_index_locidx (tree_elements_from, offset + (t8_locidx_t) isibling); if (scheme->element_get_child_id (tree_class, elements_from[isibling]) != isibling) { return false; } } /* elements_are_family expects t8_element_t *const *; build a non-const pointer array */ - std::vector children_nonconst(num_siblings); + std::vector children_nonconst (num_siblings); for (int i = 0; i < num_siblings; ++i) - children_nonconst[i] = const_cast(elements_from[i]); - const bool is_family = scheme->elements_are_family (tree_class, children_nonconst.data()); + children_nonconst[i] = const_cast (elements_from[i]); + const bool is_family = scheme->elements_are_family (tree_class, children_nonconst.data ()); return is_family; } + }; - t8_forest_t forest; /**< The target forest */ - t8_forest_t forest_from; /**< The source forest to adapt from. */ - std::vector adapt_actions; /**< The adaptation actions for each element in the source forest. */ - bool profiling = false; /**< Flag to indicate if profiling is enabled. */ -}; + struct manipulator + { + void + element_manipulator (t8_element_array_t *elements, const t8_element_array_t *const elements_from, + const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t &el_considered, + t8_locidx_t &el_inserted, const adapt_action action) + { + if (!is_family && action == adapt_action::COARSEN) { + action = adapt_action::KEEP; + } + /* Check that all siblings want to be coarsened */ + if (is_family && action == adapt_action::COARSEN) { + const auto start = adapt_actions.begin () + static_cast (el_offset + el_considered); + const auto end = start + static_cast (num_siblings); + if (!std::all_of (start, end, [] (const adapt_action &a) { return a == adapt_action::COARSEN; })) { + action = adapt_action::KEEP; + } + } -}; + switch (action) { + case adapt_action::COARSEN: + el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, + el_inserted, el_offset + el_considered); + break; + case adapt_action::KEEP: + el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, + el_inserted, el_offset + el_considered); + break; + case adapt_action::REFINE: + el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, + el_inserted, el_offset + el_considered); + break; + default: { + t8_errorf ("Unknown adapt action.\n"); + SC_ABORT_NOT_REACHED (); + break; + } + } + } + + /** * Class implementing a basic adaptation strategy for a forest of trees. + */ + template + class adaptor: private TCollect, private TFamily, private TManipulate { + public: + /** Constructor for basic_adaptation class. + * \param [in] forest_in The forest to be adapted. + * \param [in] callback_in The callback function to determine adaptation actions. + */ + adaptor (t8_forest_t forest, t8_forest_t forest_from, element_callback callback_in) + : forest (forest), forest_from (forest_from), callback (callback_in) + { + T8_ASSERT (forest != nullptr); + T8_ASSERT (callback); + if (forest_from != nullptr) { + t8_forest_ref (forest_from); + } + T8_ASSERT (forest != nullptr); + if (forest != nullptr) { + t8_forest_ref (forest); + } + } + + /** Destructor for adaptor class. */ + ~adaptor () + { + if (forest_from != nullptr) { + t8_forest_unref (&forest_from); + } + if (forest != nullptr) { + t8_forest_unref (&forest); + } + } + + /** Perform the adaptation process on the forest. */ + void + adapt (); + + /** Type alias for the callback function used in adaptation. + * The type depends on whether TCollect uses element_callback or batched_element_callback. + */ + using callback_type + = std::conditional_t, element_callback, batched_element_callback>; + + callback_type callback; /**< The callback function to determine adaptation actions. */ + private: + /** + * Profile the adaptation process. + */ + inline void + profile_adaptation () + { + T8_ASSERT (forest->profile != nullptr); + forest->profile->adapt_runtime = -sc_MPI_Wtime (); + } + + t8_forest_t forest; /**< The target forest */ + t8_forest_t forest_from; /**< The source forest to adapt from. */ + std::vector adapt_actions; /**< The adaptation actions for each element in the source forest. */ + bool profiling = false; /**< Flag to indicate if profiling is enabled. */ + }; + }; -#endif /* T8_FOREST_ADAPT_HXX */ \ No newline at end of file +#endif /* T8_FOREST_ADAPT_HXX */ From ab8aa1f855a9a77ea8a9bf18f3ff94713b44d8db Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 12:05:55 +0100 Subject: [PATCH 08/24] move adapt implementation --- .../t8_forest_adapt/t8_forest_adapt.cxx | 76 ------------------- .../t8_forest_adapt/t8_forest_adapt.hxx | 69 ++++++++++++++++- 2 files changed, 67 insertions(+), 78 deletions(-) diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx index d2a50c5e81..30bbd27e7f 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx @@ -19,79 +19,3 @@ along with t8code; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -/** - * \file t8_forest_adapt/t8_forest_adapt.cxx - * Implementation of the adaptation routine to refine and coarsen a forest of trees. - */ - -#include - -void -t8_forest_adapt_namespace::adaptor::adapt () -{ - T8_ASSERT (forest != nullptr); - - if (profiling) { - profile_adaptation (); - } - T8_ASSERT (forest_from != nullptr); - - TCollect::collect_adapt_actions (forest_from, adapt_actions, callback); - - /* Offset per tree in the source forest */ - t8_locidx_t el_offset = 0; - const t8_locidx_t num_trees = t8_forest_get_num_local_trees (forest_from); - /* Get the scheme used by the forest */ - const t8_scheme *scheme = t8_forest_get_scheme (forest_from); - - for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { - /* get the trees from both forests. */ - t8_tree_t tree = t8_forest_get_tree (forest, ltree_id); - const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); - /* get the leaf arrays from both forests */ - t8_element_array_t *elements = &tree->leaf_elements; - const t8_element_array_t *tree_elements_from = &tree_from->leaf_elements; - /* Get the number of elements in the source tree */ - const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (tree_elements_from); - T8_ASSERT (num_el_from == t8_forest_get_tree_num_leaf_elements (forest_from, ltree_id)); - const t8_eclass_t tree_class = tree_from->eclass; - /* Continue only if tree_from is not empty */ - if (num_el_from < 0) { - const t8_element_t *first_element_from = t8_element_array_index_locidx (tree_elements_from, 0); - t8_locidx_t curr_size_elements_from = scheme->element_get_num_siblings (tree_class, first_element_from); - /* index of the elements in source tree */ - t8_locidx_t el_considered = 0; - /* index of the elements in target tree */ - t8_locidx_t el_inserted = 0; - std::vector elements_temp; - - while (el_considered < num_el_from) { - const t8_locidx_t num_siblings = scheme->element_get_num_siblings ( - tree_class, t8_element_array_index_locidx (tree_elements_from, el_considered)); - if (num_siblings > curr_size_elements_from) { - elements_temp.resize (num_siblings); - curr_size_elements_from = num_siblings; - } - for (int isibling = 0; isibling < num_siblings && el_considered + isibling < num_el_from; isibling++) { - elements_temp[isibling] = (const t8_element_t *) t8_element_array_index_locidx ( - tree_elements_from, el_considered + (t8_locidx_t) isibling); - if (scheme->element_get_child_id (tree_class, elements_temp[isibling]) != isibling) { - break; - } - } - - const bool is_family - = TFamily::family_check (tree_elements_from, elements_temp, el_considered, scheme, tree_class); - const adapt_action action = adapt_actions[el_offset + el_considered]; - - /* manipulator step*/ - TManipulate::element_manipulator (elements, tree_elements_from, scheme, tree_class, el_considered, el_inserted, - action, is_family); - el_considered++; - } - } - tree->elements_offset = el_offset; - el_offset += num_el_from; - } -} diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index 9b999d87cf..52aca89822 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -196,7 +196,7 @@ struct adapt_collector break; } } - } + }; /** * Class implementing a basic adaptation strategy for a forest of trees. */ @@ -234,7 +234,72 @@ struct adapt_collector /** Perform the adaptation process on the forest. */ void - adapt (); + adapt () + { + T8_ASSERT (forest != nullptr); + if (profiling) { + profile_adaptation (); + } + T8_ASSERT (forest_from != nullptr); + + TCollect::collect_adapt_actions (forest_from, adapt_actions, callback); + + /* Offset per tree in the source forest */ + t8_locidx_t el_offset = 0; + const t8_locidx_t num_trees = t8_forest_get_num_local_trees (forest_from); + /* Get the scheme used by the forest */ + const t8_scheme *scheme = t8_forest_get_scheme (forest_from); + + for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { + /* get the trees from both forests. */ + t8_tree_t tree = t8_forest_get_tree (forest, ltree_id); + const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); + /* get the leaf arrays from both forests */ + t8_element_array_t *elements = &tree->leaf_elements; + const t8_element_array_t *tree_elements_from = &tree_from->leaf_elements; + /* Get the number of elements in the source tree */ + const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (tree_elements_from); + T8_ASSERT (num_el_from == t8_forest_get_tree_num_leaf_elements (forest_from, ltree_id)); + const t8_eclass_t tree_class = tree_from->eclass; + /* Continue only if tree_from is not empty */ + if (num_el_from < 0) { + const t8_element_t *first_element_from = t8_element_array_index_locidx (tree_elements_from, 0); + t8_locidx_t curr_size_elements_from = scheme->element_get_num_siblings (tree_class, first_element_from); + /* index of the elements in source tree */ + t8_locidx_t el_considered = 0; + /* index of the elements in target tree */ + t8_locidx_t el_inserted = 0; + std::vector elements_temp; + + while (el_considered < num_el_from) { + const t8_locidx_t num_siblings = scheme->element_get_num_siblings ( + tree_class, t8_element_array_index_locidx (tree_elements_from, el_considered)); + if (num_siblings > curr_size_elements_from) { + elements_temp.resize (num_siblings); + curr_size_elements_from = num_siblings; + } + for (int isibling = 0; isibling < num_siblings && el_considered + isibling < num_el_from; isibling++) { + elements_temp[isibling] = (const t8_element_t *) t8_element_array_index_locidx ( + tree_elements_from, el_considered + (t8_locidx_t) isibling); + if (scheme->element_get_child_id (tree_class, elements_temp[isibling]) != isibling) { + break; + } + } + + const bool is_family + = TFamily::family_check (tree_elements_from, elements_temp, el_considered, scheme, tree_class); + const adapt_action action = adapt_actions[el_offset + el_considered]; + + /* manipulator step*/ + TManipulate::element_manipulator (elements, tree_elements_from, scheme, tree_class, el_considered, + el_inserted, action, is_family); + el_considered++; + } + } + tree->elements_offset = el_offset; + el_offset += num_el_from; + } + } /** Type alias for the callback function used in adaptation. * The type depends on whether TCollect uses element_callback or batched_element_callback. From c51c9a7e9a8b346f9a06c6b26439097d385ae3d6 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 12:06:45 +0100 Subject: [PATCH 09/24] remove empty file --- .../t8_forest_adapt/t8_forest_adapt.cxx | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx deleted file mode 100644 index 30bbd27e7f..0000000000 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.cxx +++ /dev/null @@ -1,21 +0,0 @@ -/* - This file is part of t8code. - t8code is a C library to manage a collection (a forest) of multiple - connected adaptive space-trees of general element 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. -*/ From 955dff6378b9a5c4c4558630a5e242f7d7322dc3 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 12:30:41 +0100 Subject: [PATCH 10/24] start a test for batched implementation --- .../t8_forest_adapt_batched.hxx | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx new file mode 100644 index 0000000000..593bc985c6 --- /dev/null +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx @@ -0,0 +1,134 @@ +/* + 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) 2026 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef T8_FOREST_ADAPT_BATCHED_HXX +#define T8_FOREST_ADAPT_BATCHED_HXX + +#include + +/** Function to manipulate elements based on the specified adaptation action. + * \tparam action The adaptation action to be performed. + * \param [in, out] elements The element array to be modified. + * \param [in] elements_from The source element array. + * \param [in] scheme The element scheme. + * \param [in] tree_class The eclass of the tree used by the scheme + * \param [in] elements_index The index in the target element array. + * \param [in] elements_from_index The index in the source element array. + * \return The number of elements created in the target array. + */ +template +t8_locidx_t +manipulate_elements (t8_element_array_t *elements, const t8_element_array_t *const elements_from, + const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t elements_index, + const t8_locidx_t elements_from_index); + +/** Specialization for the KEEP action. No element modification is performed; + * the element is copied as is. + * \see manipulate_elements + */ +template <> +t8_locidx_t +manipulate_elements ( + t8_element_array_t *elements, const t8_element_array_t *const elements_from, const t8_scheme *scheme, + const t8_eclass_t tree_class, const t8_locidx_t elements_index, const t8_locidx_t elements_from_index) +{ + t8_element_t *element = t8_element_array_push (elements); + const t8_element_t *element_from = t8_element_array_index_locidx (elements, elements_index); + scheme->element_copy (tree_class, element_from, element); + return 1; +}; + +/** Specialization for the COARSEN action. The parent element of the source elements is created + * in the target array. + * \see manipulate_elements + */ +template <> +t8_locidx_t +manipulate_elements ( + t8_element_array_t *elements, const t8_element_array_t *const elements_from, const t8_scheme *scheme, + const t8_eclass_t tree_class, const t8_locidx_t elements_index, const t8_locidx_t elements_from_index) +{ + t8_element_t *element = t8_element_array_push (elements); + const t8_element_t *element_from = t8_element_array_index_locidx (elements, elements_index); + T8_ASSERT (scheme->element_get_level (tree_class, element_from) > 0); + scheme->element_get_parent (tree_class, element_from, element); + + /* Hier eventuell noch was mit num_children = num_siblings*/ + return 1; +}; + +/** Specialization for the REFINE action. The children elements of the source element are created + * in the target array. + * \see manipulate_elements + */ +template <> +t8_locidx_t +manipulate_elements (t8_element_array_t *elements, const t8_element_array_t *const elements_from, + const t8_scheme *scheme, const t8_eclass_t tree_class, + const t8_locidx_t elements_index, const t8_locidx_t elements_from_index) +{ + const t8_element_t *element_from = t8_element_array_index_locidx (elements_from, elements_from_index); + const int num_children = scheme->element_get_num_children (tree_class, element_from); + /* CONTINUE WORK HERE */ + (void) t8_element_array_push_count (elements, num_children); + std::vector children (num_children); + for (int ichildren = 0; ichildren < num_children; ichildren++) { + children[ichildren] = t8_element_array_index_locidx_mutable (elements, elements_index + ichildren); + } + scheme->element_get_children (tree_class, element_from, num_children, children.data ()); + return num_children; +}; + +/** + * Collect adaptation actions for all elements in the source forest. + */ +struct batched_adapt_collector +{ + void + collect_adapt_actions (const t8_forest_t forest_from, std::vector &adapt_actions, + element_callback callback) + { + T8_ASSERT (forest_from != nullptr); + + t8_locidx_t el_offset = 0; + const t8_locidx_t num_trees = t8_forest_get_num_local_trees (forest_from); + const t8_locidx_t local_num_elements = t8_forest_get_local_num_leaf_elements (forest_from); + adapt_actions.resize (local_num_elements); + + const t8_scheme *scheme = t8_forest_get_scheme (forest_from); + + /* For each element get the adaptation action */ + for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { + const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); + const t8_eclass_t tree_class = tree_from->eclass; + const t8_element_array_t *elements_from = &tree_from->leaf_elements; + const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (elements_from); + for (t8_locidx_t i = 0; i < num_el_from; i++) { + const t8_element_t *element = t8_element_array_index_locidx (elements_from, i); + adapt_actions[el_offset + i] = callback (forest_from, ltree_id, element, scheme, tree_class); + } + el_offset += num_el_from; + } + } +}; + +#endif /* T8_FOREST_ADAPT_BATCHED_HXX */ From 3ba4356c44cd2afc7cb3306e2f3984b4fa847747 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 12:31:00 +0100 Subject: [PATCH 11/24] Remove adapt.cxx from CMake, the file does not exist anymore --- src/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7f8538ebe5..e02137cad4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -175,7 +175,6 @@ target_sources( T8 PRIVATE t8_data/t8_shmem.c t8_data/t8_containers.cxx t8_forest/t8_forest_adapt.cxx - t8_forest/t8_forest_adapt/t8_forest_adapt.cxx t8_forest/t8_forest_partition.cxx t8_forest/t8_forest_partition_for_coarsening.cxx t8_forest/t8_forest_pfc_helper.cxx From be5513a8c95a339f7376bff1cb3a3f93928dd810 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 12:59:33 +0100 Subject: [PATCH 12/24] Document the concepts and the class --- .../t8_forest_adapt/t8_forest_adapt.hxx | 547 ++++++++++++------ .../t8_forest_adapt_batched.hxx | 10 +- 2 files changed, 375 insertions(+), 182 deletions(-) diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index 52aca89822..f8563300d8 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -57,17 +57,46 @@ class adapt_action { static const int REFINE = 1; }; -/** - * Callback function type for element adaptation. - */ +/** Callback function type for element adaptation. + * \param [in] forest The forest containing the element. + * \param [in] ltreeid The local tree ID of the tree containing the element. + * \param [in] element The element to be adapted. + * \param [in] scheme The scheme used for the element. + * \param [in] tree_class The eclass of the tree containing the element. + * \return The adaptation action to be taken on the element. +*/ using element_callback = std::function; +/** Callback function type for batched element adaptation. + * \param [in] forest The forest containing the elements. + * \param [in] ltreeid The local tree ID of the tree containing the elements. + * \param [in] element The array of elements to be adapted. + * \param [in] scheme The scheme used for the elements. + * \param [in] tree_class The eclass of the tree containing the elements. + * \param [out] action The vector to store the adaptation actions for the elements. +*/ using batched_element_callback - = std::function &action)>; +/** + * Concept that detects whether a type T provides a member function + * with the signature compatible with: + * a.collect_adapt_actions(const t8_forest_t, std::vector&, element_callback) + * returning void. + * + * @tparam T + * Type under test. The concept is satisfied when an object `a` of type T can be + * used in an expression + * a.collect_adapt_actions(forest_from, adapt_actions, cb) + * where: + * - forest_from is of type const t8_forest_t, + * - adapt_actions is of type std::vector&, + * - cb is of type element_callback, + * and the expression is well-formed and yields void. + */ template concept has_element_callback_collect = requires (T a, const t8_forest_t forest_from, std::vector &adapt_actions, element_callback cb) { @@ -76,6 +105,20 @@ concept has_element_callback_collect } -> std::same_as; }; +/** Concept that detects whether a type T provides a member function + * with the signature compatible with: + * a.collect_adapt_actions(const t8_forest_t, std::vector&, batched_element_callback) + * returning void. + * @tparam T + * Type under test. The concept is satisfied when an object `a` of type T can be + * used in an expression + * a.collect_adapt_actions(forest_from, adapt_actions, cb) + * where: + * - forest_from is of type const t8_forest_t, + * - adapt_actions is of type std::vector&, + * - cb is of type batched_element_callback, + * and the expression is well-formed and yields void. + */ template concept has_batched_callback_collect = requires ( T a, const t8_forest_t forest_from, std::vector &adapt_actions, batched_element_callback cb) { @@ -84,9 +127,42 @@ concept has_batched_callback_collect = requires ( } -> std::same_as; }; +/** Concept that detects whether a type T provides a member function + * with the signature compatible with either: + * a.collect_adapt_actions(const t8_forest_t, std::vector&, element_callback) + * or + * a.collect_adapt_actions(const t8_forest_t, std::vector&, batched_element_callback) + * returning void. + * @tparam T + * Type under test. The concept is satisfied when an object `a` of type T can be + * used in an expression + * a.collect_adapt_actions(forest_from, adapt_actions, cb) + * where: + * - forest_from is of type const t8_forest_t, + * - adapt_actions is of type std::vector&, + * - cb is of type element_callback or batched_element_callback, + * and the expression is well-formed and yields void. + */ template concept adapt_actions_collectable = has_element_callback_collect || has_batched_callback_collect; +/** Concept that detects whether a type T provides a member function + * with the signature compatible with: + * a.family_check(const t8_element_array_t*, std::vector&, const t8_locidx_t, + * const t8_scheme*, const t8_eclass_t) + * returning bool. + * @tparam T + * Type under test. The concept is satisfied when an object `a` of type T can be + * used in an expression + * a.family_check(tree_elements_from, elements_from, offset, scheme, tree_class) + * where: + * - tree_elements_from is of type const t8_element_array_t*, + * - elements_from is of type std::vector&, + * - offset is of type const t8_locidx_t, + * - scheme is of type const t8_scheme*, + * - tree_class is of type const t8_eclass_t, + * and the expression is well-formed and yields a type convertible to bool. + */ template concept family_checkable = requires (T a, const t8_element_array_t *tree_elements_from, std::vector &elements_from, @@ -96,6 +172,26 @@ concept family_checkable } -> std::convertible_to; }; +/** Concept that detects whether a type T provides a member function + * with the signature compatible with: + * a.element_manipulator(t8_element_array_t*, const t8_element_array_t*, + * const t8_scheme*, const t8_eclass_t, const t8_locidx_t&, + * t8_locidx_t&, const adapt_action) + * returning void. + * @tparam T + * Type under test. The concept is satisfied when an object `a` of type T can be + * used in an expression + * a.element_manipulator(elements, elements_from, scheme, tree_class, el_considered, el_inserted, action) + * where: + * - elements is of type t8_element_array_t*, + * - elements_from is of type const t8_element_array_t*, + * - scheme is of type const t8_scheme*, + * - tree_class is of type const t8_eclass_t, + * - el_considered is of type const t8_locidx_t&, + * - el_inserted is of type t8_locidx_t&, + * - action is of type const adapt_action, + * and the expression is well-formed and yields void. + */ template concept element_manipulatable = requires ( T a, t8_element_array_t *elements, const t8_element_array_t *elements_from, const t8_scheme *scheme, @@ -105,8 +201,14 @@ concept element_manipulatable = requires ( } -> std::same_as; }; +/** Standard adapt action collector implementation. */ struct adapt_collector { + /** Collect adapt actions for all elements in the forest. + * \param [in] forest_from The forest containing the elements to be adapted. + * \param [out] adapt_actions The vector to store the adapt actions for all elements. + * \param [in] callback The callback function to determine the adapt action for each element. + */ void collect_adapt_actions (const t8_forest_t forest_from, std::vector &adapt_actions, element_callback callback) @@ -132,198 +234,293 @@ struct adapt_collector } el_offset += num_el_from; } - }; + } +}; - /** Standard family checker implementation. s*/ - struct family_checker +/** Standard family checker implementation. */ +struct family_checker +{ + /** + * Check if the elements in the given array are siblings0 in the tree and form a family. + * \param [in] tree_elements_from The array of elements in the tree. + * \param [out] elements_from The vector to store the elements from the tree. + * \param [in] offset The offset to start checking from. + * \param [in] scheme The scheme to use for checking. + * \param [in] tree_class The class of the tree. + * \return True if the elements are siblings and form a family, false otherwise. + */ + bool + family_check (const t8_element_array_t *tree_elements_from, std::vector &elements_from, + const t8_locidx_t offset, const t8_scheme *scheme, const t8_eclass_t tree_class) { - bool - family_check (const t8_element_array_t *tree_elements_from, std::vector &elements_from, - const t8_locidx_t offset, const t8_scheme *scheme, const t8_eclass_t tree_class) - { - const int num_siblings = scheme->element_get_num_siblings (tree_class, elements_from[offset]); - for (int isibling = 0; isibling < num_siblings; isibling++) { - elements_from[isibling] - = (const t8_element_t *) t8_element_array_index_locidx (tree_elements_from, offset + (t8_locidx_t) isibling); - if (scheme->element_get_child_id (tree_class, elements_from[isibling]) != isibling) { - return false; - } + const int num_siblings = scheme->element_get_num_siblings (tree_class, elements_from[offset]); + for (int isibling = 0; isibling < num_siblings; isibling++) { + elements_from[isibling] + = (const t8_element_t *) t8_element_array_index_locidx (tree_elements_from, offset + (t8_locidx_t) isibling); + if (scheme->element_get_child_id (tree_class, elements_from[isibling]) != isibling) { + return false; } - /* elements_are_family expects t8_element_t *const *; build a non-const pointer array */ - std::vector children_nonconst (num_siblings); - for (int i = 0; i < num_siblings; ++i) - children_nonconst[i] = const_cast (elements_from[i]); - const bool is_family = scheme->elements_are_family (tree_class, children_nonconst.data ()); - return is_family; } - }; + /* elements_are_family expects t8_element_t *const *; build a non-const pointer array */ + std::vector children_nonconst (num_siblings); + for (int i = 0; i < num_siblings; ++i) + children_nonconst[i] = const_cast (elements_from[i]); + const bool is_family = scheme->elements_are_family (tree_class, children_nonconst.data ()); + return is_family; + } +}; - struct manipulator +/** + * Class implementing a basic element manipulation strategy. + */ +struct manipulator +{ + /** Manipulate elements based on the given adapt action. + * \param [in,out] elements The array of elements to be manipulated. + * \param [in] tree_elements_from The array of elements from the source tree. + * \param [in] scheme The scheme to use for manipulation. + * \param [in] tree_class The class of the tree. + * \param [in] el_considered The index of the element being considered. + * \param [in,out] el_inserted The index of the next element to be inserted. + * \param [in] action The adapt action to be performed. + */ + void + element_manipulator (t8_element_array_t *elements, const t8_element_array_t *const elements_from, + const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t &el_considered, + t8_locidx_t &el_inserted, const adapt_action action) { - void - element_manipulator (t8_element_array_t *elements, const t8_element_array_t *const elements_from, - const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t &el_considered, - t8_locidx_t &el_inserted, const adapt_action action) - { - if (!is_family && action == adapt_action::COARSEN) { + if (!is_family && action == adapt_action::COARSEN) { + action = adapt_action::KEEP; + } + /* Check that all siblings want to be coarsened */ + if (is_family && action == adapt_action::COARSEN) { + const auto start = adapt_actions.begin () + static_cast (el_offset + el_considered); + const auto end = start + static_cast (num_siblings); + if (!std::all_of (start, end, [] (const adapt_action &a) { return a == adapt_action::COARSEN; })) { action = adapt_action::KEEP; } - /* Check that all siblings want to be coarsened */ - if (is_family && action == adapt_action::COARSEN) { - const auto start = adapt_actions.begin () + static_cast (el_offset + el_considered); - const auto end = start + static_cast (num_siblings); - if (!std::all_of (start, end, [] (const adapt_action &a) { return a == adapt_action::COARSEN; })) { - action = adapt_action::KEEP; - } - } + } - switch (action) { - case adapt_action::COARSEN: - el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, - el_inserted, el_offset + el_considered); - break; - case adapt_action::KEEP: - el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, + switch (action) { + case adapt_action::COARSEN: + el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, + el_inserted, el_offset + el_considered); + break; + case adapt_action::KEEP: + el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, + el_inserted, el_offset + el_considered); + break; + case adapt_action::REFINE: + el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, el_inserted, el_offset + el_considered); - break; - case adapt_action::REFINE: - el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, - el_inserted, el_offset + el_considered); - break; - default: { - t8_errorf ("Unknown adapt action.\n"); - SC_ABORT_NOT_REACHED (); - break; - } - } - }; + break; + default: { + t8_errorf ("Unknown adapt action.\n"); + SC_ABORT_NOT_REACHED (); + break; + } + } + }; +}; - /** * Class implementing a basic adaptation strategy for a forest of trees. +/** + * Policy-based adaptor that drives element-wise adaptation of a target forest from a source forest. + * + * This class coordinates the process of collecting adaptation actions from a source forest and applying + * element-level manipulations to a target forest. It is implemented as a policy-composition class template + * and privately inherits the provided policy types: + * - TCollect: provides collect_adapt_actions(forest_from, adapt_actions, callback) to produce actions. + * - TFamily: provides family_check(...) to identify family/grouped elements (siblings). + * - TManipulate: provides element_manipulator(...) to perform /coarsening/refinement or general element manipulation logic. + * + * Template parameters + * @tparam TCollect A type satisfying adapt_actions_collectable: must expose collect_adapt_actions. + * @tparam TFamily A type satisfying family_checkable: must expose family_check to detect families. + * @tparam TManipulate A type satisfying element_manipulatable: must expose element_manipulator. + * + * Overview + * The adaptor holds references to a "target" forest and a "source" forest (forest and forest_from). On construction it + * retains references (increments reference counts) for any non-null forest handles; on destruction it releases them. + * The adapt() method: + * - Asserts valid state and optionally starts profiling. + * - Uses the TCollect policy to populate adapt_actions for each element in the source forest. + * - Iterates over local trees of the source forest, and for each tree: + * - Retrieves corresponding tree data from both source and target forests. + * - Accesses the source tree's leaf element array and the number of source elements. + * - For each considered source element (taking element siblings/families into account), uses the TFamily + * policy to determine whether the current elements form a family, reads the precomputed adapt action for + * the element, and calls the TManipulate policy to update the target tree's element array accordingly. + * - Maintains per-tree and global offsets so that adapt_actions are applied using a linear index across + * all source elements. + * + * Ownership and lifetime + * - The adaptor stores raw t8_forest_t handles for both forest and forest_from. When constructed it will call + * t8_forest_ref on non-null inputs and t8_forest_unref on destruction. Callers should treat the adaptor as + * owning an additional reference to the provided forest handles for the lifetime of the adaptor. + * + * Public interface + * - adaptor(forest, forest_from, callback_in) + * - Constructs an adaptor for adapting `forest` from `forest_from` using `callback_in` to decide actions. + * - Preconditions: `callback_in` must be valid; `forest` is asserted non-null. + * - Increments reference counts for non-null forest handles. + * + * - ~adaptor() + * - Releases retained references to the forests. + * + * - void adapt() + * - Performs the full adaptation: collects actions via TCollect, then visits source elements and applies + * element-level manipulations via TManipulate. Updates per-tree element offsets in the target forest. + * - If profiling is enabled, profile_adaptation() is invoked to start timing. + * + * - using callback_type = std::conditional_t, element_callback, batched_element_callback> + * - Alias describing the type of callback expected by the collect policy. The stored member `callback` uses + * this alias so the adaptor can support either single-element or batched callbacks depending on TCollect. + * + * Protected / private helpers + * - inline void profile_adaptation() + * - Starts adaptation timing by writing into forest->profile->adapt_runtime using the MPI wall-time helper. + * - Assumes a non-null profiling structure on the forest. + * + * Data members + * - t8_forest_t forest + * - The target forest that will be modified by adaptation operations. + * - t8_forest_t forest_from + * - The source forest from which adaptation decisions are derived. + * - std::vector adapt_actions + * - Linear array of adaptation actions for each element in the source forest. Populated by the TCollect policy. + * - callback_type callback + * - The callback provided by the caller; forwarded to the collect policy. + * - bool profiling + * - When true, the adaptor calls profile_adaptation() before starting adaptation. + * + * Notes and implementation considerations + * - The adaptor is designed to separate responsibilities: collection of adapt decisions, detection of sibling/family + * groupings, and the low-level manipulation of element arrays are all delegated to policy types. This allows + * different collection and manipulation strategies to be plugged in without changing the control flow. + * - The adaptor relies on the forest and tree data structures exposing stable array indexing via t8_element_array_* APIs. */ - template - class adaptor: private TCollect, private TFamily, private TManipulate { - public: - /** Constructor for basic_adaptation class. +template +class adaptor: private TCollect, private TFamily, private TManipulate { + public: + /** Constructor for basic_adaptation class. * \param [in] forest_in The forest to be adapted. * \param [in] callback_in The callback function to determine adaptation actions. */ - adaptor (t8_forest_t forest, t8_forest_t forest_from, element_callback callback_in) - : forest (forest), forest_from (forest_from), callback (callback_in) - { - T8_ASSERT (forest != nullptr); - T8_ASSERT (callback); - if (forest_from != nullptr) { - t8_forest_ref (forest_from); - } - T8_ASSERT (forest != nullptr); - if (forest != nullptr) { - t8_forest_ref (forest); - } - } + adaptor (t8_forest_t forest, t8_forest_t forest_from, element_callback callback_in) + : forest (forest), forest_from (forest_from), callback (callback_in) + { + T8_ASSERT (forest != nullptr); + T8_ASSERT (callback); + if (forest_from != nullptr) { + t8_forest_ref (forest_from); + } + T8_ASSERT (forest != nullptr); + if (forest != nullptr) { + t8_forest_ref (forest); + } + } - /** Destructor for adaptor class. */ - ~adaptor () - { - if (forest_from != nullptr) { - t8_forest_unref (&forest_from); - } - if (forest != nullptr) { - t8_forest_unref (&forest); - } - } + /** Destructor for adaptor class. */ + ~adaptor () + { + if (forest_from != nullptr) { + t8_forest_unref (&forest_from); + } + if (forest != nullptr) { + t8_forest_unref (&forest); + } + } - /** Perform the adaptation process on the forest. */ - void - adapt () - { - T8_ASSERT (forest != nullptr); - if (profiling) { - profile_adaptation (); - } - T8_ASSERT (forest_from != nullptr); - - TCollect::collect_adapt_actions (forest_from, adapt_actions, callback); - - /* Offset per tree in the source forest */ - t8_locidx_t el_offset = 0; - const t8_locidx_t num_trees = t8_forest_get_num_local_trees (forest_from); - /* Get the scheme used by the forest */ - const t8_scheme *scheme = t8_forest_get_scheme (forest_from); - - for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { - /* get the trees from both forests. */ - t8_tree_t tree = t8_forest_get_tree (forest, ltree_id); - const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); - /* get the leaf arrays from both forests */ - t8_element_array_t *elements = &tree->leaf_elements; - const t8_element_array_t *tree_elements_from = &tree_from->leaf_elements; - /* Get the number of elements in the source tree */ - const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (tree_elements_from); - T8_ASSERT (num_el_from == t8_forest_get_tree_num_leaf_elements (forest_from, ltree_id)); - const t8_eclass_t tree_class = tree_from->eclass; - /* Continue only if tree_from is not empty */ - if (num_el_from < 0) { - const t8_element_t *first_element_from = t8_element_array_index_locidx (tree_elements_from, 0); - t8_locidx_t curr_size_elements_from = scheme->element_get_num_siblings (tree_class, first_element_from); - /* index of the elements in source tree */ - t8_locidx_t el_considered = 0; - /* index of the elements in target tree */ - t8_locidx_t el_inserted = 0; - std::vector elements_temp; - - while (el_considered < num_el_from) { - const t8_locidx_t num_siblings = scheme->element_get_num_siblings ( - tree_class, t8_element_array_index_locidx (tree_elements_from, el_considered)); - if (num_siblings > curr_size_elements_from) { - elements_temp.resize (num_siblings); - curr_size_elements_from = num_siblings; - } - for (int isibling = 0; isibling < num_siblings && el_considered + isibling < num_el_from; isibling++) { - elements_temp[isibling] = (const t8_element_t *) t8_element_array_index_locidx ( - tree_elements_from, el_considered + (t8_locidx_t) isibling); - if (scheme->element_get_child_id (tree_class, elements_temp[isibling]) != isibling) { - break; - } - } - - const bool is_family - = TFamily::family_check (tree_elements_from, elements_temp, el_considered, scheme, tree_class); - const adapt_action action = adapt_actions[el_offset + el_considered]; - - /* manipulator step*/ - TManipulate::element_manipulator (elements, tree_elements_from, scheme, tree_class, el_considered, - el_inserted, action, is_family); - el_considered++; + /** Perform the adaptation process on the forest. */ + void + adapt () + { + T8_ASSERT (forest != nullptr); + if (profiling) { + profile_adaptation (); + } + T8_ASSERT (forest_from != nullptr); + + TCollect::collect_adapt_actions (forest_from, adapt_actions, callback); + + /* Offset per tree in the source forest */ + t8_locidx_t el_offset = 0; + const t8_locidx_t num_trees = t8_forest_get_num_local_trees (forest_from); + /* Get the scheme used by the forest */ + const t8_scheme *scheme = t8_forest_get_scheme (forest_from); + + for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { + /* get the trees from both forests. */ + t8_tree_t tree = t8_forest_get_tree (forest, ltree_id); + const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); + /* get the leaf arrays from both forests */ + t8_element_array_t *elements = &tree->leaf_elements; + const t8_element_array_t *tree_elements_from = &tree_from->leaf_elements; + /* Get the number of elements in the source tree */ + const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (tree_elements_from); + T8_ASSERT (num_el_from == t8_forest_get_tree_num_leaf_elements (forest_from, ltree_id)); + const t8_eclass_t tree_class = tree_from->eclass; + /* Continue only if tree_from is not empty */ + if (num_el_from < 0) { + const t8_element_t *first_element_from = t8_element_array_index_locidx (tree_elements_from, 0); + t8_locidx_t curr_size_elements_from = scheme->element_get_num_siblings (tree_class, first_element_from); + /* index of the elements in source tree */ + t8_locidx_t el_considered = 0; + /* index of the elements in target tree */ + t8_locidx_t el_inserted = 0; + std::vector elements_temp; + + while (el_considered < num_el_from) { + const t8_locidx_t num_siblings = scheme->element_get_num_siblings ( + tree_class, t8_element_array_index_locidx (tree_elements_from, el_considered)); + if (num_siblings > curr_size_elements_from) { + elements_temp.resize (num_siblings); + curr_size_elements_from = num_siblings; + } + for (int isibling = 0; isibling < num_siblings && el_considered + isibling < num_el_from; isibling++) { + elements_temp[isibling] = (const t8_element_t *) t8_element_array_index_locidx ( + tree_elements_from, el_considered + (t8_locidx_t) isibling); + if (scheme->element_get_child_id (tree_class, elements_temp[isibling]) != isibling) { + break; } } - tree->elements_offset = el_offset; - el_offset += num_el_from; - } - } - /** Type alias for the callback function used in adaptation. - * The type depends on whether TCollect uses element_callback or batched_element_callback. - */ - using callback_type - = std::conditional_t, element_callback, batched_element_callback>; + const bool is_family + = TFamily::family_check (tree_elements_from, elements_temp, el_considered, scheme, tree_class); + const adapt_action action = adapt_actions[el_offset + el_considered]; - callback_type callback; /**< The callback function to determine adaptation actions. */ - private: - /** + /* manipulator step*/ + TManipulate::element_manipulator (elements, tree_elements_from, scheme, tree_class, el_considered, + el_inserted, action, is_family); + el_considered++; + } + } + tree->elements_offset = el_offset; + el_offset += num_el_from; + } + } + /** The type of callback used for collecting adaptation actions. */ + using callback_type + = std::conditional_t, element_callback, batched_element_callback>; + + callback_type callback; /**< The callback function to determine adaptation actions. */ + private: + /** * Profile the adaptation process. */ - inline void - profile_adaptation () - { - T8_ASSERT (forest->profile != nullptr); - forest->profile->adapt_runtime = -sc_MPI_Wtime (); - } - - t8_forest_t forest; /**< The target forest */ - t8_forest_t forest_from; /**< The source forest to adapt from. */ - std::vector adapt_actions; /**< The adaptation actions for each element in the source forest. */ - bool profiling = false; /**< Flag to indicate if profiling is enabled. */ - }; - }; - + inline void + profile_adaptation () + { + T8_ASSERT (forest->profile != nullptr); + forest->profile->adapt_runtime = -sc_MPI_Wtime (); + } + + t8_forest_t forest; /**< The target forest */ + t8_forest_t forest_from; /**< The source forest to adapt from. */ + std::vector adapt_actions; /**< The adaptation actions for each element in the source forest. */ + bool profiling = false; /**< Flag to indicate if profiling is enabled. */ +}; +}; +} +; #endif /* T8_FOREST_ADAPT_HXX */ diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx index 593bc985c6..eb6cc8eb47 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx @@ -105,7 +105,7 @@ struct batched_adapt_collector { void collect_adapt_actions (const t8_forest_t forest_from, std::vector &adapt_actions, - element_callback callback) + batched_element_callback callback) { T8_ASSERT (forest_from != nullptr); @@ -121,12 +121,8 @@ struct batched_adapt_collector const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); const t8_eclass_t tree_class = tree_from->eclass; const t8_element_array_t *elements_from = &tree_from->leaf_elements; - const t8_locidx_t num_el_from = (t8_locidx_t) t8_element_array_get_count (elements_from); - for (t8_locidx_t i = 0; i < num_el_from; i++) { - const t8_element_t *element = t8_element_array_index_locidx (elements_from, i); - adapt_actions[el_offset + i] = callback (forest_from, ltree_id, element, scheme, tree_class); - } - el_offset += num_el_from; + callback (forest_from, ltree_id, elements_from, scheme, tree_class, adapt_actions); + el_offset += (t8_locidx_t) t8_element_array_get_count (elements_from); } } }; From 69ddd3c889b39f99875e3cce511e1de87cd28990 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 13:21:20 +0100 Subject: [PATCH 13/24] profiling --- .../t8_forest_adapt/t8_forest_adapt.hxx | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index f8563300d8..c6480f5b58 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -87,7 +87,7 @@ using batched_element_callback * a.collect_adapt_actions(const t8_forest_t, std::vector&, element_callback) * returning void. * - * @tparam T + * \tparam T * Type under test. The concept is satisfied when an object `a` of type T can be * used in an expression * a.collect_adapt_actions(forest_from, adapt_actions, cb) @@ -109,7 +109,7 @@ concept has_element_callback_collect * with the signature compatible with: * a.collect_adapt_actions(const t8_forest_t, std::vector&, batched_element_callback) * returning void. - * @tparam T + * \tparam T * Type under test. The concept is satisfied when an object `a` of type T can be * used in an expression * a.collect_adapt_actions(forest_from, adapt_actions, cb) @@ -133,7 +133,7 @@ concept has_batched_callback_collect = requires ( * or * a.collect_adapt_actions(const t8_forest_t, std::vector&, batched_element_callback) * returning void. - * @tparam T + * \tparam T * Type under test. The concept is satisfied when an object `a` of type T can be * used in an expression * a.collect_adapt_actions(forest_from, adapt_actions, cb) @@ -151,7 +151,7 @@ concept adapt_actions_collectable = has_element_callback_collect || has_batch * a.family_check(const t8_element_array_t*, std::vector&, const t8_locidx_t, * const t8_scheme*, const t8_eclass_t) * returning bool. - * @tparam T + * \tparam T * Type under test. The concept is satisfied when an object `a` of type T can be * used in an expression * a.family_check(tree_elements_from, elements_from, offset, scheme, tree_class) @@ -178,7 +178,7 @@ concept family_checkable * const t8_scheme*, const t8_eclass_t, const t8_locidx_t&, * t8_locidx_t&, const adapt_action) * returning void. - * @tparam T + * \tparam T * Type under test. The concept is satisfied when an object `a` of type T can be * used in an expression * a.element_manipulator(elements, elements_from, scheme, tree_class, el_considered, el_inserted, action) @@ -334,9 +334,9 @@ struct manipulator * - TManipulate: provides element_manipulator(...) to perform /coarsening/refinement or general element manipulation logic. * * Template parameters - * @tparam TCollect A type satisfying adapt_actions_collectable: must expose collect_adapt_actions. - * @tparam TFamily A type satisfying family_checkable: must expose family_check to detect families. - * @tparam TManipulate A type satisfying element_manipulatable: must expose element_manipulator. + * \tparam TCollect A type satisfying adapt_actions_collectable: must expose collect_adapt_actions. + * \tparam TFamily A type satisfying family_checkable: must expose family_check to detect families. + * \tparam TManipulate A type satisfying element_manipulatable: must expose element_manipulator. * * Overview * The adaptor holds references to a "target" forest and a "source" forest (forest and forest_from). On construction it @@ -437,7 +437,7 @@ class adaptor: private TCollect, private TFamily, private TManipulate { { T8_ASSERT (forest != nullptr); if (profiling) { - profile_adaptation (); + profile_adaptation_start (); } T8_ASSERT (forest_from != nullptr); @@ -498,6 +498,9 @@ class adaptor: private TCollect, private TFamily, private TManipulate { tree->elements_offset = el_offset; el_offset += num_el_from; } + if (profiling) { + profile_adaptation_end (); + } } /** The type of callback used for collecting adaptation actions. */ using callback_type @@ -509,12 +512,19 @@ class adaptor: private TCollect, private TFamily, private TManipulate { * Profile the adaptation process. */ inline void - profile_adaptation () + profile_adaptation_start () { T8_ASSERT (forest->profile != nullptr); forest->profile->adapt_runtime = -sc_MPI_Wtime (); } + inline void + profile_adaptation_end () + { + T8_ASSERT (forest->profile != nullptr); + forest->profile->adapt_runtime += sc_MPI_Wtime (); + } + t8_forest_t forest; /**< The target forest */ t8_forest_t forest_from; /**< The source forest to adapt from. */ std::vector adapt_actions; /**< The adaptation actions for each element in the source forest. */ From d6b2d627846e76ffd8ea193094671b7b29fe22c9 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 15:00:39 +0100 Subject: [PATCH 14/24] encapsulate versions of adapt in macro to minimize integration size --- src/t8_forest/t8_forest_adapt.cxx | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/t8_forest/t8_forest_adapt.cxx b/src/t8_forest/t8_forest_adapt.cxx index b6f6486e77..2d869fb690 100644 --- a/src/t8_forest/t8_forest_adapt.cxx +++ b/src/t8_forest/t8_forest_adapt.cxx @@ -29,10 +29,16 @@ #include #include #include +#include /* We want to export the whole implementation to be callable from "C" */ T8_EXTERN_C_BEGIN (); +/* Set to 1 to enable legacy adaptation behavior */ +#define T8_FOREST_ADAPT_LEGACY 1 + +#if T8_FOREST_ADAPT_LEGACY + #if T8_ENABLE_DEBUG /** Return zero if the first \a num_elements in \a elements are not a (sub)family. * \param [in] scheme The element scheme for current local tree @@ -376,8 +382,11 @@ t8_forest_adapt_refine_recursive (t8_forest_t forest, t8_locidx_t ltreeid, t8_ec } } /* End while loop */ } +#endif /* TODO: optimize this when we own forest_from */ + +#if T8_FOREST_ADAPT_LEGACY void t8_forest_adapt (t8_forest_t forest) { @@ -688,5 +697,32 @@ t8_forest_adapt (t8_forest_t forest) t8_global_productionf ("End adapt %f %f\n", sc_MPI_Wtime (), forest->profile->adapt_runtime); } } +#else + +t8_forest_adapt_namespace::adapt_action +dummy_callback ([[maybe_unused]] const t8_forest_t forest, [[maybe_unused]] const t8_locidx_t ltreeid, + [[maybe_unused]] const t8_element_t *element, [[maybe_unused]] const t8_scheme *scheme, + [[maybe_unused]] const t8_eclass_t tree_class) +{ + return t8_forest_adapt_namespace::adapt_action::KEEP; +} + +void +t8_forest_adapt (t8_forest_t forest) +{ + T8_ASSERT (forest != NULL); + T8_ASSERT (forest->set_from != NULL); + T8_ASSERT (forest->set_adapt_recursive != -1); + t8_forest_t forest_from = forest->set_from; + using namespace t8_forest_adapt_namespace; + /**TODO: currently only using a dummy callback to check compilation. + * For proper usage we need to change the layout of the adapt function pointer + * in t8_forest_t. + */ + adaptor standard_adaptor (forest, forest_from, dummy_callback, + forest->profile != NULL); + standard_adaptor.adapt (); +} +#endif /* T8_FOREST_ADAPT_LEGACY */ T8_EXTERN_C_END (); From d2d013f4994eabafdcebd82810764b80f3879043 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 15:01:45 +0100 Subject: [PATCH 15/24] compilable version --- .../t8_forest_adapt/t8_forest_adapt.hxx | 222 +++++++++++++----- .../t8_forest_adapt_batched.hxx | 73 ------ 2 files changed, 164 insertions(+), 131 deletions(-) diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index c6480f5b58..c6a838dbe7 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -55,6 +55,52 @@ class adapt_action { static const int COARSEN = -1; static const int KEEP = 0; static const int REFINE = 1; + + adapt_action (): value (KEEP) + { + } + adapt_action (int v): value (v) + { + } + + /* implicit conversion to int so comparisons with the static constants work */ + operator int () const + { + return value; + } + + /* equality helpers */ + bool + operator== (int other) const + { + return value == other; + } + bool + operator!= (int other) const + { + return value != other; + } + bool + operator== (const adapt_action &other) const + { + return value == other.value; + } + bool + operator!= (const adapt_action &other) const + { + return value != other.value; + } + + /* allow assigning from an int constant */ + adapt_action & + operator= (int v) + { + value = v; + return *this; + } + + private: + int value; }; /** Callback function type for element adaptation. @@ -174,16 +220,16 @@ concept family_checkable /** Concept that detects whether a type T provides a member function * with the signature compatible with: - * a.element_manipulator(t8_element_array_t*, const t8_element_array_t*, - * const t8_scheme*, const t8_eclass_t, const t8_locidx_t&, - * t8_locidx_t&, const adapt_action) - * returning void. - * \tparam T - * Type under test. The concept is satisfied when an object `a` of type T can be - * used in an expression - * a.element_manipulator(elements, elements_from, scheme, tree_class, el_considered, el_inserted, action) - * where: - * - elements is of type t8_element_array_t*, +template +concept element_manipulatable = requires ( + T a, t8_element_array_t *elements, const t8_element_array_t *const elements_from, + const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t &el_considered, + const t8_locidx_t el_offset, t8_locidx_t &el_inserted, const std::vector &adapt_actions, + adapt_action action, const bool is_family, const int num_siblings) { + { + a.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_offset, el_inserted, adapt_actions, action, is_family, num_siblings) + } -> std::same_as; +}; * - elements_from is of type const t8_element_array_t*, * - scheme is of type const t8_scheme*, * - tree_class is of type const t8_eclass_t, @@ -194,10 +240,12 @@ concept family_checkable */ template concept element_manipulatable = requires ( - T a, t8_element_array_t *elements, const t8_element_array_t *elements_from, const t8_scheme *scheme, - const t8_eclass_t tree_class, const t8_locidx_t &el_considered, t8_locidx_t &el_inserted, const adapt_action action) { + T a, t8_element_array_t *elements, const t8_element_array_t *const elements_from, const t8_scheme *scheme, + const t8_eclass_t tree_class, const t8_locidx_t &el_considered, const t8_locidx_t el_offset, t8_locidx_t &el_inserted, + const std::vector &action, const bool is_family, const int num_siblings) { { - a.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_inserted, action) + a.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_offset, el_inserted, action, + is_family, num_siblings) } -> std::same_as; }; @@ -267,12 +315,86 @@ struct family_checker children_nonconst[i] = const_cast (elements_from[i]); const bool is_family = scheme->elements_are_family (tree_class, children_nonconst.data ()); return is_family; - } + }; }; -/** - * Class implementing a basic element manipulation strategy. +/** Function to manipulate elements based on the specified adaptation action. + * \tparam action The adaptation action to be performed. + * \param [in, out] elements The element array to be modified. + * \param [in] elements_from The source element array. + * \param [in] scheme The element scheme. + * \param [in] tree_class The eclass of the tree used by the scheme + * \param [in] elements_index The index in the target element array. + * \param [in] elements_from_index The index in the source element array. + * \return The number of elements created in the target array. */ +template +t8_locidx_t +manipulate_elements (t8_element_array_t *elements, [[maybe_unused]] const t8_element_array_t *const elements_from, + const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t elements_index, + const t8_locidx_t elements_from_index); + +/** Specialization for the KEEP action. No element modification is performed; + * the element is copied as is. + * \see manipulate_elements + */ +template <> +t8_locidx_t +manipulate_elements (t8_element_array_t *elements, + [[maybe_unused]] const t8_element_array_t *const elements_from, + const t8_scheme *scheme, const t8_eclass_t tree_class, + const t8_locidx_t elements_index, + [[maybe_unused]] const t8_locidx_t elements_from_index) +{ + t8_element_t *element = t8_element_array_push (elements); + const t8_element_t *element_from = t8_element_array_index_locidx (elements, elements_index); + scheme->element_copy (tree_class, element_from, element); + return 1; +}; + +/** Specialization for the COARSEN action. The parent element of the source elements is created + * in the target array. + * \see manipulate_elements + */ +template <> +t8_locidx_t +manipulate_elements (t8_element_array_t *elements, + [[maybe_unused]] const t8_element_array_t *const elements_from, + const t8_scheme *scheme, const t8_eclass_t tree_class, + const t8_locidx_t elements_index, + [[maybe_unused]] const t8_locidx_t elements_from_index) +{ + t8_element_t *element = t8_element_array_push (elements); + const t8_element_t *element_from = t8_element_array_index_locidx (elements, elements_index); + T8_ASSERT (scheme->element_get_level (tree_class, element_from) > 0); + scheme->element_get_parent (tree_class, element_from, element); + + /* Hier eventuell noch was mit num_children = num_siblings*/ + return 1; +}; + +/** Specialization for the REFINE action. The children elements of the source element are created + * in the target array. + * \see manipulate_elements + */ +template <> +t8_locidx_t +manipulate_elements (t8_element_array_t *elements, const t8_element_array_t *const elements_from, + const t8_scheme *scheme, const t8_eclass_t tree_class, + const t8_locidx_t elements_index, const t8_locidx_t elements_from_index) +{ + const t8_element_t *element_from = t8_element_array_index_locidx (elements_from, elements_from_index); + const int num_children = scheme->element_get_num_children (tree_class, element_from); + /* CONTINUE WORK HERE */ + (void) t8_element_array_push_count (elements, num_children); + std::vector children (num_children); + for (int ichildren = 0; ichildren < num_children; ichildren++) { + children[ichildren] = t8_element_array_index_locidx_mutable (elements, elements_index + ichildren); + } + scheme->element_get_children (tree_class, element_from, num_children, children.data ()); + return num_children; +}; + struct manipulator { /** Manipulate elements based on the given adapt action. @@ -282,13 +404,16 @@ struct manipulator * \param [in] tree_class The class of the tree. * \param [in] el_considered The index of the element being considered. * \param [in,out] el_inserted The index of the next element to be inserted. - * \param [in] action The adapt action to be performed. + * \param [in] adapt_actions The global adapt actions vector (source forest linearized). + * \param [in] action The adapt action to be performed (by-value so it can be changed). */ void element_manipulator (t8_element_array_t *elements, const t8_element_array_t *const elements_from, const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t &el_considered, - t8_locidx_t &el_inserted, const adapt_action action) + const t8_locidx_t el_offset, t8_locidx_t &el_inserted, + const std::vector &adapt_actions, const bool is_family, const int num_siblings) { + adapt_action action = adapt_actions[el_offset + el_considered]; if (!is_family && action == adapt_action::COARSEN) { action = adapt_action::KEEP; } @@ -301,17 +426,17 @@ struct manipulator } } - switch (action) { + switch (static_cast (action)) { case adapt_action::COARSEN: - el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, + el_inserted += manipulate_elements (elements, elements_from, scheme, tree_class, el_inserted, el_offset + el_considered); break; case adapt_action::KEEP: - el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, - el_inserted, el_offset + el_considered); + el_inserted += manipulate_elements (elements, elements_from, scheme, tree_class, el_inserted, + el_offset + el_considered); break; case adapt_action::REFINE: - el_inserted += manipulate_elements (elements, tree_elements_from, scheme, tree_class, + el_inserted += manipulate_elements (elements, elements_from, scheme, tree_class, el_inserted, el_offset + el_considered); break; default: { @@ -403,11 +528,11 @@ template elements_temp; + const bool is_family + = TFamily::family_check (tree_elements_from, elements_temp, el_considered, scheme, tree_class); - while (el_considered < num_el_from) { - const t8_locidx_t num_siblings = scheme->element_get_num_siblings ( - tree_class, t8_element_array_index_locidx (tree_elements_from, el_considered)); - if (num_siblings > curr_size_elements_from) { - elements_temp.resize (num_siblings); - curr_size_elements_from = num_siblings; - } - for (int isibling = 0; isibling < num_siblings && el_considered + isibling < num_el_from; isibling++) { - elements_temp[isibling] = (const t8_element_t *) t8_element_array_index_locidx ( - tree_elements_from, el_considered + (t8_locidx_t) isibling); - if (scheme->element_get_child_id (tree_class, elements_temp[isibling]) != isibling) { - break; - } - } - - const bool is_family - = TFamily::family_check (tree_elements_from, elements_temp, el_considered, scheme, tree_class); - const adapt_action action = adapt_actions[el_offset + el_considered]; - - /* manipulator step*/ - TManipulate::element_manipulator (elements, tree_elements_from, scheme, tree_class, el_considered, - el_inserted, action, is_family); - el_considered++; - } + /* manipulator step*/ + TManipulate::element_manipulator (elements, tree_elements_from, scheme, tree_class, el_considered, el_offset, + el_inserted, adapt_actions, is_family, curr_size_elements_from); + el_considered++; } tree->elements_offset = el_offset; el_offset += num_el_from; @@ -509,8 +616,8 @@ class adaptor: private TCollect, private TFamily, private TManipulate { callback_type callback; /**< The callback function to determine adaptation actions. */ private: /** - * Profile the adaptation process. - */ + * Profile the adaptation process. + */ inline void profile_adaptation_start () { @@ -529,8 +636,7 @@ class adaptor: private TCollect, private TFamily, private TManipulate { t8_forest_t forest_from; /**< The source forest to adapt from. */ std::vector adapt_actions; /**< The adaptation actions for each element in the source forest. */ bool profiling = false; /**< Flag to indicate if profiling is enabled. */ -}; -}; -} -; +}; // class adaptor + +}; // namespace t8_forest_adapt_namespace #endif /* T8_FOREST_ADAPT_HXX */ diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx index eb6cc8eb47..409056461e 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx @@ -25,79 +25,6 @@ #include -/** Function to manipulate elements based on the specified adaptation action. - * \tparam action The adaptation action to be performed. - * \param [in, out] elements The element array to be modified. - * \param [in] elements_from The source element array. - * \param [in] scheme The element scheme. - * \param [in] tree_class The eclass of the tree used by the scheme - * \param [in] elements_index The index in the target element array. - * \param [in] elements_from_index The index in the source element array. - * \return The number of elements created in the target array. - */ -template -t8_locidx_t -manipulate_elements (t8_element_array_t *elements, const t8_element_array_t *const elements_from, - const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t elements_index, - const t8_locidx_t elements_from_index); - -/** Specialization for the KEEP action. No element modification is performed; - * the element is copied as is. - * \see manipulate_elements - */ -template <> -t8_locidx_t -manipulate_elements ( - t8_element_array_t *elements, const t8_element_array_t *const elements_from, const t8_scheme *scheme, - const t8_eclass_t tree_class, const t8_locidx_t elements_index, const t8_locidx_t elements_from_index) -{ - t8_element_t *element = t8_element_array_push (elements); - const t8_element_t *element_from = t8_element_array_index_locidx (elements, elements_index); - scheme->element_copy (tree_class, element_from, element); - return 1; -}; - -/** Specialization for the COARSEN action. The parent element of the source elements is created - * in the target array. - * \see manipulate_elements - */ -template <> -t8_locidx_t -manipulate_elements ( - t8_element_array_t *elements, const t8_element_array_t *const elements_from, const t8_scheme *scheme, - const t8_eclass_t tree_class, const t8_locidx_t elements_index, const t8_locidx_t elements_from_index) -{ - t8_element_t *element = t8_element_array_push (elements); - const t8_element_t *element_from = t8_element_array_index_locidx (elements, elements_index); - T8_ASSERT (scheme->element_get_level (tree_class, element_from) > 0); - scheme->element_get_parent (tree_class, element_from, element); - - /* Hier eventuell noch was mit num_children = num_siblings*/ - return 1; -}; - -/** Specialization for the REFINE action. The children elements of the source element are created - * in the target array. - * \see manipulate_elements - */ -template <> -t8_locidx_t -manipulate_elements (t8_element_array_t *elements, const t8_element_array_t *const elements_from, - const t8_scheme *scheme, const t8_eclass_t tree_class, - const t8_locidx_t elements_index, const t8_locidx_t elements_from_index) -{ - const t8_element_t *element_from = t8_element_array_index_locidx (elements_from, elements_from_index); - const int num_children = scheme->element_get_num_children (tree_class, element_from); - /* CONTINUE WORK HERE */ - (void) t8_element_array_push_count (elements, num_children); - std::vector children (num_children); - for (int ichildren = 0; ichildren < num_children; ichildren++) { - children[ichildren] = t8_element_array_index_locidx_mutable (elements, elements_index + ichildren); - } - scheme->element_get_children (tree_class, element_from, num_children, children.data ()); - return num_children; -}; - /** * Collect adaptation actions for all elements in the source forest. */ From 13ad04a0e99bc6007aee8f2416a9b9523715723f Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 15:29:03 +0100 Subject: [PATCH 16/24] renamte t8_forest_adapt_namespace -> t8_adapt --- src/t8_forest/t8_forest_adapt.cxx | 8 +-- .../t8_forest_adapt/t8_forest_adapt.hxx | 61 ++++++++++++++++--- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/t8_forest/t8_forest_adapt.cxx b/src/t8_forest/t8_forest_adapt.cxx index 2d869fb690..d57ecbfb7d 100644 --- a/src/t8_forest/t8_forest_adapt.cxx +++ b/src/t8_forest/t8_forest_adapt.cxx @@ -35,7 +35,7 @@ T8_EXTERN_C_BEGIN (); /* Set to 1 to enable legacy adaptation behavior */ -#define T8_FOREST_ADAPT_LEGACY 1 +#define T8_FOREST_ADAPT_LEGACY 0 #if T8_FOREST_ADAPT_LEGACY @@ -699,12 +699,12 @@ t8_forest_adapt (t8_forest_t forest) } #else -t8_forest_adapt_namespace::adapt_action +t8_adapt::adapt_action dummy_callback ([[maybe_unused]] const t8_forest_t forest, [[maybe_unused]] const t8_locidx_t ltreeid, [[maybe_unused]] const t8_element_t *element, [[maybe_unused]] const t8_scheme *scheme, [[maybe_unused]] const t8_eclass_t tree_class) { - return t8_forest_adapt_namespace::adapt_action::KEEP; + return t8_adapt::adapt_action::KEEP; } void @@ -714,7 +714,7 @@ t8_forest_adapt (t8_forest_t forest) T8_ASSERT (forest->set_from != NULL); T8_ASSERT (forest->set_adapt_recursive != -1); t8_forest_t forest_from = forest->set_from; - using namespace t8_forest_adapt_namespace; + using namespace t8_adapt; /**TODO: currently only using a dummy callback to check compilation. * For proper usage we need to change the layout of the adapt function pointer * in t8_forest_t. diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index c6a838dbe7..c80e82d096 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -41,8 +41,7 @@ * Namespace for adaptation related classes and functions. */ -/* TODO rename to t8_forest_adapt as soon as it is not used as function name anymore. */ -namespace t8_forest_adapt_namespace +namespace t8_adapt { /** The action to be taken on an element during adaptation. * COARSEN: The element should be coarsened. @@ -56,42 +55,77 @@ class adapt_action { static const int KEEP = 0; static const int REFINE = 1; + /** + * Default constructor: KEEP action. + * \note implicit conversion from int to adapt_action. + */ adapt_action (): value (KEEP) { } + /** Constructor from int value. + * \param [in] v The integer value representing the action. + * \note implicit conversion from int to adapt_action. + */ adapt_action (int v): value (v) { } - /* implicit conversion to int so comparisons with the static constants work */ + /** Conversion operator to int. + * \return The integer value representing the action. + * \note implicit conversion from adapt_action to int. + */ operator int () const { return value; } - /* equality helpers */ + /** + * Comparison operators with int and adapt_action. + * \param [in] other The other value to compare with. + * \return True if the values are equal, false otherwise. + */ bool operator== (int other) const { return value == other; } + + /** Inequality operator with int. + * \param [in] other The other value to compare with. + * \return True if the values are not equal, false otherwise. + */ bool operator!= (int other) const { return value != other; } + + /** Equality operator with another adapt_action. + * \param [in] other The other adapt_action to compare with. + * \return True if the values are equal, false otherwise. + */ bool operator== (const adapt_action &other) const { return value == other.value; } + + /** Inequality operator with another adapt_action. + * \param [in] other The other adapt_action to compare with. + * \return True if the values are not equal, false otherwise. + */ bool operator!= (const adapt_action &other) const { return value != other.value; } - /* allow assigning from an int constant */ + /** + * Assignment operator from int. + * \param [in] v The integer value representing the action to assign. + * \return A reference to this adapt_action after assignment. + * \note implicit conversion from int to adapt_action. + */ adapt_action & operator= (int v) { @@ -502,9 +536,12 @@ struct manipulator * this alias so the adaptor can support either single-element or batched callbacks depending on TCollect. * * Protected / private helpers - * - inline void profile_adaptation() + * - inline void profile_adaptation_start() * - Starts adaptation timing by writing into forest->profile->adapt_runtime using the MPI wall-time helper. * - Assumes a non-null profiling structure on the forest. + * - inline void profile_adaptation_end() + * - Ends adaptation timing by computing the elapsed time since profile_adaptation_start and accumulating it + * into forest->profile->adapt_runtime. * * Data members * - t8_forest_t forest @@ -528,9 +565,13 @@ template Date: Wed, 4 Feb 2026 15:38:48 +0100 Subject: [PATCH 17/24] adapt_action -> action --- src/t8_forest/t8_forest_adapt.cxx | 4 +- .../t8_forest_adapt/t8_forest_adapt.hxx | 187 +++++++++--------- 2 files changed, 95 insertions(+), 96 deletions(-) diff --git a/src/t8_forest/t8_forest_adapt.cxx b/src/t8_forest/t8_forest_adapt.cxx index d57ecbfb7d..6f1f03fa24 100644 --- a/src/t8_forest/t8_forest_adapt.cxx +++ b/src/t8_forest/t8_forest_adapt.cxx @@ -699,12 +699,12 @@ t8_forest_adapt (t8_forest_t forest) } #else -t8_adapt::adapt_action +t8_adapt::action dummy_callback ([[maybe_unused]] const t8_forest_t forest, [[maybe_unused]] const t8_locidx_t ltreeid, [[maybe_unused]] const t8_element_t *element, [[maybe_unused]] const t8_scheme *scheme, [[maybe_unused]] const t8_eclass_t tree_class) { - return t8_adapt::adapt_action::KEEP; + return t8_adapt::action::KEEP; } void diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index c80e82d096..5e318c8891 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -49,7 +49,7 @@ namespace t8_adapt * REFINE: The element should be refined. * Can be extended (e.g., for special refinement types). */ -class adapt_action { +class action { public: static const int COARSEN = -1; static const int KEEP = 0; @@ -57,22 +57,22 @@ class adapt_action { /** * Default constructor: KEEP action. - * \note implicit conversion from int to adapt_action. + * \note implicit conversion from int to action. */ - adapt_action (): value (KEEP) + action (): value (KEEP) { } /** Constructor from int value. * \param [in] v The integer value representing the action. - * \note implicit conversion from int to adapt_action. + * \note implicit conversion from int to action. */ - adapt_action (int v): value (v) + action (int v): value (v) { } /** Conversion operator to int. * \return The integer value representing the action. - * \note implicit conversion from adapt_action to int. + * \note implicit conversion from action to int. */ operator int () const { @@ -80,7 +80,7 @@ class adapt_action { } /** - * Comparison operators with int and adapt_action. + * Comparison operators with int and action. * \param [in] other The other value to compare with. * \return True if the values are equal, false otherwise. */ @@ -100,22 +100,22 @@ class adapt_action { return value != other; } - /** Equality operator with another adapt_action. - * \param [in] other The other adapt_action to compare with. + /** Equality operator with another action. + * \param [in] other The other action to compare with. * \return True if the values are equal, false otherwise. */ bool - operator== (const adapt_action &other) const + operator== (const action &other) const { return value == other.value; } - /** Inequality operator with another adapt_action. - * \param [in] other The other adapt_action to compare with. + /** Inequality operator with another action. + * \param [in] other The other action to compare with. * \return True if the values are not equal, false otherwise. */ bool - operator!= (const adapt_action &other) const + operator!= (const action &other) const { return value != other.value; } @@ -123,10 +123,10 @@ class adapt_action { /** * Assignment operator from int. * \param [in] v The integer value representing the action to assign. - * \return A reference to this adapt_action after assignment. - * \note implicit conversion from int to adapt_action. + * \return A reference to this action after assignment. + * \note implicit conversion from int to action. */ - adapt_action & + action & operator= (int v) { value = v; @@ -146,8 +146,8 @@ class adapt_action { * \return The adaptation action to be taken on the element. */ using element_callback - = std::function; + = std::function; /** Callback function type for batched element adaptation. * \param [in] forest The forest containing the elements. @@ -159,72 +159,72 @@ using element_callback */ using batched_element_callback = std::function &action)>; + const t8_scheme *scheme, const t8_eclass_t tree_class, std::vector &action)>; /** * Concept that detects whether a type T provides a member function * with the signature compatible with: - * a.collect_adapt_actions(const t8_forest_t, std::vector&, element_callback) + * a.collect_actions(const t8_forest_t, std::vector&, element_callback) * returning void. * * \tparam T * Type under test. The concept is satisfied when an object `a` of type T can be * used in an expression - * a.collect_adapt_actions(forest_from, adapt_actions, cb) + * a.collect_actions(forest_from, actions, cb) * where: * - forest_from is of type const t8_forest_t, - * - adapt_actions is of type std::vector&, + * - actions is of type std::vector&, * - cb is of type element_callback, * and the expression is well-formed and yields void. */ template concept has_element_callback_collect - = requires (T a, const t8_forest_t forest_from, std::vector &adapt_actions, element_callback cb) { + = requires (T a, const t8_forest_t forest_from, std::vector &actions, element_callback cb) { { - a.collect_adapt_actions (forest_from, adapt_actions, cb) + a.collect_actions (forest_from, actions, cb) } -> std::same_as; }; /** Concept that detects whether a type T provides a member function * with the signature compatible with: - * a.collect_adapt_actions(const t8_forest_t, std::vector&, batched_element_callback) + * a.collect_actions(const t8_forest_t, std::vector&, batched_element_callback) * returning void. * \tparam T * Type under test. The concept is satisfied when an object `a` of type T can be * used in an expression - * a.collect_adapt_actions(forest_from, adapt_actions, cb) + * a.collect_actions(forest_from, actions, cb) * where: * - forest_from is of type const t8_forest_t, - * - adapt_actions is of type std::vector&, + * - actions is of type std::vector&, * - cb is of type batched_element_callback, * and the expression is well-formed and yields void. */ template -concept has_batched_callback_collect = requires ( - T a, const t8_forest_t forest_from, std::vector &adapt_actions, batched_element_callback cb) { - { - a.collect_adapt_actions (forest_from, adapt_actions, cb) - } -> std::same_as; -}; +concept has_batched_callback_collect + = requires (T a, const t8_forest_t forest_from, std::vector &actions, batched_element_callback cb) { + { + a.collect_actions (forest_from, actions, cb) + } -> std::same_as; + }; /** Concept that detects whether a type T provides a member function * with the signature compatible with either: - * a.collect_adapt_actions(const t8_forest_t, std::vector&, element_callback) + * a.collect_actions(const t8_forest_t, std::vector&, element_callback) * or - * a.collect_adapt_actions(const t8_forest_t, std::vector&, batched_element_callback) + * a.collect_actions(const t8_forest_t, std::vector&, batched_element_callback) * returning void. * \tparam T * Type under test. The concept is satisfied when an object `a` of type T can be * used in an expression - * a.collect_adapt_actions(forest_from, adapt_actions, cb) + * a.collect_actions(forest_from, actions, cb) * where: * - forest_from is of type const t8_forest_t, - * - adapt_actions is of type std::vector&, + * - actions is of type std::vector&, * - cb is of type element_callback or batched_element_callback, * and the expression is well-formed and yields void. */ template -concept adapt_actions_collectable = has_element_callback_collect || has_batched_callback_collect; +concept actions_collectable = has_element_callback_collect || has_batched_callback_collect; /** Concept that detects whether a type T provides a member function * with the signature compatible with: @@ -258,10 +258,10 @@ template concept element_manipulatable = requires ( T a, t8_element_array_t *elements, const t8_element_array_t *const elements_from, const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t &el_considered, - const t8_locidx_t el_offset, t8_locidx_t &el_inserted, const std::vector &adapt_actions, - adapt_action action, const bool is_family, const int num_siblings) { + const t8_locidx_t el_offset, t8_locidx_t &el_inserted, const std::vector &actions, + action action, const bool is_family, const int num_siblings) { { - a.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_offset, el_inserted, adapt_actions, action, is_family, num_siblings) + a.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_offset, el_inserted, actions, action, is_family, num_siblings) } -> std::same_as; }; * - elements_from is of type const t8_element_array_t*, @@ -269,14 +269,14 @@ concept element_manipulatable = requires ( * - tree_class is of type const t8_eclass_t, * - el_considered is of type const t8_locidx_t&, * - el_inserted is of type t8_locidx_t&, - * - action is of type const adapt_action, + * - action is of type const action, * and the expression is well-formed and yields void. */ template concept element_manipulatable = requires ( T a, t8_element_array_t *elements, const t8_element_array_t *const elements_from, const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t &el_considered, const t8_locidx_t el_offset, t8_locidx_t &el_inserted, - const std::vector &action, const bool is_family, const int num_siblings) { + const std::vector &action, const bool is_family, const int num_siblings) { { a.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_offset, el_inserted, action, is_family, num_siblings) @@ -288,19 +288,18 @@ struct adapt_collector { /** Collect adapt actions for all elements in the forest. * \param [in] forest_from The forest containing the elements to be adapted. - * \param [out] adapt_actions The vector to store the adapt actions for all elements. + * \param [out] actions The vector to store the adapt actions for all elements. * \param [in] callback The callback function to determine the adapt action for each element. */ void - collect_adapt_actions (const t8_forest_t forest_from, std::vector &adapt_actions, - element_callback callback) + collect_actions (const t8_forest_t forest_from, std::vector &actions, element_callback callback) { T8_ASSERT (forest_from != nullptr); t8_locidx_t el_offset = 0; const t8_locidx_t num_trees = t8_forest_get_num_local_trees (forest_from); const t8_locidx_t local_num_elements = t8_forest_get_local_num_leaf_elements (forest_from); - adapt_actions.resize (static_cast (local_num_elements)); + actions.resize (static_cast (local_num_elements)); for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); @@ -312,7 +311,7 @@ struct adapt_collector for (t8_locidx_t el_considered = 0; el_considered < num_el_from; el_considered++) { const t8_element_t *element_from = t8_element_array_index_locidx (tree_elements_from, el_considered); - adapt_actions[el_offset + el_considered] = callback (forest_from, ltree_id, element_from, scheme, tree_class); + actions[el_offset + el_considered] = callback (forest_from, ltree_id, element_from, scheme, tree_class); } el_offset += num_el_from; } @@ -374,11 +373,11 @@ manipulate_elements (t8_element_array_t *elements, [[maybe_unused]] const t8_ele */ template <> t8_locidx_t -manipulate_elements (t8_element_array_t *elements, - [[maybe_unused]] const t8_element_array_t *const elements_from, - const t8_scheme *scheme, const t8_eclass_t tree_class, - const t8_locidx_t elements_index, - [[maybe_unused]] const t8_locidx_t elements_from_index) +manipulate_elements (t8_element_array_t *elements, + [[maybe_unused]] const t8_element_array_t *const elements_from, + const t8_scheme *scheme, const t8_eclass_t tree_class, + const t8_locidx_t elements_index, + [[maybe_unused]] const t8_locidx_t elements_from_index) { t8_element_t *element = t8_element_array_push (elements); const t8_element_t *element_from = t8_element_array_index_locidx (elements, elements_index); @@ -392,11 +391,11 @@ manipulate_elements (t8_element_array_t *elements, */ template <> t8_locidx_t -manipulate_elements (t8_element_array_t *elements, - [[maybe_unused]] const t8_element_array_t *const elements_from, - const t8_scheme *scheme, const t8_eclass_t tree_class, - const t8_locidx_t elements_index, - [[maybe_unused]] const t8_locidx_t elements_from_index) +manipulate_elements (t8_element_array_t *elements, + [[maybe_unused]] const t8_element_array_t *const elements_from, + const t8_scheme *scheme, const t8_eclass_t tree_class, + const t8_locidx_t elements_index, + [[maybe_unused]] const t8_locidx_t elements_from_index) { t8_element_t *element = t8_element_array_push (elements); const t8_element_t *element_from = t8_element_array_index_locidx (elements, elements_index); @@ -413,9 +412,9 @@ manipulate_elements (t8_element_array_t *elements, */ template <> t8_locidx_t -manipulate_elements (t8_element_array_t *elements, const t8_element_array_t *const elements_from, - const t8_scheme *scheme, const t8_eclass_t tree_class, - const t8_locidx_t elements_index, const t8_locidx_t elements_from_index) +manipulate_elements (t8_element_array_t *elements, const t8_element_array_t *const elements_from, + const t8_scheme *scheme, const t8_eclass_t tree_class, + const t8_locidx_t elements_index, const t8_locidx_t elements_from_index) { const t8_element_t *element_from = t8_element_array_index_locidx (elements_from, elements_from_index); const int num_children = scheme->element_get_num_children (tree_class, element_from); @@ -438,40 +437,40 @@ struct manipulator * \param [in] tree_class The class of the tree. * \param [in] el_considered The index of the element being considered. * \param [in,out] el_inserted The index of the next element to be inserted. - * \param [in] adapt_actions The global adapt actions vector (source forest linearized). + * \param [in] actions The global adapt actions vector (source forest linearized). * \param [in] action The adapt action to be performed (by-value so it can be changed). */ void element_manipulator (t8_element_array_t *elements, const t8_element_array_t *const elements_from, const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t &el_considered, - const t8_locidx_t el_offset, t8_locidx_t &el_inserted, - const std::vector &adapt_actions, const bool is_family, const int num_siblings) + const t8_locidx_t el_offset, t8_locidx_t &el_inserted, const std::vector &actions, + const bool is_family, const int num_siblings) { - adapt_action action = adapt_actions[el_offset + el_considered]; - if (!is_family && action == adapt_action::COARSEN) { - action = adapt_action::KEEP; + action iaction = actions[el_offset + el_considered]; + if (!is_family && iaction == action::COARSEN) { + iaction = action::KEEP; } /* Check that all siblings want to be coarsened */ - if (is_family && action == adapt_action::COARSEN) { - const auto start = adapt_actions.begin () + static_cast (el_offset + el_considered); + if (is_family && iaction == action::COARSEN) { + const auto start = actions.begin () + static_cast (el_offset + el_considered); const auto end = start + static_cast (num_siblings); - if (!std::all_of (start, end, [] (const adapt_action &a) { return a == adapt_action::COARSEN; })) { - action = adapt_action::KEEP; + if (!std::all_of (start, end, [] (const action &a) { return a == action::COARSEN; })) { + iaction = action::KEEP; } } - switch (static_cast (action)) { - case adapt_action::COARSEN: - el_inserted += manipulate_elements (elements, elements_from, scheme, tree_class, - el_inserted, el_offset + el_considered); + switch (static_cast (iaction)) { + case action::COARSEN: + el_inserted += manipulate_elements (elements, elements_from, scheme, tree_class, el_inserted, + el_offset + el_considered); break; - case adapt_action::KEEP: - el_inserted += manipulate_elements (elements, elements_from, scheme, tree_class, el_inserted, - el_offset + el_considered); + case action::KEEP: + el_inserted += manipulate_elements (elements, elements_from, scheme, tree_class, el_inserted, + el_offset + el_considered); break; - case adapt_action::REFINE: - el_inserted += manipulate_elements (elements, elements_from, scheme, tree_class, - el_inserted, el_offset + el_considered); + case action::REFINE: + el_inserted += manipulate_elements (elements, elements_from, scheme, tree_class, el_inserted, + el_offset + el_considered); break; default: { t8_errorf ("Unknown adapt action.\n"); @@ -488,12 +487,12 @@ struct manipulator * This class coordinates the process of collecting adaptation actions from a source forest and applying * element-level manipulations to a target forest. It is implemented as a policy-composition class template * and privately inherits the provided policy types: - * - TCollect: provides collect_adapt_actions(forest_from, adapt_actions, callback) to produce actions. + * - TCollect: provides collect_actions(forest_from, actions, callback) to produce actions. * - TFamily: provides family_check(...) to identify family/grouped elements (siblings). * - TManipulate: provides element_manipulator(...) to perform /coarsening/refinement or general element manipulation logic. * * Template parameters - * \tparam TCollect A type satisfying adapt_actions_collectable: must expose collect_adapt_actions. + * \tparam TCollect A type satisfying actions_collectable: must expose collect_actions. * \tparam TFamily A type satisfying family_checkable: must expose family_check to detect families. * \tparam TManipulate A type satisfying element_manipulatable: must expose element_manipulator. * @@ -502,14 +501,14 @@ struct manipulator * retains references (increments reference counts) for any non-null forest handles; on destruction it releases them. * The adapt() method: * - Asserts valid state and optionally starts profiling. - * - Uses the TCollect policy to populate adapt_actions for each element in the source forest. + * - Uses the TCollect policy to populate actions for each element in the source forest. * - Iterates over local trees of the source forest, and for each tree: * - Retrieves corresponding tree data from both source and target forests. * - Accesses the source tree's leaf element array and the number of source elements. * - For each considered source element (taking element siblings/families into account), uses the TFamily * policy to determine whether the current elements form a family, reads the precomputed adapt action for * the element, and calls the TManipulate policy to update the target tree's element array accordingly. - * - Maintains per-tree and global offsets so that adapt_actions are applied using a linear index across + * - Maintains per-tree and global offsets so that actions are applied using a linear index across * all source elements. * * Ownership and lifetime @@ -548,7 +547,7 @@ struct manipulator * - The target forest that will be modified by adaptation operations. * - t8_forest_t forest_from * - The source forest from which adaptation decisions are derived. - * - std::vector adapt_actions + * - std::vector actions * - Linear array of adaptation actions for each element in the source forest. Populated by the TCollect policy. * - callback_type callback * - The callback provided by the caller; forwarded to the collect policy. @@ -561,7 +560,7 @@ struct manipulator * different collection and manipulation strategies to be plugged in without changing the control flow. * - The adaptor relies on the forest and tree data structures exposing stable array indexing via t8_element_array_* APIs. */ -template +template class adaptor: private TCollect, private TFamily, private TManipulate { public: /** Constructor for basic_adaptation class. @@ -607,7 +606,7 @@ class adaptor: private TCollect, private TFamily, private TManipulate { } T8_ASSERT (forest_from != nullptr); - TCollect::collect_adapt_actions (forest_from, adapt_actions, callback); + TCollect::collect_actions (forest_from, actions, callback); /* Offset per tree in the source forest */ t8_locidx_t el_offset = 0; @@ -640,7 +639,7 @@ class adaptor: private TCollect, private TFamily, private TManipulate { /* manipulator step*/ TManipulate::element_manipulator (elements, tree_elements_from, scheme, tree_class, el_considered, el_offset, - el_inserted, adapt_actions, is_family, curr_size_elements_from); + el_inserted, actions, is_family, curr_size_elements_from); el_considered++; } tree->elements_offset = el_offset; @@ -673,11 +672,11 @@ class adaptor: private TCollect, private TFamily, private TManipulate { forest->profile->adapt_runtime += sc_MPI_Wtime (); } - t8_forest_t forest; /**< The target forest */ - t8_forest_t forest_from; /**< The source forest to adapt from. */ - std::vector adapt_actions; /**< The adaptation actions for each element in the source forest. */ - bool profiling = false; /**< Flag to indicate if profiling is enabled. */ -}; // class adaptor + t8_forest_t forest; /**< The target forest */ + t8_forest_t forest_from; /**< The source forest to adapt from. */ + std::vector actions; /**< The adaptation actions for each element in the source forest. */ + bool profiling = false; /**< Flag to indicate if profiling is enabled. */ +}; // class adaptor }; // namespace t8_adapt #endif /* T8_FOREST_ADAPT_HXX */ From f2262cd7671389b5e7b85e6e01c77febccd6127d Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 15:44:33 +0100 Subject: [PATCH 18/24] Improve names in concepts --- .../t8_forest_adapt/t8_forest_adapt.hxx | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index 5e318c8891..98d0d9534f 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -167,74 +167,74 @@ using batched_element_callback * a.collect_actions(const t8_forest_t, std::vector&, element_callback) * returning void. * - * \tparam T - * Type under test. The concept is satisfied when an object `a` of type T can be + * \tparam TType + * Type under test. The concept is satisfied when an object `object` of type TType can be * used in an expression - * a.collect_actions(forest_from, actions, cb) + * object.collect_actions(forest_from, actions, cb) * where: * - forest_from is of type const t8_forest_t, * - actions is of type std::vector&, * - cb is of type element_callback, * and the expression is well-formed and yields void. */ -template +template concept has_element_callback_collect - = requires (T a, const t8_forest_t forest_from, std::vector &actions, element_callback cb) { + = requires (TType object, const t8_forest_t forest_from, std::vector &actions, element_callback cb) { { - a.collect_actions (forest_from, actions, cb) + object.collect_actions (forest_from, actions, cb) } -> std::same_as; }; -/** Concept that detects whether a type T provides a member function +/** Concept that detects whether a type TType provides a member function * with the signature compatible with: - * a.collect_actions(const t8_forest_t, std::vector&, batched_element_callback) + * object.collect_actions(const t8_forest_t, std::vector&, batched_element_callback) * returning void. - * \tparam T - * Type under test. The concept is satisfied when an object `a` of type T can be + * \tparam TType + * Type under test. The concept is satisfied when an object `object` of type TType can be * used in an expression - * a.collect_actions(forest_from, actions, cb) + * object.collect_actions(forest_from, actions, cb) * where: * - forest_from is of type const t8_forest_t, * - actions is of type std::vector&, * - cb is of type batched_element_callback, * and the expression is well-formed and yields void. */ -template +template concept has_batched_callback_collect - = requires (T a, const t8_forest_t forest_from, std::vector &actions, batched_element_callback cb) { + = requires (TType object, const t8_forest_t forest_from, std::vector &actions, batched_element_callback cb) { { - a.collect_actions (forest_from, actions, cb) + object.collect_actions (forest_from, actions, cb) } -> std::same_as; }; -/** Concept that detects whether a type T provides a member function +/** Concept that detects whether a type TType provides a member function * with the signature compatible with either: - * a.collect_actions(const t8_forest_t, std::vector&, element_callback) + * object.collect_actions(const t8_forest_t, std::vector&, element_callback) * or - * a.collect_actions(const t8_forest_t, std::vector&, batched_element_callback) + * object.collect_actions(const t8_forest_t, std::vector&, batched_element_callback) * returning void. - * \tparam T - * Type under test. The concept is satisfied when an object `a` of type T can be + * \tparam TType + * Type under test. The concept is satisfied when an object `object` of type T can be * used in an expression - * a.collect_actions(forest_from, actions, cb) + * object.collect_actions(forest_from, actions, cb) * where: * - forest_from is of type const t8_forest_t, * - actions is of type std::vector&, * - cb is of type element_callback or batched_element_callback, * and the expression is well-formed and yields void. */ -template -concept actions_collectable = has_element_callback_collect || has_batched_callback_collect; +template +concept actions_collectable = has_element_callback_collect || has_batched_callback_collect; -/** Concept that detects whether a type T provides a member function +/** Concept that detects whether a type TType provides a member function * with the signature compatible with: - * a.family_check(const t8_element_array_t*, std::vector&, const t8_locidx_t, + * object.family_check(const t8_element_array_t*, std::vector&, const t8_locidx_t, * const t8_scheme*, const t8_eclass_t) * returning bool. - * \tparam T - * Type under test. The concept is satisfied when an object `a` of type T can be + * \tparam TType + * Type under test. The concept is satisfied when an object `object` of type TType can be * used in an expression - * a.family_check(tree_elements_from, elements_from, offset, scheme, tree_class) + * object.family_check(tree_elements_from, elements_from, offset, scheme, tree_class) * where: * - tree_elements_from is of type const t8_element_array_t*, * - elements_from is of type std::vector&, @@ -243,25 +243,25 @@ concept actions_collectable = has_element_callback_collect || has_batched_cal * - tree_class is of type const t8_eclass_t, * and the expression is well-formed and yields a type convertible to bool. */ -template -concept family_checkable - = requires (T a, const t8_element_array_t *tree_elements_from, std::vector &elements_from, - const t8_locidx_t offset, const t8_scheme *scheme, const t8_eclass_t tree_class) { - { - a.family_check (tree_elements_from, elements_from, offset, scheme, tree_class) - } -> std::convertible_to; - }; +template +concept family_checkable = requires (TType object, const t8_element_array_t *tree_elements_from, + std::vector &elements_from, const t8_locidx_t offset, + const t8_scheme *scheme, const t8_eclass_t tree_class) { + { + object.family_check (tree_elements_from, elements_from, offset, scheme, tree_class) + } -> std::convertible_to; +}; -/** Concept that detects whether a type T provides a member function +/** Concept that detects whether a type TType provides a member function * with the signature compatible with: -template +template concept element_manipulatable = requires ( - T a, t8_element_array_t *elements, const t8_element_array_t *const elements_from, - const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t &el_considered, + TType object, t8_element_array_t *elements, const t8_element_array_t *const elements_from, + const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t &el_considered, const t8_locidx_t el_offset, t8_locidx_t &el_inserted, const std::vector &actions, action action, const bool is_family, const int num_siblings) { { - a.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_offset, el_inserted, actions, action, is_family, num_siblings) + object.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_offset, el_inserted, actions, action, is_family, num_siblings) } -> std::same_as; }; * - elements_from is of type const t8_element_array_t*, @@ -272,14 +272,14 @@ concept element_manipulatable = requires ( * - action is of type const action, * and the expression is well-formed and yields void. */ -template +template concept element_manipulatable = requires ( - T a, t8_element_array_t *elements, const t8_element_array_t *const elements_from, const t8_scheme *scheme, + TType object, t8_element_array_t *elements, const t8_element_array_t *const elements_from, const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t &el_considered, const t8_locidx_t el_offset, t8_locidx_t &el_inserted, const std::vector &action, const bool is_family, const int num_siblings) { { - a.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_offset, el_inserted, action, - is_family, num_siblings) + object.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_offset, el_inserted, + action, is_family, num_siblings) } -> std::same_as; }; From 6db0e98c41518af3ccffe83b088ae943d7e53b65 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 16:00:17 +0100 Subject: [PATCH 19/24] fix documentation --- .../t8_forest_adapt/t8_forest_adapt.hxx | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index 98d0d9534f..060de15a30 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -155,33 +155,33 @@ using element_callback * \param [in] element The array of elements to be adapted. * \param [in] scheme The scheme used for the elements. * \param [in] tree_class The eclass of the tree containing the elements. - * \param [out] action The vector to store the adaptation actions for the elements. + * \param [out] actions The vector to store the adaptation actions for the elements. */ using batched_element_callback = std::function &action)>; + const t8_scheme *scheme, const t8_eclass_t tree_class, std::vector &actions)>; /** - * Concept that detects whether a type T provides a member function + * Concept that detects whether a type TType provides a member function * with the signature compatible with: - * a.collect_actions(const t8_forest_t, std::vector&, element_callback) + * object.collect_actions(const t8_forest_t, std::vector&, element_callback) * returning void. * * \tparam TType * Type under test. The concept is satisfied when an object `object` of type TType can be * used in an expression - * object.collect_actions(forest_from, actions, cb) + * object.collect_actions(forest_from, actions, callback) * where: * - forest_from is of type const t8_forest_t, * - actions is of type std::vector&, - * - cb is of type element_callback, + * - callback is of type element_callback, * and the expression is well-formed and yields void. */ template concept has_element_callback_collect - = requires (TType object, const t8_forest_t forest_from, std::vector &actions, element_callback cb) { + = requires (TType object, const t8_forest_t forest_from, std::vector &actions, element_callback callback) { { - object.collect_actions (forest_from, actions, cb) + object.collect_actions (forest_from, actions, callback) } -> std::same_as; }; @@ -192,20 +192,20 @@ concept has_element_callback_collect * \tparam TType * Type under test. The concept is satisfied when an object `object` of type TType can be * used in an expression - * object.collect_actions(forest_from, actions, cb) + * object.collect_actions(forest_from, actions, callback) * where: * - forest_from is of type const t8_forest_t, * - actions is of type std::vector&, - * - cb is of type batched_element_callback, + * - callback is of type batched_element_callback, * and the expression is well-formed and yields void. */ template -concept has_batched_callback_collect - = requires (TType object, const t8_forest_t forest_from, std::vector &actions, batched_element_callback cb) { - { - object.collect_actions (forest_from, actions, cb) - } -> std::same_as; - }; +concept has_batched_callback_collect = requires (TType object, const t8_forest_t forest_from, + std::vector &actions, batched_element_callback callback) { + { + object.collect_actions (forest_from, actions, callback) + } -> std::same_as; +}; /** Concept that detects whether a type TType provides a member function * with the signature compatible with either: @@ -216,11 +216,11 @@ concept has_batched_callback_collect * \tparam TType * Type under test. The concept is satisfied when an object `object` of type T can be * used in an expression - * object.collect_actions(forest_from, actions, cb) + * object.collect_actions(forest_from, actions, callback) * where: * - forest_from is of type const t8_forest_t, * - actions is of type std::vector&, - * - cb is of type element_callback or batched_element_callback, + * - callback is of type element_callback or batched_element_callback, * and the expression is well-formed and yields void. */ template @@ -259,9 +259,9 @@ concept element_manipulatable = requires ( TType object, t8_element_array_t *elements, const t8_element_array_t *const elements_from, const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t &el_considered, const t8_locidx_t el_offset, t8_locidx_t &el_inserted, const std::vector &actions, - action action, const bool is_family, const int num_siblings) { + const bool is_family, const int num_siblings) { { - object.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_offset, el_inserted, actions, action, is_family, num_siblings) + object.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_offset, el_inserted, actions, is_family, num_siblings) } -> std::same_as; }; * - elements_from is of type const t8_element_array_t*, @@ -269,7 +269,9 @@ concept element_manipulatable = requires ( * - tree_class is of type const t8_eclass_t, * - el_considered is of type const t8_locidx_t&, * - el_inserted is of type t8_locidx_t&, - * - action is of type const action, + * - actions is of type const std::vector&, + * - is_family is of type const bool, + * - num_siblings is of type const int, * and the expression is well-formed and yields void. */ template @@ -432,13 +434,15 @@ struct manipulator { /** Manipulate elements based on the given adapt action. * \param [in,out] elements The array of elements to be manipulated. - * \param [in] tree_elements_from The array of elements from the source tree. + * \param [in] elements_from The array of elements from the source tree. * \param [in] scheme The scheme to use for manipulation. * \param [in] tree_class The class of the tree. * \param [in] el_considered The index of the element being considered. - * \param [in,out] el_inserted The index of the next element to be inserted. - * \param [in] actions The global adapt actions vector (source forest linearized). - * \param [in] action The adapt action to be performed (by-value so it can be changed). + * \param [in] el_offset The offset of the element being considered. + * \param [in,out] el_inserted The index of the elements in the target tree. + * \param [in] actions The global adapt actions vector. + * \param [in] is_family Whether the current elements form a family. + * \param [in] num_siblings The number of siblings in the family. */ void element_manipulator (t8_element_array_t *elements, const t8_element_array_t *const elements_from, From 3cd21d40e3e9087202e286cfc60bec3aa586b530 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 16:12:56 +0100 Subject: [PATCH 20/24] fix documentation --- src/t8_forest/t8_forest_adapt.cxx | 2 +- .../t8_forest_adapt/t8_forest_adapt.hxx | 28 ++++++++++--------- .../t8_forest_adapt_batched.hxx | 14 ++++++++-- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/t8_forest/t8_forest_adapt.cxx b/src/t8_forest/t8_forest_adapt.cxx index 6f1f03fa24..528530b21d 100644 --- a/src/t8_forest/t8_forest_adapt.cxx +++ b/src/t8_forest/t8_forest_adapt.cxx @@ -34,7 +34,7 @@ /* We want to export the whole implementation to be callable from "C" */ T8_EXTERN_C_BEGIN (); -/* Set to 1 to enable legacy adaptation behavior */ +/** Set to 1 to enable legacy adaptation behavior */ #define T8_FOREST_ADAPT_LEGACY 0 #if T8_FOREST_ADAPT_LEGACY diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index 060de15a30..7f61a9fbbc 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -51,9 +51,9 @@ namespace t8_adapt */ class action { public: - static const int COARSEN = -1; - static const int KEEP = 0; - static const int REFINE = 1; + static const int COARSEN = -1; /**< Coarsen the element */ + static const int KEEP = 0; /**< Keep the element unchanged */ + static const int REFINE = 1; /**< Refine the element */ /** * Default constructor: KEEP action. @@ -254,16 +254,17 @@ concept family_checkable = requires (TType object, const t8_element_array_t *tre /** Concept that detects whether a type TType provides a member function * with the signature compatible with: -template -concept element_manipulatable = requires ( - TType object, t8_element_array_t *elements, const t8_element_array_t *const elements_from, - const t8_scheme *scheme, const t8_eclass_t tree_class, const t8_locidx_t &el_considered, - const t8_locidx_t el_offset, t8_locidx_t &el_inserted, const std::vector &actions, - const bool is_family, const int num_siblings) { - { - object.element_manipulator (elements, elements_from, scheme, tree_class, el_considered, el_offset, el_inserted, actions, is_family, num_siblings) - } -> std::same_as; -}; + * object.element_manipulator(t8_element_array_t*, const t8_element_array_t*, const t8_scheme*, + * const t8_eclass_t, const t8_locidx_t&, const t8_locidx_t, + * t8_locidx_t&, const std::vector&, const bool, const int) + * returning void. + * \tparam TType + * Type under test. The concept is satisfied when an object `object` of type TType can be + * used in an expression + * object.element_manipulator(elements, elements_from, scheme, tree_class, el_considered, el_offset, + * el_inserted, actions, is_family, num_siblings) + * where: + * - elements is of type t8_element_array_t*, * - elements_from is of type const t8_element_array_t*, * - scheme is of type const t8_scheme*, * - tree_class is of type const t8_eclass_t, @@ -430,6 +431,7 @@ manipulate_elements (t8_element_array_t *elements, const t8_elem return num_children; }; +/** Standard element manipulator class for adapting elements between trees. */ struct manipulator { /** Manipulate elements based on the given adapt action. diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx index 409056461e..918f8bef98 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt_batched.hxx @@ -26,12 +26,20 @@ #include /** - * Collect adaptation actions for all elements in the source forest. - */ + * Collect adaptation actions for all elements in the source forest. + */ struct batched_adapt_collector { + /** + * Collect adaptation actions for all elements in the source forest. + * \param [in] forest_from The source forest from which to collect adaptation actions. + * \param [out] adapt_actions The vector to store the collected adaptation actions. + * \param [in] callback The callback function to determine the adaptation action for each element. + * + * \warning Currently only used for testing purposes. + */ void - collect_adapt_actions (const t8_forest_t forest_from, std::vector &adapt_actions, + collect_adapt_actions (const t8_forest_t forest_from, std::vector &adapt_actions, batched_element_callback callback) { T8_ASSERT (forest_from != nullptr); From 5f03128ebd7115cd1b03d49b3c25a9bb0b3a7ff1 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 16:19:56 +0100 Subject: [PATCH 21/24] document dummy callback --- src/t8_forest/t8_forest_adapt.cxx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/t8_forest/t8_forest_adapt.cxx b/src/t8_forest/t8_forest_adapt.cxx index 528530b21d..e94ce857c8 100644 --- a/src/t8_forest/t8_forest_adapt.cxx +++ b/src/t8_forest/t8_forest_adapt.cxx @@ -699,6 +699,17 @@ t8_forest_adapt (t8_forest_t forest) } #else +/** + * Dummy callback function for element adaptation. + * This function always returns the KEEP action. + * + * \param [in] forest The forest + * \param [in] ltreeid The local tree id + * \param [in] element The element to adapt + * \param [in] scheme The scheme for this element + * \param [in] tree_class The eclass of tree the element is part of. + * \return The KEEP action. + */ t8_adapt::action dummy_callback ([[maybe_unused]] const t8_forest_t forest, [[maybe_unused]] const t8_locidx_t ltreeid, [[maybe_unused]] const t8_element_t *element, [[maybe_unused]] const t8_scheme *scheme, From 28919b61773421b39f1c0d92f15729b9d006b9ea Mon Sep 17 00:00:00 2001 From: David Knapp Date: Wed, 4 Feb 2026 16:54:12 +0100 Subject: [PATCH 22/24] callback_type has to be defined at the beginning --- src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index 7f61a9fbbc..68cc5dc5e3 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -569,6 +569,10 @@ struct manipulator template class adaptor: private TCollect, private TFamily, private TManipulate { public: + /** The type of callback used for collecting adaptation actions. */ + using callback_type + = std::conditional_t, element_callback, batched_element_callback>; + /** Constructor for basic_adaptation class. * \param [in] forest The target forest to be adapted. * \param [in] forest_from The source forest to adapt from. @@ -577,7 +581,7 @@ class adaptor: private TCollect, private TFamily, private TManipulate { * * \note The constructor increments reference counts for non-null forest handles, and the destructor will release them. */ - adaptor (t8_forest_t forest, t8_forest_t forest_from, element_callback callback_in, bool profiling_in = false) + adaptor (t8_forest_t forest, t8_forest_t forest_from, callback_type callback_in, bool profiling_in = false) : callback (callback_in), forest (forest), forest_from (forest_from), profiling (profiling_in) { T8_ASSERT (forest != nullptr); @@ -655,9 +659,6 @@ class adaptor: private TCollect, private TFamily, private TManipulate { profile_adaptation_end (); } } - /** The type of callback used for collecting adaptation actions. */ - using callback_type - = std::conditional_t, element_callback, batched_element_callback>; callback_type callback; /**< The callback function to determine adaptation actions. */ private: From 47810a216f0ddcaefec546391e15f96393ee60b4 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Thu, 5 Feb 2026 16:46:26 +0100 Subject: [PATCH 23/24] Activate experimental features from CMake --- CMakeLists.txt | 3 +++ src/CMakeLists.txt | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a7d10c002..80b25c5cd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,9 @@ option( T8CODE_ENABLE_NETCDF "Enable t8code's features which rely on netCDF" OFF option( T8CODE_USE_SYSTEM_SC "Use system-installed sc library" OFF ) option( T8CODE_USE_SYSTEM_P4EST "Use system-installed p4est library" OFF ) +option ( T8CODE_ACTIVATE_EXPERIMENTAL_ADAPT "Activate experimental adaptation implementation (may be unstable)" OFF ) +mark_as_advanced( FORCE T8CODE_ACTIVATE_EXPERIMENTAL_ADAPT) + option( T8CODE_BUILD_DOCUMENTATION "Build t8code's documentation" OFF ) cmake_dependent_option( T8CODE_BUILD_DOCUMENTATION_SPHINX "Build t8code's documentation using sphinx" OFF "T8CODE_BUILD_DOCUMENTATION" OFF ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e02137cad4..f03e79d04a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,6 +47,12 @@ if( CMAKE_BUILD_TYPE STREQUAL "Debug" ) target_compile_definitions( T8 PUBLIC T8_ENABLE_DEBUG=1 ) endif() +if (T8CODE_ACTIVATE_EXPERIMENTAL_ADAPT) + target_compile_definitions( T8 PUBLIC T8CODE_EXPERIMENTAL_ADAPT=1 ) +else() + target_compile_definitions( T8 PUBLIC T8CODE_EXPERIMENTAL_ADAPT=0 ) +endif() + if( T8CODE_EXPORT_COMPILE_COMMANDS ) set_target_properties( T8 PROPERTIES EXPORT_COMPILE_COMMANDS ON ) endif( T8CODE_EXPORT_COMPILE_COMMANDS ) From b9fc6835b38cde4e6edaac0824d5731e713464e8 Mon Sep 17 00:00:00 2001 From: David Knapp Date: Thu, 5 Feb 2026 16:47:07 +0100 Subject: [PATCH 24/24] Debugging TODO: execute element_manipulation on every leaf of the tree --- src/t8_forest/t8_forest.cxx | 1 + src/t8_forest/t8_forest_adapt.cxx | 12 ++-- .../t8_forest_adapt/t8_forest_adapt.hxx | 56 ++++++++++++++----- 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index e882a6f831..8dcb527da3 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -3569,6 +3569,7 @@ t8_forest_get_tree_leaf_element_count (t8_tree_t tree) T8_ASSERT (tree != NULL); element_count = t8_element_array_get_count (&tree->leaf_elements); + t8_debugf ("[D] Tree has %li leaf elements.\n", (long) element_count); /* check for type conversion errors */ T8_ASSERT ((size_t) element_count == t8_element_array_get_count (&tree->leaf_elements)); return element_count; diff --git a/src/t8_forest/t8_forest_adapt.cxx b/src/t8_forest/t8_forest_adapt.cxx index e94ce857c8..a1980d5f5b 100644 --- a/src/t8_forest/t8_forest_adapt.cxx +++ b/src/t8_forest/t8_forest_adapt.cxx @@ -34,10 +34,7 @@ /* We want to export the whole implementation to be callable from "C" */ T8_EXTERN_C_BEGIN (); -/** Set to 1 to enable legacy adaptation behavior */ -#define T8_FOREST_ADAPT_LEGACY 0 - -#if T8_FOREST_ADAPT_LEGACY +#if !T8CODE_EXPERIMENTAL_ADAPT #if T8_ENABLE_DEBUG /** Return zero if the first \a num_elements in \a elements are not a (sub)family. @@ -382,11 +379,9 @@ t8_forest_adapt_refine_recursive (t8_forest_t forest, t8_locidx_t ltreeid, t8_ec } } /* End while loop */ } -#endif /* TODO: optimize this when we own forest_from */ -#if T8_FOREST_ADAPT_LEGACY void t8_forest_adapt (t8_forest_t forest) { @@ -715,7 +710,7 @@ dummy_callback ([[maybe_unused]] const t8_forest_t forest, [[maybe_unused]] cons [[maybe_unused]] const t8_element_t *element, [[maybe_unused]] const t8_scheme *scheme, [[maybe_unused]] const t8_eclass_t tree_class) { - return t8_adapt::action::KEEP; + return t8_adapt::action::REFINE; } void @@ -733,6 +728,9 @@ t8_forest_adapt (t8_forest_t forest) adaptor standard_adaptor (forest, forest_from, dummy_callback, forest->profile != NULL); standard_adaptor.adapt (); + /*TODO: Update incomplete trees logic. */ + forest->incomplete_trees = 0; + t8_debugf ("t8_forest_adapt: Adaptation completed using dummy callback.\n"); } #endif /* T8_FOREST_ADAPT_LEGACY */ diff --git a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx index 68cc5dc5e3..d09a340189 100644 --- a/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx +++ b/src/t8_forest/t8_forest_adapt/t8_forest_adapt.hxx @@ -334,21 +334,27 @@ struct family_checker * \return True if the elements are siblings and form a family, false otherwise. */ bool - family_check (const t8_element_array_t *tree_elements_from, std::vector &elements_from, - const t8_locidx_t offset, const t8_scheme *scheme, const t8_eclass_t tree_class) + family_check (const t8_element_array_t *tree_elements_from, + [[maybe_unused]] std::vector &elements_from, const t8_locidx_t offset, + const t8_scheme *scheme, const t8_eclass_t tree_class) { - const int num_siblings = scheme->element_get_num_siblings (tree_class, elements_from[offset]); - for (int isibling = 0; isibling < num_siblings; isibling++) { - elements_from[isibling] - = (const t8_element_t *) t8_element_array_index_locidx (tree_elements_from, offset + (t8_locidx_t) isibling); - if (scheme->element_get_child_id (tree_class, elements_from[isibling]) != isibling) { + t8_debugf ("[D] family_check at offset %d\n", offset); + const t8_locidx_t num_elements_from = (t8_locidx_t) t8_element_array_get_count (tree_elements_from); + const t8_element_t *first_element_from = t8_element_array_index_locidx (tree_elements_from, offset); + const int num_siblings = scheme->element_get_num_siblings (tree_class, first_element_from); + std::vector children_nonconst (num_siblings); + int added_siblings = 0; + for (int isibling = 0; isibling < num_siblings && offset + (t8_locidx_t) isibling < num_elements_from; isibling++) { + children_nonconst[isibling] = const_cast ( + t8_element_array_index_locidx (tree_elements_from, offset + (t8_locidx_t) isibling)); + if (scheme->element_get_child_id (tree_class, children_nonconst[isibling]) != isibling) { return false; } + added_siblings++; + } + if (added_siblings != num_siblings) { + return false; } - /* elements_are_family expects t8_element_t *const *; build a non-const pointer array */ - std::vector children_nonconst (num_siblings); - for (int i = 0; i < num_siblings; ++i) - children_nonconst[i] = const_cast (elements_from[i]); const bool is_family = scheme->elements_are_family (tree_class, children_nonconst.data ()); return is_family; }; @@ -420,14 +426,24 @@ manipulate_elements (t8_element_array_t *elements, const t8_elem const t8_locidx_t elements_index, const t8_locidx_t elements_from_index) { const t8_element_t *element_from = t8_element_array_index_locidx (elements_from, elements_from_index); + const int level = scheme->element_get_level (tree_class, element_from); const int num_children = scheme->element_get_num_children (tree_class, element_from); /* CONTINUE WORK HERE */ (void) t8_element_array_push_count (elements, num_children); + t8_debugf ("[D] Created %ld children for element %d\n", t8_element_array_get_count (elements), elements_index); std::vector children (num_children); for (int ichildren = 0; ichildren < num_children; ichildren++) { children[ichildren] = t8_element_array_index_locidx_mutable (elements, elements_index + ichildren); } scheme->element_get_children (tree_class, element_from, num_children, children.data ()); + for (int ichildren = 0; ichildren < num_children; ichildren++) { + t8_debugf ("[D] Created child %d for element %d\n", ichildren, elements_index); + t8_element_t *child = t8_element_array_index_locidx_mutable (elements, elements_index + ichildren); + scheme->element_debug_print (tree_class, child); + const t8_linearidx_t child_linear_idx = scheme->element_get_linear_id (tree_class, child, level + 1); + t8_debugf ("[D] Child %d has linear index %ld\n", ichildren, child_linear_idx); + } + t8_debugf ("[D] created %d children \n", num_children); return num_children; }; @@ -452,6 +468,7 @@ struct manipulator const t8_locidx_t el_offset, t8_locidx_t &el_inserted, const std::vector &actions, const bool is_family, const int num_siblings) { + t8_debugf ("[D] Manipulating element"); action iaction = actions[el_offset + el_considered]; if (!is_family && iaction == action::COARSEN) { iaction = action::KEEP; @@ -618,13 +635,22 @@ class adaptor: private TCollect, private TFamily, private TManipulate { TCollect::collect_actions (forest_from, actions, callback); + t8_debugf ("Adaptation: collected %zu actions.\n", actions.size ()); + /* print adapt actions */ + for (const auto &action : actions) { + t8_debugf (" - Action: %d\n", int (action)); + } + /* Offset per tree in the source forest */ t8_locidx_t el_offset = 0; const t8_locidx_t num_trees = t8_forest_get_num_local_trees (forest_from); /* Get the scheme used by the forest */ const t8_scheme *scheme = t8_forest_get_scheme (forest_from); + t8_debugf ("Starting adaptation over %d local trees.\n", num_trees); + for (t8_locidx_t ltree_id = 0; ltree_id < num_trees; ltree_id++) { + t8_debugf ("Adapting tree %d\n", ltree_id); /* get the trees from both forests. */ t8_tree_t tree = t8_forest_get_tree (forest, ltree_id); const t8_tree_t tree_from = t8_forest_get_tree (forest_from, ltree_id); @@ -636,7 +662,7 @@ class adaptor: private TCollect, private TFamily, private TManipulate { T8_ASSERT (num_el_from == t8_forest_get_tree_num_leaf_elements (forest_from, ltree_id)); const t8_eclass_t tree_class = tree_from->eclass; /* Continue only if tree_from is not empty */ - if (num_el_from < 0) { + if (num_el_from > 0) { const t8_element_t *first_element_from = t8_element_array_index_locidx (tree_elements_from, 0); t8_locidx_t curr_size_elements_from = scheme->element_get_num_siblings (tree_class, first_element_from); /* index of the elements in source tree */ @@ -650,10 +676,14 @@ class adaptor: private TCollect, private TFamily, private TManipulate { /* manipulator step*/ TManipulate::element_manipulator (elements, tree_elements_from, scheme, tree_class, el_considered, el_offset, el_inserted, actions, is_family, curr_size_elements_from); + t8_debugf ("[D] After manipulation: el_inserted = %d\n", el_inserted); el_considered++; + el_offset += el_inserted; + forest->local_num_leaf_elements += el_inserted; } + t8_debugf ("[D] Finished adapting tree %d: inserted %d elements.\n", ltree_id, el_offset); tree->elements_offset = el_offset; - el_offset += num_el_from; + t8_debugf ("[D] Updated tree %d element offset to %d.\n", ltree_id, tree->elements_offset); } if (profiling) { profile_adaptation_end ();