From 8760dcfeefe0f916361bf0fee5f4f7d8c0687fe9 Mon Sep 17 00:00:00 2001 From: Alex Dutka <97711898+dutkalex@users.noreply.github.com> Date: Fri, 6 Mar 2026 14:38:36 +0100 Subject: [PATCH 1/3] add basic implementation --- src/t8_forest/t8_forest.cxx | 62 +++++++++++++++++++++++-------- src/t8_forest/t8_forest_general.h | 50 +++++++++++++------------ 2 files changed, 73 insertions(+), 39 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 004a313113..d64cae4e70 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -83,7 +83,7 @@ t8_forest_is_incomplete_family (const t8_forest_t forest, const t8_locidx_t ltre scheme->element_new (tree_class, 1, &element_parent_current); scheme->element_new (tree_class, 1, &element_compare); - /* We first assume that we have an (in)complete family with the size of array elements. + /* We first assume that we have an (in)complete family with the size of array elements. * In the following we try to disprove this. */ int family_size = elements_size; @@ -92,9 +92,9 @@ t8_forest_is_incomplete_family (const t8_forest_t forest, const t8_locidx_t ltre const int child_id_current = scheme->element_get_child_id (tree_class, elements[0]); scheme->element_get_parent (tree_class, elements[0], element_parent_current); - /* Elements of the current family could already be passed, so that + /* Elements of the current family could already be passed, so that * the element/family currently under consideration can no longer be coarsened. - * Also, there may be successors of a hypothetical previous family member + * Also, there may be successors of a hypothetical previous family member * that would be overlapped after coarsening. * */ if (child_id_current > 0 && el_considered > 0) { @@ -136,12 +136,12 @@ t8_forest_is_incomplete_family (const t8_forest_t forest, const t8_locidx_t ltre T8_ASSERT (family_size > 0); T8_ASSERT (family_size >= 0 && family_size <= elements_size); - /* There may be successors of a hypothetical later family member (with index + /* There may be successors of a hypothetical later family member (with index * family_size in this family) that would be overlapped after coarsening. */ if (family_size < elements_size) { /* Get level of element after last element of current possible family */ const int level = scheme->element_get_level (tree_class, elements[family_size]); - /* Only elements with higher level then level of current element, can get + /* Only elements with higher level then level of current element, can get * potentially be overlapped. */ if (level > level_current) { /* Compare ancestors */ @@ -164,7 +164,7 @@ t8_forest_is_incomplete_family (const t8_forest_t forest, const t8_locidx_t ltre const int num_siblings = scheme->element_get_num_siblings (tree_class, elements[0]); T8_ASSERT (family_size <= num_siblings); /* If the first/last element at a process boundary is not the first/last - * element of a possible family, we are not guaranteed to consider all + * element of a possible family, we are not guaranteed to consider all * family members.*/ if (el_considered == 0 && child_id_current > 0 && ltree_id == 0 && forest->mpirank > 0) { return 0; @@ -300,9 +300,9 @@ t8_forest_no_overlap ([[maybe_unused]] t8_forest_t forest) * More detailed: * Let e_a and e_b be two elements. * If the level of e_a is equal to the level of the nca of e_a and e_b, - * then e_b is a descendant of e_a. + * then e_b is a descendant of e_a. * If the level of e_b is equal to the level of the nca of e_a and e_b, - * then e_a is a descendant of e_b. + * then e_a is a descendant of e_b. * Thus e_a and e_b overlap in both cases. * Note: If e_a equals e_b, e_a is the descendant of e_b and vice versa. * */ @@ -1305,9 +1305,9 @@ t8_forest_tree_shared ([[maybe_unused]] t8_forest_t forest, [[maybe_unused]] int else { SC_ABORT ("For incomplete trees the method t8_forest_last_tree_shared aka " "t8_forest_tree_shared(forest, 1) is not implemented.\n"); - /* TODO: If last_local_tree is 0 of the current process and it gets 0 as the - * first_local_tree of the bigger process, then it cannot be said whether - * the tree with id 0 is shared or not, since the bigger process could also + /* TODO: If last_local_tree is 0 of the current process and it gets 0 as the + * first_local_tree of the bigger process, then it cannot be said whether + * the tree with id 0 is shared or not, since the bigger process could also * carry an empty forest. */ } /* If global_neighbour_tree_idx == forest->first_local_tree tree is shared */ @@ -1885,6 +1885,36 @@ t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8 pelement_indices, pneigh_eclass, forest_is_balanced, NULL, NULL); } +int +t8_forest_leaf_neighbor_subface(t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t* leaf, int face, + t8_eclass_t neighbor_tree_class, const t8_element_t* neighbor_leaf, int neighbor_face){ + t8_scheme const* scheme = t8_forest_get_scheme( forest ); + + t8_element_t* target = nullptr; + scheme->element_new( neighbor_tree_class, 1, &target ); + + int dummy; // Can't pass a nullptr to t8_forest_element_face_neighbor below (see #2214) + t8_forest_element_face_neighbor( forest, ltreeid, leaf, target, neighbor_tree_class, face, &dummy ); + + int const num_children = scheme->element_get_num_face_children( neighbor_tree_class, neighbor_leaf, neighbor_face ); + + std::array< t8_element_t*, 4 > children; // assumes a 2:1 balanced forest + scheme->element_new( neighbor_tree_class, 4, children.begin() ); + + scheme->element_get_children_at_face( neighbor_tree_class, neighbor_leaf, neighbor_face, children.begin(), num_children, nullptr ); + + int result = -1; + for ( int i_child = 0; i_child < num_children; ++i_child ){ + if ( scheme->element_compare( neighbor_tree_class, target, children[i_child] ) == 0 ){ + result = i_child; + } + } + + scheme->element_destroy( neighbor_tree_class, 4, children.begin() ); + scheme->element_destroy( neighbor_tree_class, 1, &target ); + return result; +} + void t8_forest_print_all_leaf_neighbors (t8_forest_t forest) { @@ -1960,7 +1990,7 @@ t8_forest_element_is_leaf (const t8_forest_t forest, const t8_element_t *element T8_ASSERT (t8_forest_is_committed (forest)); T8_ASSERT (t8_forest_tree_is_local (forest, local_tree)); - /* We get the array of the tree's elements and then search in the array of elements for our + /* We get the array of the tree's elements and then search in the array of elements for our * element candidate. */ /* Get the array */ const t8_element_array_t *elements = t8_forest_get_tree_leaf_element_array (forest, local_tree); @@ -1982,7 +2012,7 @@ t8_forest_element_is_leaf (const t8_forest_t forest, const t8_element_t *element /* The element was not found. */ return 0; } - /* An element was found but it may not be the candidate element. + /* An element was found but it may not be the candidate element. * To identify whether the element was found, we compare these two. */ const t8_element_t *check_element = t8_element_array_index_locidx (elements, search_result); T8_ASSERT (check_element != NULL); @@ -2323,7 +2353,7 @@ t8_forest_element_find_owner_old (t8_forest_t forest, t8_gloidx_t gtreeid, t8_el return proc; } else { - /* Get the next owning process. Its first descendant is in fact an element of the tree. + /* Get the next owning process. Its first descendant is in fact an element of the tree. * If it is bigger than the descendant we look for, then proc is the owning process of element. */ proc_next = *(int *) sc_array_index (owners_of_tree, 1); if (*(t8_linearidx_t *) t8_shmem_array_index (forest->global_first_desc, (size_t) proc_next) @@ -2966,9 +2996,9 @@ t8_forest_comm_global_num_leaf_elements (t8_forest_t forest) * Check if any tree in a forest refines irregularly. * An irregular refining tree is a tree with an element that does not * refine into 2^dim children. For example the default implementation - * of pyramids. + * of pyramids. * \note This function is MPI collective - * + * * \param[in] forest The forest to check * \return Non-zero if any tree refines irregular */ diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index 2e5b047aaa..ff6cfb41c1 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -48,7 +48,7 @@ typedef enum { T8_GHOST_VERTICES /**< Consider all vertex (codimension 3) and edge and face neighbors. */ } t8_ghost_type_t; -/** This typedef is needed as a helper construct to +/** This typedef is needed as a helper construct to * properly be able to define a function that returns * a pointer to a void fun(void) function. \see t8_forest_get_user_function. */ @@ -77,12 +77,12 @@ T8_EXTERN_C_BEGIN (); * \param [in] first_incoming The tree local index of the first incoming element. * 0 <= first_incom < new_which_tree->num_elements * - * If an element is being refined, \a refine and \a num_outgoing will be 1 and + * If an element is being refined, \a refine and \a num_outgoing will be 1 and * \a num_incoming will be the number of children. - * If a family is being coarsened, \a refine will be -1, \a num_outgoing will be - * the number of family members and \a num_incoming will be 1. - * If an element is being removed, \a refine and \a num_outgoing will be 1 and - * \a num_incoming will be 0. + * If a family is being coarsened, \a refine will be -1, \a num_outgoing will be + * the number of family members and \a num_incoming will be 1. + * If an element is being removed, \a refine and \a num_outgoing will be 1 and + * \a num_incoming will be 0. * Else \a refine will be 0 and \a num_outgoing and \a num_incoming will both be 1. * \see t8_forest_iterate_replace */ @@ -96,7 +96,7 @@ typedef void (*t8_forest_replace_t) (t8_forest_t forest_old, t8_forest_t forest_ * form a family and we decide whether this family should be coarsened * or only the first element should be refined. * Otherwise \a is_family must equal zero and we consider the first entry - * of the element array for refinement. + * of the element array for refinement. * Entries of the element array beyond the first \a num_elements are undefined. * \param [in] forest The forest to which the new elements belong. * \param [in] forest_from The forest that is adapted. @@ -121,7 +121,7 @@ typedef int (*t8_forest_adapt_t) (t8_forest_t forest, t8_forest_t forest_from, t /** Create a new forest with reference count one. * This forest needs to be specialized with the t8_forest_set_* calls. - * Currently it is mandatory to either call the functions \see t8_forest_set_mpicomm, + * Currently it is mandatory to either call the functions \see t8_forest_set_mpicomm, * \ref t8_forest_set_cmesh, and \ref t8_forest_set_scheme, * or to call one of \ref t8_forest_set_copy, \ref t8_forest_set_adapt, or * \ref t8_forest_set_partition. It is illegal to mix these calls, or to @@ -159,7 +159,7 @@ t8_forest_is_committed (t8_forest_t forest); * \param [in] forest The forest to consider. * \return True if \a forest has no elements which are inside each other. * \note This function is collective, but only checks local overlapping on each process. - * \see t8_forest_partition_test_boundary_element if you also want to test for + * \see t8_forest_partition_test_boundary_element if you also want to test for * global overlap across the process boundaries. */ int @@ -379,8 +379,8 @@ t8_forest_set_ghost_ext (t8_forest_t forest, int do_ghost, t8_ghost_type_t ghost /** * Use assertions and document that the forest_set (..., from) and - * set_load are mutually exclusive. - * + * set_load are mutually exclusive. + * * TODO: Unused function -> remove? */ void @@ -452,7 +452,7 @@ t8_forest_get_eclass (const t8_forest_t forest, const t8_locidx_t ltreeid); /** * Check whether a given tree id belongs to a local tree in a forest. - * + * * \param [in] forest The forest. * \param [in] local_tree A tree id. * \return True if and only if the id \a local_tree belongs to a local tree of \a forest. @@ -467,8 +467,8 @@ t8_forest_tree_is_local (const t8_forest_t forest, const t8_locidx_t local_tree) * \param [in] forest The forest. * \param [in] gtreeid The global id of a tree. * \return The tree's local id in \a forest, if it is a local tree. - * A negative number if not. Ghosts trees are not considered - * as local. + * A negative number if not. Ghosts trees are not considered + * as local. * \see t8_forest_get_local_or_ghost_id for ghost trees. * \see https://github.com/DLR-AMR/t8code/wiki/Tree-indexing for more details about tree indexing. */ @@ -522,7 +522,7 @@ t8_forest_get_coarse_tree (t8_forest_t forest, t8_locidx_t ltreeid); /** * Query whether a given element is a leaf in a forest. - * + * * \param [in] forest The forest. * \param [in] element An element of a local tree in \a forest. * \param [in] local_tree A local tree id of \a forest. @@ -585,7 +585,7 @@ t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8 t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], int *num_neighbors, t8_locidx_t **pelement_indices, t8_eclass_t *pneigh_eclass, int forest_is_balanced); -/** Like \ref t8_forest_leaf_face_neighbors but also provides information about the global neighbors and the orientation. +/** Like \ref t8_forest_leaf_face_neighbors but also provides information about the global neighbors and the orientation. * \param [in] forest The forest. Must have a valid ghost layer. * \param [in] ltreeid A local tree id. * \param [in] leaf A leaf in tree \a ltreeid of \a forest. @@ -603,8 +603,8 @@ t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8 * \param [in] forest_is_balanced True if we know that \a forest is balanced, false * otherwise. * \param [out] gneigh_tree The global tree IDs of the neighbor trees. - * \param [out] orientation If not NULL on input, the face orientation is computed and stored here. - * Thus, if the face connection is an inter-tree connection the orientation of the tree-to-tree connection is stored. + * \param [out] orientation If not NULL on input, the face orientation is computed and stored here. + * Thus, if the face connection is an inter-tree connection the orientation of the tree-to-tree connection is stored. * Otherwise, the value 0 is stored. * All other parameters and behavior are identical to \ref t8_forest_leaf_face_neighbors. * \note If there are no face neighbors, then *neighbor_leaves = NULL, num_neighbors = 0, @@ -628,6 +628,10 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_locidx_t **pelement_indices, t8_eclass_t *pneigh_eclass, int forest_is_balanced, t8_gloidx_t *gneigh_tree, int *orientation); +int +t8_forest_leaf_neighbor_subface(t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t* leaf, int face, + t8_eclass_t neighbor_tree_class, const t8_element_t* neighbor_leaf, int neighbor_face); + /** Exchange ghost information of user defined element data. * \param [in] forest The forest. Must be committed. * \param [in] element_data An array of length num_local_elements + num_ghosts @@ -854,7 +858,7 @@ t8_forest_element_face_neighbor (t8_forest_t forest, t8_locidx_t ltreeid, const /** * TODO: Can be removed since it is unused. - * + * * \param[in] forest The forest. */ void @@ -862,16 +866,16 @@ t8_forest_iterate (t8_forest_t forest); /** Query whether a batch of points lies inside an element. For bilinearly interpolated elements. * \note For 2D quadrilateral elements this function is only an approximation. It is correct - * if the four vertices lie in the same plane, but it may produce only approximate results if + * if the four vertices lie in the same plane, but it may produce only approximate results if * the vertices do not lie in the same plane. * \param [in] forest The forest. * \param [in] ltreeid The forest local id of the tree in which the element is. * \param [in] element The element. * \param [in] points 3-dimensional coordinates of the points to check * \param [in] num_points The number of points to check - * \param [in, out] is_inside An array of length \a num_points, filled with 0/1 on output. True (non-zero) if a \a point - * lies within an \a element, false otherwise. The return value is also true if the point - * lies on the element boundary. Thus, this function may return true for different leaf + * \param [in, out] is_inside An array of length \a num_points, filled with 0/1 on output. True (non-zero) if a \a point + * lies within an \a element, false otherwise. The return value is also true if the point + * lies on the element boundary. Thus, this function may return true for different leaf * elements, if they are neighbors and the point lies on the common boundary. * \param [in] tolerance Tolerance that we allow the point to not exactly match the element. * If this value is larger we detect more points. From 7908780187d9ac7a286e4c129516eab0319a2dd0 Mon Sep 17 00:00:00 2001 From: Alex Dutka <97711898+dutkalex@users.noreply.github.com> Date: Fri, 6 Mar 2026 14:42:10 +0100 Subject: [PATCH 2/3] indent --- src/t8_forest/t8_forest.cxx | 40 ++++++++++++++++--------------- src/t8_forest/t8_forest_general.h | 4 ++-- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index d64cae4e70..7d2904c9a0 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1886,33 +1886,35 @@ t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8 } int -t8_forest_leaf_neighbor_subface(t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t* leaf, int face, - t8_eclass_t neighbor_tree_class, const t8_element_t* neighbor_leaf, int neighbor_face){ - t8_scheme const* scheme = t8_forest_get_scheme( forest ); +t8_forest_leaf_neighbor_subface (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf, int face, + t8_eclass_t neighbor_tree_class, const t8_element_t *neighbor_leaf, int neighbor_face) +{ + t8_scheme const *scheme = t8_forest_get_scheme (forest); - t8_element_t* target = nullptr; - scheme->element_new( neighbor_tree_class, 1, &target ); + t8_element_t *target = nullptr; + scheme->element_new (neighbor_tree_class, 1, &target); - int dummy; // Can't pass a nullptr to t8_forest_element_face_neighbor below (see #2214) - t8_forest_element_face_neighbor( forest, ltreeid, leaf, target, neighbor_tree_class, face, &dummy ); + int dummy; // Can't pass a nullptr to t8_forest_element_face_neighbor below (see #2214) + t8_forest_element_face_neighbor (forest, ltreeid, leaf, target, neighbor_tree_class, face, &dummy); - int const num_children = scheme->element_get_num_face_children( neighbor_tree_class, neighbor_leaf, neighbor_face ); + int const num_children = scheme->element_get_num_face_children (neighbor_tree_class, neighbor_leaf, neighbor_face); - std::array< t8_element_t*, 4 > children; // assumes a 2:1 balanced forest - scheme->element_new( neighbor_tree_class, 4, children.begin() ); + std::array children; // assumes a 2:1 balanced forest + scheme->element_new (neighbor_tree_class, 4, children.begin ()); - scheme->element_get_children_at_face( neighbor_tree_class, neighbor_leaf, neighbor_face, children.begin(), num_children, nullptr ); + scheme->element_get_children_at_face (neighbor_tree_class, neighbor_leaf, neighbor_face, children.begin (), + num_children, nullptr); - int result = -1; - for ( int i_child = 0; i_child < num_children; ++i_child ){ - if ( scheme->element_compare( neighbor_tree_class, target, children[i_child] ) == 0 ){ - result = i_child; - } + int result = -1; + for (int i_child = 0; i_child < num_children; ++i_child) { + if (scheme->element_compare (neighbor_tree_class, target, children[i_child]) == 0) { + result = i_child; } + } - scheme->element_destroy( neighbor_tree_class, 4, children.begin() ); - scheme->element_destroy( neighbor_tree_class, 1, &target ); - return result; + scheme->element_destroy (neighbor_tree_class, 4, children.begin ()); + scheme->element_destroy (neighbor_tree_class, 1, &target); + return result; } void diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index ff6cfb41c1..752f5cff47 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -629,8 +629,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_gloidx_t *gneigh_tree, int *orientation); int -t8_forest_leaf_neighbor_subface(t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t* leaf, int face, - t8_eclass_t neighbor_tree_class, const t8_element_t* neighbor_leaf, int neighbor_face); +t8_forest_leaf_neighbor_subface (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf, int face, + t8_eclass_t neighbor_tree_class, const t8_element_t *neighbor_leaf, int neighbor_face); /** Exchange ghost information of user defined element data. * \param [in] forest The forest. Must be committed. From 2e89aefa0b8dd3b7e0aff82dcc2b964967a03b9c Mon Sep 17 00:00:00 2001 From: Alex Dutka <97711898+dutkalex@users.noreply.github.com> Date: Fri, 6 Mar 2026 15:05:47 +0100 Subject: [PATCH 3/3] doc --- src/t8_forest/t8_forest_general.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index 752f5cff47..3f70f02ff4 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -628,6 +628,17 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_locidx_t **pelement_indices, t8_eclass_t *pneigh_eclass, int forest_is_balanced, t8_gloidx_t *gneigh_tree, int *orientation); +/** Compute the subface index for a coarser neighbor + * \param [in] forest The forest. Must be committed and balanced. + * \param [in] ltreeid A local tree id. + * \param [in] leaf A leaf in \a ltreeid. + * \param [in] face The face index of \a leaf to consider. + * \param [in] neighbor_tree_eclass The eclass of the neighbor element. + * \param [in] neighbor_leaf The leaf of \a forest on the other side of the face of index \a face of element \a leaf. Must be one level coarser than \a leaf. + * \param [in] neighbor_face The face index of \a neighbor_leaf (i.e. the dual face of \a face). + * \returns The index of the subface of \a neighbor_face which corresponds to \a face. + * \note This function is designed to be called after \ref t8_forest_leaf_face_neighbors_ext to complement its output. + */ int t8_forest_leaf_neighbor_subface (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf, int face, t8_eclass_t neighbor_tree_class, const t8_element_t *neighbor_leaf, int neighbor_face);