From 6cc10ee09d205901b53debb20d2113456c19ddfd Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 13 Nov 2024 13:24:57 +0100 Subject: [PATCH 001/133] proper abort message when using lfn with unbalanced forest --- src/t8_forest/t8_forest.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 51b60d5814..0c6f60a1f2 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1948,7 +1948,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons } else { /* TODO: implement unbalanced version */ - SC_ABORT_NOT_REACHED (); + SC_ABORT ("Computing leaf face neighbors is only supported for balanced forest.\n"); } } From a390ed6f2fcc6647386584624ad9bf95b0abe763 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 13 Nov 2024 15:23:04 +0100 Subject: [PATCH 002/133] change leaf parameter name. Add ghost todo comments --- src/t8_forest/t8_forest.cxx | 52 ++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 0c6f60a1f2..f75935f0c2 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1474,6 +1474,7 @@ t8_forest_bin_search_lower (const t8_element_array_t *elements, const t8_lineari return elem_iter.GetCurrentIndex () - 1; } +// TODO: Extend to ghost elements t8_eclass_t t8_forest_element_neighbor_eclass (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *elem, int face) { @@ -1531,6 +1532,7 @@ t8_forest_element_face_neighbor (t8_forest_t forest, t8_locidx_t ltreeid, const ts = t8_forest_get_eclass_scheme (forest, eclass); if (neigh_scheme == ts && ts->t8_element_face_neighbor_inside (elem, neigh, face, neigh_face)) { /* The neighbor was constructed and is inside the current tree. */ + // TODO: replace with function t8_forest_local_treeid_to_global_treeid that incorporates ghosts return ltreeid + t8_forest_get_first_local_tree_id (forest); } else { @@ -1712,8 +1714,9 @@ t8_forest_leaf_face_orientation (t8_forest_t forest, const t8_locidx_t ltreeid, return orientation; } +// TODO: ltreeid must be forest ghost tree id i.e. num_local_trees + N void -t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf, +t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf_or_ghost, t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], int *num_neighbors, t8_locidx_t **pelement_indices, t8_eclass_scheme_c **pneigh_scheme, int forest_is_balanced, t8_gloidx_t *gneigh_tree, int *orientation) @@ -1730,7 +1733,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons int ineigh, *owners, different_owners, have_ghosts; T8_ASSERT (t8_forest_is_committed (forest)); - T8_ASSERT (t8_forest_element_is_leaf (forest, leaf, ltreeid)); + // TODO (ghosts): extend to ghost or add is_ghost function + T8_ASSERT (t8_forest_element_is_leaf (forest, leaf_or_ghost, ltreeid)); T8_ASSERT (!forest_is_balanced || t8_forest_is_balanced (forest)); SC_CHECK_ABORT (forest_is_balanced, "leaf face neighbors is not implemented " "for unbalanced forests.\n"); /* TODO: write version for unbalanced forests */ @@ -1745,33 +1749,38 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons ts = t8_forest_get_eclass_scheme (forest, eclass); if (orientation) { - *orientation = t8_forest_leaf_face_orientation (forest, ltreeid, ts, leaf, face); + // temp note: this will work for ghost elements + *orientation = t8_forest_leaf_face_orientation (forest, ltreeid, ts, leaf_or_ghost, face); } /* At first we compute these children of the face neighbor elements of leaf. For this, we need the * neighbor tree's eclass, scheme, and tree id */ - neigh_class = t8_forest_element_neighbor_eclass (forest, ltreeid, leaf, face); + // TODO (ghosts): Extend to support ghosts + neigh_class = t8_forest_element_neighbor_eclass (forest, ltreeid, leaf_or_ghost, face); neigh_scheme = *pneigh_scheme = t8_forest_get_eclass_scheme (forest, neigh_class); /* If we are at the maximum refinement level, we compute the neighbor instead */ - at_maxlevel = ts->t8_element_level (leaf) == t8_forest_get_maxlevel (forest); + at_maxlevel = ts->t8_element_level (leaf_or_ghost) == t8_forest_get_maxlevel (forest); + // TODO (ghosts): There is some duplicated code in the if/else block. Get rid of it if (at_maxlevel) { num_children_at_face = 1; neighbor_leaves = *pneighbor_leaves = T8_ALLOC (t8_element_t *, 1); *dual_faces = T8_ALLOC (int, 1); neigh_scheme->t8_element_new (num_children_at_face, neighbor_leaves); /* Compute neighbor element and global treeid of the neighbor */ - gneigh_treeid - = t8_forest_element_face_neighbor (forest, ltreeid, leaf, neighbor_leaves[0], neigh_scheme, face, *dual_faces); + // TODO (ghosts): Extend this to ghosts + gneigh_treeid = t8_forest_element_face_neighbor (forest, ltreeid, leaf_or_ghost, neighbor_leaves[0], neigh_scheme, + face, *dual_faces); } else { /* Allocate neighbor element */ - num_children_at_face = ts->t8_element_num_face_children (leaf, face); + num_children_at_face = ts->t8_element_num_face_children (leaf_or_ghost, face); neighbor_leaves = *pneighbor_leaves = T8_ALLOC (t8_element_t *, num_children_at_face); *dual_faces = T8_ALLOC (int, num_children_at_face); neigh_scheme->t8_element_new (num_children_at_face, neighbor_leaves); /* Compute neighbor elements and global treeid of the neighbor */ - gneigh_treeid = t8_forest_element_half_face_neighbors (forest, ltreeid, leaf, neighbor_leaves, neigh_scheme, face, - num_children_at_face, *dual_faces); + // TODO (ghosts): Extend this to ghosts + gneigh_treeid = t8_forest_element_half_face_neighbors (forest, ltreeid, leaf_or_ghost, neighbor_leaves, + neigh_scheme, face, num_children_at_face, *dual_faces); } if (gneigh_tree) { *gneigh_tree = gneigh_treeid; @@ -1817,22 +1826,31 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons if (have_ghosts) { /* At least one neighbor is a ghost, we compute the ghost treeid of the neighbor * tree. */ + // TODO (ghosts): If input is a ghost, this call may not find a tree since the neighbor may not be an element or ghost. + // In that case, we cannot return any element for this neighbor lghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, gneigh_treeid); T8_ASSERT (lghost_treeid >= 0); } - /* TODO: Maybe we do not need to compute the owners. It suffices to know + /* TODO (ghosts): Maybe we do not need to compute the owners. It suffices to know * whether the neighbor is owned by mpirank or not. */ + // TODO (ghost): Definitely check whether we can improve this whole owner logic if (!different_owners) { + // TODO (ghost): This is more complex when the neighbor is neither element nor ghost, since a subset + // could still be elements or ghosts. In that case we must identify those somehow. It is not + // enough to query neighbor_leaves[0] /* The face neighbors belong to the same process, we thus need to determine * if they are leaves or their parent or grandparent. */ neigh_id = neigh_scheme->t8_element_get_linear_id (neighbor_leaves[0], forest->maxlevel); if (owners[0] != forest->mpirank) { /* The elements are ghost elements of the same owner */ + // TODO (ghost): If the ghost tree does not exist since the input was a ghost and the neighbor is neither + // element or ghost, we cannot do the call here const t8_element_array_t *element_array = t8_forest_ghost_get_tree_elements (forest, lghost_treeid); /* Find the index in element_array of the leaf ancestor of the first neighbor. * This is either the neighbor itself or its parent, or its grandparent */ element_index = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); + // TODO (ghost): If the input is a ghost and the neighbor is neither element nor ghost, this search may not find anything T8_ASSERT (element_index >= 0); /* Get the element */ @@ -1843,6 +1861,9 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons T8_ASSERT (forest->local_num_elements <= element_index && element_index < forest->local_num_elements + t8_forest_get_num_ghosts (forest)); } + /* TEMP COMMENT + * GHOSTS REWORK CONTINUE GOING THROUGH AND PLANNING FROM HERE + */ else { /* the elements are local elements */ const t8_element_array_t *element_array = t8_forest_get_tree_element_array (forest, lneigh_treeid); @@ -1858,7 +1879,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons /* ancestor is a real ancestor, and thus the neighbor is either the parent * or the grandparent of the half neighbors. We can return it and the indices. */ /* We need to determine the dual face */ - if (neigh_scheme->t8_element_level (ancestor) == ts->t8_element_level (leaf)) { + if (neigh_scheme->t8_element_level (ancestor) == ts->t8_element_level (leaf_or_ghost)) { /* The ancestor is the same-level neighbor of leaf */ if (!at_maxlevel) { /* its dual face is the face of the parent of the first neighbor leaf */ @@ -1867,7 +1888,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons } else { /* The ancestor is the parent of the parent */ - T8_ASSERT (neigh_scheme->t8_element_level (ancestor) == ts->t8_element_level (leaf) - 1); + T8_ASSERT (neigh_scheme->t8_element_level (ancestor) == ts->t8_element_level (leaf_or_ghost) - 1); *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); if (!at_maxlevel) { @@ -1891,7 +1912,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons T8_FREE (owners); return; } - } + } // if (different owners) + /* The leaves are the face neighbors that we are looking for. */ /* The face neighbors either belong to different processes and thus must be leaves * in the forest, or the ancestor leaf of the first half neighbor is the half @@ -1948,7 +1970,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons } else { /* TODO: implement unbalanced version */ - SC_ABORT ("Computing leaf face neighbors is only supported for balanced forest.\n"); + SC_ABORT ("Computing leaf face neighbors is only supported for balanced forests.\n"); } } From a12e38e004346daf5bf4ebfb91ab9b362b4559f7 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 11:48:28 +0100 Subject: [PATCH 003/133] adapt iterate face example --- example/forest/t8_test_face_iterate.cxx | 16 ++-- src/t8_forest/t8_forest_iterate.cxx | 100 ++++++++++++++---------- src/t8_forest/t8_forest_iterate.h | 3 +- 3 files changed, 67 insertions(+), 52 deletions(-) diff --git a/example/forest/t8_test_face_iterate.cxx b/example/forest/t8_test_face_iterate.cxx index b469afbc0f..cde37afc3e 100644 --- a/example/forest/t8_test_face_iterate.cxx +++ b/example/forest/t8_test_face_iterate.cxx @@ -42,17 +42,16 @@ typedef struct } t8_test_fiterate_udata_t; static int -t8_test_fiterate_callback (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, - void *user_data, t8_locidx_t leaf_index) +t8_test_fiterate_callback (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, int is_leaf, + const t8_element_array_t *leaf_elements, t8_locidx_t leaf_index) { - double *coords; - - if (leaf_index >= 0) { - coords = ((t8_test_fiterate_udata_t *) user_data)->coords; + if (is_leaf) { + t8_test_fiterate_udata_t *user_data = (t8_test_fiterate_udata_t *) t8_forest_get_user_data (forest); + double *coords = user_data->coords; t8_forest_element_coordinate (forest, ltreeid, element, 0, coords); t8_debugf ("Leaf element in tree %i at face %i, tree local index %i has corner 0 coords %lf %lf %lf\n", ltreeid, face, (int) leaf_index, coords[0], coords[1], coords[2]); - ((t8_test_fiterate_udata_t *) user_data)->count++; + user_data->count++; } return 1; } @@ -83,7 +82,7 @@ t8_test_fiterate (t8_forest_t forest) t8_test_fiterate_udata_t udata; int iface; - num_trees = t8_forest_get_num_local_trees (forest); + num_trees = t8_forest_get_num_local_trees (forest) + t8_forest_get_num_ghost_trees (forest); for (itree = 0; itree < num_trees; itree++) { eclass = t8_forest_get_tree_class (forest, itree); ts = t8_forest_get_eclass_scheme (forest, eclass); @@ -96,6 +95,7 @@ t8_test_fiterate (t8_forest_t forest) for (iface = 0; iface < ts->t8_element_num_faces (nca); iface++) { udata.count = 0; + t8_forest_set_user_data (forest, &udata); t8_forest_iterate_faces (forest, itree, nca, iface, leaf_elements, &udata, 0, t8_test_fiterate_callback); t8_debugf ("Leaf elements at face %i:\t%i\n", iface, udata.count); } diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index b870fb4b9e..2f153b2f3a 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -81,13 +81,20 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme t8_eclass_scheme_c *ts; t8_eclass_t eclass; t8_element_t **face_children; - int child_face, num_face_children, iface; + int num_face_children, iface; int *child_indices; - size_t *split_offsets, indexa, indexb, elem_count; + size_t *split_offsets, elem_count; t8_element_array_t face_child_leaves; T8_ASSERT (t8_forest_is_committed (forest)); - T8_ASSERT (0 <= ltreeid && ltreeid < t8_forest_get_num_local_trees (forest)); + const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); + const t8_locidx_t num_ghost_trees = t8_forest_get_num_ghost_trees (forest); + const t8_locidx_t num_local_and_ghost_trees = num_local_trees + num_ghost_trees; + T8_ASSERT (0 <= ltreeid && ltreeid < num_local_and_ghost_trees); + + // Check whether we are in a local tree or ghost tree + const bool tree_is_local = t8_forest_tree_is_local (forest, ltreeid); + const bool tree_is_ghost = !tree_is_local; elem_count = t8_element_array_get_count (leaf_elements); if (elem_count == 0) { @@ -97,56 +104,63 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme eclass = t8_forest_get_tree_class (forest, ltreeid); ts = t8_forest_get_eclass_scheme (forest, eclass); + int is_leaf = 0; if (elem_count == 1) { /* There is only one leaf left, we check whether it is the same as element * and if so call the callback function */ const t8_element_t *leaf = t8_element_array_index_locidx (leaf_elements, 0); - T8_ASSERT (t8_forest_element_is_leaf (forest, leaf, ltreeid)); if (ts->t8_element_equal (element, leaf)) { - /* The element is the leaf, we are at the last stage of the recursion - * and can call the callback. */ - (void) callback (forest, ltreeid, leaf, face, user_data, tree_lindex_of_first_leaf); - return; + // TODO: this might break for ghosts, extend to ghost + T8_ASSERT (t8_forest_element_is_leaf (forest, leaf, ltreeid)); + /* The element is the leaf, we are at the last stage of the recursion */ + is_leaf = 1; } } #ifdef T8_ENABLE_DEBUG - /* Check whether element has greater level than the first leaf */ - const t8_element_t *leaf = t8_element_array_index_locidx (leaf_elements, 0); - T8_ASSERT (t8_forest_element_is_leaf (forest, leaf, ltreeid)); - T8_ASSERT (ts->t8_element_level (element) < ts->t8_element_level (leaf)); + if (!is_leaf) { + /* Check whether element has greater level than the first leaf */ + const t8_element_t *leaf = t8_element_array_index_locidx (leaf_elements, 0); + T8_ASSERT (t8_forest_element_is_leaf (forest, leaf, ltreeid)); + T8_ASSERT (ts->t8_element_level (element) < ts->t8_element_level (leaf)); + } #endif - /* Call the callback function element, we pass -index - 1 as index to indicate - * element is not a leaf, if it returns true, we continue with the top-down recursion */ - if (callback (forest, ltreeid, element, face, user_data, -tree_lindex_of_first_leaf - 1)) { - /* Enter the recursion */ - /* We compute all face children of E, compute their leaf arrays and call iterate_faces */ - /* allocate the memory to store the face children */ - num_face_children = ts->t8_element_num_face_children (element, face); - face_children = T8_ALLOC (t8_element_t *, num_face_children); - ts->t8_element_new (num_face_children, face_children); - /* Memory for the child indices of the face children */ - child_indices = T8_ALLOC (int, num_face_children); - /* Memory for the indices that split the leaf_elements array */ - split_offsets = T8_ALLOC (size_t, ts->t8_element_num_children (element) + 1); - /* Compute the face children */ - ts->t8_element_children_at_face (element, face, face_children, num_face_children, child_indices); - /* Split the leaves array in portions belonging to the children of element */ - t8_forest_split_array (element, leaf_elements, split_offsets); - for (iface = 0; iface < num_face_children; iface++) { - /* Check if there are any leaf elements for this face child */ - indexa = split_offsets[child_indices[iface]]; /* first leaf of this face child */ - indexb = split_offsets[child_indices[iface] + 1]; /* first leaf of next child */ - if (indexa < indexb) { - /* There exist leaves of this face child in leaf_elements, - * we construct an array of these leaves */ - t8_element_array_init_view (&face_child_leaves, leaf_elements, indexa, indexb - indexa); - /* Compute the corresponding face number of this face child */ - child_face = ts->t8_element_face_child_face (element, face, iface); - /* Enter the recursion */ - t8_forest_iterate_faces (forest, ltreeid, face_children[iface], child_face, &face_child_leaves, user_data, - indexa + tree_lindex_of_first_leaf, callback); - } + /* Call the callback function element, if it returns true, we continue with the top-down recursion */ + const int ret = callback (forest, ltreeid, element, face, is_leaf, leaf_elements, tree_lindex_of_first_leaf); + if (!ret || is_leaf) { + // The callback returned false or the element is a leaf. + // We abort the recursion. + return; + } + + /* Enter the recursion */ + /* We compute all face children of E, compute their leaf arrays and call iterate_faces */ + /* allocate the memory to store the face children */ + num_face_children = ts->t8_element_num_face_children (element, face); + face_children = T8_ALLOC (t8_element_t *, num_face_children); + ts->t8_element_new (num_face_children, face_children); + /* Memory for the child indices of the face children */ + child_indices = T8_ALLOC (int, num_face_children); + /* Memory for the indices that split the leaf_elements array */ + split_offsets = T8_ALLOC (size_t, ts->t8_element_num_children (element) + 1); + /* Compute the face children */ + ts->t8_element_children_at_face (element, face, face_children, num_face_children, child_indices); + /* Split the leaves array in portions belonging to the children of element */ + t8_forest_split_array (element, leaf_elements, split_offsets); + for (iface = 0; iface < num_face_children; iface++) { + /* Check if there are any leaf elements for this face child */ + const size_t indexa = split_offsets[child_indices[iface]]; /* first leaf of this face child */ + const size_t indexb = split_offsets[child_indices[iface] + 1]; /* first leaf of next child */ + if (indexa < indexb) { + /* There exist leaves of this face child in leaf_elements, + * we construct an array of these leaves */ + t8_element_array_init_view (&face_child_leaves, leaf_elements, indexa, indexb - indexa); + /* Compute the corresponding face number of this face child */ + const int child_face = ts->t8_element_face_child_face (element, face, iface); + /* Enter the recursion */ + // TODO: Change element index when ghost. + t8_forest_iterate_faces (forest, ltreeid, face_children[iface], child_face, &face_child_leaves, user_data, + indexa + tree_lindex_of_first_leaf, callback); } /* clean-up */ ts->t8_element_destroy (num_face_children, face_children); diff --git a/src/t8_forest/t8_forest_iterate.h b/src/t8_forest/t8_forest_iterate.h index cbca823f13..1ddc5a8044 100644 --- a/src/t8_forest/t8_forest_iterate.h +++ b/src/t8_forest/t8_forest_iterate.h @@ -34,7 +34,8 @@ #include typedef int (*t8_forest_iterate_face_fn) (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, - int face, void *user_data, t8_locidx_t tree_leaf_index); + int face, int is_leaf, const t8_element_array_t *leaf_elements, + t8_locidx_t tree_leaf_index); /** * A call-back function used by \ref t8_forest_search describing a search-criterion. Is called on an element and the From 413b23d693a7317d793c4709bc1c7e4442439623 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 12:43:28 +0100 Subject: [PATCH 004/133] fix deleted bracket --- src/t8_forest/t8_forest_iterate.cxx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index 2f153b2f3a..6ede79afdf 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -82,7 +82,6 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme t8_eclass_t eclass; t8_element_t **face_children; int num_face_children, iface; - int *child_indices; size_t *split_offsets, elem_count; t8_element_array_t face_child_leaves; @@ -140,20 +139,23 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme face_children = T8_ALLOC (t8_element_t *, num_face_children); ts->t8_element_new (num_face_children, face_children); /* Memory for the child indices of the face children */ - child_indices = T8_ALLOC (int, num_face_children); + int *child_indices = T8_ALLOC (int, num_face_children); /* Memory for the indices that split the leaf_elements array */ - split_offsets = T8_ALLOC (size_t, ts->t8_element_num_children (element) + 1); + const int num_children = ts->t8_element_num_children (element); + split_offsets = T8_ALLOC (size_t, num_children + 1); /* Compute the face children */ ts->t8_element_children_at_face (element, face, face_children, num_face_children, child_indices); /* Split the leaves array in portions belonging to the children of element */ t8_forest_split_array (element, leaf_elements, split_offsets); for (iface = 0; iface < num_face_children; iface++) { /* Check if there are any leaf elements for this face child */ + T8_ASSERT (0 <= child_indices[iface]); + T8_ASSERT (child_indices[iface] < num_children + 1); const size_t indexa = split_offsets[child_indices[iface]]; /* first leaf of this face child */ const size_t indexb = split_offsets[child_indices[iface] + 1]; /* first leaf of next child */ if (indexa < indexb) { /* There exist leaves of this face child in leaf_elements, - * we construct an array of these leaves */ + * we construct an array of these leaves */ t8_element_array_init_view (&face_child_leaves, leaf_elements, indexa, indexb - indexa); /* Compute the corresponding face number of this face child */ const int child_face = ts->t8_element_face_child_face (element, face, iface); @@ -162,12 +164,12 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme t8_forest_iterate_faces (forest, ltreeid, face_children[iface], child_face, &face_child_leaves, user_data, indexa + tree_lindex_of_first_leaf, callback); } - /* clean-up */ - ts->t8_element_destroy (num_face_children, face_children); - T8_FREE (face_children); - T8_FREE (child_indices); - T8_FREE (split_offsets); } + /* clean-up */ + ts->t8_element_destroy (num_face_children, face_children); + T8_FREE (face_children); + T8_FREE (child_indices); + T8_FREE (split_offsets); } /* The recursion that is called from t8_forest_search_tree From 0d5fe2a86f4f583a457317130fbbc7abff6d5a7b Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 12:47:11 +0100 Subject: [PATCH 005/133] use RAII where possible --- src/t8_forest/t8_forest_iterate.cxx | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index 6ede79afdf..754d835087 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -78,12 +78,6 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme t8_element_array_t *leaf_elements, void *user_data, t8_locidx_t tree_lindex_of_first_leaf, t8_forest_iterate_face_fn callback) { - t8_eclass_scheme_c *ts; - t8_eclass_t eclass; - t8_element_t **face_children; - int num_face_children, iface; - size_t *split_offsets, elem_count; - t8_element_array_t face_child_leaves; T8_ASSERT (t8_forest_is_committed (forest)); const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); @@ -95,13 +89,13 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme const bool tree_is_local = t8_forest_tree_is_local (forest, ltreeid); const bool tree_is_ghost = !tree_is_local; - elem_count = t8_element_array_get_count (leaf_elements); + const size_t elem_count = t8_element_array_get_count (leaf_elements); if (elem_count == 0) { /* There are no leaves left, so we have nothing to do */ return; } - eclass = t8_forest_get_tree_class (forest, ltreeid); - ts = t8_forest_get_eclass_scheme (forest, eclass); + const t8_eclass_t eclass = t8_forest_get_tree_class (forest, ltreeid); + const t8_eclass_scheme_c *ts = t8_forest_get_eclass_scheme (forest, eclass); int is_leaf = 0; if (elem_count == 1) { @@ -135,19 +129,19 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme /* Enter the recursion */ /* We compute all face children of E, compute their leaf arrays and call iterate_faces */ /* allocate the memory to store the face children */ - num_face_children = ts->t8_element_num_face_children (element, face); - face_children = T8_ALLOC (t8_element_t *, num_face_children); + const int num_face_children = ts->t8_element_num_face_children (element, face); + t8_element_t **face_children = T8_ALLOC (t8_element_t *, num_face_children); ts->t8_element_new (num_face_children, face_children); /* Memory for the child indices of the face children */ int *child_indices = T8_ALLOC (int, num_face_children); /* Memory for the indices that split the leaf_elements array */ const int num_children = ts->t8_element_num_children (element); - split_offsets = T8_ALLOC (size_t, num_children + 1); + size_t *split_offsets = T8_ALLOC (size_t, num_children + 1); /* Compute the face children */ ts->t8_element_children_at_face (element, face, face_children, num_face_children, child_indices); /* Split the leaves array in portions belonging to the children of element */ t8_forest_split_array (element, leaf_elements, split_offsets); - for (iface = 0; iface < num_face_children; iface++) { + for (int iface = 0; iface < num_face_children; iface++) { /* Check if there are any leaf elements for this face child */ T8_ASSERT (0 <= child_indices[iface]); T8_ASSERT (child_indices[iface] < num_children + 1); @@ -156,6 +150,7 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme if (indexa < indexb) { /* There exist leaves of this face child in leaf_elements, * we construct an array of these leaves */ + t8_element_array_t face_child_leaves; t8_element_array_init_view (&face_child_leaves, leaf_elements, indexa, indexb - indexa); /* Compute the corresponding face number of this face child */ const int child_face = ts->t8_element_face_child_face (element, face, iface); From e6969d50fd8dc3e5f52d5fb482ff24045c166362 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 15:03:55 +0100 Subject: [PATCH 006/133] update iterate face example to ghost face iterate --- example/forest/t8_test_face_iterate.cxx | 39 +++++++++++++++---------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/example/forest/t8_test_face_iterate.cxx b/example/forest/t8_test_face_iterate.cxx index cde37afc3e..8169fe4621 100644 --- a/example/forest/t8_test_face_iterate.cxx +++ b/example/forest/t8_test_face_iterate.cxx @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -74,28 +75,35 @@ t8_basic_adapt (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_t static void t8_test_fiterate (t8_forest_t forest) { - t8_locidx_t itree, num_trees; - t8_eclass_t eclass; - t8_eclass_scheme_c *ts; - t8_element_t *nca; - t8_element_array_t *leaf_elements; t8_test_fiterate_udata_t udata; - int iface; - num_trees = t8_forest_get_num_local_trees (forest) + t8_forest_get_num_ghost_trees (forest); - for (itree = 0; itree < num_trees; itree++) { - eclass = t8_forest_get_tree_class (forest, itree); - ts = t8_forest_get_eclass_scheme (forest, eclass); - const t8_element_t *first_el = t8_forest_get_element_in_tree (forest, itree, 0); + t8_forest_set_user_data (forest, &udata); + const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); + const t8_locidx_t num_trees = num_local_trees + t8_forest_get_num_ghost_trees (forest); + for (t8_locidx_t itree = 0; itree < num_trees; itree++) { + const t8_eclass_t eclass = t8_forest_get_tree_class (forest, itree); + const t8_eclass_scheme_c *ts = t8_forest_get_eclass_scheme (forest, eclass); + // Query whether this tree is a ghost and compute its ghost tree id. + const bool is_ghost = itree >= num_local_trees; + const t8_locidx_t ghost_tree_id = is_ghost ? itree - num_local_trees : -1; + // Get the number of elements or ghosts in this tree + const t8_locidx_t num_tree_elements = is_ghost ? t8_forest_ghost_tree_num_elements (forest, ghost_tree_id) + : t8_forest_get_tree_num_elements (forest, itree); + // Get all leaf elements + const t8_element_array_t *leaf_elements = !is_ghost ? t8_forest_tree_get_leaves (forest, itree) + : t8_forest_ghost_get_tree_elements (forest, ghost_tree_id); + // Get the first and last element + const t8_element_t *first_el = (const t8_element_t *) t8_element_array_index_locidx (leaf_elements, 0); const t8_element_t *last_el - = t8_forest_get_element_in_tree (forest, itree, t8_forest_get_tree_num_elements (forest, itree) - 1); + = (const t8_element_t *) t8_element_array_index_locidx (leaf_elements, num_tree_elements - 1); + + t8_element_t *nca; ts->t8_element_new (1, &nca); ts->t8_element_nca (first_el, last_el, nca); - leaf_elements = t8_forest_tree_get_leaves (forest, itree); - for (iface = 0; iface < ts->t8_element_num_faces (nca); iface++) { + // + for (int iface = 0; iface < ts->t8_element_num_faces (nca); iface++) { udata.count = 0; - t8_forest_set_user_data (forest, &udata); t8_forest_iterate_faces (forest, itree, nca, iface, leaf_elements, &udata, 0, t8_test_fiterate_callback); t8_debugf ("Leaf elements at face %i:\t%i\n", iface, udata.count); } @@ -140,6 +148,7 @@ t8_test_fiterate_refine_and_partition (t8_cmesh_t cmesh, int level, sc_MPI_Comm /* partition the adapted forest */ t8_forest_init (&forest_partition); t8_forest_set_partition (forest_partition, forest_adapt, 0); + t8_forest_set_ghost (forest_partition, 1, T8_GHOST_FACES); t8_forest_commit (forest_partition); t8_debugf ("Created ghost structure with %li ghost elements.\n", (long) t8_forest_get_num_ghosts (forest_partition)); if (!no_vtk) { From 32571235f8c857f950a8d4d9a801286317251126 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 15:18:46 +0100 Subject: [PATCH 007/133] more const for split_array functions --- src/t8_data/t8_containers.cxx | 4 ++-- src/t8_data/t8_containers.h | 2 +- src/t8_forest/t8_forest_iterate.cxx | 8 ++++---- src/t8_forest/t8_forest_iterate.h | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/t8_data/t8_containers.cxx b/src/t8_data/t8_containers.cxx index 70d7495cad..c4ea724dfb 100644 --- a/src/t8_data/t8_containers.cxx +++ b/src/t8_data/t8_containers.cxx @@ -114,12 +114,12 @@ t8_element_array_init_size (t8_element_array_t *element_array, t8_eclass_scheme_ } void -t8_element_array_init_view (t8_element_array_t *view, t8_element_array_t *array, size_t offset, size_t length) +t8_element_array_init_view (t8_element_array_t *view, const t8_element_array_t *array, size_t offset, size_t length) { T8_ASSERT (t8_element_array_is_valid (array)); /* Initialize the element array */ - sc_array_init_view (&view->array, &array->array, offset, length); + sc_array_init_view (&view->array, &((t8_element_array_t *) array)->array, offset, length); /* Set the scheme */ view->scheme = array->scheme; T8_ASSERT (t8_element_array_is_valid (view)); diff --git a/src/t8_data/t8_containers.h b/src/t8_data/t8_containers.h index 28f6547bce..3999ff334f 100644 --- a/src/t8_data/t8_containers.h +++ b/src/t8_data/t8_containers.h @@ -91,7 +91,7 @@ t8_element_array_init_size (t8_element_array_t *element_array, t8_eclass_scheme_ * It is not necessary to call sc_array_reset later. */ void -t8_element_array_init_view (t8_element_array_t *view, t8_element_array_t *array, size_t offset, size_t length); +t8_element_array_init_view (t8_element_array_t *view, const t8_element_array_t *array, size_t offset, size_t length); /** Initializes an already allocated (or static) view from given plain C data * (array of t8_element_t). diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index 754d835087..8bb9c51912 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -51,7 +51,7 @@ t8_forest_determine_child_type (sc_array_t *leaf_elements, size_t index, void *d } void -t8_forest_split_array (const t8_element_t *element, t8_element_array_t *leaf_elements, size_t *offsets) +t8_forest_split_array (const t8_element_t *element, const t8_element_array_t *leaf_elements, size_t *offsets) { sc_array_t offset_view; t8_forest_child_type_query_t query_data; @@ -62,20 +62,20 @@ t8_forest_split_array (const t8_element_t *element, t8_element_array_t *leaf_ele query_data.level = ts->t8_element_level (element); query_data.ts = ts; - sc_array_t *element_array = t8_element_array_get_array_mutable (leaf_elements); + const sc_array_t *element_array = t8_element_array_get_array (leaf_elements); /* Split the elements array according to the elements' ancestor id at * the given level. In other words for each child C of element, find * the indices i, j such that all descendants of C are * elements[i], ..., elements[j-1] */ sc_array_init_data (&offset_view, offsets, sizeof (size_t), query_data.num_children + 1); - sc_array_split (element_array, &offset_view, query_data.num_children, t8_forest_determine_child_type, + sc_array_split ((sc_array_t *) element_array, &offset_view, query_data.num_children, t8_forest_determine_child_type, (void *) &query_data); } void t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, - t8_element_array_t *leaf_elements, void *user_data, t8_locidx_t tree_lindex_of_first_leaf, + const t8_element_array_t *leaf_elements, void *user_data, t8_locidx_t tree_lindex_of_first_leaf, t8_forest_iterate_face_fn callback) { diff --git a/src/t8_forest/t8_forest_iterate.h b/src/t8_forest/t8_forest_iterate.h index 1ddc5a8044..1b783616af 100644 --- a/src/t8_forest/t8_forest_iterate.h +++ b/src/t8_forest/t8_forest_iterate.h @@ -85,7 +85,7 @@ T8_EXTERN_C_BEGIN (); /* TODO: Document */ void -t8_forest_split_array (const t8_element_t *element, t8_element_array_t *leaf_elements, size_t *offsets); +t8_forest_split_array (const t8_element_t *element, const t8_element_array_t *leaf_elements, size_t *offsets); /* TODO: comment */ /* Iterate over all leaves of an element that touch a given face of the element */ From 8118b9513eaecffa0902f1e4618c923a225ff27f Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 15:21:27 +0100 Subject: [PATCH 008/133] add ghost check functionality to t8_forest_element_is_leaf --- src/t8_forest/t8_forest.cxx | 13 ++++++++++++- src/t8_forest/t8_forest_general.h | 19 +++++++++++++++++++ src/t8_forest/t8_forest_ghost.cxx | 18 +++++++++++++++++- src/t8_forest/t8_forest_ghost.h | 14 ++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index f75935f0c2..9d36d86838 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -2056,6 +2056,15 @@ t8_forest_tree_is_local (const t8_forest_t forest, const t8_locidx_t local_tree) int t8_forest_element_is_leaf (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t local_tree) +{ + bool check_ghost = false; + T8_ASSERT (t8_forest_tree_is_local (forest, local_tree)); + return t8_forest_element_is_leaf_or_ghost (forest, element, local_tree, check_ghost); +} + +int +t8_forest_element_is_leaf_or_ghost (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t local_tree, + const int check_ghost) { T8_ASSERT (t8_forest_is_committed (forest)); T8_ASSERT (t8_forest_tree_is_local (forest, local_tree)); @@ -2063,7 +2072,9 @@ t8_forest_element_is_leaf (const t8_forest_t forest, const t8_element_t *element /* 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_element_array (forest, local_tree); + const t8_element_array_t *elements = !check_ghost ? t8_forest_tree_get_leaves (forest, local_tree) + : t8_forest_ghost_get_tree_elements (forest, local_tree); + T8_ASSERT (elements != NULL); /* In order to find the element, we need to compute its linear id. diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index cf4d4db3f3..51e1490cb0 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -517,6 +517,25 @@ t8_forest_get_coarse_tree (t8_forest_t forest, t8_locidx_t ltreeid); int t8_forest_element_is_leaf (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t local_tree); +/** + * Query whether a given element or a ghost is a leaf of a local or ghost tree 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 or a ghost tree id + * \param [in] check_ghost If true \a element is interpreted as a ghost element and + * \a local_tree as the id of a ghost tree (0 <= \a local_tree < num_ghost_trees). + * If false \a element is interpreted as an element and \a local_tree as + * the id of a local tree (0 <= \a local_tree < num_local_trees). + * \return True (non-zero) if and only if \a element is a leaf (or ghost) in \a local_tree of \a forest. + * \note \a forest must be committed before calling this function. + * \ref t8_forest_element_is_leaf + * \ref t8_forest_element_is_ghost + */ +int +t8_forest_element_is_leaf_or_ghost (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t local_tree, + const int check_ghost); + /** Compute the leaf face orientation at given face in a forest. * \param [in] forest The forest. Must have a valid ghost layer. * \param [in] ltreeid A local tree id. diff --git a/src/t8_forest/t8_forest_ghost.cxx b/src/t8_forest/t8_forest_ghost.cxx index c2c9691caf..d26cf7beb8 100644 --- a/src/t8_forest/t8_forest_ghost.cxx +++ b/src/t8_forest/t8_forest_ghost.cxx @@ -263,6 +263,14 @@ t8_forest_ghost_num_trees (const t8_forest_t forest) return forest->ghosts->ghost_trees->elem_count; } +static bool +t8_forest_tree_is_ghost (const t8_forest_t forest, const t8_locidx_t lghost_tree) +{ + T8_ASSERT (t8_forest_is_committed (forest)); + + return 0 <= lghost_tree && lghost_tree < t8_forest_get_num_ghost_trees (forest); +} + /* Given an index into the ghost_trees array return the ghost tree */ static t8_ghost_tree_t * t8_forest_ghost_get_tree (const t8_forest_t forest, const t8_locidx_t lghost_tree) @@ -274,7 +282,7 @@ t8_forest_ghost_get_tree (const t8_forest_t forest, const t8_locidx_t lghost_tre ghost = forest->ghosts; T8_ASSERT (ghost != NULL); T8_ASSERT (ghost->ghost_trees != NULL); - T8_ASSERT (0 <= lghost_tree && lghost_tree < t8_forest_ghost_num_trees (forest)); + T8_ASSERT (t8_forest_tree_is_ghost (forest, lghost_tree)); ghost_tree = (t8_ghost_tree_t *) t8_sc_array_index_locidx (ghost->ghost_trees, lghost_tree); return ghost_tree; @@ -365,6 +373,14 @@ t8_forest_ghost_get_element (t8_forest_t forest, t8_locidx_t lghost_tree, t8_loc return t8_element_array_index_locidx_mutable (&ghost_tree->elements, lelement); } +int +t8_forest_element_is_ghost (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t lghost_tree) +{ + bool check_ghost = true; + T8_ASSERT (t8_forest_tree_is_ghost (forest, lghost_tree)); + return t8_forest_element_is_leaf_or_ghost (forest, element, lghost_tree, check_ghost); +} + /* Initialize a t8_ghost_remote_tree_t */ static void t8_ghost_init_remote_tree (t8_forest_t forest, t8_gloidx_t gtreeid, int remote_rank, t8_eclass_t eclass, diff --git a/src/t8_forest/t8_forest_ghost.h b/src/t8_forest/t8_forest_ghost.h index 427a73109e..f9b3e02785 100644 --- a/src/t8_forest/t8_forest_ghost.h +++ b/src/t8_forest/t8_forest_ghost.h @@ -108,6 +108,20 @@ t8_forest_ghost_get_global_treeid (const t8_forest_t forest, const t8_locidx_t l t8_element_t * t8_forest_ghost_get_element (t8_forest_t forest, t8_locidx_t lghost_tree, t8_locidx_t lelement); + +/** + * Query whether a given element is a ghost of a certrain tree in a forest. + * + * \param [in] forest The forest. + * \param [in] element An element of a ghost tree in \a forest. + * \param [in] lghost_tree A local ghost tree id of \a forest. (0 <= \a lghost_tree < num_ghost_trees) + * \return True (non-zero) if and only if \a element is a ghost in \a lghost_tree of \a forest. + * \note \a forest must be committed before calling this function. + */ +int +t8_forest_element_is_ghost (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t lghost_tree); + + /** Return the array of remote ranks. * \param [in] forest A forest with constructed ghost layer. * \param [in,out] num_remotes On output the number of remote ranks is stored here. From 6c331bd4b8f857a3142e1d7fdb59c03f2a221b2e Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 15:22:11 +0100 Subject: [PATCH 009/133] comment and remove empty lines --- src/t8_forest/t8_forest_ghost.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/t8_forest/t8_forest_ghost.h b/src/t8_forest/t8_forest_ghost.h index f9b3e02785..3b8042df10 100644 --- a/src/t8_forest/t8_forest_ghost.h +++ b/src/t8_forest/t8_forest_ghost.h @@ -96,7 +96,7 @@ t8_forest_ghost_get_tree_class (const t8_forest_t forest, const t8_locidx_t lgho /** Given a local ghost tree compute the global tree id of it. * \param [in] forest The forest. Ghost layer must exist. - * \param [in] lghost_tree The ghost tree id of a ghost tree. + * \param [in] lghost_tree The ghost tree id of a ghost tree. (0 <= \a lghost_tree < num_ghost_trees) * \return The global id of the local ghost tree \a lghost_tree. * \a forest must be committed before calling this function. * \see https://github.com/DLR-AMR/t8code/wiki/Tree-indexing for more details about tree indexing. @@ -108,7 +108,6 @@ t8_forest_ghost_get_global_treeid (const t8_forest_t forest, const t8_locidx_t l t8_element_t * t8_forest_ghost_get_element (t8_forest_t forest, t8_locidx_t lghost_tree, t8_locidx_t lelement); - /** * Query whether a given element is a ghost of a certrain tree in a forest. * @@ -121,7 +120,6 @@ t8_forest_ghost_get_element (t8_forest_t forest, t8_locidx_t lghost_tree, t8_loc int t8_forest_element_is_ghost (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t lghost_tree); - /** Return the array of remote ranks. * \param [in] forest A forest with constructed ghost layer. * \param [in,out] num_remotes On output the number of remote ranks is stored here. From 6f2b7b74b490705f42e26b84743fa28ca067e904 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 15:22:31 +0100 Subject: [PATCH 010/133] iterate faces now supports ghosts --- src/t8_forest/t8_forest_iterate.cxx | 7 ++++--- src/t8_forest/t8_forest_iterate.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index 8bb9c51912..52c8c0194d 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -75,7 +75,7 @@ t8_forest_split_array (const t8_element_t *element, const t8_element_array_t *le void t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, - const t8_element_array_t *leaf_elements, void *user_data, t8_locidx_t tree_lindex_of_first_leaf, + const t8_element_array_t *leaf_elements, t8_locidx_t tree_lindex_of_first_leaf, t8_forest_iterate_face_fn callback) { @@ -88,6 +88,7 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme // Check whether we are in a local tree or ghost tree const bool tree_is_local = t8_forest_tree_is_local (forest, ltreeid); const bool tree_is_ghost = !tree_is_local; + const bool local_or_ghost_tree_id = tree_is_local ? ltreeid : ltreeid - num_local_trees; const size_t elem_count = t8_element_array_get_count (leaf_elements); if (elem_count == 0) { @@ -104,7 +105,7 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme const t8_element_t *leaf = t8_element_array_index_locidx (leaf_elements, 0); if (ts->t8_element_equal (element, leaf)) { // TODO: this might break for ghosts, extend to ghost - T8_ASSERT (t8_forest_element_is_leaf (forest, leaf, ltreeid)); + T8_ASSERT (t8_forest_element_is_leaf_or_ghost (forest, leaf, local_or_ghost_tree_id, tree_is_ghost)); /* The element is the leaf, we are at the last stage of the recursion */ is_leaf = 1; } @@ -113,7 +114,7 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme if (!is_leaf) { /* Check whether element has greater level than the first leaf */ const t8_element_t *leaf = t8_element_array_index_locidx (leaf_elements, 0); - T8_ASSERT (t8_forest_element_is_leaf (forest, leaf, ltreeid)); + T8_ASSERT (t8_forest_element_is_leaf_or_ghost (forest, leaf, local_or_ghost_tree_id, tree_is_ghost)); T8_ASSERT (ts->t8_element_level (element) < ts->t8_element_level (leaf)); } #endif diff --git a/src/t8_forest/t8_forest_iterate.h b/src/t8_forest/t8_forest_iterate.h index 1b783616af..66097c5b55 100644 --- a/src/t8_forest/t8_forest_iterate.h +++ b/src/t8_forest/t8_forest_iterate.h @@ -97,7 +97,7 @@ t8_forest_split_array (const t8_element_t *element, const t8_element_array_t *le * If it returns false, the current element is not traversed further */ void t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, - t8_element_array_t *leaf_elements, void *user_data, t8_locidx_t tree_lindex_of_first_leaf, + const t8_element_array_t *leaf_elements, t8_locidx_t tree_lindex_of_first_leaf, t8_forest_iterate_face_fn callback); /* Perform a top-down search of the forest, executing a callback on each From 6226b685a38002b1e0ab932fd0bb12ba7460d3b3 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 15:21:27 +0100 Subject: [PATCH 011/133] add ghost check functionality to t8_forest_element_is_leaf --- src/t8_forest/t8_forest.cxx | 13 ++++++++++++- src/t8_forest/t8_forest_general.h | 19 +++++++++++++++++++ src/t8_forest/t8_forest_ghost.cxx | 18 +++++++++++++++++- src/t8_forest/t8_forest_ghost.h | 14 ++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 51b60d5814..ca2add47df 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -2034,6 +2034,15 @@ t8_forest_tree_is_local (const t8_forest_t forest, const t8_locidx_t local_tree) int t8_forest_element_is_leaf (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t local_tree) +{ + bool check_ghost = false; + T8_ASSERT (t8_forest_tree_is_local (forest, local_tree)); + return t8_forest_element_is_leaf_or_ghost (forest, element, local_tree, check_ghost); +} + +int +t8_forest_element_is_leaf_or_ghost (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t local_tree, + const int check_ghost) { T8_ASSERT (t8_forest_is_committed (forest)); T8_ASSERT (t8_forest_tree_is_local (forest, local_tree)); @@ -2041,7 +2050,9 @@ t8_forest_element_is_leaf (const t8_forest_t forest, const t8_element_t *element /* 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_element_array (forest, local_tree); + const t8_element_array_t *elements = !check_ghost ? t8_forest_tree_get_leaves (forest, local_tree) + : t8_forest_ghost_get_tree_elements (forest, local_tree); + T8_ASSERT (elements != NULL); /* In order to find the element, we need to compute its linear id. diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index cf4d4db3f3..51e1490cb0 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -517,6 +517,25 @@ t8_forest_get_coarse_tree (t8_forest_t forest, t8_locidx_t ltreeid); int t8_forest_element_is_leaf (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t local_tree); +/** + * Query whether a given element or a ghost is a leaf of a local or ghost tree 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 or a ghost tree id + * \param [in] check_ghost If true \a element is interpreted as a ghost element and + * \a local_tree as the id of a ghost tree (0 <= \a local_tree < num_ghost_trees). + * If false \a element is interpreted as an element and \a local_tree as + * the id of a local tree (0 <= \a local_tree < num_local_trees). + * \return True (non-zero) if and only if \a element is a leaf (or ghost) in \a local_tree of \a forest. + * \note \a forest must be committed before calling this function. + * \ref t8_forest_element_is_leaf + * \ref t8_forest_element_is_ghost + */ +int +t8_forest_element_is_leaf_or_ghost (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t local_tree, + const int check_ghost); + /** Compute the leaf face orientation at given face in a forest. * \param [in] forest The forest. Must have a valid ghost layer. * \param [in] ltreeid A local tree id. diff --git a/src/t8_forest/t8_forest_ghost.cxx b/src/t8_forest/t8_forest_ghost.cxx index c2c9691caf..d26cf7beb8 100644 --- a/src/t8_forest/t8_forest_ghost.cxx +++ b/src/t8_forest/t8_forest_ghost.cxx @@ -263,6 +263,14 @@ t8_forest_ghost_num_trees (const t8_forest_t forest) return forest->ghosts->ghost_trees->elem_count; } +static bool +t8_forest_tree_is_ghost (const t8_forest_t forest, const t8_locidx_t lghost_tree) +{ + T8_ASSERT (t8_forest_is_committed (forest)); + + return 0 <= lghost_tree && lghost_tree < t8_forest_get_num_ghost_trees (forest); +} + /* Given an index into the ghost_trees array return the ghost tree */ static t8_ghost_tree_t * t8_forest_ghost_get_tree (const t8_forest_t forest, const t8_locidx_t lghost_tree) @@ -274,7 +282,7 @@ t8_forest_ghost_get_tree (const t8_forest_t forest, const t8_locidx_t lghost_tre ghost = forest->ghosts; T8_ASSERT (ghost != NULL); T8_ASSERT (ghost->ghost_trees != NULL); - T8_ASSERT (0 <= lghost_tree && lghost_tree < t8_forest_ghost_num_trees (forest)); + T8_ASSERT (t8_forest_tree_is_ghost (forest, lghost_tree)); ghost_tree = (t8_ghost_tree_t *) t8_sc_array_index_locidx (ghost->ghost_trees, lghost_tree); return ghost_tree; @@ -365,6 +373,14 @@ t8_forest_ghost_get_element (t8_forest_t forest, t8_locidx_t lghost_tree, t8_loc return t8_element_array_index_locidx_mutable (&ghost_tree->elements, lelement); } +int +t8_forest_element_is_ghost (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t lghost_tree) +{ + bool check_ghost = true; + T8_ASSERT (t8_forest_tree_is_ghost (forest, lghost_tree)); + return t8_forest_element_is_leaf_or_ghost (forest, element, lghost_tree, check_ghost); +} + /* Initialize a t8_ghost_remote_tree_t */ static void t8_ghost_init_remote_tree (t8_forest_t forest, t8_gloidx_t gtreeid, int remote_rank, t8_eclass_t eclass, diff --git a/src/t8_forest/t8_forest_ghost.h b/src/t8_forest/t8_forest_ghost.h index 427a73109e..f9b3e02785 100644 --- a/src/t8_forest/t8_forest_ghost.h +++ b/src/t8_forest/t8_forest_ghost.h @@ -108,6 +108,20 @@ t8_forest_ghost_get_global_treeid (const t8_forest_t forest, const t8_locidx_t l t8_element_t * t8_forest_ghost_get_element (t8_forest_t forest, t8_locidx_t lghost_tree, t8_locidx_t lelement); + +/** + * Query whether a given element is a ghost of a certrain tree in a forest. + * + * \param [in] forest The forest. + * \param [in] element An element of a ghost tree in \a forest. + * \param [in] lghost_tree A local ghost tree id of \a forest. (0 <= \a lghost_tree < num_ghost_trees) + * \return True (non-zero) if and only if \a element is a ghost in \a lghost_tree of \a forest. + * \note \a forest must be committed before calling this function. + */ +int +t8_forest_element_is_ghost (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t lghost_tree); + + /** Return the array of remote ranks. * \param [in] forest A forest with constructed ghost layer. * \param [in,out] num_remotes On output the number of remote ranks is stored here. From 0fa3f7b28a91d31600781f74b70a916e485ca383 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 15:22:11 +0100 Subject: [PATCH 012/133] comment and remove empty lines --- src/t8_forest/t8_forest_ghost.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/t8_forest/t8_forest_ghost.h b/src/t8_forest/t8_forest_ghost.h index f9b3e02785..3b8042df10 100644 --- a/src/t8_forest/t8_forest_ghost.h +++ b/src/t8_forest/t8_forest_ghost.h @@ -96,7 +96,7 @@ t8_forest_ghost_get_tree_class (const t8_forest_t forest, const t8_locidx_t lgho /** Given a local ghost tree compute the global tree id of it. * \param [in] forest The forest. Ghost layer must exist. - * \param [in] lghost_tree The ghost tree id of a ghost tree. + * \param [in] lghost_tree The ghost tree id of a ghost tree. (0 <= \a lghost_tree < num_ghost_trees) * \return The global id of the local ghost tree \a lghost_tree. * \a forest must be committed before calling this function. * \see https://github.com/DLR-AMR/t8code/wiki/Tree-indexing for more details about tree indexing. @@ -108,7 +108,6 @@ t8_forest_ghost_get_global_treeid (const t8_forest_t forest, const t8_locidx_t l t8_element_t * t8_forest_ghost_get_element (t8_forest_t forest, t8_locidx_t lghost_tree, t8_locidx_t lelement); - /** * Query whether a given element is a ghost of a certrain tree in a forest. * @@ -121,7 +120,6 @@ t8_forest_ghost_get_element (t8_forest_t forest, t8_locidx_t lghost_tree, t8_loc int t8_forest_element_is_ghost (const t8_forest_t forest, const t8_element_t *element, const t8_locidx_t lghost_tree); - /** Return the array of remote ranks. * \param [in] forest A forest with constructed ghost layer. * \param [in,out] num_remotes On output the number of remote ranks is stored here. From 108664c890d72377457420354f38fad056e31f95 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 15:35:23 +0100 Subject: [PATCH 013/133] extend is leaf test to ghosts --- test/t8_forest/t8_gtest_element_is_leaf.cxx | 65 ++++++++++++++++++--- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/test/t8_forest/t8_gtest_element_is_leaf.cxx b/test/t8_forest/t8_gtest_element_is_leaf.cxx index 51d32755ae..bcb193122a 100644 --- a/test/t8_forest/t8_gtest_element_is_leaf.cxx +++ b/test/t8_forest/t8_gtest_element_is_leaf.cxx @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "test/t8_cmesh_generator/t8_cmesh_example_sets.hxx" #include @@ -62,7 +63,7 @@ t8_test_adapt_first_child (t8_forest_t forest, t8_forest_t forest_from, t8_locid return 0; } -class element_is_leaf: public testing::TestWithParam> { +class element_is_leaf_or_ghost: public testing::TestWithParam> { protected: void SetUp () override @@ -77,7 +78,7 @@ class element_is_leaf: public testing::TestWithParamt8_element_new (1, ¬_leaf); /* Iterate over all the tree's leaf elements, check whether the leaf - * is correctly identified by t8_forest_element_is_leaf, + * is correctly identified by t8_forest_element_is_leaf and t8_forest_element_is_leaf_or_ghost, * build its parent and its first child (if they exist), and verify - * that t8_forest_element_is_leaf returns false. */ + * that t8_forest_element_is_leaf and t8_forest_element_is_leaf_or_ghost returns false. */ for (t8_locidx_t ielement = 0; ielement < num_elements_in_tree; ++ielement) { const t8_element_t *leaf_element = t8_forest_get_element_in_tree (forest, itree, ielement); EXPECT_TRUE (t8_forest_element_is_leaf (forest, leaf_element, itree)); + EXPECT_TRUE (t8_forest_element_is_leaf_or_ghost (forest, leaf_element, itree, 0)); /* Compute parent and first child of element and check that they are not in the tree */ const int element_level = scheme->t8_element_level (leaf_element); if (element_level > 0) { scheme->t8_element_parent (leaf_element, not_leaf); EXPECT_FALSE (t8_forest_element_is_leaf (forest, not_leaf, itree)); + EXPECT_FALSE (t8_forest_element_is_leaf_or_ghost (forest, not_leaf, itree, 0)); } if (element_level < scheme->t8_element_maxlevel ()) { scheme->t8_element_child (leaf_element, 0, not_leaf); EXPECT_FALSE (t8_forest_element_is_leaf (forest, not_leaf, itree)); + EXPECT_FALSE (t8_forest_element_is_leaf_or_ghost (forest, not_leaf, itree, 0)); } } scheme->t8_element_destroy (1, ¬_leaf); } } -TEST_P (element_is_leaf, element_is_leaf) +TEST_P (element_is_leaf_or_ghost, element_is_leaf) { t8_test_element_is_leaf_for_forest (forest); } -TEST_P (element_is_leaf, element_is_leaf_adapt) +TEST_P (element_is_leaf_or_ghost, element_is_leaf_adapt) +{ + t8_test_element_is_leaf_for_forest (forest_adapt); +} + +void +t8_test_element_is_ghost_for_forest (t8_forest_t forest) +{ + const t8_locidx_t num_ghost_trees = t8_forest_get_num_ghost_trees (forest); + + for (t8_locidx_t ighost_tree = 0; ighost_tree < num_ghost_trees; ++ighost_tree) { + const t8_locidx_t num_elements_in_tree = t8_forest_ghost_tree_num_elements (forest, ighost_tree); + const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, ighost_tree); + const t8_eclass_scheme_c *scheme = t8_forest_get_eclass_scheme (forest, tree_class); + /* Allocate memory to build a non-ghost element. */ + t8_element_t *not_ghost; + scheme->t8_element_new (1, ¬_ghost); + /* Iterate over all the tree's ghost elements, check whether the ghost + * is correctly identified by t8_forest_element_is_ghost and t8_forest_element_is_leaf_or_ghost, + * build its parent and its first child (if they exist), and verify + * that t8_forest_element_is_leaf and t8_forest_element_is_leaf_or_ghost returns false. */ + for (t8_locidx_t ielement = 0; ielement < num_elements_in_tree; ++ielement) { + const t8_element_t *ghost_element = t8_forest_ghost_get_element (forest, ighost_tree, ielement); + EXPECT_TRUE (t8_forest_element_is_ghost (forest, ghost_element, ighost_tree)); + EXPECT_TRUE (t8_forest_element_is_leaf_or_ghost (forest, ghost_element, ighost_tree, 1)); + /* Compute parent and first child of element and check that they are not in the tree */ + const int element_level = scheme->t8_element_level (ghost_element); + if (element_level > 0) { + scheme->t8_element_parent (ghost_element, not_ghost); + EXPECT_FALSE (t8_forest_element_is_ghost (forest, not_ghost, ighost_tree)); + EXPECT_FALSE (t8_forest_element_is_leaf_or_ghost (forest, not_ghost, ighost_tree, 1)); + } + if (element_level < scheme->t8_element_maxlevel ()) { + scheme->t8_element_child (ghost_element, 0, not_ghost); + EXPECT_FALSE (t8_forest_element_is_ghost (forest, not_ghost, ighost_tree)); + EXPECT_FALSE (t8_forest_element_is_leaf_or_ghost (forest, not_ghost, ighost_tree, 1)); + } + } + scheme->t8_element_destroy (1, ¬_ghost); + } +} + +TEST_P (element_is_leaf_or_ghost, element_is_ghost) +{ + t8_test_element_is_leaf_for_forest (forest); +} + +TEST_P (element_is_leaf_or_ghost, element_is_ghost_adapt) { t8_test_element_is_leaf_for_forest (forest_adapt); } @@ -156,6 +207,6 @@ auto pretty_print_level_and_cmesh_params return name; }; -INSTANTIATE_TEST_SUITE_P (t8_gtest_element_is_leaf, element_is_leaf, +INSTANTIATE_TEST_SUITE_P (t8_gtest_element_is_leaf_or_ghost, element_is_leaf_or_ghost, testing::Combine (testing::Range (0, T8_IS_LEAF_MAX_LVL), AllCmeshsParam), pretty_print_level_and_cmesh_params); From 9402ee637e142f6fd97606a0dbd4583a9f571612 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 15:36:59 +0100 Subject: [PATCH 014/133] activate parallel testing for is leaf check --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ef9f9ba71a..bfd28922e3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -101,7 +101,7 @@ add_t8_test( NAME t8_gtest_ghost_and_owner_parallel SOURCES t8_gtest_main.cx add_t8_test( NAME t8_gtest_balance_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_balance.cxx ) add_t8_test( NAME t8_gtest_forest_commit_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_forest_commit.cxx ) add_t8_test( NAME t8_gtest_forest_face_normal_serial SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_forest_face_normal.cxx ) -add_t8_test( NAME t8_gtest_element_is_leaf_serial SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_element_is_leaf.cxx ) +add_t8_test( NAME t8_gtest_element_is_leaf_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_element_is_leaf.cxx ) add_t8_test( NAME t8_gtest_partition_data_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_partition_data.cxx ) add_t8_test( NAME t8_gtest_permute_hole_serial SOURCES t8_gtest_main.cxx t8_forest_incomplete/t8_gtest_permute_hole.cxx ) From 8f1b4d0ca9d7837e6e8f3d629a13c873b98d10dc Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 16:36:59 +0100 Subject: [PATCH 015/133] debug guards around check used only in debug mode --- src/t8_forest/t8_forest_ghost.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/t8_forest/t8_forest_ghost.cxx b/src/t8_forest/t8_forest_ghost.cxx index d26cf7beb8..9075a16dea 100644 --- a/src/t8_forest/t8_forest_ghost.cxx +++ b/src/t8_forest/t8_forest_ghost.cxx @@ -263,6 +263,7 @@ t8_forest_ghost_num_trees (const t8_forest_t forest) return forest->ghosts->ghost_trees->elem_count; } +#if T8_ENABLE_DEBUG static bool t8_forest_tree_is_ghost (const t8_forest_t forest, const t8_locidx_t lghost_tree) { @@ -270,6 +271,7 @@ t8_forest_tree_is_ghost (const t8_forest_t forest, const t8_locidx_t lghost_tree return 0 <= lghost_tree && lghost_tree < t8_forest_get_num_ghost_trees (forest); } +#endif /* Given an index into the ghost_trees array return the ghost tree */ static t8_ghost_tree_t * From 562cd4a5e339f689c03389278037cb261bf09295 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 16:36:59 +0100 Subject: [PATCH 016/133] debug guards around check used only in debug mode --- src/t8_forest/t8_forest_ghost.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/t8_forest/t8_forest_ghost.cxx b/src/t8_forest/t8_forest_ghost.cxx index d26cf7beb8..9075a16dea 100644 --- a/src/t8_forest/t8_forest_ghost.cxx +++ b/src/t8_forest/t8_forest_ghost.cxx @@ -263,6 +263,7 @@ t8_forest_ghost_num_trees (const t8_forest_t forest) return forest->ghosts->ghost_trees->elem_count; } +#if T8_ENABLE_DEBUG static bool t8_forest_tree_is_ghost (const t8_forest_t forest, const t8_locidx_t lghost_tree) { @@ -270,6 +271,7 @@ t8_forest_tree_is_ghost (const t8_forest_t forest, const t8_locidx_t lghost_tree return 0 <= lghost_tree && lghost_tree < t8_forest_get_num_ghost_trees (forest); } +#endif /* Given an index into the ghost_trees array return the ghost tree */ static t8_ghost_tree_t * From e97696a9d81467a8cd2d5eba559220ae6c0b6a81 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 14 Nov 2024 16:43:36 +0100 Subject: [PATCH 017/133] typo --- test/t8_forest/t8_gtest_element_is_leaf.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/t8_forest/t8_gtest_element_is_leaf.cxx b/test/t8_forest/t8_gtest_element_is_leaf.cxx index bcb193122a..0c6c1faab4 100644 --- a/test/t8_forest/t8_gtest_element_is_leaf.cxx +++ b/test/t8_forest/t8_gtest_element_is_leaf.cxx @@ -196,7 +196,7 @@ TEST_P (element_is_leaf_or_ghost, element_is_ghost_adapt) t8_test_element_is_leaf_for_forest (forest_adapt); } -/* Define a lambda to beatify gtest output for tuples . +/* Define a lambda to beautify gtest output for tuples . * This will set the correct level and cmesh name as part of the test case name. */ auto pretty_print_level_and_cmesh_params = [] (const testing::TestParamInfo> &info) { From 1734d0f63830b92d9d82486ef8c1a2a2769395cc Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 15 Nov 2024 13:17:15 +0100 Subject: [PATCH 018/133] make comment of bin search clearer --- src/t8_forest/t8_forest.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 9d36d86838..f4a9319ad9 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1441,7 +1441,7 @@ t8_forest_copy_trees (t8_forest_t forest, t8_forest_t from, int copy_elements) } } -/** \brief Search for a linear element id (at forest->maxlevel) in a sorted array of +/** \brief Search for a linear element id maxlevel in a sorted array of * elements. If the element does not exist, return the largest index i * such that the element at position i has a smaller id than the given one. * If no such i exists, return -1. From 26a28a3387c69d2da93423cef57dce2293b87951 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 15 Nov 2024 13:18:45 +0100 Subject: [PATCH 019/133] start with lfn extension for ghost, have structure done and know what to do now --- src/t8_forest/t8_forest.cxx | 466 ++++++++++++++++++++---------------- 1 file changed, 259 insertions(+), 207 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index f4a9319ad9..df0c09ecc6 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1721,11 +1721,9 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_locidx_t **pelement_indices, t8_eclass_scheme_c **pneigh_scheme, int forest_is_balanced, t8_gloidx_t *gneigh_tree, int *orientation) { - t8_eclass_t neigh_class, eclass; t8_gloidx_t gneigh_treeid; t8_locidx_t lneigh_treeid = -1; t8_locidx_t lghost_treeid = -1, *element_indices, element_index; - t8_eclass_scheme_c *ts, *neigh_scheme; const t8_element_t *ancestor; t8_element_t **neighbor_leaves; t8_linearidx_t neigh_id; @@ -1735,243 +1733,297 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons T8_ASSERT (t8_forest_is_committed (forest)); // TODO (ghosts): extend to ghost or add is_ghost function T8_ASSERT (t8_forest_element_is_leaf (forest, leaf_or_ghost, ltreeid)); - T8_ASSERT (!forest_is_balanced || t8_forest_is_balanced (forest)); - SC_CHECK_ABORT (forest_is_balanced, "leaf face neighbors is not implemented " - "for unbalanced forests.\n"); /* TODO: write version for unbalanced forests */ + SC_CHECK_ABORT (forest->incomplete_trees, "Leaf face neighbor is not supported for " + "forests with deleted elements.\n"); SC_CHECK_ABORT (forest->mpisize == 1 || forest->ghosts != NULL, "Ghost structure is needed for t8_forest_leaf_face_neighbors " "but was not found in forest.\n"); - if (forest_is_balanced) { - /* In a balanced forest, the leaf neighbor of a leaf is either the neighbor element itself, - * its parent or its children at the face. */ - eclass = t8_forest_get_tree_class (forest, ltreeid); - ts = t8_forest_get_eclass_scheme (forest, eclass); + const t8_eclass_t eclass = t8_forest_get_tree_class (forest, ltreeid); + const t8_eclass_scheme_c *ts = t8_forest_get_eclass_scheme (forest, eclass); - if (orientation) { - // temp note: this will work for ghost elements - *orientation = t8_forest_leaf_face_orientation (forest, ltreeid, ts, leaf_or_ghost, face); - } + if (orientation) { + // temp note: this will work for ghost elements + *orientation = t8_forest_leaf_face_orientation (forest, ltreeid, ts, leaf_or_ghost, face); + } - /* At first we compute these children of the face neighbor elements of leaf. For this, we need the - * neighbor tree's eclass, scheme, and tree id */ - // TODO (ghosts): Extend to support ghosts - neigh_class = t8_forest_element_neighbor_eclass (forest, ltreeid, leaf_or_ghost, face); - neigh_scheme = *pneigh_scheme = t8_forest_get_eclass_scheme (forest, neigh_class); - /* If we are at the maximum refinement level, we compute the neighbor instead */ - at_maxlevel = ts->t8_element_level (leaf_or_ghost) == t8_forest_get_maxlevel (forest); - // TODO (ghosts): There is some duplicated code in the if/else block. Get rid of it - if (at_maxlevel) { - num_children_at_face = 1; - neighbor_leaves = *pneighbor_leaves = T8_ALLOC (t8_element_t *, 1); - *dual_faces = T8_ALLOC (int, 1); - neigh_scheme->t8_element_new (num_children_at_face, neighbor_leaves); - /* Compute neighbor element and global treeid of the neighbor */ - // TODO (ghosts): Extend this to ghosts - gneigh_treeid = t8_forest_element_face_neighbor (forest, ltreeid, leaf_or_ghost, neighbor_leaves[0], neigh_scheme, - face, *dual_faces); - } - else { - /* Allocate neighbor element */ - num_children_at_face = ts->t8_element_num_face_children (leaf_or_ghost, face); - neighbor_leaves = *pneighbor_leaves = T8_ALLOC (t8_element_t *, num_children_at_face); - *dual_faces = T8_ALLOC (int, num_children_at_face); - neigh_scheme->t8_element_new (num_children_at_face, neighbor_leaves); - /* Compute neighbor elements and global treeid of the neighbor */ - // TODO (ghosts): Extend this to ghosts - gneigh_treeid = t8_forest_element_half_face_neighbors (forest, ltreeid, leaf_or_ghost, neighbor_leaves, - neigh_scheme, face, num_children_at_face, *dual_faces); - } - if (gneigh_tree) { - *gneigh_tree = gneigh_treeid; - } - if (gneigh_treeid < 0) { - /* There exists no face neighbor across this face, we return with this info */ - neigh_scheme->t8_element_destroy (num_children_at_face, neighbor_leaves); - T8_FREE (neighbor_leaves); - T8_FREE (*dual_faces); - *dual_faces = NULL; - *num_neighbors = 0; - *pelement_indices = NULL; - *pneighbor_leaves = NULL; - return; - } - T8_ASSERT (gneigh_treeid >= 0 && gneigh_treeid < forest->global_num_trees); - /* We have computed the half face neighbor elements, we now compute their owners, + /* At first we compute these children of the face neighbor elements of leaf. For this, we need the + * neighbor tree's eclass, scheme, and tree id */ + // TODO (ghosts): Extend to support ghosts + const t8_eclass_t neigh_class = t8_forest_element_neighbor_eclass (forest, ltreeid, leaf_or_ghost, face); + const t8_eclass_scheme_c *neigh_scheme = *pneigh_scheme = t8_forest_get_eclass_scheme (forest, neigh_class); + + // Compute the same level face neighbor + t8_element_t *same_level_neighbor; + neigh_scheme->t8_element_new (&same_level_neighbor, 1); + int neigh_face; + gneigh_treeid = t8_forest_element_face_neighbor (forest, ltreeid, leaf_or_ghost, same_level_neighbor, neigh_scheme, + face, *neigh_face); + // Compute the neighbors level and linear id + const int neighbor_level = neigh_scheme->t8_element_level (neighbor); + const t8_linearidx_t neighbor_id = neigh_scheme->t8_element_get_linear_id (neighbor); + const int maxlevel = neigh_scheme->t8_element_maxlevel (); + + // Compute the first and last face descendant of the neighbor + t8_element_t **first_and_last_face_desc; + neigh_scheme->t8_element_new (first_and_last_desc, 2); + neigh_scheme->t8_element_first_descendant_face (neighbor, neigh_face, first_and_last_face_desc[0], maxlevel); + neigh_scheme->t8_element_last_descendant_face (neighbor, neigh_face, first_and_last_face_desc[1], maxlevel); + // Allocate memory for the nca of first and last face desc + t8_element_t *nca_of_face_desc; + neigh_scheme->t8_element_new (nca_of_face_desc, 1); + + // Compute the local id of the neighbor tree and check if it is a local tree + const t8_locidx_t local_neighbor_tree = t8_forest_get_local_id (forest, gneigh_treeid); + if (0 <= local_neighbor_tree) { + // The neighbor tree is a local tree and hence there may be local neighbor elements. + } + + // Somehow collect leaf arrays + + //static t8_locidx_t + //t8_forest_bin_search_lower (const t8_element_array_t *elements, const t8_linearidx_t element_id, const int maxlevel); + const t8_element_array_t *tree_leafs = t8_forest_tree_get_leaves (forest, local_neighbor_tree); + const t8_locidx_t first_desc_search = t8_forest_bin_search_lower (tree_leafs, first_and_last_face_desc[0], maxlevel); + const t8_locidx_t last_desc_search = t8_forest_bin_search_lower (tree_leafs, first_and_last_face_desc[1], maxlevel); + if (first_desc_search < 0 && last_desc_search < 0) { + // TODO: No neighbor in this leaf array + } + else { + // The first descendant may not be in the leaf array, we then + // start with the first leaf. + const first_desc_index = SC_MAX (0, first_desc_search); + const last_desc_index = last_desc_search; + // The last descendant must have been found by the search, since the neighbor + // element itself would be a match for the search. + T8_ASSERT (0 <= last_desc_index); + } +} + +// Compute first face desc and last face desc +// Search for them in the element array +// if both not found -> no neighbor +// otherwise take 0 if first not found. Last must have been found (check) +// Compute NCA of search results +// Use NCA as start for face it. +// If first_found = last_found, there is only one neighbor and that is NCA +// Otherwise NCA will be a descendant of neighbor. + +/* If we are at the maximum refinement level, we compute the neighbor instead */ +at_maxlevel = ts->t8_element_level (leaf_or_ghost) == t8_forest_get_maxlevel (forest); +// TODO (ghosts): There is some duplicated code in the if/else block. Get rid of it +if (at_maxlevel) { + num_children_at_face = 1; + neighbor_leaves = *pneighbor_leaves = T8_ALLOC (t8_element_t *, 1); + *dual_faces = T8_ALLOC (int, 1); + neigh_scheme->t8_element_new (num_children_at_face, neighbor_leaves); + /* Compute neighbor element and global treeid of the neighbor */ + // TODO (ghosts): Extend this to ghosts + gneigh_treeid = t8_forest_element_face_neighbor (forest, ltreeid, leaf_or_ghost, neighbor_leaves[0], neigh_scheme, + face, *dual_faces); +} +else { + /* Allocate neighbor element */ + num_children_at_face = ts->t8_element_num_face_children (leaf_or_ghost, face); + neighbor_leaves = *pneighbor_leaves = T8_ALLOC (t8_element_t *, num_children_at_face); + *dual_faces = T8_ALLOC (int, num_children_at_face); + neigh_scheme->t8_element_new (num_children_at_face, neighbor_leaves); + /* Compute neighbor elements and global treeid of the neighbor */ + // TODO (ghosts): Extend this to ghosts + gneigh_treeid = t8_forest_element_half_face_neighbors (forest, ltreeid, leaf_or_ghost, neighbor_leaves, neigh_scheme, + face, num_children_at_face, *dual_faces); +} +if (gneigh_tree) { + *gneigh_tree = gneigh_treeid; +} +if (gneigh_treeid < 0) { + /* There exists no face neighbor across this face, we return with this info */ + neigh_scheme->t8_element_destroy (num_children_at_face, neighbor_leaves); + T8_FREE (neighbor_leaves); + T8_FREE (*dual_faces); + *dual_faces = NULL; + *num_neighbors = 0; + *pelement_indices = NULL; + *pneighbor_leaves = NULL; + return; +} +T8_ASSERT (gneigh_treeid >= 0 && gneigh_treeid < forest->global_num_trees); +/* We have computed the half face neighbor elements, we now compute their owners, * if they differ, we know that the half face neighbors are the neighbor leaves. * If the owners do not differ, we have to check if the neighbor leaf is their * parent or grandparent. */ - owners = T8_ALLOC (int, num_children_at_face); - different_owners = 0; - have_ghosts = 0; - for (ineigh = 0; ineigh < num_children_at_face; ineigh++) { - /* At first, we check whether the current rank owns the neighbor, since +owners = T8_ALLOC (int, num_children_at_face); +different_owners = 0; +have_ghosts = 0; +for (ineigh = 0; ineigh < num_children_at_face; ineigh++) { + /* At first, we check whether the current rank owns the neighbor, since * this is a constant time check and it is the most common case */ - if (t8_forest_element_check_owner (forest, neighbor_leaves[ineigh], gneigh_treeid, neigh_class, forest->mpirank, - at_maxlevel)) { - owners[ineigh] = forest->mpirank; - /* The neighbor tree is also a local tree. we store its local treeid */ - lneigh_treeid = t8_forest_get_local_id (forest, gneigh_treeid); - } - else { - owners[ineigh] = t8_forest_element_find_owner (forest, gneigh_treeid, neighbor_leaves[ineigh], neigh_class); - /* Store that at least one neighbor is a ghost */ - have_ghosts = 1; - } - if (ineigh > 0) { - /* Check if all owners are the same for all neighbors or not */ - different_owners = different_owners || (owners[ineigh] != owners[ineigh - 1]); - } - } - if (have_ghosts) { - /* At least one neighbor is a ghost, we compute the ghost treeid of the neighbor + if (t8_forest_element_check_owner (forest, neighbor_leaves[ineigh], gneigh_treeid, neigh_class, forest->mpirank, + at_maxlevel)) { + owners[ineigh] = forest->mpirank; + /* The neighbor tree is also a local tree. we store its local treeid */ + lneigh_treeid = t8_forest_get_local_id (forest, gneigh_treeid); + } + else { + owners[ineigh] = t8_forest_element_find_owner (forest, gneigh_treeid, neighbor_leaves[ineigh], neigh_class); + /* Store that at least one neighbor is a ghost */ + have_ghosts = 1; + } + if (ineigh > 0) { + /* Check if all owners are the same for all neighbors or not */ + different_owners = different_owners || (owners[ineigh] != owners[ineigh - 1]); + } +} +if (have_ghosts) { + /* At least one neighbor is a ghost, we compute the ghost treeid of the neighbor * tree. */ - // TODO (ghosts): If input is a ghost, this call may not find a tree since the neighbor may not be an element or ghost. - // In that case, we cannot return any element for this neighbor - lghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, gneigh_treeid); - T8_ASSERT (lghost_treeid >= 0); - } - /* TODO (ghosts): Maybe we do not need to compute the owners. It suffices to know + // TODO (ghosts): If input is a ghost, this call may not find a tree since the neighbor may not be an element or ghost. + // In that case, we cannot return any element for this neighbor + lghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, gneigh_treeid); + T8_ASSERT (lghost_treeid >= 0); +} +/* TODO (ghosts): Maybe we do not need to compute the owners. It suffices to know * whether the neighbor is owned by mpirank or not. */ - // TODO (ghost): Definitely check whether we can improve this whole owner logic - if (!different_owners) { - // TODO (ghost): This is more complex when the neighbor is neither element nor ghost, since a subset - // could still be elements or ghosts. In that case we must identify those somehow. It is not - // enough to query neighbor_leaves[0] - /* The face neighbors belong to the same process, we thus need to determine +// TODO (ghost): Definitely check whether we can improve this whole owner logic +if (!different_owners) { + // TODO (ghost): This is more complex when the neighbor is neither element nor ghost, since a subset + // could still be elements or ghosts. In that case we must identify those somehow. It is not + // enough to query neighbor_leaves[0] + /* The face neighbors belong to the same process, we thus need to determine * if they are leaves or their parent or grandparent. */ - neigh_id = neigh_scheme->t8_element_get_linear_id (neighbor_leaves[0], forest->maxlevel); - if (owners[0] != forest->mpirank) { - /* The elements are ghost elements of the same owner */ - // TODO (ghost): If the ghost tree does not exist since the input was a ghost and the neighbor is neither - // element or ghost, we cannot do the call here - const t8_element_array_t *element_array = t8_forest_ghost_get_tree_elements (forest, lghost_treeid); - /* Find the index in element_array of the leaf ancestor of the first neighbor. + neigh_id = neigh_scheme->t8_element_get_linear_id (neighbor_leaves[0], forest->maxlevel); + if (owners[0] != forest->mpirank) { + /* The elements are ghost elements of the same owner */ + // TODO (ghost): If the ghost tree does not exist since the input was a ghost and the neighbor is neither + // element or ghost, we cannot do the call here + const t8_element_array_t *element_array = t8_forest_ghost_get_tree_elements (forest, lghost_treeid); + /* Find the index in element_array of the leaf ancestor of the first neighbor. * This is either the neighbor itself or its parent, or its grandparent */ - element_index = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); - // TODO (ghost): If the input is a ghost and the neighbor is neither element nor ghost, this search may not find anything - T8_ASSERT (element_index >= 0); - - /* Get the element */ - ancestor = t8_forest_ghost_get_element (forest, lghost_treeid, element_index); - /* Add the number of ghost elements on previous ghost trees and the number of local elements. */ - element_index += t8_forest_ghost_get_tree_element_offset (forest, lghost_treeid); - element_index += t8_forest_get_local_num_elements (forest); - T8_ASSERT (forest->local_num_elements <= element_index - && element_index < forest->local_num_elements + t8_forest_get_num_ghosts (forest)); - } - /* TEMP COMMENT + element_index = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); + // TODO (ghost): If the input is a ghost and the neighbor is neither element nor ghost, this search may not find anything + T8_ASSERT (element_index >= 0); + + /* Get the element */ + ancestor = t8_forest_ghost_get_element (forest, lghost_treeid, element_index); + /* Add the number of ghost elements on previous ghost trees and the number of local elements. */ + element_index += t8_forest_ghost_get_tree_element_offset (forest, lghost_treeid); + element_index += t8_forest_get_local_num_elements (forest); + T8_ASSERT (forest->local_num_elements <= element_index + && element_index < forest->local_num_elements + t8_forest_get_num_ghosts (forest)); + } + /* TEMP COMMENT * GHOSTS REWORK CONTINUE GOING THROUGH AND PLANNING FROM HERE */ - else { - /* the elements are local elements */ - const t8_element_array_t *element_array = t8_forest_get_tree_element_array (forest, lneigh_treeid); - /* Find the index in element_array of the leaf ancestor of the first neighbor. + else { + /* the elements are local elements */ + const t8_element_array_t *element_array = t8_forest_get_tree_element_array (forest, lneigh_treeid); + /* Find the index in element_array of the leaf ancestor of the first neighbor. * This is either the neighbor itself or its parent, or its grandparent */ - element_index = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); - /* Get the element */ - ancestor = t8_forest_get_tree_element (t8_forest_get_tree (forest, lneigh_treeid), element_index); - /* Add the element offset of this tree to the index */ - element_index += t8_forest_get_tree_element_offset (forest, lneigh_treeid); - } - if (neigh_scheme->t8_element_compare (ancestor, neighbor_leaves[0]) < 0) { - /* ancestor is a real ancestor, and thus the neighbor is either the parent + element_index = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); + /* Get the element */ + ancestor = t8_forest_get_tree_element (t8_forest_get_tree (forest, lneigh_treeid), element_index); + /* Add the element offset of this tree to the index */ + element_index += t8_forest_get_tree_element_offset (forest, lneigh_treeid); + } + if (neigh_scheme->t8_element_compare (ancestor, neighbor_leaves[0]) < 0) { + /* ancestor is a real ancestor, and thus the neighbor is either the parent * or the grandparent of the half neighbors. We can return it and the indices. */ - /* We need to determine the dual face */ - if (neigh_scheme->t8_element_level (ancestor) == ts->t8_element_level (leaf_or_ghost)) { - /* The ancestor is the same-level neighbor of leaf */ - if (!at_maxlevel) { - /* its dual face is the face of the parent of the first neighbor leaf */ - *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); - } - } - else { - /* The ancestor is the parent of the parent */ - T8_ASSERT (neigh_scheme->t8_element_level (ancestor) == ts->t8_element_level (leaf_or_ghost) - 1); - - *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); - if (!at_maxlevel) { - /* We need to compute the dual face of the grandparent. */ - /* Construct the parent of the grand child */ - neigh_scheme->t8_element_parent (neighbor_leaves[0], neighbor_leaves[0]); - /* Compute the face id of the parent's face */ - *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); - } - } - - /* free memory */ - neigh_scheme->t8_element_destroy (num_children_at_face - 1, neighbor_leaves + 1); - /* copy the ancestor */ - neigh_scheme->t8_element_copy (ancestor, neighbor_leaves[0]); - /* set return values */ - *num_neighbors = 1; - *pelement_indices = T8_ALLOC (t8_locidx_t, 1); - (*pelement_indices)[0] = element_index; - - T8_FREE (owners); - return; + /* We need to determine the dual face */ + if (neigh_scheme->t8_element_level (ancestor) == ts->t8_element_level (leaf_or_ghost)) { + /* The ancestor is the same-level neighbor of leaf */ + if (!at_maxlevel) { + /* its dual face is the face of the parent of the first neighbor leaf */ + *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); } - } // if (different owners) + } + else { + /* The ancestor is the parent of the parent */ + T8_ASSERT (neigh_scheme->t8_element_level (ancestor) == ts->t8_element_level (leaf_or_ghost) - 1); + + *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); + if (!at_maxlevel) { + /* We need to compute the dual face of the grandparent. */ + /* Construct the parent of the grand child */ + neigh_scheme->t8_element_parent (neighbor_leaves[0], neighbor_leaves[0]); + /* Compute the face id of the parent's face */ + *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); + } + } - /* The leaves are the face neighbors that we are looking for. */ - /* The face neighbors either belong to different processes and thus must be leaves + /* free memory */ + neigh_scheme->t8_element_destroy (num_children_at_face - 1, neighbor_leaves + 1); + /* copy the ancestor */ + neigh_scheme->t8_element_copy (ancestor, neighbor_leaves[0]); + /* set return values */ + *num_neighbors = 1; + *pelement_indices = T8_ALLOC (t8_locidx_t, 1); + (*pelement_indices)[0] = element_index; + + T8_FREE (owners); + return; + } +} // if (different owners) + +/* The leaves are the face neighbors that we are looking for. */ +/* The face neighbors either belong to different processes and thus must be leaves * in the forest, or the ancestor leaf of the first half neighbor is the half * neighbor itself and thus all half neighbors must be leaves. * Since the forest is balanced, we found all neighbor leaves. * It remains to compute their local ids */ - *num_neighbors = num_children_at_face; - *pelement_indices = T8_ALLOC (t8_locidx_t, num_children_at_face); - element_indices = *pelement_indices; - for (ineigh = 0; ineigh < num_children_at_face; ineigh++) { - /* Compute the linear id at maxlevel of the neighbor leaf */ - neigh_id = neigh_scheme->t8_element_get_linear_id (neighbor_leaves[ineigh], forest->maxlevel); - /* Get a pointer to the element array in which the neighbor lies and search for the element's index in this array. +*num_neighbors = num_children_at_face; +*pelement_indices = T8_ALLOC (t8_locidx_t, num_children_at_face); +element_indices = *pelement_indices; +for (ineigh = 0; ineigh < num_children_at_face; ineigh++) { + /* Compute the linear id at maxlevel of the neighbor leaf */ + neigh_id = neigh_scheme->t8_element_get_linear_id (neighbor_leaves[ineigh], forest->maxlevel); + /* Get a pointer to the element array in which the neighbor lies and search for the element's index in this array. * This is either the local leaf array of the local tree or the corresponding leaf array in the ghost structure */ - if (owners[ineigh] == forest->mpirank) { - /* The neighbor is a local leaf */ - const t8_element_array_t *element_array = t8_forest_get_tree_element_array (forest, lneigh_treeid); - /* Find the index of the neighbor in the array */ - element_indices[ineigh] = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); - T8_ASSERT (element_indices[ineigh] >= 0); - /* We have to add the tree's element offset to the index found to get the actual local element id */ - element_indices[ineigh] += t8_forest_get_tree_element_offset (forest, lneigh_treeid); + if (owners[ineigh] == forest->mpirank) { + /* The neighbor is a local leaf */ + const t8_element_array_t *element_array = t8_forest_get_tree_element_array (forest, lneigh_treeid); + /* Find the index of the neighbor in the array */ + element_indices[ineigh] = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); + T8_ASSERT (element_indices[ineigh] >= 0); + /* We have to add the tree's element offset to the index found to get the actual local element id */ + element_indices[ineigh] += t8_forest_get_tree_element_offset (forest, lneigh_treeid); #if T8_ENABLE_DEBUG - /* We check whether the element is really the element at this local id */ - { - t8_locidx_t check_ltreeid; - const t8_element_t *check_element = t8_forest_get_element (forest, element_indices[ineigh], &check_ltreeid); - T8_ASSERT (check_ltreeid == lneigh_treeid); - T8_ASSERT (neigh_scheme->t8_element_equal (check_element, neighbor_leaves[ineigh])); - } + /* We check whether the element is really the element at this local id */ + { + t8_locidx_t check_ltreeid; + const t8_element_t *check_element = t8_forest_get_element (forest, element_indices[ineigh], &check_ltreeid); + T8_ASSERT (check_ltreeid == lneigh_treeid); + T8_ASSERT (neigh_scheme->t8_element_equal (check_element, neighbor_leaves[ineigh])); + } #endif - } - else { - /* The neighbor is a ghost */ - const t8_element_array_t *element_array = t8_forest_ghost_get_tree_elements (forest, lghost_treeid); - /* Find the index of the neighbor in the array */ - element_indices[ineigh] = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); + } + else { + /* The neighbor is a ghost */ + const t8_element_array_t *element_array = t8_forest_ghost_get_tree_elements (forest, lghost_treeid); + /* Find the index of the neighbor in the array */ + element_indices[ineigh] = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); #if T8_ENABLE_DEBUG - /* We check whether the element is really the element at this local id */ - { - t8_element_t *check_element; - check_element = t8_forest_ghost_get_element (forest, lghost_treeid, element_indices[ineigh]); - T8_ASSERT (neigh_scheme->t8_element_equal (check_element, neighbor_leaves[ineigh])); - } + /* We check whether the element is really the element at this local id */ + { + t8_element_t *check_element; + check_element = t8_forest_ghost_get_element (forest, lghost_treeid, element_indices[ineigh]); + T8_ASSERT (neigh_scheme->t8_element_equal (check_element, neighbor_leaves[ineigh])); + } #endif - /* Add the element offset of previous ghosts to this index */ - element_indices[ineigh] += t8_forest_ghost_get_tree_element_offset (forest, lghost_treeid); - /* Add the number of all local elements to this index */ - element_indices[ineigh] += t8_forest_get_local_num_elements (forest); - } - } /* End for loop over neighbor leaves */ - T8_FREE (owners); - } - else { - /* TODO: implement unbalanced version */ - SC_ABORT ("Computing leaf face neighbors is only supported for balanced forests.\n"); + /* Add the element offset of previous ghosts to this index */ + element_indices[ineigh] += t8_forest_ghost_get_tree_element_offset (forest, lghost_treeid); + /* Add the number of all local elements to this index */ + element_indices[ineigh] += t8_forest_get_local_num_elements (forest); } +} /* End for loop over neighbor leaves */ +T8_FREE (owners); +} +else +{ + /* TODO: implement unbalanced version */ + SC_ABORT ("Computing leaf face neighbors is only supported for balanced forests.\n"); +} } void From 6ee399ae20a5f570621c590fe1c4cbc38d69f04d Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 15 Nov 2024 13:18:57 +0100 Subject: [PATCH 020/133] remove TODO comment --- src/t8_forest/t8_forest_iterate.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index 52c8c0194d..26086bd2cd 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -156,7 +156,6 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme /* Compute the corresponding face number of this face child */ const int child_face = ts->t8_element_face_child_face (element, face, iface); /* Enter the recursion */ - // TODO: Change element index when ghost. t8_forest_iterate_faces (forest, ltreeid, face_children[iface], child_face, &face_child_leaves, user_data, indexa + tree_lindex_of_first_leaf, callback); } From abdff17d063d2f5b92c8e63d3ac88e7f1a3a795c Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 21 Nov 2024 21:42:22 +0100 Subject: [PATCH 021/133] todo comments --- src/t8_forest/t8_forest.cxx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index df0c09ecc6..b85a59163b 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1780,6 +1780,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons } // Somehow collect leaf arrays + // std::vector + // iterate over vector //static t8_locidx_t //t8_forest_bin_search_lower (const t8_element_array_t *elements, const t8_linearidx_t element_id, const int maxlevel); @@ -1797,6 +1799,24 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // The last descendant must have been found by the search, since the neighbor // element itself would be a match for the search. T8_ASSERT (0 <= last_desc_index); + + // Get the actual leaf elements that contain the first and last face desc + t8_element_t **first_and_last_face_leafs; + neigh_scheme->t8_element_new (first_and_last_face_leafs, 2); + first_and_last_face_leafs[0] = t8_element_array_index_locidx (tree_leafs, first_desc_index); + first_and_last_face_leafs[1] = t8_element_array_index_locidx (tree_leafs, last_desc_index); + // Compute their nearest common ancestor + neigh_scheme->t8_element_nca (first_and_last_face_leafs[0], first_and_last_face_leafs[1], nca_of_face_desc); + + t8_forest_iterate_faces (forest, local_neighbor_tree, nca_of_face_desc, tree_leafs, first_desc_index, + t8_leaf_face_neighbor_face_it_callback); + // Output of iterate_faces: + // Array of indices in tree_leafs of all the face neighbor elements + // Assign pneighbor_leaves + // Assign dual_faces + // Assign pelement_indices + // (all as growing std::vectors, resp t8_element_array) + t8_locidx_t *neighbor_indices_in_leafs; } } From be072c076ac5c2081ca1bc380f23831d4883c1f4 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 21 Nov 2024 23:16:42 +0100 Subject: [PATCH 022/133] Add face iterate callback to leaf face neighbor --- src/t8_forest/t8_forest.cxx | 64 ++++++++++++++++++++++++++++--- src/t8_forest/t8_forest_iterate.h | 5 ++- 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index b85a59163b..c67ad17dd9 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -46,6 +46,7 @@ #include #include +#include /* We want to export the whole implementation to be callable from "C" */ T8_EXTERN_C_BEGIN (); @@ -1714,12 +1715,56 @@ t8_forest_leaf_face_orientation (t8_forest_t forest, const t8_locidx_t ltreeid, return orientation; } +struct t8_lfn_user_data +{ + std::vector element_indices; + std::vector dual_faces; + std::vector neighbors; + const t8_eclass_scheme_c &scheme; +}; + +static int +t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, + int is_leaf, const t8_element_array_t *leaf_elements, + t8_locidx_t tree_leaf_index, void *user_data) +{ + // Output of iterate_faces: + // Array of indices in tree_leafs of all the face neighbor elements + // Assign pneighbor_leaves + // Assign dual_faces + // Assign pelement_indices + if (!is_leaf) { + // continue search until leaf level + return 1; + } + T8_ASSERT (is_leaf); + // Query whether this tree is a ghost and if so + // compute its id as a ghost tree ( 0 <= id < num_ghost_trees) + const bool is_ghost_tree = !t8_forest_tree_is_local (forest, ltreeid); + const t8_locidx_t adjusted_tree_id = !is_ghost_tree ? ltreeid : ltreeid - t8_forest_get_num_local_trees (forest); + T8_ASSERT (t8_forest_element_is_leaf_or_ghost (forest, element, adjusted_tree_id, is_ghost_tree)); + + struct t8_lfn_user_data &lfn_data = static_cast (user_data); + // face is the face of the considered leaf neighbor element and thus the + // corresponding dual face + lfn_data.dual_faces.push_back (face); + // Compute the index of the element + const t8_locidx_t tree_offset = !is_ghost_tree ? t8_forest_get_tree_element_offset (forest, ltreeid); + : t8_forest_ghost_get_tree_element_offset (adjusted_tree_id); + const t8_locidx_t element_index = tree_offset + tree_leaf_index; + lfn_data.element_indices.push_back (element_index)); + // Add the pointer to the current element + const t8_element_t *pnew_element = lfn_data.neighbors.emplace_back (); + pnew_element = element; +} + // TODO: ltreeid must be forest ghost tree id i.e. num_local_trees + N void t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf_or_ghost, - t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], int *num_neighbors, - t8_locidx_t **pelement_indices, t8_eclass_scheme_c **pneigh_scheme, - int forest_is_balanced, t8_gloidx_t *gneigh_tree, int *orientation) + const t8_element_t **pneighbor_leaves[], int face, const int *dual_faces[], + const int *num_neighbors, const t8_locidx_t **pelement_indices, + const t8_eclass_scheme_c **pneigh_scheme, int forest_is_balanced, + t8_gloidx_t *gneigh_tree, const int *orientation) { t8_gloidx_t gneigh_treeid; t8_locidx_t lneigh_treeid = -1; @@ -1808,15 +1853,24 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // Compute their nearest common ancestor neigh_scheme->t8_element_nca (first_and_last_face_leafs[0], first_and_last_face_leafs[1], nca_of_face_desc); + struct t8_lfn_user_data user_data; + t8_forest_iterate_faces (forest, local_neighbor_tree, nca_of_face_desc, tree_leafs, first_desc_index, - t8_leaf_face_neighbor_face_it_callback); + t8_leaf_face_neighbor_face_it_callback, &user_data); // Output of iterate_faces: // Array of indices in tree_leafs of all the face neighbor elements // Assign pneighbor_leaves // Assign dual_faces // Assign pelement_indices // (all as growing std::vectors, resp t8_element_array) - t8_locidx_t *neighbor_indices_in_leafs; + *num_neighbors = user_data.neighbors.size (); + // Copy neighbor element pointers + *pneighbor_leaves = T8_ALLOC (t8_element_t *, num_neighbors); + std::memcpy (*pneighbor_leaves, user_data.neighbors.data (), num_neighbors * sizeof (t8_element_t *)); + *pelement_indices = T8_ALLOC (t8_locidx_t, num_neighbors); + std::memcpy (*pelement_indices, user_data.element_indices.data (), num_neighbors * sizeof (t8_locidx_t)); + *dual_faces = T8_ALLOC (int, num_neighbors); + std::memcpy (*dual_faces, user_data.dual_faces.data (), num_neighbors * sizeof (int)); } } diff --git a/src/t8_forest/t8_forest_iterate.h b/src/t8_forest/t8_forest_iterate.h index 66097c5b55..db746f874e 100644 --- a/src/t8_forest/t8_forest_iterate.h +++ b/src/t8_forest/t8_forest_iterate.h @@ -35,7 +35,7 @@ typedef int (*t8_forest_iterate_face_fn) (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, int is_leaf, const t8_element_array_t *leaf_elements, - t8_locidx_t tree_leaf_index); + t8_locidx_t tree_leaf_index, void *user_data); /** * A call-back function used by \ref t8_forest_search describing a search-criterion. Is called on an element and the @@ -88,6 +88,7 @@ void t8_forest_split_array (const t8_element_t *element, const t8_element_array_t *leaf_elements, size_t *offsets); /* TODO: comment */ +// TODO: Test this function. Uniform mesh test. Refine always same corner, know that neighbors follow geometric series. /* Iterate over all leaves of an element that touch a given face of the element */ /* Callback is called in each recursive step with element as input. * leaf_index is only not negative if element is a leaf, in which case it indicates @@ -98,7 +99,7 @@ t8_forest_split_array (const t8_element_t *element, const t8_element_array_t *le void t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, const t8_element_array_t *leaf_elements, t8_locidx_t tree_lindex_of_first_leaf, - t8_forest_iterate_face_fn callback); + t8_forest_iterate_face_fn callback, void *user_data); /* Perform a top-down search of the forest, executing a callback on each * intermediate element. The search will enter each tree at least once. From 716097dcc235750b33b480eee44a30210106e962 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 21 Nov 2024 23:17:11 +0100 Subject: [PATCH 023/133] is_leaf_or_ghost correct assertion --- src/t8_forest/t8_forest.cxx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index c67ad17dd9..625e611141 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -2193,7 +2193,14 @@ t8_forest_element_is_leaf_or_ghost (const t8_forest_t forest, const t8_element_t const int check_ghost) { T8_ASSERT (t8_forest_is_committed (forest)); - T8_ASSERT (t8_forest_tree_is_local (forest, local_tree)); +#if T8_ENABLE_DEBUG + if (!check_ghost) { + T8_ASSERT (t8_forest_tree_is_local (forest, local_tree)); + } + else { + T8_ASSERT (0 <= local_tree && local_tree < t8_forest_get_num_ghost_trees (forest)); + } +#endif /* We get the array of the tree's elements and then search in the array of elements for our * element candidate. */ From a3e7ec2ddb398bb4e99a7508dc59a065dea83527 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 21 Nov 2024 23:17:11 +0100 Subject: [PATCH 024/133] is_leaf_or_ghost correct assertion --- src/t8_forest/t8_forest.cxx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index ca2add47df..47b9a015c5 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -2045,7 +2045,14 @@ t8_forest_element_is_leaf_or_ghost (const t8_forest_t forest, const t8_element_t const int check_ghost) { T8_ASSERT (t8_forest_is_committed (forest)); - T8_ASSERT (t8_forest_tree_is_local (forest, local_tree)); +#if T8_ENABLE_DEBUG + if (!check_ghost) { + T8_ASSERT (t8_forest_tree_is_local (forest, local_tree)); + } + else { + T8_ASSERT (0 <= local_tree && local_tree < t8_forest_get_num_ghost_trees (forest)); + } +#endif /* We get the array of the tree's elements and then search in the array of elements for our * element candidate. */ From d8bdecc43264af34f54bf116ffa9b165d3a61a40 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 12:55:37 +0100 Subject: [PATCH 025/133] adaptations in face iterate callback --- example/forest/t8_test_face_iterate.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/example/forest/t8_test_face_iterate.cxx b/example/forest/t8_test_face_iterate.cxx index 8169fe4621..6fb3f31ec8 100644 --- a/example/forest/t8_test_face_iterate.cxx +++ b/example/forest/t8_test_face_iterate.cxx @@ -44,15 +44,15 @@ typedef struct static int t8_test_fiterate_callback (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, int is_leaf, - const t8_element_array_t *leaf_elements, t8_locidx_t leaf_index) + const t8_element_array_t *leaf_elements, t8_locidx_t leaf_index, void *user_data) { if (is_leaf) { - t8_test_fiterate_udata_t *user_data = (t8_test_fiterate_udata_t *) t8_forest_get_user_data (forest); - double *coords = user_data->coords; + t8_test_fiterate_udata_t *test_user_data = (t8_test_fiterate_udata_t *) user_data; + double *coords = test_user_data->coords; t8_forest_element_coordinate (forest, ltreeid, element, 0, coords); t8_debugf ("Leaf element in tree %i at face %i, tree local index %i has corner 0 coords %lf %lf %lf\n", ltreeid, face, (int) leaf_index, coords[0], coords[1], coords[2]); - user_data->count++; + test_user_data->count++; } return 1; } @@ -104,7 +104,7 @@ t8_test_fiterate (t8_forest_t forest) // for (int iface = 0; iface < ts->t8_element_num_faces (nca); iface++) { udata.count = 0; - t8_forest_iterate_faces (forest, itree, nca, iface, leaf_elements, &udata, 0, t8_test_fiterate_callback); + t8_forest_iterate_faces (forest, itree, nca, iface, leaf_elements, 0, t8_test_fiterate_callback, &udata); t8_debugf ("Leaf elements at face %i:\t%i\n", iface, udata.count); } ts->t8_element_destroy (1, &nca); From 554ab7b7c9a3a320ac748dac5d676ccb4f95710d Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 12:56:41 +0100 Subject: [PATCH 026/133] add ghost support to t8_forest_element_neighbor_eclass --- src/t8_forest/t8_forest.cxx | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 625e611141..ec02da1aae 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include #include @@ -1475,37 +1477,28 @@ t8_forest_bin_search_lower (const t8_element_array_t *elements, const t8_lineari return elem_iter.GetCurrentIndex () - 1; } -// TODO: Extend to ghost elements t8_eclass_t -t8_forest_element_neighbor_eclass (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *elem, int face) +t8_forest_element_neighbor_eclass (const t8_forest_t forest, const t8_locidx_t ltreeid, const t8_element_t *elem, + const int face) { - t8_eclass_scheme_c *ts; - t8_tree_t tree; - t8_ctree_t coarse_tree; - t8_eclass_t eclass; - int tree_face; - t8_locidx_t lcoarse_neighbor; - t8_cmesh_t cmesh; + const t8_eclass_t eclass = t8_forest_get_tree_class (forest, ltreeid); + const t8_eclass_scheme_c *ts = t8_forest_get_eclass_scheme (forest, eclass); - /* Get a pointer to the tree to read its element class */ - tree = t8_forest_get_tree (forest, ltreeid); - eclass = tree->eclass; - ts = t8_forest_get_eclass_scheme (forest, eclass); if (!ts->t8_element_is_root_boundary (elem, face)) { /* The neighbor element is inside the current tree. */ - return tree->eclass; + return eclass; } else { /* The neighbor is in a neighbor tree */ /* If the face neighbor is not inside the tree, we have to find out the tree * face and the tree's face neighbor along that face. */ - tree_face = ts->t8_element_tree_face (elem, face); + const int tree_face = ts->t8_element_tree_face (elem, face); - cmesh = t8_forest_get_cmesh (forest); + const t8_cmesh_t cmesh = t8_forest_get_cmesh (forest); /* Get the coarse tree corresponding to tree */ - coarse_tree = t8_forest_get_coarse_tree (forest, ltreeid); + const t8_ctree_t coarse_tree = t8_forest_get_coarse_tree (forest, ltreeid); /* Get the (coarse) local id of the tree neighbor */ - lcoarse_neighbor = t8_cmesh_trees_get_face_neighbor (coarse_tree, tree_face); + const t8_locidx_t lcoarse_neighbor = t8_cmesh_trees_get_face_neighbor (coarse_tree, tree_face); T8_ASSERT (0 <= lcoarse_neighbor); if (lcoarse_neighbor < t8_cmesh_get_num_local_trees (cmesh)) { /* The tree neighbor is a local tree */ From 39a9bf0fa14337b60722bdce5842915fc85b711e Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 12:57:34 +0100 Subject: [PATCH 027/133] final extensions of lfn to ghost elements --- src/t8_forest/t8_forest.cxx | 195 ++++++++++++++++++------------ src/t8_forest/t8_forest_general.h | 2 +- 2 files changed, 118 insertions(+), 79 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index ec02da1aae..643581c861 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1712,8 +1712,13 @@ struct t8_lfn_user_data { std::vector element_indices; std::vector dual_faces; - std::vector neighbors; + std::vector neighbors; const t8_eclass_scheme_c &scheme; + + public: + t8_lfn_user_data (const t8_eclass_scheme_c &scheme): scheme (scheme) + { + } }; static int @@ -1737,40 +1742,40 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, const t8_locidx_t adjusted_tree_id = !is_ghost_tree ? ltreeid : ltreeid - t8_forest_get_num_local_trees (forest); T8_ASSERT (t8_forest_element_is_leaf_or_ghost (forest, element, adjusted_tree_id, is_ghost_tree)); - struct t8_lfn_user_data &lfn_data = static_cast (user_data); + struct t8_lfn_user_data &lfn_data = reinterpret_cast (user_data); // face is the face of the considered leaf neighbor element and thus the // corresponding dual face lfn_data.dual_faces.push_back (face); // Compute the index of the element - const t8_locidx_t tree_offset = !is_ghost_tree ? t8_forest_get_tree_element_offset (forest, ltreeid); - : t8_forest_ghost_get_tree_element_offset (adjusted_tree_id); - const t8_locidx_t element_index = tree_offset + tree_leaf_index; - lfn_data.element_indices.push_back (element_index)); + const t8_locidx_t tree_offset = !is_ghost_tree ? t8_forest_get_tree_element_offset (forest, ltreeid) + : t8_forest_ghost_get_tree_element_offset (forest, adjusted_tree_id); + const t8_locidx_t element_index = tree_offset + tree_leaf_index; + lfn_data.element_indices.push_back (element_index); // Add the pointer to the current element - const t8_element_t *pnew_element = lfn_data.neighbors.emplace_back (); + const t8_element_t *&pnew_element = lfn_data.neighbors.emplace_back (); pnew_element = element; + return 1; } // TODO: ltreeid must be forest ghost tree id i.e. num_local_trees + N void t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf_or_ghost, - const t8_element_t **pneighbor_leaves[], int face, const int *dual_faces[], - const int *num_neighbors, const t8_locidx_t **pelement_indices, - const t8_eclass_scheme_c **pneigh_scheme, int forest_is_balanced, - t8_gloidx_t *gneigh_tree, const int *orientation) + t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], int *num_neighbors, + t8_locidx_t **pelement_indices, t8_eclass_scheme_c **pneigh_scheme, + int forest_is_balanced, t8_gloidx_t *gneigh_tree, int *orientation) { - t8_gloidx_t gneigh_treeid; - t8_locidx_t lneigh_treeid = -1; - t8_locidx_t lghost_treeid = -1, *element_indices, element_index; - const t8_element_t *ancestor; - t8_element_t **neighbor_leaves; - t8_linearidx_t neigh_id; - int num_children_at_face, at_maxlevel; - int ineigh, *owners, different_owners, have_ghosts; T8_ASSERT (t8_forest_is_committed (forest)); - // TODO (ghosts): extend to ghost or add is_ghost function - T8_ASSERT (t8_forest_element_is_leaf (forest, leaf_or_ghost, ltreeid)); +#if T8_ENABLE_DEBUG + const bool tree_is_local = t8_forest_tree_is_local (forest, ltreeid); + if (tree_is_local) { + T8_ASSERT (t8_forest_element_is_leaf (forest, leaf_or_ghost, ltreeid)); + } + else { + const t8_locidx_t local_ghost_treeid = ltreeid - t8_forest_get_num_local_trees (forest); + T8_ASSERT (t8_forest_element_is_ghost (forest, leaf_or_ghost, local_ghost_treeid)); + } +#endif SC_CHECK_ABORT (forest->incomplete_trees, "Leaf face neighbor is not supported for " "forests with deleted elements.\n"); SC_CHECK_ABORT (forest->mpisize == 1 || forest->ghosts != NULL, @@ -1781,92 +1786,125 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_eclass_scheme_c *ts = t8_forest_get_eclass_scheme (forest, eclass); if (orientation) { - // temp note: this will work for ghost elements + // Compute the orientation of the face neighbor connection *orientation = t8_forest_leaf_face_orientation (forest, ltreeid, ts, leaf_or_ghost, face); } /* At first we compute these children of the face neighbor elements of leaf. For this, we need the * neighbor tree's eclass, scheme, and tree id */ - // TODO (ghosts): Extend to support ghosts const t8_eclass_t neigh_class = t8_forest_element_neighbor_eclass (forest, ltreeid, leaf_or_ghost, face); const t8_eclass_scheme_c *neigh_scheme = *pneigh_scheme = t8_forest_get_eclass_scheme (forest, neigh_class); // Compute the same level face neighbor t8_element_t *same_level_neighbor; - neigh_scheme->t8_element_new (&same_level_neighbor, 1); + neigh_scheme->t8_element_new (1, &same_level_neighbor); int neigh_face; - gneigh_treeid = t8_forest_element_face_neighbor (forest, ltreeid, leaf_or_ghost, same_level_neighbor, neigh_scheme, - face, *neigh_face); - // Compute the neighbors level and linear id - const int neighbor_level = neigh_scheme->t8_element_level (neighbor); - const t8_linearidx_t neighbor_id = neigh_scheme->t8_element_get_linear_id (neighbor); + const t8_gloidx_t gneigh_treeid = t8_forest_element_face_neighbor ( + forest, ltreeid, leaf_or_ghost, same_level_neighbor, neigh_scheme, face, &neigh_face); + const int maxlevel = neigh_scheme->t8_element_maxlevel (); // Compute the first and last face descendant of the neighbor - t8_element_t **first_and_last_face_desc; - neigh_scheme->t8_element_new (first_and_last_desc, 2); - neigh_scheme->t8_element_first_descendant_face (neighbor, neigh_face, first_and_last_face_desc[0], maxlevel); - neigh_scheme->t8_element_last_descendant_face (neighbor, neigh_face, first_and_last_face_desc[1], maxlevel); + t8_element_t *first_face_desc; + t8_element_t *last_face_desc; + neigh_scheme->t8_element_new (1, &first_face_desc); + neigh_scheme->t8_element_new (1, &last_face_desc); + neigh_scheme->t8_element_first_descendant_face (same_level_neighbor, neigh_face, first_face_desc, maxlevel); + neigh_scheme->t8_element_last_descendant_face (same_level_neighbor, neigh_face, last_face_desc, maxlevel); + const t8_linearidx_t first_face_desc_id = neigh_scheme->t8_element_get_linear_id (first_face_desc, maxlevel); + const t8_linearidx_t last_face_desc_id = neigh_scheme->t8_element_get_linear_id (last_face_desc, maxlevel); // Allocate memory for the nca of first and last face desc t8_element_t *nca_of_face_desc; - neigh_scheme->t8_element_new (nca_of_face_desc, 1); + neigh_scheme->t8_element_new (1, &nca_of_face_desc); + // The neighbor leafs could be distributed across a local tree and a ghost + // tree. We thus possibly need to search in two different arrays. + // We store these in a vector and iterate over the entries. + std::vector leaf_arrays; // Compute the local id of the neighbor tree and check if it is a local tree const t8_locidx_t local_neighbor_tree = t8_forest_get_local_id (forest, gneigh_treeid); if (0 <= local_neighbor_tree) { // The neighbor tree is a local tree and hence there may be local neighbor elements. + const t8_element_array_t *tree_leafs = t8_forest_tree_get_leaves (forest, local_neighbor_tree); + if (tree_leafs != nullptr) { + leaf_arrays.push_back (tree_leafs); + } + } + const t8_locidx_t local_neighbor_ghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, gneigh_treeid); + if (local_neighbor_ghost_treeid >= 0) { + // The neighbor tree is also a ghost tree and face neighbors of our element might + // be ghost elements. + // We add the ghost elements of that tree to our search array. + const t8_element_array_t *ghost_leafs = t8_forest_ghost_get_tree_elements (forest, local_neighbor_ghost_treeid); + if (ghost_leafs != nullptr) { + leaf_arrays.push_back (ghost_leafs); + } } // Somehow collect leaf arrays // std::vector // iterate over vector - //static t8_locidx_t - //t8_forest_bin_search_lower (const t8_element_array_t *elements, const t8_linearidx_t element_id, const int maxlevel); - const t8_element_array_t *tree_leafs = t8_forest_tree_get_leaves (forest, local_neighbor_tree); - const t8_locidx_t first_desc_search = t8_forest_bin_search_lower (tree_leafs, first_and_last_face_desc[0], maxlevel); - const t8_locidx_t last_desc_search = t8_forest_bin_search_lower (tree_leafs, first_and_last_face_desc[1], maxlevel); - if (first_desc_search < 0 && last_desc_search < 0) { - // TODO: No neighbor in this leaf array - } - else { - // The first descendant may not be in the leaf array, we then - // start with the first leaf. - const first_desc_index = SC_MAX (0, first_desc_search); - const last_desc_index = last_desc_search; - // The last descendant must have been found by the search, since the neighbor - // element itself would be a match for the search. - T8_ASSERT (0 <= last_desc_index); - - // Get the actual leaf elements that contain the first and last face desc - t8_element_t **first_and_last_face_leafs; - neigh_scheme->t8_element_new (first_and_last_face_leafs, 2); - first_and_last_face_leafs[0] = t8_element_array_index_locidx (tree_leafs, first_desc_index); - first_and_last_face_leafs[1] = t8_element_array_index_locidx (tree_leafs, last_desc_index); - // Compute their nearest common ancestor - neigh_scheme->t8_element_nca (first_and_last_face_leafs[0], first_and_last_face_leafs[1], nca_of_face_desc); - - struct t8_lfn_user_data user_data; - - t8_forest_iterate_faces (forest, local_neighbor_tree, nca_of_face_desc, tree_leafs, first_desc_index, - t8_leaf_face_neighbor_face_it_callback, &user_data); - // Output of iterate_faces: - // Array of indices in tree_leafs of all the face neighbor elements - // Assign pneighbor_leaves - // Assign dual_faces - // Assign pelement_indices - // (all as growing std::vectors, resp t8_element_array) + struct t8_lfn_user_data user_data (*neigh_scheme); + + // Now we iterate over the leaf arrays of the neighbor tree + // or neighbor ghost tree and find all leaf face neighbors of the element. + for (auto &tree_leafs : leaf_arrays) { + const t8_locidx_t first_desc_search = t8_forest_bin_search_lower (tree_leafs, first_face_desc_id, maxlevel); + const t8_locidx_t last_desc_search = t8_forest_bin_search_lower (tree_leafs, last_face_desc_id, maxlevel); + if (first_desc_search < 0 && last_desc_search < 0) { + // TODO: No neighbor in this leaf array + SC_ABORT ("Not implemented yet. TODO."); + } + else { + // The first descendant may not be in the leaf array, we then + // start with the first leaf. + const t8_locidx_t first_desc_index = SC_MAX (0, first_desc_search); + const t8_locidx_t last_desc_index = last_desc_search; + // The last descendant must have been found by the search, since the neighbor + // element itself would be a match for the search. + T8_ASSERT (0 <= last_desc_index); + + // Get the actual leaf elements that contain the first and last face desc + const t8_element_t *first_face_leaf = t8_element_array_index_locidx (tree_leafs, first_desc_index); + const t8_element_t *last_face_leaf = t8_element_array_index_locidx (tree_leafs, last_desc_index); + // Compute their nearest common ancestor + neigh_scheme->t8_element_nca (first_face_leaf, last_face_leaf, nca_of_face_desc); + const int face_of_nca = 0; + SC_ABORT ("Not implemented"); + + t8_forest_iterate_faces (forest, local_neighbor_tree, nca_of_face_desc, face_of_nca, tree_leafs, first_desc_index, + t8_forest_leaf_face_neighbors_iterate, &user_data); + // Output of iterate_faces: + // Array of indices in tree_leafs of all the face neighbor elements + // Assign pneighbor_leaves + // Assign dual_faces + // Assign pelement_indices + // (all as growing std::vectors, resp t8_element_array) + + // + // After the iteration is finished we collected all + // neighbor data. + // TODO: Since there is no other way, we copy them from the vectors. + // This should be improved in the future to get around the copy. + } *num_neighbors = user_data.neighbors.size (); // Copy neighbor element pointers - *pneighbor_leaves = T8_ALLOC (t8_element_t *, num_neighbors); - std::memcpy (*pneighbor_leaves, user_data.neighbors.data (), num_neighbors * sizeof (t8_element_t *)); - *pelement_indices = T8_ALLOC (t8_locidx_t, num_neighbors); - std::memcpy (*pelement_indices, user_data.element_indices.data (), num_neighbors * sizeof (t8_locidx_t)); - *dual_faces = T8_ALLOC (int, num_neighbors); - std::memcpy (*dual_faces, user_data.dual_faces.data (), num_neighbors * sizeof (int)); - } -} - + *pneighbor_leaves = T8_ALLOC (t8_element_t *, *num_neighbors); + memcpy (*pneighbor_leaves, user_data.neighbors.data (), *num_neighbors * sizeof (t8_element_t *)); + *pelement_indices = T8_ALLOC (t8_locidx_t, *num_neighbors); + memcpy (*pelement_indices, user_data.element_indices.data (), *num_neighbors * sizeof (t8_locidx_t)); + *dual_faces = T8_ALLOC (int, *num_neighbors); + memcpy (*dual_faces, user_data.dual_faces.data (), *num_neighbors * sizeof (int)); + } +#if 0 +t8_locidx_t lneigh_treeid = -1; + t8_locidx_t lghost_treeid = -1, *element_indices, element_index; + const t8_element_t *ancestor; + t8_element_t **neighbor_leaves; + t8_linearidx_t neigh_id; + int num_children_at_face, at_maxlevel; + int ineigh, *owners, different_owners, have_ghosts; // Compute first face desc and last face desc // Search for them in the element array // if both not found -> no neighbor @@ -2091,6 +2129,7 @@ else /* TODO: implement unbalanced version */ SC_ABORT ("Computing leaf face neighbors is only supported for balanced forests.\n"); } +#endif // if 0 } void diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index 51e1490cb0..c6d8ec7d02 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -627,7 +627,7 @@ t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8 * */ void -t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf, +t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf_or_ghost, t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], int *num_neighbors, t8_locidx_t **pelement_indices, t8_eclass_scheme_c **pneigh_scheme, int forest_is_balanced, t8_gloidx_t *gneigh_tree, int *orientation); From 1a39eb8b776f5c6601f18b8397687dad9a2d9be9 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 12:58:02 +0100 Subject: [PATCH 028/133] t8_forest_element_neighbor_eclass adaptation to declaration --- src/t8_forest/t8_forest_general.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index c6d8ec7d02..960bd618d5 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -830,17 +830,18 @@ t8_forest_get_scheme (const t8_forest_t forest); t8_eclass_scheme_c * t8_forest_get_eclass_scheme (t8_forest_t forest, t8_eclass_t eclass); -/** Return the eclass of the tree in which a face neighbor of a given element +/** Return the eclass of the tree in which a face neighbor of a given element or ghost * lies. - * \param [in] forest. A committed forest. - * \param [in] ltreeid. The local tree in which the element lies. - * \param [in] elem. An element in the tree \a ltreeid. - * \param [in] face. A face number of \a elem. + * \param [in] forest A committed forest. + * \param [in] ltreeid The local tree or ghost tree in which the element lies. 0 <= \a ltreeid < num_local_trees + num_ghost_trees + * \param [in] elem An element or ghost in the tree \a ltreeid. + * \param [in] face A face number of \a elem. * \return The local tree id of the tree in which the face * neighbor of \a elem across \a face lies. */ t8_eclass_t -t8_forest_element_neighbor_eclass (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *elem, int face); +t8_forest_element_neighbor_eclass (const t8_forest_t forest, const t8_locidx_t ltreeid, const t8_element_t *elem, + const int face); /** Construct the face neighbor of an element, possibly across tree boundaries. * Returns the global tree-id of the tree in which the neighbor element lies in. From 7e9c27e752d27e87804904613f154f3edb13100b Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 12:58:51 +0100 Subject: [PATCH 029/133] user data parameter handling for iterate faces --- src/t8_forest/t8_forest_iterate.cxx | 9 +++++---- src/t8_forest/t8_forest_iterate.h | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index 26086bd2cd..ae61eabb74 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -76,7 +76,7 @@ t8_forest_split_array (const t8_element_t *element, const t8_element_array_t *le void t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, const t8_element_array_t *leaf_elements, t8_locidx_t tree_lindex_of_first_leaf, - t8_forest_iterate_face_fn callback) + t8_forest_iterate_face_fn callback, void *user_data) { T8_ASSERT (t8_forest_is_committed (forest)); @@ -120,7 +120,8 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme #endif /* Call the callback function element, if it returns true, we continue with the top-down recursion */ - const int ret = callback (forest, ltreeid, element, face, is_leaf, leaf_elements, tree_lindex_of_first_leaf); + const int ret + = callback (forest, ltreeid, element, face, is_leaf, leaf_elements, tree_lindex_of_first_leaf, user_data); if (!ret || is_leaf) { // The callback returned false or the element is a leaf. // We abort the recursion. @@ -156,8 +157,8 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme /* Compute the corresponding face number of this face child */ const int child_face = ts->t8_element_face_child_face (element, face, iface); /* Enter the recursion */ - t8_forest_iterate_faces (forest, ltreeid, face_children[iface], child_face, &face_child_leaves, user_data, - indexa + tree_lindex_of_first_leaf, callback); + t8_forest_iterate_faces (forest, ltreeid, face_children[iface], child_face, &face_child_leaves, + indexa + tree_lindex_of_first_leaf, callback, user_data); } } /* clean-up */ diff --git a/src/t8_forest/t8_forest_iterate.h b/src/t8_forest/t8_forest_iterate.h index db746f874e..9a7a8637dc 100644 --- a/src/t8_forest/t8_forest_iterate.h +++ b/src/t8_forest/t8_forest_iterate.h @@ -89,6 +89,8 @@ t8_forest_split_array (const t8_element_t *element, const t8_element_array_t *le /* TODO: comment */ // TODO: Test this function. Uniform mesh test. Refine always same corner, know that neighbors follow geometric series. +// TODO: user data should be a template parameter in the long run +// TODO: adapt to search interface /* Iterate over all leaves of an element that touch a given face of the element */ /* Callback is called in each recursive step with element as input. * leaf_index is only not negative if element is a leaf, in which case it indicates From 2f681105761d3cb08f53acbde8d65a9ed9bc053e Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 13:51:22 +0100 Subject: [PATCH 030/133] Remove is_balanced parameter from lfn function since no longer required. --- example/advect/t8_advection.cxx | 4 ++-- src/t8_forest/t8_forest.cxx | 10 ++++------ src/t8_forest/t8_forest_general.h | 11 ++--------- test/t8_forest/t8_gtest_forest_face_normal.cxx | 3 +-- tutorials/general/t8_step6_stencil.cxx | 2 +- 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/example/advect/t8_advection.cxx b/example/advect/t8_advection.cxx index ecc0830fcb..84420974a6 100644 --- a/example/advect/t8_advection.cxx +++ b/example/advect/t8_advection.cxx @@ -1034,7 +1034,7 @@ t8_advect_problem_init_elements (t8_advect_problem_t *problem) t8_forest_leaf_face_neighbors (problem->forest, itree, element, &neighbors, iface, &elem_data->dual_faces[iface], &elem_data->num_neighbors[iface], - &elem_data->neighs[iface], &neigh_scheme, 1); + &elem_data->neighs[iface], &neigh_scheme); for (ineigh = 0; ineigh < elem_data->num_neighbors[iface]; ineigh++) { elem_data->neigh_level[iface] = neigh_scheme->t8_element_level (neighbors[ineigh]); } @@ -1287,7 +1287,7 @@ t8_advect_solve (t8_cmesh_t cmesh, t8_flow_function_3d_fn u, t8_example_level_se neighbor_time = -sc_MPI_Wtime (); t8_forest_leaf_face_neighbors (problem->forest, itree, elem, &neighs, iface, &elem_data->dual_faces[iface], &elem_data->num_neighbors[iface], - &elem_data->neighs[iface], &neigh_scheme, 1); + &elem_data->neighs[iface], &neigh_scheme); for (ineigh = 0; ineigh < elem_data->num_neighbors[iface]; ineigh++) { elem_data->neigh_level[iface] = neigh_scheme->t8_element_level (neighs[ineigh]); } diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 643581c861..d90f4a91bf 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1757,12 +1757,11 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, return 1; } -// TODO: ltreeid must be forest ghost tree id i.e. num_local_trees + N void t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf_or_ghost, t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], int *num_neighbors, t8_locidx_t **pelement_indices, t8_eclass_scheme_c **pneigh_scheme, - int forest_is_balanced, t8_gloidx_t *gneigh_tree, int *orientation) + t8_gloidx_t *gneigh_tree, int *orientation) { T8_ASSERT (t8_forest_is_committed (forest)); @@ -2135,11 +2134,10 @@ else void t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf, t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], int *num_neighbors, - t8_locidx_t **pelement_indices, t8_eclass_scheme_c **pneigh_scheme, - int forest_is_balanced) + t8_locidx_t **pelement_indices, t8_eclass_scheme_c **pneigh_scheme) { t8_forest_leaf_face_neighbors_ext (forest, ltreeid, leaf, pneighbor_leaves, face, dual_faces, num_neighbors, - pelement_indices, pneigh_scheme, forest_is_balanced, NULL, NULL); + pelement_indices, pneigh_scheme, NULL, NULL); } void @@ -2176,7 +2174,7 @@ t8_forest_print_all_leaf_neighbors (t8_forest_t forest) /* Iterate over all faces */ for (iface = 0; iface < ts->t8_element_num_faces (leaf); iface++) { t8_forest_leaf_face_neighbors (forest, ltree, leaf, &neighbor_leaves, iface, &dual_faces, &num_neighbors, - &element_indices, &neigh_scheme, 1); + &element_indices, &neigh_scheme); t8_debugf ("Element %li across face %i has %i leaf neighbors (with dual faces).\n", (long) ielem, iface, num_neighbors); snprintf (buffer, BUFSIZ, "\tIndices:\t"); diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index 960bd618d5..9513606dbe 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -566,11 +566,8 @@ t8_forest_leaf_face_orientation (t8_forest_t forest, const t8_locidx_t ltreeid, * 0, 1, ... num_local_el - 1 for local leaves and * num_local_el , ... , num_local_el + num_ghosts - 1 for ghosts. * \param [out] pneigh_scheme On output the eclass scheme of the neighbor elements. - * \param [in] forest_is_balanced True if we know that \a forest is balanced, false - * otherwise. * \note If there are no face neighbors, then *neighbor_leaves = NULL, num_neighbors = 0, * and *pelement_indices = NULL on output. - * \note Currently \a forest must be balanced. * \note \a forest must be committed before calling this function. * * \note Important! This routine allocates memory which must be freed. Do it like this: @@ -586,8 +583,7 @@ t8_forest_leaf_face_orientation (t8_forest_t forest, const t8_locidx_t ltreeid, void t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf, t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], int *num_neighbors, - t8_locidx_t **pelement_indices, t8_eclass_scheme_c **pneigh_scheme, - int forest_is_balanced); + t8_locidx_t **pelement_indices, t8_eclass_scheme_c **pneigh_scheme); /** 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. @@ -604,8 +600,6 @@ t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8 * 0, 1, ... num_local_el - 1 for local leaves and * num_local_el , ... , num_local_el + num_ghosts - 1 for ghosts. * \param [out] pneigh_scheme On output the eclass scheme of the neighbor elements. - * \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. @@ -613,7 +607,6 @@ t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8 * 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, * and *pelement_indices = NULL on output. - * \note Currently \a forest must be balanced. * \note \a forest must be committed before calling this function. * * \note Important! This routine allocates memory which must be freed. Do it like this: @@ -630,7 +623,7 @@ void t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf_or_ghost, t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], int *num_neighbors, t8_locidx_t **pelement_indices, t8_eclass_scheme_c **pneigh_scheme, - int forest_is_balanced, t8_gloidx_t *gneigh_tree, int *orientation); + t8_gloidx_t *gneigh_tree, int *orientation); /** Exchange ghost information of user defined element data. * \param[in] forest The forest. Must be committed. diff --git a/test/t8_forest/t8_gtest_forest_face_normal.cxx b/test/t8_forest/t8_gtest_forest_face_normal.cxx index 659aa53ea9..decf4fd5e5 100644 --- a/test/t8_forest/t8_gtest_forest_face_normal.cxx +++ b/test/t8_forest/t8_gtest_forest_face_normal.cxx @@ -81,14 +81,13 @@ TEST_P (class_forest_face_normal, back_and_forth) t8_element_t **neighbors; int num_neighbors; - const int forest_is_balanced = 1; t8_eclass_scheme_c *neigh_scheme; int *dual_faces; t8_locidx_t *neigh_ids; t8_gloidx_t gneightree; t8_forest_leaf_face_neighbors_ext (forest, itree, element, &neighbors, iface, &dual_faces, &num_neighbors, - &neigh_ids, &neigh_scheme, forest_is_balanced, &gneightree, NULL); + &neigh_ids, &neigh_scheme, &gneightree, NULL); /* Iterate and compute their facenormal */ for (int ineigh = 0; ineigh < num_neighbors; ineigh++) { diff --git a/tutorials/general/t8_step6_stencil.cxx b/tutorials/general/t8_step6_stencil.cxx index e3cff9ab86..ab873c0347 100644 --- a/tutorials/general/t8_step6_stencil.cxx +++ b/tutorials/general/t8_step6_stencil.cxx @@ -212,7 +212,7 @@ t8_step6_compute_stencil (t8_forest_t forest, struct data_per_element *element_d /* Collect all neighbors at the current face. */ t8_forest_leaf_face_neighbors (forest, itree, element, &neighbors, iface, &dual_faces, &num_neighbors, - &neighids, &neigh_scheme, 1); + &neighids, &neigh_scheme); /* Retrieve the `height` of the face neighbor. Account for two neighbors in case of a non-conforming interface by computing the average. */ From 58369cb3b5ab5d6877ce4c0d2d027b26e0a4fb4b Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 13:52:06 +0100 Subject: [PATCH 031/133] improve function documentation --- src/t8_forest/t8_forest.cxx | 4 ---- src/t8_forest/t8_forest_general.h | 4 ++-- src/t8_forest/t8_forest_ghost.h | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index d90f4a91bf..ad06aad4ed 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1840,10 +1840,6 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons } } - // Somehow collect leaf arrays - // std::vector - // iterate over vector - struct t8_lfn_user_data user_data (*neigh_scheme); // Now we iterate over the leaf arrays of the neighbor tree diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index 9513606dbe..ce278deb57 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -553,7 +553,7 @@ t8_forest_leaf_face_orientation (t8_forest_t forest, const t8_locidx_t ltreeid, /** Compute the leaf face neighbors of a forest. * \param [in] forest The forest. Must have a valid ghost layer. - * \param [in] ltreeid A local tree id. + * \param [in] ltreeid A local tree id (could also be a ghost tree). 0 <= \a ltreeid < num_local trees+num_ghost_trees * \param [in] leaf A leaf in tree \a ltreeid of \a forest. * \param [out] pneighbor_leaves Unallocated on input. On output the neighbor * leaves are stored here. @@ -587,7 +587,7 @@ t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8 /** 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] ltreeid A local tree id (could also be a ghost tree). 0 <= \a ltreeid < num_local trees+num_ghost_trees * \param [in] leaf A leaf in tree \a ltreeid of \a forest. * \param [out] pneighbor_leaves Unallocated on input. On output the neighbor * leaves are stored here. diff --git a/src/t8_forest/t8_forest_ghost.h b/src/t8_forest/t8_forest_ghost.h index 3b8042df10..17d03ad101 100644 --- a/src/t8_forest/t8_forest_ghost.h +++ b/src/t8_forest/t8_forest_ghost.h @@ -71,7 +71,7 @@ t8_forest_ghost_tree_num_elements (t8_forest_t forest, t8_locidx_t lghost_tree); /** Get a pointer to the ghost element array of a ghost tree. * \param [in] forest The forest. Ghost layer must exist. - * \param [in] lghost_tree The ghost tree id of a ghost tree. + * \param [in] lghost_tree The ghost tree id of a ghost tree. 0 <= \a lghost_tree < num_ghost_trees * \return A pointer to the array of ghost elements of the tree. * \a forest must be committed before calling this function. */ From 17c869b8853eecea994b2623484bd6f85ddf3a15 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 15:26:56 +0100 Subject: [PATCH 032/133] fix abort if incomplete forest --- src/t8_forest/t8_forest.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index ad06aad4ed..e1d85f3a0e 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1775,8 +1775,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons T8_ASSERT (t8_forest_element_is_ghost (forest, leaf_or_ghost, local_ghost_treeid)); } #endif - SC_CHECK_ABORT (forest->incomplete_trees, "Leaf face neighbor is not supported for " - "forests with deleted elements.\n"); + SC_CHECK_ABORT (!forest->incomplete_trees, "Leaf face neighbor is not supported for " + "forests with deleted elements.\n"); SC_CHECK_ABORT (forest->mpisize == 1 || forest->ghosts != NULL, "Ghost structure is needed for t8_forest_leaf_face_neighbors " "but was not found in forest.\n"); From f4d50df082b8dea6471f187510341756226280ce Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 15:27:14 +0100 Subject: [PATCH 033/133] return when no neighbors are found --- src/t8_forest/t8_forest.cxx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index e1d85f3a0e..6cb05c08e0 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1801,6 +1801,16 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_gloidx_t gneigh_treeid = t8_forest_element_face_neighbor ( forest, ltreeid, leaf_or_ghost, same_level_neighbor, neigh_scheme, face, &neigh_face); + if (gneigh_treeid < 0) { + // There is no face neighbor across this face + neigh_scheme->t8_element_destroy (1, &same_level_neighbor); + *dual_faces = NULL; + *num_neighbors = 0; + *pelement_indices = NULL; + *pneighbor_leaves = NULL; + return; + } + const int maxlevel = neigh_scheme->t8_element_maxlevel (); // Compute the first and last face descendant of the neighbor From dce66aeb26068c1df634aadea281b7e3ddfaa702 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 15:27:33 +0100 Subject: [PATCH 034/133] only compute ghost tree if ghosts exists --- src/t8_forest/t8_forest.cxx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 6cb05c08e0..5e34d5a7ce 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1839,14 +1839,17 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons leaf_arrays.push_back (tree_leafs); } } - const t8_locidx_t local_neighbor_ghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, gneigh_treeid); - if (local_neighbor_ghost_treeid >= 0) { - // The neighbor tree is also a ghost tree and face neighbors of our element might - // be ghost elements. - // We add the ghost elements of that tree to our search array. - const t8_element_array_t *ghost_leafs = t8_forest_ghost_get_tree_elements (forest, local_neighbor_ghost_treeid); - if (ghost_leafs != nullptr) { - leaf_arrays.push_back (ghost_leafs); + + if (forest->ghosts != NULL) { + const t8_locidx_t local_neighbor_ghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, gneigh_treeid); + if (local_neighbor_ghost_treeid >= 0) { + // The neighbor tree is also a ghost tree and face neighbors of our element might + // be ghost elements. + // We add the ghost elements of that tree to our search array. + const t8_element_array_t *ghost_leafs = t8_forest_ghost_get_tree_elements (forest, local_neighbor_ghost_treeid); + if (ghost_leafs != nullptr) { + leaf_arrays.push_back (ghost_leafs); + } } } From 591d2ff4e84fd29fbd90a9aeb24b89e7411d2865 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 15:27:54 +0100 Subject: [PATCH 035/133] skip if case when no neighbors found in search --- src/t8_forest/t8_forest.cxx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 5e34d5a7ce..ac519701c8 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1860,11 +1860,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons for (auto &tree_leafs : leaf_arrays) { const t8_locidx_t first_desc_search = t8_forest_bin_search_lower (tree_leafs, first_face_desc_id, maxlevel); const t8_locidx_t last_desc_search = t8_forest_bin_search_lower (tree_leafs, last_face_desc_id, maxlevel); - if (first_desc_search < 0 && last_desc_search < 0) { - // TODO: No neighbor in this leaf array - SC_ABORT ("Not implemented yet. TODO."); - } - else { + if (first_desc_search >= 0 || last_desc_search >= 0) { + // There will be face neighbors in this leaf array. // The first descendant may not be in the leaf array, we then // start with the first leaf. const t8_locidx_t first_desc_index = SC_MAX (0, first_desc_search); From 731c062ef04f91d27f76c710287b134b8766c3eb Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 15:28:04 +0100 Subject: [PATCH 036/133] comments --- src/t8_forest/t8_forest.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index ac519701c8..08b6f71459 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1876,6 +1876,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // Compute their nearest common ancestor neigh_scheme->t8_element_nca (first_face_leaf, last_face_leaf, nca_of_face_desc); const int face_of_nca = 0; + // TODO: Need to implement face of nca + // Any thing left TODO here??? SC_ABORT ("Not implemented"); t8_forest_iterate_faces (forest, local_neighbor_tree, nca_of_face_desc, face_of_nca, tree_leafs, first_desc_index, From 86bcfddbc79c5020f76746c7bc89267c9fbc01e7 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 15:28:18 +0100 Subject: [PATCH 037/133] fix wrongly placed brace --- src/t8_forest/t8_forest.cxx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 08b6f71459..a71fef0c9d 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1894,16 +1894,19 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // neighbor data. // TODO: Since there is no other way, we copy them from the vectors. // This should be improved in the future to get around the copy. + + *num_neighbors = user_data.neighbors.size (); + // Copy neighbor element pointers + *pneighbor_leaves = T8_ALLOC (t8_element_t *, *num_neighbors); + memcpy (*pneighbor_leaves, user_data.neighbors.data (), *num_neighbors * sizeof (t8_element_t *)); + *pelement_indices = T8_ALLOC (t8_locidx_t, *num_neighbors); + memcpy (*pelement_indices, user_data.element_indices.data (), *num_neighbors * sizeof (t8_locidx_t)); + *dual_faces = T8_ALLOC (int, *num_neighbors); + memcpy (*dual_faces, user_data.dual_faces.data (), *num_neighbors * sizeof (int)); } - *num_neighbors = user_data.neighbors.size (); - // Copy neighbor element pointers - *pneighbor_leaves = T8_ALLOC (t8_element_t *, *num_neighbors); - memcpy (*pneighbor_leaves, user_data.neighbors.data (), *num_neighbors * sizeof (t8_element_t *)); - *pelement_indices = T8_ALLOC (t8_locidx_t, *num_neighbors); - memcpy (*pelement_indices, user_data.element_indices.data (), *num_neighbors * sizeof (t8_locidx_t)); - *dual_faces = T8_ALLOC (int, *num_neighbors); - memcpy (*dual_faces, user_data.dual_faces.data (), *num_neighbors * sizeof (int)); } + // We must have found face neighbors by now. + T8_ASSERT (*num_neighbors > 0); #if 0 t8_locidx_t lneigh_treeid = -1; t8_locidx_t lghost_treeid = -1, *element_indices, element_index; From 6b61f199cdf1168bc13b17fd1b0289e94ac47d91 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 3 Dec 2024 15:28:43 +0100 Subject: [PATCH 038/133] add first test case --- test/CMakeLists.txt | 1 + test/t8_forest/t8_gtest_face_neighbors.cxx | 160 +++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 test/t8_forest/t8_gtest_face_neighbors.cxx diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bfd28922e3..8d09c8d6da 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -92,6 +92,7 @@ add_t8_test( NAME t8_gtest_shmem_parallel SOURCES t8_gtest_main.cxx t8_data/t8_ add_t8_test( NAME t8_gtest_element_volume_serial SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_element_volume.cxx ) add_t8_test( NAME t8_gtest_search_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_search.cxx ) add_t8_test( NAME t8_gtest_half_neighbors_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_half_neighbors.cxx ) +add_t8_test( NAME t8_gtest_face_neighbors_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_face_neighbors.cxx ) add_t8_test( NAME t8_gtest_find_owner_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_find_owner.cxx ) add_t8_test( NAME t8_gtest_user_data_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_user_data.cxx ) add_t8_test( NAME t8_gtest_transform_serial SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_transform.cxx ) diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx new file mode 100644 index 0000000000..045016cd95 --- /dev/null +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -0,0 +1,160 @@ +/* + 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) 2024 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "test/t8_cmesh_generator/t8_cmesh_example_sets.hxx" + +class forest_face_neighbors: public testing::TestWithParam { + protected: + void + SetUp () override + { + t8_cmesh_t cmesh = GetParam ()->cmesh_create (); + if (t8_cmesh_is_empty (cmesh)) { + /* we skip empty cmeshes case */ + t8_cmesh_unref (&cmesh); + GTEST_SKIP (); + } + t8_scheme_cxx_t *default_scheme = t8_scheme_new_default_cxx (); + const int level = 1; + const bool do_ghost = true; + forest = t8_forest_new_uniform (cmesh, default_scheme, level, do_ghost, sc_MPI_COMM_WORLD); + cmesh = t8_forest_get_cmesh (forest); + } + + void + TearDown () override + { + if (forest != nullptr) { + t8_forest_unref (&forest); + } + } + + t8_forest_t forest { nullptr }; +}; + +TEST_P (forest_face_neighbors, test_face_neighbors) +{ + /* iterate over all elements */ + const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); + const t8_locidx_t num_ghost_trees = t8_forest_get_num_ghost_trees (forest); + for (t8_locidx_t itree = 0; itree < num_local_trees + num_ghost_trees; itree++) { + const bool is_ghost = itree >= num_local_trees; + const t8_locidx_t ghost_tree_id = itree - num_local_trees; + /* Get the leaf element array */ + const t8_element_array_t *leaf_elements = !is_ghost ? t8_forest_get_tree_element_array (forest, itree) + : t8_forest_ghost_get_tree_elements (forest, ghost_tree_id); + const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, itree); + const t8_eclass_scheme_c *scheme = t8_forest_get_eclass_scheme (forest, tree_class); + const t8_locidx_t num_leafs = t8_element_array_get_count (leaf_elements); + for (t8_locidx_t ileaf = 0; ileaf < num_leafs; ++ileaf) { + // Iterate over each leaf element + const t8_element_t *element = t8_element_array_index_locidx (leaf_elements, ileaf); + const int num_faces = scheme->t8_element_num_faces (element); + for (int iface = 0; iface < num_faces; ++iface) { + // Iterate over all faces and compute the face neighbors + + // preparation + t8_element_t **neighbor_leaves; + int *dual_faces; + int num_neighbors; + t8_locidx_t *element_indices; + t8_eclass_scheme_c *neigh_scheme; + t8_gloidx_t gneigh_tree; + int orientation; + // Actual computation + t8_forest_leaf_face_neighbors_ext (forest, itree, element, &neighbor_leaves, iface, &dual_faces, &num_neighbors, + &element_indices, &neigh_scheme, &gneigh_tree, &orientation); + + // clean-up + if (num_neighbors > 0) { + scheme->t8_element_destroy (num_neighbors, neighbor_leaves); + T8_FREE (neighbor_leaves); + T8_FREE (element_indices); + T8_FREE (dual_faces); + } + } + } + } +} + +#if 0 +//// OLD CODE + + for (t8_locidx_t ielement = 0; ielement < t8_forest_get_tree_num_elements (forest, itree); ielement++) { + const t8_element_t *element = t8_forest_get_element_in_tree (forest, itree, ielement); + /* iterate over the faces */ + for (int face = 0; face < ts->t8_element_num_faces (element); face++) { + /* Get the eclass of the face neighbor and get the scheme */ + const t8_eclass_t neigh_class = t8_forest_element_neighbor_eclass (forest, itree, element, face); + t8_eclass_scheme_c *neigh_scheme = t8_forest_get_eclass_scheme (forest, neigh_class); + const int num_face_neighs = ts->t8_element_num_face_children (element, face); + t8_element_t **half_neighbors = T8_ALLOC (t8_element_t *, num_face_neighs); + ts->t8_element_new (num_face_neighs, half_neighbors); + t8_forest_element_half_face_neighbors (forest, itree, element, half_neighbors, neigh_scheme, face, + num_face_neighs, NULL); + /* allocate memory for element's neighbor and construct it */ + neigh_scheme->t8_element_new (1, &neighbor); + const t8_locidx_t neigh_tree + = t8_forest_element_face_neighbor (forest, itree, element, neighbor, neigh_scheme, face, &dual_face); + if (neigh_tree > 0) { + /* We now check whether the face children of neighbor are the half neighbors. */ + T8_ASSERT (num_face_neighs == neigh_scheme->t8_element_num_face_children (neighbor, dual_face)); + t8_element_t **neighbor_face_children = T8_ALLOC (t8_element_t *, num_face_neighs); + neigh_scheme->t8_element_new (num_face_neighs, neighbor_face_children); + int *child_ids = T8_ALLOC (int, num_face_neighs); + neigh_scheme->t8_element_children_at_face (neighbor, dual_face, neighbor_face_children, num_face_neighs, + child_ids); + /* Check that the children at face of the neighbor are the half neighbors of the element */ + for (int ineigh = 0; ineigh < num_face_neighs; ineigh++) { + EXPECT_ELEM_EQ (neigh_scheme, neighbor_face_children[ineigh], half_neighbors[ineigh]) + << "ineigh = " << ineigh << " face = " << face; + } + neigh_scheme->t8_element_destroy (num_face_neighs, neighbor_face_children); + T8_FREE (child_ids); + T8_FREE (neighbor_face_children); + } + neigh_scheme->t8_element_destroy (1, &neighbor); + neigh_scheme->t8_element_destroy (num_face_neighs, half_neighbors); + T8_FREE (half_neighbors); + } + } + } + t8_forest_unref (&forest); + sc_array_reset (&owners); +} +#endif + +INSTANTIATE_TEST_SUITE_P (t8_gtest_face_neighbors, forest_face_neighbors, AllCmeshsParam); From b0d73ba9a435145add7db1e9f7193a1381d41ff0 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 5 Dec 2024 16:23:54 +0100 Subject: [PATCH 039/133] only do face neighbors when default quad or hex --- src/t8_forest/t8_forest.cxx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index a71fef0c9d..9f72e11e44 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include #include @@ -1875,10 +1877,18 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_element_t *last_face_leaf = t8_element_array_index_locidx (tree_leafs, last_desc_index); // Compute their nearest common ancestor neigh_scheme->t8_element_nca (first_face_leaf, last_face_leaf, nca_of_face_desc); - const int face_of_nca = 0; - // TODO: Need to implement face of nca - // Any thing left TODO here??? - SC_ABORT ("Not implemented"); + const int face_of_nca = neigh_face; + // TODO: Need to implement element function to compute face id of nca face. + // Input: Element A and face f, Element B that is ancestor or successor of A, and + // shares face f (f is a subface of a face of B or B has a subface of f) + // Output: The face id of the corresponding ancestor/descendant face of B + // + // Currently we hardcode this algorithm for quads. In that case the face id of B is always f. + const bool scheme_is_default_quad_hex + = dynamic_cast (neigh_scheme) != NULL + || dynamic_cast (neigh_scheme) != NULL; + SC_CHECK_ABORT (scheme_is_default_quad_hex, + "Computing leaf face neighbors currently only works for default quad or hex schemes."); t8_forest_iterate_faces (forest, local_neighbor_tree, nca_of_face_desc, face_of_nca, tree_leafs, first_desc_index, t8_forest_leaf_face_neighbors_iterate, &user_data); From b9a6eea8aca00b6e0be2d7501f2774541776a855 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 5 Dec 2024 16:24:10 +0100 Subject: [PATCH 040/133] fix user data pointer in callback --- src/t8_forest/t8_forest.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 9f72e11e44..03b9d5d5b5 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1744,17 +1744,17 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, const t8_locidx_t adjusted_tree_id = !is_ghost_tree ? ltreeid : ltreeid - t8_forest_get_num_local_trees (forest); T8_ASSERT (t8_forest_element_is_leaf_or_ghost (forest, element, adjusted_tree_id, is_ghost_tree)); - struct t8_lfn_user_data &lfn_data = reinterpret_cast (user_data); + struct t8_lfn_user_data *lfn_data = reinterpret_cast (user_data); // face is the face of the considered leaf neighbor element and thus the // corresponding dual face - lfn_data.dual_faces.push_back (face); + lfn_data->dual_faces.push_back (face); // Compute the index of the element const t8_locidx_t tree_offset = !is_ghost_tree ? t8_forest_get_tree_element_offset (forest, ltreeid) : t8_forest_ghost_get_tree_element_offset (forest, adjusted_tree_id); const t8_locidx_t element_index = tree_offset + tree_leaf_index; - lfn_data.element_indices.push_back (element_index); + lfn_data->element_indices.push_back (element_index); // Add the pointer to the current element - const t8_element_t *&pnew_element = lfn_data.neighbors.emplace_back (); + const t8_element_t *&pnew_element = lfn_data->neighbors.emplace_back (); pnew_element = element; return 1; } From e00966772156583ed45d9bf3a407ac5090d71885 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 5 Dec 2024 16:24:28 +0100 Subject: [PATCH 041/133] fix memory counting and handling for computed neighbors --- src/t8_forest/t8_forest.cxx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 03b9d5d5b5..cf5b20c199 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1859,6 +1859,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // Now we iterate over the leaf arrays of the neighbor tree // or neighbor ghost tree and find all leaf face neighbors of the element. + *num_neighbors = 0; for (auto &tree_leafs : leaf_arrays) { const t8_locidx_t first_desc_search = t8_forest_bin_search_lower (tree_leafs, first_face_desc_id, maxlevel); const t8_locidx_t last_desc_search = t8_forest_bin_search_lower (tree_leafs, last_face_desc_id, maxlevel); @@ -1905,14 +1906,18 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // TODO: Since there is no other way, we copy them from the vectors. // This should be improved in the future to get around the copy. - *num_neighbors = user_data.neighbors.size (); + const int num_neighbors_current_tree = user_data.neighbors.size (); + const int total_num_neighbors = *num_neighbors + num_neighbors_current_tree; // Copy neighbor element pointers - *pneighbor_leaves = T8_ALLOC (t8_element_t *, *num_neighbors); - memcpy (*pneighbor_leaves, user_data.neighbors.data (), *num_neighbors * sizeof (t8_element_t *)); + T8_REALLOC (*pneighbor_leaves, t8_element_t *, total_num_neighbors); + memcpy (*pneighbor_leaves + *num_neighbors, user_data.neighbors.data (), + num_neighbors_current_tree * sizeof (t8_element_t *)); *pelement_indices = T8_ALLOC (t8_locidx_t, *num_neighbors); - memcpy (*pelement_indices, user_data.element_indices.data (), *num_neighbors * sizeof (t8_locidx_t)); - *dual_faces = T8_ALLOC (int, *num_neighbors); - memcpy (*dual_faces, user_data.dual_faces.data (), *num_neighbors * sizeof (int)); + memcpy (*pelement_indices + *num_neighbors, user_data.element_indices.data (), + num_neighbors_current_tree * sizeof (t8_locidx_t)); + T8_REALLOC (*dual_faces, int, total_num_neighbors); + memcpy (*dual_faces + *num_neighbors, user_data.dual_faces.data (), num_neighbors_current_tree * sizeof (int)); + *num_neighbors = total_num_neighbors; } } // We must have found face neighbors by now. From 963c4d2d3986437e701a2abe8331a6b63fbaa1a4 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 5 Dec 2024 16:24:41 +0100 Subject: [PATCH 042/133] face iterate use is leaf check --- src/t8_forest/t8_forest_iterate.cxx | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index ae61eabb74..9ede70a1d8 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -98,21 +98,16 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme const t8_eclass_t eclass = t8_forest_get_tree_class (forest, ltreeid); const t8_eclass_scheme_c *ts = t8_forest_get_eclass_scheme (forest, eclass); - int is_leaf = 0; - if (elem_count == 1) { - /* There is only one leaf left, we check whether it is the same as element - * and if so call the callback function */ - const t8_element_t *leaf = t8_element_array_index_locidx (leaf_elements, 0); - if (ts->t8_element_equal (element, leaf)) { - // TODO: this might break for ghosts, extend to ghost - T8_ASSERT (t8_forest_element_is_leaf_or_ghost (forest, leaf, local_or_ghost_tree_id, tree_is_ghost)); - /* The element is the leaf, we are at the last stage of the recursion */ - is_leaf = 1; - } - } + // TODO: This does a costly binary search. Is there a better criterion to check + // whether element is a leaf or not? + // We tried (elem_count == 1) but this does not work since we could + // start the search with a full family and element being one sibling. + // In that case, element is a leaf but elem_count is not 1. + bool is_leaf = t8_forest_element_is_leaf_or_ghost (forest, element, local_or_ghost_tree_id, tree_is_ghost); + #ifdef T8_ENABLE_DEBUG if (!is_leaf) { - /* Check whether element has greater level than the first leaf */ + /* Check whether element has smaller level than the first leaf */ const t8_element_t *leaf = t8_element_array_index_locidx (leaf_elements, 0); T8_ASSERT (t8_forest_element_is_leaf_or_ghost (forest, leaf, local_or_ghost_tree_id, tree_is_ghost)); T8_ASSERT (ts->t8_element_level (element) < ts->t8_element_level (leaf)); From 59533d96b1fe7e9b38b89cb886f83326d4b7684c Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 11 Dec 2024 15:02:33 +0100 Subject: [PATCH 043/133] adjustments to face neighbor computation --- src/t8_forest/t8_forest.cxx | 61 +++++++++++++++++----- src/t8_forest/t8_forest_iterate.cxx | 2 +- test/t8_forest/t8_gtest_face_neighbors.cxx | 20 ++++++- 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index cf5b20c199..52fff95ade 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1755,7 +1755,12 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, lfn_data->element_indices.push_back (element_index); // Add the pointer to the current element const t8_element_t *&pnew_element = lfn_data->neighbors.emplace_back (); - pnew_element = element; + if (!is_ghost_tree) { + pnew_element = t8_forest_get_element_in_tree (forest, ltreeid, tree_leaf_index); + } + else { + pnew_element = t8_forest_ghost_get_element (forest, adjusted_tree_id, tree_leaf_index); + } return 1; } @@ -1800,10 +1805,10 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_element_t *same_level_neighbor; neigh_scheme->t8_element_new (1, &same_level_neighbor); int neigh_face; - const t8_gloidx_t gneigh_treeid = t8_forest_element_face_neighbor ( - forest, ltreeid, leaf_or_ghost, same_level_neighbor, neigh_scheme, face, &neigh_face); + *gneigh_tree = t8_forest_element_face_neighbor (forest, ltreeid, leaf_or_ghost, same_level_neighbor, neigh_scheme, + face, &neigh_face); - if (gneigh_treeid < 0) { + if (*gneigh_tree < 0) { // There is no face neighbor across this face neigh_scheme->t8_element_destroy (1, &same_level_neighbor); *dual_faces = NULL; @@ -1824,6 +1829,10 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons neigh_scheme->t8_element_last_descendant_face (same_level_neighbor, neigh_face, last_face_desc, maxlevel); const t8_linearidx_t first_face_desc_id = neigh_scheme->t8_element_get_linear_id (first_face_desc, maxlevel); const t8_linearidx_t last_face_desc_id = neigh_scheme->t8_element_get_linear_id (last_face_desc, maxlevel); + // same level neighbor, first and last face desc not needed anymore, free memory + neigh_scheme->t8_element_destroy (1, &same_level_neighbor); + neigh_scheme->t8_element_destroy (1, &first_face_desc); + neigh_scheme->t8_element_destroy (1, &last_face_desc); // Allocate memory for the nca of first and last face desc t8_element_t *nca_of_face_desc; neigh_scheme->t8_element_new (1, &nca_of_face_desc); @@ -1833,7 +1842,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // We store these in a vector and iterate over the entries. std::vector leaf_arrays; // Compute the local id of the neighbor tree and check if it is a local tree - const t8_locidx_t local_neighbor_tree = t8_forest_get_local_id (forest, gneigh_treeid); + const t8_locidx_t local_neighbor_tree = t8_forest_get_local_id (forest, *gneigh_tree); if (0 <= local_neighbor_tree) { // The neighbor tree is a local tree and hence there may be local neighbor elements. const t8_element_array_t *tree_leafs = t8_forest_tree_get_leaves (forest, local_neighbor_tree); @@ -1843,7 +1852,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons } if (forest->ghosts != NULL) { - const t8_locidx_t local_neighbor_ghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, gneigh_treeid); + const t8_locidx_t local_neighbor_ghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, *gneigh_tree); if (local_neighbor_ghost_treeid >= 0) { // The neighbor tree is also a ghost tree and face neighbors of our element might // be ghost elements. @@ -1860,6 +1869,14 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // Now we iterate over the leaf arrays of the neighbor tree // or neighbor ghost tree and find all leaf face neighbors of the element. *num_neighbors = 0; + // Since we use REALLOC later to allocate memory of the following + // three pointers, we have to set them to NULL manually. + // This will trigger REALLOC to allocate the memory in the initial call. + // Not setting them to NULL but keeping them possibly uninitialized, will + // call REALLOC on uninitialized memory and result in memory errors. + *pneighbor_leaves = NULL; + *pelement_indices = NULL; + *dual_faces = NULL; for (auto &tree_leafs : leaf_arrays) { const t8_locidx_t first_desc_search = t8_forest_bin_search_lower (tree_leafs, first_face_desc_id, maxlevel); const t8_locidx_t last_desc_search = t8_forest_bin_search_lower (tree_leafs, last_face_desc_id, maxlevel); @@ -1906,22 +1923,42 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // TODO: Since there is no other way, we copy them from the vectors. // This should be improved in the future to get around the copy. + // num_neighbors counts the already inserted neighbors before this tree + // num_neighbors_current_tree counts the neighbors added in this tree + // total_num_neighbors temporarily counts all inserted neighbors, including this tree const int num_neighbors_current_tree = user_data.neighbors.size (); const int total_num_neighbors = *num_neighbors + num_neighbors_current_tree; // Copy neighbor element pointers - T8_REALLOC (*pneighbor_leaves, t8_element_t *, total_num_neighbors); - memcpy (*pneighbor_leaves + *num_neighbors, user_data.neighbors.data (), + *pneighbor_leaves = T8_REALLOC (*pneighbor_leaves, t8_element_t *, total_num_neighbors); + T8_ASSERT (*pneighbor_leaves != NULL); + memcpy (*pneighbor_leaves + *num_neighbors, user_data.neighbors.data () + *num_neighbors, num_neighbors_current_tree * sizeof (t8_element_t *)); - *pelement_indices = T8_ALLOC (t8_locidx_t, *num_neighbors); - memcpy (*pelement_indices + *num_neighbors, user_data.element_indices.data (), + // Copy element indices + *pelement_indices = T8_REALLOC (*pelement_indices, t8_locidx_t, total_num_neighbors); + T8_ASSERT (*pelement_indices != NULL); + memcpy (*pelement_indices + *num_neighbors, user_data.element_indices.data () + *num_neighbors, num_neighbors_current_tree * sizeof (t8_locidx_t)); - T8_REALLOC (*dual_faces, int, total_num_neighbors); - memcpy (*dual_faces + *num_neighbors, user_data.dual_faces.data (), num_neighbors_current_tree * sizeof (int)); + // Copy dual face + *dual_faces = T8_REALLOC (*dual_faces, int, total_num_neighbors); + T8_ASSERT (*dual_faces != NULL); + memcpy (*dual_faces + *num_neighbors, user_data.dual_faces.data () + *num_neighbors, + num_neighbors_current_tree * sizeof (int)); *num_neighbors = total_num_neighbors; } } +#if T8_ENABLE_DEBUG + // Debugging checks // We must have found face neighbors by now. T8_ASSERT (*num_neighbors > 0); + // All neighbor elements must be valid + for (int ineigh = 0; ineigh < *num_neighbors; ++ineigh) { + T8_ASSERT (neigh_scheme->t8_element_is_valid (*pneighbor_leaves[ineigh])); + t8_debugf ("Face neighbor %p is valid.\n", *pneighbor_leaves[ineigh]); + } +#endif // T8_ENABLE_DEBUG + + // clean-up + neigh_scheme->t8_element_destroy (1, &nca_of_face_desc); #if 0 t8_locidx_t lneigh_treeid = -1; t8_locidx_t lghost_treeid = -1, *element_indices, element_index; diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index 9ede70a1d8..1f10803b2c 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -88,7 +88,7 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme // Check whether we are in a local tree or ghost tree const bool tree_is_local = t8_forest_tree_is_local (forest, ltreeid); const bool tree_is_ghost = !tree_is_local; - const bool local_or_ghost_tree_id = tree_is_local ? ltreeid : ltreeid - num_local_trees; + const t8_locidx_t local_or_ghost_tree_id = tree_is_local ? ltreeid : ltreeid - num_local_trees; const size_t elem_count = t8_element_array_get_count (leaf_elements); if (elem_count == 0) { diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index 045016cd95..e4c71bac2e 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -30,19 +30,37 @@ #include #include #include +#include #include #include #include #include #include "test/t8_cmesh_generator/t8_cmesh_example_sets.hxx" +bool +test_face_neighbors_skip_cmesh (const t8_cmesh_t cmesh) +{ + // We only allow cmeshes with pure quad or hex elements. + // So we check all eclass and if the cmesh contains any of those + // we skip the cmesh. + for (int eclass = T8_ECLASS_ZERO; eclass < T8_ECLASS_COUNT; ++eclass) { + if (eclass != T8_ECLASS_QUAD && eclass != T8_ECLASS_HEX) { + if (cmesh->num_trees_per_eclass[eclass] > 0) { + return true; + } + } + } + // Additionally, we skip empty cmeshes. + return t8_cmesh_is_empty (cmesh); +} + class forest_face_neighbors: public testing::TestWithParam { protected: void SetUp () override { t8_cmesh_t cmesh = GetParam ()->cmesh_create (); - if (t8_cmesh_is_empty (cmesh)) { + if (test_face_neighbors_skip_cmesh (cmesh)) { /* we skip empty cmeshes case */ t8_cmesh_unref (&cmesh); GTEST_SKIP (); From c7654ea346e0e383fd5e82ed7dda80f26a4583ce Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 11 Dec 2024 15:02:50 +0100 Subject: [PATCH 044/133] extend lfn test --- test/t8_forest/t8_gtest_face_neighbors.cxx | 118 ++++++++++++++++++--- 1 file changed, 106 insertions(+), 12 deletions(-) diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index e4c71bac2e..82f8f80211 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -86,17 +86,23 @@ class forest_face_neighbors: public testing::TestWithParam TEST_P (forest_face_neighbors, test_face_neighbors) { /* iterate over all elements */ - const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); - const t8_locidx_t num_ghost_trees = t8_forest_get_num_ghost_trees (forest); + const t8_forest_t forest_uniform = forest; + const t8_cmesh_t cmesh = t8_forest_get_cmesh (forest_uniform); + const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest_uniform); + const t8_locidx_t num_ghost_trees = t8_forest_get_num_ghost_trees (forest_uniform); + const t8_locidx_t num_local_elements = t8_forest_get_local_num_elements (forest_uniform); for (t8_locidx_t itree = 0; itree < num_local_trees + num_ghost_trees; itree++) { + const t8_gloidx_t gtree_id = t8_forest_global_tree_id (forest_uniform, itree); const bool is_ghost = itree >= num_local_trees; const t8_locidx_t ghost_tree_id = itree - num_local_trees; /* Get the leaf element array */ - const t8_element_array_t *leaf_elements = !is_ghost ? t8_forest_get_tree_element_array (forest, itree) - : t8_forest_ghost_get_tree_elements (forest, ghost_tree_id); - const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, itree); - const t8_eclass_scheme_c *scheme = t8_forest_get_eclass_scheme (forest, tree_class); + const t8_element_array_t *leaf_elements = !is_ghost + ? t8_forest_get_tree_element_array (forest_uniform, itree) + : t8_forest_ghost_get_tree_elements (forest_uniform, ghost_tree_id); + const t8_eclass_t tree_class = t8_forest_get_tree_class (forest_uniform, itree); + const t8_eclass_scheme_c *scheme = t8_forest_get_eclass_scheme (forest_uniform, tree_class); const t8_locidx_t num_leafs = t8_element_array_get_count (leaf_elements); + const t8_locidx_t cmesh_tree = t8_forest_ltreeid_to_cmesh_ltreeid (forest_uniform, itree); for (t8_locidx_t ileaf = 0; ileaf < num_leafs; ++ileaf) { // Iterate over each leaf element const t8_element_t *element = t8_element_array_index_locidx (leaf_elements, ileaf); @@ -107,18 +113,106 @@ TEST_P (forest_face_neighbors, test_face_neighbors) // preparation t8_element_t **neighbor_leaves; int *dual_faces; - int num_neighbors; + int num_neighbors = 0; t8_locidx_t *element_indices; t8_eclass_scheme_c *neigh_scheme; t8_gloidx_t gneigh_tree; int orientation; - // Actual computation - t8_forest_leaf_face_neighbors_ext (forest, itree, element, &neighbor_leaves, iface, &dual_faces, &num_neighbors, - &element_indices, &neigh_scheme, &gneigh_tree, &orientation); + // Actual computation of the face neighbors + t8_forest_leaf_face_neighbors_ext (forest_uniform, itree, element, &neighbor_leaves, iface, &dual_faces, + &num_neighbors, &element_indices, &neigh_scheme, &gneigh_tree, &orientation); - // clean-up + t8_debugf ("Tree %i element %i at face %i has %i face neighbors.\n", itree, ileaf, iface, num_neighbors); + + if (gneigh_tree < 0) { + // No neighbors are found, check for correctly set return values + ASSERT_EQ (num_neighbors, 0); + ASSERT_EQ (neighbor_leaves, nullptr); + ASSERT_EQ (element_indices, nullptr); + ASSERT_EQ (dual_faces, nullptr); + } + else { + ASSERT_GE (num_neighbors, 0); + ASSERT_NE (neighbor_leaves, nullptr); + ASSERT_NE (element_indices, nullptr); + ASSERT_NE (dual_faces, nullptr); + } + // Checking for: + // uniform forest: + // - inner element has 1 face neighbors + // - boundary element has 0 face neighbors // TODO: Add this test for all forests + // - If E face f has neighbor E' face f', then + // E' face f' must have neighbor E face f. + + // Now checking for inner and boundary elements. + + // Compute whether this element is a boundary element or not. + // An element is a boundary element if it lies on the tree boundary + // and if the corresponding tree face is at the domain boundary. + const bool is_root_boundary = scheme->t8_element_is_root_boundary (element, iface); + const int tree_face = scheme->t8_element_tree_face (element, iface); + const bool is_boundary_element + = is_root_boundary && t8_cmesh_tree_face_is_boundary (cmesh, cmesh_tree, tree_face); + + if (!is_boundary_element) { + EXPECT_EQ (num_neighbors, 1) << "Inner element should have exactly 1 neighbor, has " << num_neighbors << "."; + } + else { + EXPECT_EQ (num_neighbors, 0) << "Boundary element should have exactly 0 neighbors, has " << num_neighbors + << "."; + ; + } + + // Check that the neighbor of the neighbor is the original element. + for (int ineigh = 0; ineigh < num_neighbors; ++ineigh) { + const t8_element_t *neighbor = neighbor_leaves[ineigh]; + const int dual_face = dual_faces[ineigh]; + const t8_locidx_t neigh_index = element_indices[ineigh]; + + t8_debugf ("Checking neighbor element %p.\n", neighbor); + t8_debugf ("dual face is %i, index is %i\n", dual_face, neigh_index); + ASSERT_TRUE (neigh_scheme->t8_element_is_valid (neighbor)) + << "Neighbor element " << ineigh << " is not valid"; + // Compute the local tree id of the neighbors tree depending on whether + // it is a local tree or a ghost tree. + const t8_locidx_t neigh_ltreeid + = neigh_index < num_local_elements + ? gneigh_tree - t8_forest_get_first_local_tree_id (forest_uniform) + : t8_forest_ghost_get_ghost_treeid (forest_uniform, gneigh_tree) + num_local_trees; + // preparation + t8_element_t **neigh_neighbor_leaves; + int *neigh_dual_faces; + int neigh_num_neighbors = 0; + t8_locidx_t *neigh_element_indices; + t8_eclass_scheme_c *neigh_neigh_scheme; + t8_gloidx_t neigh_gneigh_tree; + int neigh_orientation; + // Actual computation of the neighbor's face neighbors + t8_forest_leaf_face_neighbors_ext (forest_uniform, neigh_ltreeid, neighbor, &neigh_neighbor_leaves, dual_face, + &neigh_dual_faces, &neigh_num_neighbors, &neigh_element_indices, + &neigh_neigh_scheme, &neigh_gneigh_tree, &neigh_orientation); + + // We must have found at least one face neighbor, namely the original element. + EXPECT_GE (neigh_num_neighbors, 1); + // The neighbor's neighbor tree must be the current tree + EXPECT_EQ (gtree_id, neigh_gneigh_tree); + // The neighbor's scheme must be the current scheme + EXPECT_EQ (scheme, neigh_neigh_scheme); + // The neighbor's orientation must be the orientation + EXPECT_EQ (orientation, neigh_orientation); + + // TODO Check for the specific face neighbor that would match the original element + + // clean-up neighbor's neighbors + if (neigh_num_neighbors > 0) { + T8_FREE (neigh_neighbor_leaves); + T8_FREE (neigh_element_indices); + T8_FREE (neigh_dual_faces); + } + } + + // clean-up original element neighbors if (num_neighbors > 0) { - scheme->t8_element_destroy (num_neighbors, neighbor_leaves); T8_FREE (neighbor_leaves); T8_FREE (element_indices); T8_FREE (dual_faces); From 7d9a6a9630035dcc3b6c60fc0e870c90a3cbaf92 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 15 Jan 2025 11:45:20 +0100 Subject: [PATCH 045/133] comments --- src/t8_forest/t8_forest.cxx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 52fff95ade..f871586ec3 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1770,6 +1770,17 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_locidx_t **pelement_indices, t8_eclass_scheme_c **pneigh_scheme, t8_gloidx_t *gneigh_tree, int *orientation) { + /* We compute all face neighbor leaf elements of E via the following strategy: + * - Compute the same level face neighbor N + * - Compute the first and last face descendants FD, LD, of N + * - The neighbor tree could be a local tree or ghost (or both), + * for each variant get the leaf array of the neighbor tree and search in it: + * - Search for FD and LD in the leaf array and get indices of the nearest matching leaf elements. + * - For the matching leaf elements, compute their nca (nearest common ancestor) - that is the finest element that contains both of them. + * This nca will contain all face neighbors (in the local tree or ghost tree). + * - Use the nca as a starting point for a recursive search across its corresponding face. + * Each element found on the face is a matching leaf face neighbor of E. + **/ T8_ASSERT (t8_forest_is_committed (forest)); #if T8_ENABLE_DEBUG @@ -1796,8 +1807,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons *orientation = t8_forest_leaf_face_orientation (forest, ltreeid, ts, leaf_or_ghost, face); } - /* At first we compute these children of the face neighbor elements of leaf. For this, we need the - * neighbor tree's eclass, scheme, and tree id */ + /* At first we compute the same lave face neighbor element of leaf. For this, we need the + * neighbor tree's eclass and scheme. */ const t8_eclass_t neigh_class = t8_forest_element_neighbor_eclass (forest, ltreeid, leaf_or_ghost, face); const t8_eclass_scheme_c *neigh_scheme = *pneigh_scheme = t8_forest_get_eclass_scheme (forest, neigh_class); @@ -1820,7 +1831,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const int maxlevel = neigh_scheme->t8_element_maxlevel (); - // Compute the first and last face descendant of the neighbor + // Compute the first and last face descendant of the neighbor to compute their ids t8_element_t *first_face_desc; t8_element_t *last_face_desc; neigh_scheme->t8_element_new (1, &first_face_desc); From 7e7dd6b483348a3a6ce99fb46345b71ca5a1e37f Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 15 Jan 2025 11:45:58 +0100 Subject: [PATCH 046/133] Face iteration requires view into partial leaf array --- src/t8_forest/t8_forest.cxx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index f871586ec3..8bd3da6b64 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1919,8 +1919,14 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons SC_CHECK_ABORT (scheme_is_default_quad_hex, "Computing leaf face neighbors currently only works for default quad or hex schemes."); - t8_forest_iterate_faces (forest, local_neighbor_tree, nca_of_face_desc, face_of_nca, tree_leafs, first_desc_index, - t8_forest_leaf_face_neighbors_iterate, &user_data); + // Restrict search array to the leafs from first to last face desc + t8_element_array_t face_leafs; + const size_t face_leaf_count = last_desc_index - first_desc_index + 1; + T8_ASSERT (face_leaf_count > 0); + t8_element_array_init_view (&face_leafs, tree_leafs, first_desc_index, face_leaf_count); + // Iterate over all leafs at the face and collect them as neighbors. + t8_forest_iterate_faces (forest, local_neighbor_tree, nca_of_face_desc, face_of_nca, &face_leafs, + first_desc_index, t8_forest_leaf_face_neighbors_iterate, &user_data); // Output of iterate_faces: // Array of indices in tree_leafs of all the face neighbor elements // Assign pneighbor_leaves From 13cf0a537fc2d2b0b84eafd4cbf941cd648c56c3 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 15 Jan 2025 12:45:08 +0100 Subject: [PATCH 047/133] store local or ghost tree info with leaf arrays for face iteration --- src/t8_forest/t8_forest.cxx | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 8bd3da6b64..548de58ea3 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1851,14 +1851,21 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // The neighbor leafs could be distributed across a local tree and a ghost // tree. We thus possibly need to search in two different arrays. // We store these in a vector and iterate over the entries. - std::vector leaf_arrays; + // The leaf arrays themself do not store any information about their tree, + // whether it is local or ghost. + // We thus need to add this info and hence store a pair of element array and + // a bool that is true if and only if the element array corresponds to a ghost tree. + using neighbor_leaf_array = std::pair; + + std::vector leaf_arrays; // Compute the local id of the neighbor tree and check if it is a local tree const t8_locidx_t local_neighbor_tree = t8_forest_get_local_id (forest, *gneigh_tree); if (0 <= local_neighbor_tree) { // The neighbor tree is a local tree and hence there may be local neighbor elements. const t8_element_array_t *tree_leafs = t8_forest_tree_get_leaves (forest, local_neighbor_tree); if (tree_leafs != nullptr) { - leaf_arrays.push_back (tree_leafs); + neighbor_leaf_array *leaf_array = new neighbor_leaf_array (tree_leafs, false); + leaf_arrays.push_back (leaf_array); } } @@ -1870,7 +1877,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // We add the ghost elements of that tree to our search array. const t8_element_array_t *ghost_leafs = t8_forest_ghost_get_tree_elements (forest, local_neighbor_ghost_treeid); if (ghost_leafs != nullptr) { - leaf_arrays.push_back (ghost_leafs); + neighbor_leaf_array *leaf_array = new neighbor_leaf_array (ghost_leafs, true); + leaf_arrays.push_back (leaf_array); } } } @@ -1888,7 +1896,9 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons *pneighbor_leaves = NULL; *pelement_indices = NULL; *dual_faces = NULL; - for (auto &tree_leafs : leaf_arrays) { + for (auto &leaf_array : leaf_arrays) { + auto &tree_leafs = leaf_array->first; + const bool leaf_array_is_ghost = leaf_array->second; const t8_locidx_t first_desc_search = t8_forest_bin_search_lower (tree_leafs, first_face_desc_id, maxlevel); const t8_locidx_t last_desc_search = t8_forest_bin_search_lower (tree_leafs, last_face_desc_id, maxlevel); if (first_desc_search >= 0 || last_desc_search >= 0) { @@ -1925,7 +1935,13 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons T8_ASSERT (face_leaf_count > 0); t8_element_array_init_view (&face_leafs, tree_leafs, first_desc_index, face_leaf_count); // Iterate over all leafs at the face and collect them as neighbors. - t8_forest_iterate_faces (forest, local_neighbor_tree, nca_of_face_desc, face_of_nca, &face_leafs, + const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); + // Compute the local or ghost tree id depending on whether this leaf array corresponds to a local + // tree or ghost tree. + const t8_locidx_t face_iterate_tree_id + = leaf_array_is_ghost ? t8_forest_ghost_get_ghost_treeid (forest, *gneigh_tree) + num_local_trees + : local_neighbor_tree; + t8_forest_iterate_faces (forest, face_iterate_tree_id, nca_of_face_desc, face_of_nca, &face_leafs, first_desc_index, t8_forest_leaf_face_neighbors_iterate, &user_data); // Output of iterate_faces: // Array of indices in tree_leafs of all the face neighbor elements From cd7ac01b3d154371465e94f3bad08f25187044a1 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Mon, 27 Jan 2025 14:09:09 +0100 Subject: [PATCH 048/133] extend forest_element_face_neighbor to ghosts --- src/t8_forest/t8_forest.cxx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 548de58ea3..d51d0d0e7a 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1519,17 +1519,13 @@ t8_forest_element_face_neighbor (t8_forest_t forest, t8_locidx_t ltreeid, const const t8_eclass_scheme_c *neigh_scheme, int face, int *neigh_face) { t8_eclass_scheme_c *ts; - t8_tree_t tree; - t8_eclass_t eclass; /* Get a pointer to the tree to read its element class */ - tree = t8_forest_get_tree (forest, ltreeid); - eclass = tree->eclass; + const t8_eclass_t eclass = t8_forest_get_tree_class (forest, ltreeid); ts = t8_forest_get_eclass_scheme (forest, eclass); if (neigh_scheme == ts && ts->t8_element_face_neighbor_inside (elem, neigh, face, neigh_face)) { /* The neighbor was constructed and is inside the current tree. */ - // TODO: replace with function t8_forest_local_treeid_to_global_treeid that incorporates ghosts - return ltreeid + t8_forest_get_first_local_tree_id (forest); + return t8_forest_global_tree_id (forest, ltreeid); } else { /* The neighbor does not lie inside the current tree. The content of neigh is undefined right now. */ From b3c19d7792283ade33ee2ab33a1acf6300ce9c09 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Mon, 27 Jan 2025 14:09:51 +0100 Subject: [PATCH 049/133] extend leaf_face_neighbors_iterate to ghosts --- src/t8_forest/t8_forest.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index d51d0d0e7a..f72299a26e 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1743,10 +1743,13 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, struct t8_lfn_user_data *lfn_data = reinterpret_cast (user_data); // face is the face of the considered leaf neighbor element and thus the // corresponding dual face + t8_debugf ("Adding new face neighbor (leaf index %li) with dual face %i.\n", tree_leaf_index, face); lfn_data->dual_faces.push_back (face); // Compute the index of the element + const t8_locidx_t num_local_elements = t8_forest_get_local_num_elements (forest); const t8_locidx_t tree_offset = !is_ghost_tree ? t8_forest_get_tree_element_offset (forest, ltreeid) - : t8_forest_ghost_get_tree_element_offset (forest, adjusted_tree_id); + : t8_forest_ghost_get_tree_element_offset (forest, adjusted_tree_id) + + num_local_elements; const t8_locidx_t element_index = tree_offset + tree_leaf_index; lfn_data->element_indices.push_back (element_index); // Add the pointer to the current element From 3c53f03725e26e1f4272e702dbd8150a32c3bf91 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Mon, 27 Jan 2025 14:10:14 +0100 Subject: [PATCH 050/133] new check elements for ancestor function --- src/t8_forest/t8_forest.cxx | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index f72299a26e..8aeccfcb40 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1763,6 +1763,29 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, return 1; } +/** Compute whether two elements are ancestor of each other. + * TODO: Move the function inside the element module */ +static bool +t8_forest_elements_are_ancestor (const t8_eclass_scheme_c *scheme, const t8_element_t *element_A, const t8_element_t *element_B) +{ + T8_ASSERT (scheme->t8_element_is_valid (element_A)); + T8_ASSERT (scheme->t8_element_is_valid (element_B)); + + t8_element_t *nca; + scheme->t8_element_new (1, &nca); + scheme->t8_element_nca (element_A, element_B, nca); + + const bool nca_is_A = scheme->t8_element_equal (element_A, nca); + if (!nca_is_A) { + if (!scheme->t8_element_equal (element_B, nca)) { + scheme->t8_element_destroy (1, &nca); + return false; + } + } + scheme->t8_element_destroy (1, &nca); + return true; +} + void t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf_or_ghost, t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], int *num_neighbors, From f58494121e42b06a6fd3414eda77b50e5fb4ec30 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Mon, 27 Jan 2025 14:10:54 +0100 Subject: [PATCH 051/133] In face neighbor computation check whether neighbors would exist before searching --- src/t8_forest/t8_forest.cxx | 196 ++++++++++++++++++++---------------- 1 file changed, 112 insertions(+), 84 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 8aeccfcb40..f92cdab7ac 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1805,6 +1805,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons **/ T8_ASSERT (t8_forest_is_committed (forest)); + #if T8_ENABLE_DEBUG const bool tree_is_local = t8_forest_tree_is_local (forest, ltreeid); if (tree_is_local) { @@ -1863,7 +1864,6 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_linearidx_t first_face_desc_id = neigh_scheme->t8_element_get_linear_id (first_face_desc, maxlevel); const t8_linearidx_t last_face_desc_id = neigh_scheme->t8_element_get_linear_id (last_face_desc, maxlevel); // same level neighbor, first and last face desc not needed anymore, free memory - neigh_scheme->t8_element_destroy (1, &same_level_neighbor); neigh_scheme->t8_element_destroy (1, &first_face_desc); neigh_scheme->t8_element_destroy (1, &last_face_desc); // Allocate memory for the nca of first and last face desc @@ -1879,28 +1879,48 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // a bool that is true if and only if the element array corresponds to a ghost tree. using neighbor_leaf_array = std::pair; + // We compute the owners of the first and last face descendant. + // If the current rank is in between then the local process might have neighbor elements + // and we search the local tree. + // If other processes are in the interval of owners (lower_bound < q != p < upper_bound), + // then we (additionally or alone) search the ghost tree. + int face_owners_lower_bound = 0; + int face_owners_upper_bound = forest->mpisize -1; + const int mpirank = forest->mpirank; + t8_forest_element_owners_at_face_bounds (forest, *gneigh_tree, same_level_neighbor, neigh_class, neigh_face, &face_owners_lower_bound, &face_owners_upper_bound); + + std::vector leaf_arrays; - // Compute the local id of the neighbor tree and check if it is a local tree + const t8_locidx_t local_neighbor_tree = t8_forest_get_local_id (forest, *gneigh_tree); - if (0 <= local_neighbor_tree) { - // The neighbor tree is a local tree and hence there may be local neighbor elements. - const t8_element_array_t *tree_leafs = t8_forest_tree_get_leaves (forest, local_neighbor_tree); - if (tree_leafs != nullptr) { - neighbor_leaf_array *leaf_array = new neighbor_leaf_array (tree_leafs, false); - leaf_arrays.push_back (leaf_array); + if (face_owners_lower_bound <= mpirank && mpirank <= face_owners_upper_bound) { + // Add the local neighbor tree's elements to the search array. + // Compute the local id of the neighbor tree and check if it is a local tree + t8_debugf ("Adding local tree to search.\n"); + if (0 <= local_neighbor_tree) { + // The neighbor tree is a local tree and hence there may be local neighbor elements. + const t8_element_array_t *tree_leafs = t8_forest_tree_get_leaves (forest, local_neighbor_tree); + if (tree_leafs != nullptr) { + neighbor_leaf_array leaf_array(tree_leafs, false); + leaf_arrays.push_back (&leaf_array); + } } } if (forest->ghosts != NULL) { - const t8_locidx_t local_neighbor_ghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, *gneigh_tree); - if (local_neighbor_ghost_treeid >= 0) { - // The neighbor tree is also a ghost tree and face neighbors of our element might - // be ghost elements. - // We add the ghost elements of that tree to our search array. - const t8_element_array_t *ghost_leafs = t8_forest_ghost_get_tree_elements (forest, local_neighbor_ghost_treeid); - if (ghost_leafs != nullptr) { - neighbor_leaf_array *leaf_array = new neighbor_leaf_array (ghost_leafs, true); - leaf_arrays.push_back (leaf_array); + if (face_owners_lower_bound != mpirank || face_owners_upper_bound != mpirank) { + // Add the neighbor tree ghost elements to the search array + t8_debugf ("Adding ghost tree to search.\n"); + const t8_locidx_t local_neighbor_ghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, *gneigh_tree); + if (local_neighbor_ghost_treeid >= 0) { + // The neighbor tree is also a ghost tree and face neighbors of our element might + // be ghost elements. + // We add the ghost elements of that tree to our search array. + const t8_element_array_t *ghost_leafs = t8_forest_ghost_get_tree_elements (forest, local_neighbor_ghost_treeid); + if (ghost_leafs != nullptr) { + neighbor_leaf_array leaf_array(ghost_leafs, true); + leaf_arrays.push_back (&leaf_array); + } } } } @@ -1924,87 +1944,95 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_locidx_t first_desc_search = t8_forest_bin_search_lower (tree_leafs, first_face_desc_id, maxlevel); const t8_locidx_t last_desc_search = t8_forest_bin_search_lower (tree_leafs, last_face_desc_id, maxlevel); if (first_desc_search >= 0 || last_desc_search >= 0) { - // There will be face neighbors in this leaf array. + // There may be face neighbors in this leaf array. // The first descendant may not be in the leaf array, we then // start with the first leaf. const t8_locidx_t first_desc_index = SC_MAX (0, first_desc_search); const t8_locidx_t last_desc_index = last_desc_search; - // The last descendant must have been found by the search, since the neighbor - // element itself would be a match for the search. - T8_ASSERT (0 <= last_desc_index); + // Get the actual leaf elements that contain the first and last face desc const t8_element_t *first_face_leaf = t8_element_array_index_locidx (tree_leafs, first_desc_index); const t8_element_t *last_face_leaf = t8_element_array_index_locidx (tree_leafs, last_desc_index); // Compute their nearest common ancestor neigh_scheme->t8_element_nca (first_face_leaf, last_face_leaf, nca_of_face_desc); - const int face_of_nca = neigh_face; - // TODO: Need to implement element function to compute face id of nca face. - // Input: Element A and face f, Element B that is ancestor or successor of A, and - // shares face f (f is a subface of a face of B or B has a subface of f) - // Output: The face id of the corresponding ancestor/descendant face of B - // - // Currently we hardcode this algorithm for quads. In that case the face id of B is always f. - const bool scheme_is_default_quad_hex - = dynamic_cast (neigh_scheme) != NULL - || dynamic_cast (neigh_scheme) != NULL; - SC_CHECK_ABORT (scheme_is_default_quad_hex, - "Computing leaf face neighbors currently only works for default quad or hex schemes."); - - // Restrict search array to the leafs from first to last face desc - t8_element_array_t face_leafs; - const size_t face_leaf_count = last_desc_index - first_desc_index + 1; - T8_ASSERT (face_leaf_count > 0); - t8_element_array_init_view (&face_leafs, tree_leafs, first_desc_index, face_leaf_count); - // Iterate over all leafs at the face and collect them as neighbors. - const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); - // Compute the local or ghost tree id depending on whether this leaf array corresponds to a local - // tree or ghost tree. - const t8_locidx_t face_iterate_tree_id - = leaf_array_is_ghost ? t8_forest_ghost_get_ghost_treeid (forest, *gneigh_tree) + num_local_trees - : local_neighbor_tree; - t8_forest_iterate_faces (forest, face_iterate_tree_id, nca_of_face_desc, face_of_nca, &face_leafs, - first_desc_index, t8_forest_leaf_face_neighbors_iterate, &user_data); - // Output of iterate_faces: - // Array of indices in tree_leafs of all the face neighbor elements - // Assign pneighbor_leaves - // Assign dual_faces - // Assign pelement_indices - // (all as growing std::vectors, resp t8_element_array) - - // - // After the iteration is finished we collected all - // neighbor data. - // TODO: Since there is no other way, we copy them from the vectors. - // This should be improved in the future to get around the copy. - - // num_neighbors counts the already inserted neighbors before this tree - // num_neighbors_current_tree counts the neighbors added in this tree - // total_num_neighbors temporarily counts all inserted neighbors, including this tree - const int num_neighbors_current_tree = user_data.neighbors.size (); - const int total_num_neighbors = *num_neighbors + num_neighbors_current_tree; - // Copy neighbor element pointers - *pneighbor_leaves = T8_REALLOC (*pneighbor_leaves, t8_element_t *, total_num_neighbors); - T8_ASSERT (*pneighbor_leaves != NULL); - memcpy (*pneighbor_leaves + *num_neighbors, user_data.neighbors.data () + *num_neighbors, - num_neighbors_current_tree * sizeof (t8_element_t *)); - // Copy element indices - *pelement_indices = T8_REALLOC (*pelement_indices, t8_locidx_t, total_num_neighbors); - T8_ASSERT (*pelement_indices != NULL); - memcpy (*pelement_indices + *num_neighbors, user_data.element_indices.data () + *num_neighbors, - num_neighbors_current_tree * sizeof (t8_locidx_t)); - // Copy dual face - *dual_faces = T8_REALLOC (*dual_faces, int, total_num_neighbors); - T8_ASSERT (*dual_faces != NULL); - memcpy (*dual_faces + *num_neighbors, user_data.dual_faces.data () + *num_neighbors, - num_neighbors_current_tree * sizeof (int)); - *num_neighbors = total_num_neighbors; + /* Check whether the computed nca element does contain or is contained by the same level + * face neighbor. If not, then there are no face neighbors and we do not continue. + * Otherwise, face neighbors exist and are descendants of nca_of_face_desc. */ + + if (t8_forest_elements_are_ancestor (neigh_scheme, same_level_neighbor, nca_of_face_desc)) + { + const int face_of_nca = neigh_face; + // TODO: Need to implement element function to compute face id of nca face. + // Input: Element A and face f, Element B that is ancestor or successor of A, and + // shares face f (f is a subface of a face of B or B has a subface of f) + // Output: The face id of the corresponding ancestor/descendant face of B + // + // Currently we hardcode this algorithm for quads. In that case the face id of B is always f. + const bool scheme_is_default_quad_hex + = dynamic_cast (neigh_scheme) != NULL + || dynamic_cast (neigh_scheme) != NULL; + SC_CHECK_ABORT (scheme_is_default_quad_hex, + "Computing leaf face neighbors currently only works for default quad or hex schemes."); + + // Restrict search array to the leafs from first to last face desc + t8_element_array_t face_leafs; + const size_t face_leaf_count = last_desc_index - first_desc_index + 1; + T8_ASSERT (face_leaf_count > 0); + t8_element_array_init_view (&face_leafs, tree_leafs, first_desc_index, face_leaf_count); + // Iterate over all leafs at the face and collect them as neighbors. + const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); + // Compute the local or ghost tree id depending on whether this leaf array corresponds to a local + // tree or ghost tree. + const t8_locidx_t face_iterate_tree_id + = leaf_array_is_ghost ? t8_forest_ghost_get_ghost_treeid (forest, *gneigh_tree) + num_local_trees + : local_neighbor_tree; + t8_forest_iterate_faces (forest, face_iterate_tree_id, nca_of_face_desc, face_of_nca, &face_leafs, + first_desc_index, t8_forest_leaf_face_neighbors_iterate, &user_data); + // Output of iterate_faces: + // Array of indices in tree_leafs of all the face neighbor elements + // Assign pneighbor_leaves + // Assign dual_faces + // Assign pelement_indices + // (all as growing std::vectors, resp t8_element_array) + + // + // After the iteration is finished we collected all + // neighbor data. + // TODO: Since there is no other way, we copy them from the vectors. + // This should be improved in the future to get around the copy. + + // num_neighbors counts the already inserted neighbors before this tree + // num_neighbors_current_tree counts the neighbors added in this tree + // total_num_neighbors temporarily counts all inserted neighbors, including this tree + const int num_neighbors_current_tree = user_data.neighbors.size (); + const int total_num_neighbors = *num_neighbors + num_neighbors_current_tree; + // Copy neighbor element pointers + *pneighbor_leaves = T8_REALLOC (*pneighbor_leaves, t8_element_t *, total_num_neighbors); + T8_ASSERT (*pneighbor_leaves != NULL); + memcpy (*pneighbor_leaves + *num_neighbors, user_data.neighbors.data () + *num_neighbors, + num_neighbors_current_tree * sizeof (t8_element_t *)); + // Copy element indices + *pelement_indices = T8_REALLOC (*pelement_indices, t8_locidx_t, total_num_neighbors); + T8_ASSERT (*pelement_indices != NULL); + memcpy (*pelement_indices + *num_neighbors, user_data.element_indices.data () + *num_neighbors, + num_neighbors_current_tree * sizeof (t8_locidx_t)); + // Copy dual face + *dual_faces = T8_REALLOC (*dual_faces, int, total_num_neighbors); + T8_ASSERT (*dual_faces != NULL); + memcpy (*dual_faces + *num_neighbors, user_data.dual_faces.data () + *num_neighbors, + num_neighbors_current_tree * sizeof (int)); + *num_neighbors = total_num_neighbors; + } } } + neigh_scheme->t8_element_destroy (1, &same_level_neighbor); #if T8_ENABLE_DEBUG // Debugging checks - // We must have found face neighbors by now. - T8_ASSERT (*num_neighbors > 0); + if (tree_is_local) { + // For local elements we must have found face neighbors by now. + T8_ASSERT (*num_neighbors > 0); + } // All neighbor elements must be valid for (int ineigh = 0; ineigh < *num_neighbors; ++ineigh) { T8_ASSERT (neigh_scheme->t8_element_is_valid (*pneighbor_leaves[ineigh])); From b5ec55163b697acb3792d5ee162bd8a609b384d4 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Mon, 27 Jan 2025 14:11:24 +0100 Subject: [PATCH 052/133] more output in test --- test/t8_forest/t8_gtest_face_neighbors.cxx | 46 ++++++++++++++++------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index 82f8f80211..86b184184a 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,7 @@ TEST_P (forest_face_neighbors, test_face_neighbors) const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest_uniform); const t8_locidx_t num_ghost_trees = t8_forest_get_num_ghost_trees (forest_uniform); const t8_locidx_t num_local_elements = t8_forest_get_local_num_elements (forest_uniform); + t8_locidx_t ielement_index = 0; for (t8_locidx_t itree = 0; itree < num_local_trees + num_ghost_trees; itree++) { const t8_gloidx_t gtree_id = t8_forest_global_tree_id (forest_uniform, itree); const bool is_ghost = itree >= num_local_trees; @@ -103,7 +105,7 @@ TEST_P (forest_face_neighbors, test_face_neighbors) const t8_eclass_scheme_c *scheme = t8_forest_get_eclass_scheme (forest_uniform, tree_class); const t8_locidx_t num_leafs = t8_element_array_get_count (leaf_elements); const t8_locidx_t cmesh_tree = t8_forest_ltreeid_to_cmesh_ltreeid (forest_uniform, itree); - for (t8_locidx_t ileaf = 0; ileaf < num_leafs; ++ileaf) { + for (t8_locidx_t ileaf = 0; ileaf < num_leafs; ++ileaf, ++ielement_index) { // Iterate over each leaf element const t8_element_t *element = t8_element_array_index_locidx (leaf_elements, ileaf); const int num_faces = scheme->t8_element_num_faces (element); @@ -118,6 +120,12 @@ TEST_P (forest_face_neighbors, test_face_neighbors) t8_eclass_scheme_c *neigh_scheme; t8_gloidx_t gneigh_tree; int orientation; + + t8_debugf ("Compute face neighbor for tree %i (%s) element %i (index %i), at face %i.\n", itree, + is_ghost ? "ghost" : "local", + ileaf, ielement_index, iface); + const t8_pquad_t* quad = (const t8_pquad_t*) element; + t8_debugf ("Element (x,y,level): (%i,%i,%i)\n", quad->x, quad->y, quad->level); // Actual computation of the face neighbors t8_forest_leaf_face_neighbors_ext (forest_uniform, itree, element, &neighbor_leaves, iface, &dual_faces, &num_neighbors, &element_indices, &neigh_scheme, &gneigh_tree, &orientation); @@ -125,21 +133,27 @@ TEST_P (forest_face_neighbors, test_face_neighbors) t8_debugf ("Tree %i element %i at face %i has %i face neighbors.\n", itree, ileaf, iface, num_neighbors); if (gneigh_tree < 0) { - // No neighbors are found, check for correctly set return values + // If there is no neighbor tree then there cannot be any face neighbors. + // Note that there can also be no face neighbors computed if a neighbor tree exists, but + // the element is a ghost and the neighbor would is neither a local element nor ghost. ASSERT_EQ (num_neighbors, 0); - ASSERT_EQ (neighbor_leaves, nullptr); - ASSERT_EQ (element_indices, nullptr); - ASSERT_EQ (dual_faces, nullptr); + } + if (num_neighbors == 0) { + // No neighbors are found, check for correctly set return values + ASSERT_TRUE (element_indices == NULL); + ASSERT_TRUE (neighbor_leaves == NULL); + ASSERT_TRUE (dual_faces == NULL); } else { ASSERT_GE (num_neighbors, 0); - ASSERT_NE (neighbor_leaves, nullptr); - ASSERT_NE (element_indices, nullptr); - ASSERT_NE (dual_faces, nullptr); + ASSERT_TRUE (neighbor_leaves != NULL); + ASSERT_TRUE (element_indices != NULL); + ASSERT_TRUE (dual_faces != NULL); } // Checking for: // uniform forest: - // - inner element has 1 face neighbors + // - inner local element has 1 face neighbors + // - inner ghost element has 0 or 1 face neighbors // - boundary element has 0 face neighbors // TODO: Add this test for all forests // - If E face f has neighbor E' face f', then // E' face f' must have neighbor E face f. @@ -155,7 +169,12 @@ TEST_P (forest_face_neighbors, test_face_neighbors) = is_root_boundary && t8_cmesh_tree_face_is_boundary (cmesh, cmesh_tree, tree_face); if (!is_boundary_element) { - EXPECT_EQ (num_neighbors, 1) << "Inner element should have exactly 1 neighbor, has " << num_neighbors << "."; + if (!is_ghost) { + EXPECT_EQ (num_neighbors, 1) << "Inner local element should have exactly 1 neighbor, has " << num_neighbors << "."; + } + else { + EXPECT_TRUE (num_neighbors == 0 || num_neighbors == 1) << "Inner ghost element should have exactly 1 or 0 neighbors, has " << num_neighbors << "."; + } } else { EXPECT_EQ (num_neighbors, 0) << "Boundary element should have exactly 0 neighbors, has " << num_neighbors @@ -169,8 +188,11 @@ TEST_P (forest_face_neighbors, test_face_neighbors) const int dual_face = dual_faces[ineigh]; const t8_locidx_t neigh_index = element_indices[ineigh]; - t8_debugf ("Checking neighbor element %p.\n", neighbor); + t8_debugf ("Checking neighbor element %p in (global) tree %li.\n", neighbor, gneigh_tree); t8_debugf ("dual face is %i, index is %i\n", dual_face, neigh_index); + const t8_pquad_t* quad = (const t8_pquad_t*) neighbor; + t8_debugf ("Element (x,y,level): (%i,%i,%i)\n", quad->x, quad->y, quad->level); + ASSERT_TRUE (neigh_scheme->t8_element_is_valid (neighbor)) << "Neighbor element " << ineigh << " is not valid"; // Compute the local tree id of the neighbors tree depending on whether @@ -269,4 +291,4 @@ TEST_P (forest_face_neighbors, test_face_neighbors) } #endif -INSTANTIATE_TEST_SUITE_P (t8_gtest_face_neighbors, forest_face_neighbors, AllCmeshsParam); +INSTANTIATE_TEST_SUITE_P (t8_gtest_face_neighbors, forest_face_neighbors, AllCmeshsParam, pretty_print_base_example); From 35843c51d5cf3e79fa96ba9943adec37d61b68d1 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Mon, 27 Jan 2025 14:11:57 +0100 Subject: [PATCH 053/133] indent --- src/t8_forest/t8_forest.cxx | 29 +++++++++++----------- test/t8_forest/t8_gtest_face_neighbors.cxx | 17 +++++++------ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index f92cdab7ac..ab5d4f0a78 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1747,9 +1747,9 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, lfn_data->dual_faces.push_back (face); // Compute the index of the element const t8_locidx_t num_local_elements = t8_forest_get_local_num_elements (forest); - const t8_locidx_t tree_offset = !is_ghost_tree ? t8_forest_get_tree_element_offset (forest, ltreeid) - : t8_forest_ghost_get_tree_element_offset (forest, adjusted_tree_id) - + num_local_elements; + const t8_locidx_t tree_offset + = !is_ghost_tree ? t8_forest_get_tree_element_offset (forest, ltreeid) + : t8_forest_ghost_get_tree_element_offset (forest, adjusted_tree_id) + num_local_elements; const t8_locidx_t element_index = tree_offset + tree_leaf_index; lfn_data->element_indices.push_back (element_index); // Add the pointer to the current element @@ -1766,7 +1766,8 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, /** Compute whether two elements are ancestor of each other. * TODO: Move the function inside the element module */ static bool -t8_forest_elements_are_ancestor (const t8_eclass_scheme_c *scheme, const t8_element_t *element_A, const t8_element_t *element_B) +t8_forest_elements_are_ancestor (const t8_eclass_scheme_c *scheme, const t8_element_t *element_A, + const t8_element_t *element_B) { T8_ASSERT (scheme->t8_element_is_valid (element_A)); T8_ASSERT (scheme->t8_element_is_valid (element_B)); @@ -1805,7 +1806,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons **/ T8_ASSERT (t8_forest_is_committed (forest)); - + #if T8_ENABLE_DEBUG const bool tree_is_local = t8_forest_tree_is_local (forest, ltreeid); if (tree_is_local) { @@ -1885,10 +1886,10 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // If other processes are in the interval of owners (lower_bound < q != p < upper_bound), // then we (additionally or alone) search the ghost tree. int face_owners_lower_bound = 0; - int face_owners_upper_bound = forest->mpisize -1; + int face_owners_upper_bound = forest->mpisize - 1; const int mpirank = forest->mpirank; - t8_forest_element_owners_at_face_bounds (forest, *gneigh_tree, same_level_neighbor, neigh_class, neigh_face, &face_owners_lower_bound, &face_owners_upper_bound); - + t8_forest_element_owners_at_face_bounds (forest, *gneigh_tree, same_level_neighbor, neigh_class, neigh_face, + &face_owners_lower_bound, &face_owners_upper_bound); std::vector leaf_arrays; @@ -1901,7 +1902,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // The neighbor tree is a local tree and hence there may be local neighbor elements. const t8_element_array_t *tree_leafs = t8_forest_tree_get_leaves (forest, local_neighbor_tree); if (tree_leafs != nullptr) { - neighbor_leaf_array leaf_array(tree_leafs, false); + neighbor_leaf_array leaf_array (tree_leafs, false); leaf_arrays.push_back (&leaf_array); } } @@ -1918,7 +1919,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // We add the ghost elements of that tree to our search array. const t8_element_array_t *ghost_leafs = t8_forest_ghost_get_tree_elements (forest, local_neighbor_ghost_treeid); if (ghost_leafs != nullptr) { - neighbor_leaf_array leaf_array(ghost_leafs, true); + neighbor_leaf_array leaf_array (ghost_leafs, true); leaf_arrays.push_back (&leaf_array); } } @@ -1944,12 +1945,11 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_locidx_t first_desc_search = t8_forest_bin_search_lower (tree_leafs, first_face_desc_id, maxlevel); const t8_locidx_t last_desc_search = t8_forest_bin_search_lower (tree_leafs, last_face_desc_id, maxlevel); if (first_desc_search >= 0 || last_desc_search >= 0) { - // There may be face neighbors in this leaf array. + // There may be face neighbors in this leaf array. // The first descendant may not be in the leaf array, we then // start with the first leaf. const t8_locidx_t first_desc_index = SC_MAX (0, first_desc_search); const t8_locidx_t last_desc_index = last_desc_search; - // Get the actual leaf elements that contain the first and last face desc const t8_element_t *first_face_leaf = t8_element_array_index_locidx (tree_leafs, first_desc_index); @@ -1960,8 +1960,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons * face neighbor. If not, then there are no face neighbors and we do not continue. * Otherwise, face neighbors exist and are descendants of nca_of_face_desc. */ - if (t8_forest_elements_are_ancestor (neigh_scheme, same_level_neighbor, nca_of_face_desc)) - { + if (t8_forest_elements_are_ancestor (neigh_scheme, same_level_neighbor, nca_of_face_desc)) { const int face_of_nca = neigh_face; // TODO: Need to implement element function to compute face id of nca face. // Input: Element A and face f, Element B that is ancestor or successor of A, and @@ -1988,7 +1987,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons = leaf_array_is_ghost ? t8_forest_ghost_get_ghost_treeid (forest, *gneigh_tree) + num_local_trees : local_neighbor_tree; t8_forest_iterate_faces (forest, face_iterate_tree_id, nca_of_face_desc, face_of_nca, &face_leafs, - first_desc_index, t8_forest_leaf_face_neighbors_iterate, &user_data); + first_desc_index, t8_forest_leaf_face_neighbors_iterate, &user_data); // Output of iterate_faces: // Array of indices in tree_leafs of all the face neighbor elements // Assign pneighbor_leaves diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index 86b184184a..fa03dfd91b 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -121,10 +121,9 @@ TEST_P (forest_face_neighbors, test_face_neighbors) t8_gloidx_t gneigh_tree; int orientation; - t8_debugf ("Compute face neighbor for tree %i (%s) element %i (index %i), at face %i.\n", itree, - is_ghost ? "ghost" : "local", - ileaf, ielement_index, iface); - const t8_pquad_t* quad = (const t8_pquad_t*) element; + t8_debugf ("Compute face neighbor for tree %i (%s) element %i (index %i), at face %i.\n", itree, + is_ghost ? "ghost" : "local", ileaf, ielement_index, iface); + const t8_pquad_t *quad = (const t8_pquad_t *) element; t8_debugf ("Element (x,y,level): (%i,%i,%i)\n", quad->x, quad->y, quad->level); // Actual computation of the face neighbors t8_forest_leaf_face_neighbors_ext (forest_uniform, itree, element, &neighbor_leaves, iface, &dual_faces, @@ -170,10 +169,12 @@ TEST_P (forest_face_neighbors, test_face_neighbors) if (!is_boundary_element) { if (!is_ghost) { - EXPECT_EQ (num_neighbors, 1) << "Inner local element should have exactly 1 neighbor, has " << num_neighbors << "."; + EXPECT_EQ (num_neighbors, 1) << "Inner local element should have exactly 1 neighbor, has " << num_neighbors + << "."; } else { - EXPECT_TRUE (num_neighbors == 0 || num_neighbors == 1) << "Inner ghost element should have exactly 1 or 0 neighbors, has " << num_neighbors << "."; + EXPECT_TRUE (num_neighbors == 0 || num_neighbors == 1) + << "Inner ghost element should have exactly 1 or 0 neighbors, has " << num_neighbors << "."; } } else { @@ -190,9 +191,9 @@ TEST_P (forest_face_neighbors, test_face_neighbors) t8_debugf ("Checking neighbor element %p in (global) tree %li.\n", neighbor, gneigh_tree); t8_debugf ("dual face is %i, index is %i\n", dual_face, neigh_index); - const t8_pquad_t* quad = (const t8_pquad_t*) neighbor; + const t8_pquad_t *quad = (const t8_pquad_t *) neighbor; t8_debugf ("Element (x,y,level): (%i,%i,%i)\n", quad->x, quad->y, quad->level); - + ASSERT_TRUE (neigh_scheme->t8_element_is_valid (neighbor)) << "Neighbor element " << ineigh << " is not valid"; // Compute the local tree id of the neighbors tree depending on whether From 841e3ce649ab4bfb0fd60314da5281fbc60899b2 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Mon, 27 Jan 2025 14:45:08 +0100 Subject: [PATCH 054/133] index checks in face neighbor test --- test/t8_forest/t8_gtest_face_neighbors.cxx | 34 ++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index fa03dfd91b..93ae3e5980 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -180,7 +180,6 @@ TEST_P (forest_face_neighbors, test_face_neighbors) else { EXPECT_EQ (num_neighbors, 0) << "Boundary element should have exactly 0 neighbors, has " << num_neighbors << "."; - ; } // Check that the neighbor of the neighbor is the original element. @@ -196,12 +195,25 @@ TEST_P (forest_face_neighbors, test_face_neighbors) ASSERT_TRUE (neigh_scheme->t8_element_is_valid (neighbor)) << "Neighbor element " << ineigh << " is not valid"; + + t8_locidx_t neigh_ltreeid_from_index; + // Check that neighbor index correctly yields neighbor element. + if (neigh_index < num_local_elements) { + const t8_element_t *neighbor_from_index + = t8_forest_get_element (forest, neigh_index, &neigh_ltreeid_from_index); + EXPECT_TRUE (neigh_scheme->t8_element_equal (neighbor_from_index, neighbor)); + } + // TODO: Check neighbor index if ghost + // Compute the local tree id of the neighbors tree depending on whether // it is a local tree or a ghost tree. const t8_locidx_t neigh_ltreeid = neigh_index < num_local_elements ? gneigh_tree - t8_forest_get_first_local_tree_id (forest_uniform) : t8_forest_ghost_get_ghost_treeid (forest_uniform, gneigh_tree) + num_local_trees; + if (neigh_index < num_local_elements) { + EXPECT_EQ (neigh_ltreeid, neigh_ltreeid_from_index); + } // TODO: Check neighbor ltreeid if ghost tree // preparation t8_element_t **neigh_neighbor_leaves; int *neigh_dual_faces; @@ -224,7 +236,25 @@ TEST_P (forest_face_neighbors, test_face_neighbors) // The neighbor's orientation must be the orientation EXPECT_EQ (orientation, neigh_orientation); - // TODO Check for the specific face neighbor that would match the original element + // Check that the neighbor of the neighbor element is the original element + const t8_element_t *neigh_of_neigh = neigh_neighbor_leaves[0]; + EXPECT_TRUE (scheme->t8_element_equal (element, neigh_of_neigh)); + + // Check that the dual face of the dual face is the original face + const int neigh_dual_face = neigh_dual_faces[0]; + EXPECT_EQ (neigh_dual_face, iface); + + // Check that the index is correct, i.e. when getting the neighbor neighbor element from the index, + // we retrieve the original element. + const t8_locidx_t element_index = neigh_element_indices[0]; + EXPECT_GE (element_index, 0); + + if (element_index < num_local_elements) { + const t8_element_t *element_from_index = t8_forest_get_element (forest, element_index, NULL); + EXPECT_EQ (element_from_index, element) + << "Neighbor neighbor element at index " << element_index << " is not original element."; + } + // TODO: Check element index if original element is a ghost element // clean-up neighbor's neighbors if (neigh_num_neighbors > 0) { From f9a5f17bc8e38a7430ad72821c2d296a7abc26ae Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 28 Jan 2025 10:46:11 +0100 Subject: [PATCH 055/133] Add own test file for reusable adapt callbacks --- test/CMakeLists.txt | 4 +- test/t8_forest/t8_gtest_element_is_leaf.cxx | 26 +--------- test/t8_gtest_adapt_callbacks.cxx | 57 +++++++++++++++++++++ test/t8_gtest_adapt_callbacks.hxx | 44 ++++++++++++++++ 4 files changed, 104 insertions(+), 27 deletions(-) create mode 100644 test/t8_gtest_adapt_callbacks.cxx create mode 100644 test/t8_gtest_adapt_callbacks.hxx diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8d09c8d6da..ffb83cc113 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -92,7 +92,7 @@ add_t8_test( NAME t8_gtest_shmem_parallel SOURCES t8_gtest_main.cxx t8_data/t8_ add_t8_test( NAME t8_gtest_element_volume_serial SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_element_volume.cxx ) add_t8_test( NAME t8_gtest_search_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_search.cxx ) add_t8_test( NAME t8_gtest_half_neighbors_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_half_neighbors.cxx ) -add_t8_test( NAME t8_gtest_face_neighbors_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_face_neighbors.cxx ) +add_t8_test( NAME t8_gtest_face_neighbors_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_face_neighbors.cxx t8_gtest_adapt_callbacks.cxx ) add_t8_test( NAME t8_gtest_find_owner_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_find_owner.cxx ) add_t8_test( NAME t8_gtest_user_data_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_user_data.cxx ) add_t8_test( NAME t8_gtest_transform_serial SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_transform.cxx ) @@ -102,7 +102,7 @@ add_t8_test( NAME t8_gtest_ghost_and_owner_parallel SOURCES t8_gtest_main.cx add_t8_test( NAME t8_gtest_balance_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_balance.cxx ) add_t8_test( NAME t8_gtest_forest_commit_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_forest_commit.cxx ) add_t8_test( NAME t8_gtest_forest_face_normal_serial SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_forest_face_normal.cxx ) -add_t8_test( NAME t8_gtest_element_is_leaf_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_element_is_leaf.cxx ) +add_t8_test( NAME t8_gtest_element_is_leaf_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_element_is_leaf.cxx t8_gtest_adapt_callbacks.cxx ) add_t8_test( NAME t8_gtest_partition_data_parallel SOURCES t8_gtest_main.cxx t8_forest/t8_gtest_partition_data.cxx ) add_t8_test( NAME t8_gtest_permute_hole_serial SOURCES t8_gtest_main.cxx t8_forest_incomplete/t8_gtest_permute_hole.cxx ) diff --git a/test/t8_forest/t8_gtest_element_is_leaf.cxx b/test/t8_forest/t8_gtest_element_is_leaf.cxx index bcb193122a..fcbda7f1cd 100644 --- a/test/t8_forest/t8_gtest_element_is_leaf.cxx +++ b/test/t8_forest/t8_gtest_element_is_leaf.cxx @@ -28,6 +28,7 @@ #include #include "test/t8_cmesh_generator/t8_cmesh_example_sets.hxx" #include +#include /* In this test we check the t8_forest_element_is_leaf function. * Iterating over all cmesh test cases, we creat a uniform and an adaptive forest. @@ -38,31 +39,6 @@ /* Maximum uniform level for forest. */ #define T8_IS_LEAF_MAX_LVL 4 -/* Adapt a forest such that always the first child of a - * family is refined and no other elements. This results in a highly - * imbalanced forest. */ -static int -t8_test_adapt_first_child (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, t8_locidx_t lelement_id, - t8_eclass_scheme_c *ts, const int is_family, const int num_elements, - t8_element_t *elements[]) -{ - T8_ASSERT (!is_family || (is_family && num_elements == ts->t8_element_num_children (elements[0]))); - - int level = ts->t8_element_level (elements[0]); - - /* we set a maximum refinement level as forest user data */ - int maxlevel = *(int *) t8_forest_get_user_data (forest); - if (level >= maxlevel) { - /* Do not refine after the maxlevel */ - return 0; - } - int child_id = ts->t8_element_child_id (elements[0]); - if (child_id == 1) { - return 1; - } - return 0; -} - class element_is_leaf_or_ghost: public testing::TestWithParam> { protected: void diff --git a/test/t8_gtest_adapt_callbacks.cxx b/test/t8_gtest_adapt_callbacks.cxx new file mode 100644 index 0000000000..916a654311 --- /dev/null +++ b/test/t8_gtest_adapt_callbacks.cxx @@ -0,0 +1,57 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_gtest_adapt_callbacks.cxx +* Provide forest adapt callback functions that we use in our tests. +*/ + +#include +#include + +/* Adapt a forest such that always the first child of a + * family is refined and no other elements. This results in a highly + * imbalanced forest. + * + * This adapt callbacks requires an integer as forest user data. + * This integer is the maximum refinement level. + */ +int +t8_test_adapt_first_child (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, t8_locidx_t lelement_id, + t8_eclass_scheme_c *scheme, const int is_family, const int num_elements, + t8_element_t *elements[]) +{ + T8_ASSERT (!is_family || (is_family && num_elements == scheme->t8_element_num_children (elements[0]))); + + int level = scheme->t8_element_level (elements[0]); + + /* we set a maximum refinement level as forest user data */ + int maxlevel = *(int *) t8_forest_get_user_data (forest); + if (level >= maxlevel) { + /* Do not refine after the maxlevel */ + return 0; + } + int child_id = scheme->t8_element_child_id (elements[0]); + if (child_id == 1) { + return 1; + } + return 0; +} diff --git a/test/t8_gtest_adapt_callbacks.hxx b/test/t8_gtest_adapt_callbacks.hxx new file mode 100644 index 0000000000..806215d16e --- /dev/null +++ b/test/t8_gtest_adapt_callbacks.hxx @@ -0,0 +1,44 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_gtest_adapt_callbacks.hxx +* Provide forest adapt callback functions that we use in our tests. +*/ + +#ifndef T8_GTEST_ADAPT_CALLBACKS +#define T8_GTEST_ADAPT_CALLBACKS + +#include + +/* Adapt a forest such that always the first child of a + * family is refined and no other elements. This results in a highly + * imbalanced forest. + * + * This adapt callbacks requires an integer as forest user data. + * This integer is the maximum refinement level. + */ +int +t8_test_adapt_first_child (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, t8_locidx_t lelement_id, + t8_eclass_scheme_c *scheme, const int is_family, const int num_elements, + t8_element_t *elements[]); + +#endif /* T8_GTEST_ADAPT_CALLBACKS */ From d96348de8d57880c7850ae8dec53163f7424ef69 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 28 Jan 2025 10:47:03 +0100 Subject: [PATCH 056/133] add testing of adapted forest to face neighbor test --- test/t8_forest/t8_gtest_face_neighbors.cxx | 423 ++++++++++----------- 1 file changed, 198 insertions(+), 225 deletions(-) diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index 93ae3e5980..c20eb0c899 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -36,6 +35,7 @@ #include #include #include +#include #include "test/t8_cmesh_generator/t8_cmesh_example_sets.hxx" bool @@ -68,258 +68,231 @@ class forest_face_neighbors: public testing::TestWithParam } t8_scheme_cxx_t *default_scheme = t8_scheme_new_default_cxx (); const int level = 1; + const int adapt_levels = 2; + const int max_adapt_level = level + adapt_levels; const bool do_ghost = true; - forest = t8_forest_new_uniform (cmesh, default_scheme, level, do_ghost, sc_MPI_COMM_WORLD); - cmesh = t8_forest_get_cmesh (forest); + const bool do_recursive_adapt = true; + forests[0] = t8_forest_new_uniform (cmesh, default_scheme, level, do_ghost, sc_MPI_COMM_WORLD); + cmesh = t8_forest_get_cmesh (forests[0]); + t8_forest_ref (forests[0]); + forests[1] = t8_forest_new_adapt (forests[0], t8_test_adapt_first_child, do_recursive_adapt, do_ghost, + (void *) &max_adapt_level); } void TearDown () override { - if (forest != nullptr) { - t8_forest_unref (&forest); + for (auto &forest : forests) { + if (forest != nullptr) { + t8_forest_unref (&forest); + } } } - t8_forest_t forest { nullptr }; + t8_forest_t forests[2] { nullptr, nullptr }; }; TEST_P (forest_face_neighbors, test_face_neighbors) { /* iterate over all elements */ - const t8_forest_t forest_uniform = forest; - const t8_cmesh_t cmesh = t8_forest_get_cmesh (forest_uniform); - const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest_uniform); - const t8_locidx_t num_ghost_trees = t8_forest_get_num_ghost_trees (forest_uniform); - const t8_locidx_t num_local_elements = t8_forest_get_local_num_elements (forest_uniform); - t8_locidx_t ielement_index = 0; - for (t8_locidx_t itree = 0; itree < num_local_trees + num_ghost_trees; itree++) { - const t8_gloidx_t gtree_id = t8_forest_global_tree_id (forest_uniform, itree); - const bool is_ghost = itree >= num_local_trees; - const t8_locidx_t ghost_tree_id = itree - num_local_trees; - /* Get the leaf element array */ - const t8_element_array_t *leaf_elements = !is_ghost - ? t8_forest_get_tree_element_array (forest_uniform, itree) - : t8_forest_ghost_get_tree_elements (forest_uniform, ghost_tree_id); - const t8_eclass_t tree_class = t8_forest_get_tree_class (forest_uniform, itree); - const t8_eclass_scheme_c *scheme = t8_forest_get_eclass_scheme (forest_uniform, tree_class); - const t8_locidx_t num_leafs = t8_element_array_get_count (leaf_elements); - const t8_locidx_t cmesh_tree = t8_forest_ltreeid_to_cmesh_ltreeid (forest_uniform, itree); - for (t8_locidx_t ileaf = 0; ileaf < num_leafs; ++ileaf, ++ielement_index) { - // Iterate over each leaf element - const t8_element_t *element = t8_element_array_index_locidx (leaf_elements, ileaf); - const int num_faces = scheme->t8_element_num_faces (element); - for (int iface = 0; iface < num_faces; ++iface) { - // Iterate over all faces and compute the face neighbors - - // preparation - t8_element_t **neighbor_leaves; - int *dual_faces; - int num_neighbors = 0; - t8_locidx_t *element_indices; - t8_eclass_scheme_c *neigh_scheme; - t8_gloidx_t gneigh_tree; - int orientation; - - t8_debugf ("Compute face neighbor for tree %i (%s) element %i (index %i), at face %i.\n", itree, - is_ghost ? "ghost" : "local", ileaf, ielement_index, iface); - const t8_pquad_t *quad = (const t8_pquad_t *) element; - t8_debugf ("Element (x,y,level): (%i,%i,%i)\n", quad->x, quad->y, quad->level); - // Actual computation of the face neighbors - t8_forest_leaf_face_neighbors_ext (forest_uniform, itree, element, &neighbor_leaves, iface, &dual_faces, - &num_neighbors, &element_indices, &neigh_scheme, &gneigh_tree, &orientation); - - t8_debugf ("Tree %i element %i at face %i has %i face neighbors.\n", itree, ileaf, iface, num_neighbors); - - if (gneigh_tree < 0) { - // If there is no neighbor tree then there cannot be any face neighbors. - // Note that there can also be no face neighbors computed if a neighbor tree exists, but - // the element is a ghost and the neighbor would is neither a local element nor ghost. - ASSERT_EQ (num_neighbors, 0); - } - if (num_neighbors == 0) { - // No neighbors are found, check for correctly set return values - ASSERT_TRUE (element_indices == NULL); - ASSERT_TRUE (neighbor_leaves == NULL); - ASSERT_TRUE (dual_faces == NULL); - } - else { - ASSERT_GE (num_neighbors, 0); - ASSERT_TRUE (neighbor_leaves != NULL); - ASSERT_TRUE (element_indices != NULL); - ASSERT_TRUE (dual_faces != NULL); - } - // Checking for: - // uniform forest: - // - inner local element has 1 face neighbors - // - inner ghost element has 0 or 1 face neighbors - // - boundary element has 0 face neighbors // TODO: Add this test for all forests - // - If E face f has neighbor E' face f', then - // E' face f' must have neighbor E face f. - - // Now checking for inner and boundary elements. - - // Compute whether this element is a boundary element or not. - // An element is a boundary element if it lies on the tree boundary - // and if the corresponding tree face is at the domain boundary. - const bool is_root_boundary = scheme->t8_element_is_root_boundary (element, iface); - const int tree_face = scheme->t8_element_tree_face (element, iface); - const bool is_boundary_element - = is_root_boundary && t8_cmesh_tree_face_is_boundary (cmesh, cmesh_tree, tree_face); - - if (!is_boundary_element) { - if (!is_ghost) { - EXPECT_EQ (num_neighbors, 1) << "Inner local element should have exactly 1 neighbor, has " << num_neighbors - << "."; + for (auto &forest : forests) { + const t8_cmesh_t cmesh = t8_forest_get_cmesh (forest); + const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); + const t8_locidx_t num_ghost_trees = t8_forest_get_num_ghost_trees (forest); + const t8_locidx_t num_local_elements = t8_forest_get_local_num_elements (forest); + t8_locidx_t ielement_index = 0; + for (t8_locidx_t itree = 0; itree < num_local_trees + num_ghost_trees; itree++) { + const t8_gloidx_t gtree_id = t8_forest_global_tree_id (forest, itree); + const bool is_ghost = itree >= num_local_trees; + const t8_locidx_t ghost_tree_id = itree - num_local_trees; + /* Get the leaf element array */ + const t8_element_array_t *leaf_elements = !is_ghost ? t8_forest_get_tree_element_array (forest, itree) + : t8_forest_ghost_get_tree_elements (forest, ghost_tree_id); + const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, itree); + const t8_eclass_scheme_c *scheme = t8_forest_get_eclass_scheme (forest, tree_class); + const t8_locidx_t num_leafs = t8_element_array_get_count (leaf_elements); + const t8_locidx_t cmesh_tree = t8_forest_ltreeid_to_cmesh_ltreeid (forest, itree); + for (t8_locidx_t ileaf = 0; ileaf < num_leafs; ++ileaf, ++ielement_index) { + // Iterate over each leaf element + const t8_element_t *element = t8_element_array_index_locidx (leaf_elements, ileaf); + const int num_faces = scheme->t8_element_num_faces (element); + for (int iface = 0; iface < num_faces; ++iface) { + // Iterate over all faces and compute the face neighbors + + // preparation + t8_element_t **neighbor_leaves; + int *dual_faces; + int num_neighbors = 0; + t8_locidx_t *element_indices; + t8_eclass_scheme_c *neigh_scheme; + t8_gloidx_t gneigh_tree; + int orientation; + + t8_debugf ("Compute face neighbor for tree %i (%s) element %i (index %i), at face %i.\n", itree, + is_ghost ? "ghost" : "local", ileaf, ielement_index, iface); + + // Actual computation of the face neighbors + t8_forest_leaf_face_neighbors_ext (forest, itree, element, &neighbor_leaves, iface, &dual_faces, + &num_neighbors, &element_indices, &neigh_scheme, &gneigh_tree, + &orientation); + + t8_debugf ("Tree %i element %i at face %i has %i face neighbors.\n", itree, ileaf, iface, num_neighbors); + + if (gneigh_tree < 0) { + // If there is no neighbor tree then there cannot be any face neighbors. + // Note that there can also be no face neighbors computed if a neighbor tree exists, but + // the element is a ghost and the neighbor would is neither a local element nor ghost. + ASSERT_EQ (num_neighbors, 0); + } + if (num_neighbors == 0) { + // No neighbors are found, check for correctly set return values + ASSERT_TRUE (element_indices == NULL); + ASSERT_TRUE (neighbor_leaves == NULL); + ASSERT_TRUE (dual_faces == NULL); } else { - EXPECT_TRUE (num_neighbors == 0 || num_neighbors == 1) - << "Inner ghost element should have exactly 1 or 0 neighbors, has " << num_neighbors << "."; + ASSERT_GE (num_neighbors, 0); + ASSERT_TRUE (neighbor_leaves != NULL); + ASSERT_TRUE (element_indices != NULL); + ASSERT_TRUE (dual_faces != NULL); } - } - else { - EXPECT_EQ (num_neighbors, 0) << "Boundary element should have exactly 0 neighbors, has " << num_neighbors - << "."; - } - // Check that the neighbor of the neighbor is the original element. - for (int ineigh = 0; ineigh < num_neighbors; ++ineigh) { - const t8_element_t *neighbor = neighbor_leaves[ineigh]; - const int dual_face = dual_faces[ineigh]; - const t8_locidx_t neigh_index = element_indices[ineigh]; - - t8_debugf ("Checking neighbor element %p in (global) tree %li.\n", neighbor, gneigh_tree); - t8_debugf ("dual face is %i, index is %i\n", dual_face, neigh_index); - const t8_pquad_t *quad = (const t8_pquad_t *) neighbor; - t8_debugf ("Element (x,y,level): (%i,%i,%i)\n", quad->x, quad->y, quad->level); - - ASSERT_TRUE (neigh_scheme->t8_element_is_valid (neighbor)) - << "Neighbor element " << ineigh << " is not valid"; - - t8_locidx_t neigh_ltreeid_from_index; - // Check that neighbor index correctly yields neighbor element. - if (neigh_index < num_local_elements) { - const t8_element_t *neighbor_from_index - = t8_forest_get_element (forest, neigh_index, &neigh_ltreeid_from_index); - EXPECT_TRUE (neigh_scheme->t8_element_equal (neighbor_from_index, neighbor)); + // Checking for: + // uniform and adapted forest: + // - inner local element has >= 1 face neighbors (= 1 for uniform) + // - inner ghost element has 0 or 1 face neighbors (= 1 for uniform) + // - boundary element has 0 face neighbors + // - If E face f has neighbor E' face f', then + // E' face f' must have neighbor E face f. + + // Now checking for inner and boundary elements. + + // Compute whether this element is a boundary element or not. + // An element is a boundary element if it lies on the tree boundary + // and if the corresponding tree face is at the domain boundary. + const bool is_root_boundary = scheme->t8_element_is_root_boundary (element, iface); + const int tree_face = scheme->t8_element_tree_face (element, iface); + const bool is_boundary_element + = is_root_boundary && t8_cmesh_tree_face_is_boundary (cmesh, cmesh_tree, tree_face); + + if (!is_boundary_element) { + if (!is_ghost) { + EXPECT_EQ (num_neighbors, 1) + << "Inner local element should have exactly 1 neighbor, has " << num_neighbors << "."; + } + else { + EXPECT_TRUE (num_neighbors == 0 || num_neighbors == 1) + << "Inner ghost element should have exactly 1 or 0 neighbors, has " << num_neighbors << "."; + } } - // TODO: Check neighbor index if ghost - - // Compute the local tree id of the neighbors tree depending on whether - // it is a local tree or a ghost tree. - const t8_locidx_t neigh_ltreeid - = neigh_index < num_local_elements - ? gneigh_tree - t8_forest_get_first_local_tree_id (forest_uniform) - : t8_forest_ghost_get_ghost_treeid (forest_uniform, gneigh_tree) + num_local_trees; - if (neigh_index < num_local_elements) { - EXPECT_EQ (neigh_ltreeid, neigh_ltreeid_from_index); - } // TODO: Check neighbor ltreeid if ghost tree - // preparation - t8_element_t **neigh_neighbor_leaves; - int *neigh_dual_faces; - int neigh_num_neighbors = 0; - t8_locidx_t *neigh_element_indices; - t8_eclass_scheme_c *neigh_neigh_scheme; - t8_gloidx_t neigh_gneigh_tree; - int neigh_orientation; - // Actual computation of the neighbor's face neighbors - t8_forest_leaf_face_neighbors_ext (forest_uniform, neigh_ltreeid, neighbor, &neigh_neighbor_leaves, dual_face, - &neigh_dual_faces, &neigh_num_neighbors, &neigh_element_indices, - &neigh_neigh_scheme, &neigh_gneigh_tree, &neigh_orientation); - - // We must have found at least one face neighbor, namely the original element. - EXPECT_GE (neigh_num_neighbors, 1); - // The neighbor's neighbor tree must be the current tree - EXPECT_EQ (gtree_id, neigh_gneigh_tree); - // The neighbor's scheme must be the current scheme - EXPECT_EQ (scheme, neigh_neigh_scheme); - // The neighbor's orientation must be the orientation - EXPECT_EQ (orientation, neigh_orientation); - - // Check that the neighbor of the neighbor element is the original element - const t8_element_t *neigh_of_neigh = neigh_neighbor_leaves[0]; - EXPECT_TRUE (scheme->t8_element_equal (element, neigh_of_neigh)); - - // Check that the dual face of the dual face is the original face - const int neigh_dual_face = neigh_dual_faces[0]; - EXPECT_EQ (neigh_dual_face, iface); - - // Check that the index is correct, i.e. when getting the neighbor neighbor element from the index, - // we retrieve the original element. - const t8_locidx_t element_index = neigh_element_indices[0]; - EXPECT_GE (element_index, 0); - - if (element_index < num_local_elements) { - const t8_element_t *element_from_index = t8_forest_get_element (forest, element_index, NULL); - EXPECT_EQ (element_from_index, element) - << "Neighbor neighbor element at index " << element_index << " is not original element."; + else { + EXPECT_EQ (num_neighbors, 0) << "Boundary element should have exactly 0 neighbors, has " << num_neighbors + << "."; } - // TODO: Check element index if original element is a ghost element - // clean-up neighbor's neighbors - if (neigh_num_neighbors > 0) { - T8_FREE (neigh_neighbor_leaves); - T8_FREE (neigh_element_indices); - T8_FREE (neigh_dual_faces); + // Check that the neighbor of the neighbor is the original element. + for (int ineigh = 0; ineigh < num_neighbors; ++ineigh) { + const t8_element_t *neighbor = neighbor_leaves[ineigh]; + const int dual_face = dual_faces[ineigh]; + const t8_locidx_t neigh_index = element_indices[ineigh]; + + t8_debugf ("Checking neighbor element %p in (global) tree %li.\n", neighbor, gneigh_tree); + t8_debugf ("dual face is %i, index is %i\n", dual_face, neigh_index); + + ASSERT_TRUE (neigh_scheme->t8_element_is_valid (neighbor)) + << "Neighbor element " << ineigh << " is not valid"; + + t8_locidx_t neigh_ltreeid_from_index; + // Check that neighbor index correctly yields neighbor element. + if (neigh_index < num_local_elements) { + const t8_element_t *neighbor_from_index + = t8_forest_get_element (forest, neigh_index, &neigh_ltreeid_from_index); + EXPECT_TRUE (neigh_scheme->t8_element_equal (neighbor_from_index, neighbor)); + } + // TODO: Check neighbor index if the element is a ghost element + + // Compute the local tree id of the neighbors tree depending on whether + // it is a local tree or a ghost tree. + const t8_locidx_t neigh_ltreeid + = neigh_index < num_local_elements + ? gneigh_tree - t8_forest_get_first_local_tree_id (forest) + : t8_forest_ghost_get_ghost_treeid (forest, gneigh_tree) + num_local_trees; + if (neigh_index < num_local_elements) { + EXPECT_EQ (neigh_ltreeid, neigh_ltreeid_from_index); + } // TODO: Check neighbor ltreeid if ghost tree + // preparation + t8_element_t **neigh_neighbor_leaves; + int *neigh_dual_faces; + int neigh_num_neighbors = 0; + t8_locidx_t *neigh_element_indices; + t8_eclass_scheme_c *neigh_neigh_scheme; + t8_gloidx_t neigh_gneigh_tree; + int neigh_orientation; + // Actual computation of the neighbor's face neighbors + t8_forest_leaf_face_neighbors_ext (forest, neigh_ltreeid, neighbor, &neigh_neighbor_leaves, dual_face, + &neigh_dual_faces, &neigh_num_neighbors, &neigh_element_indices, + &neigh_neigh_scheme, &neigh_gneigh_tree, &neigh_orientation); + + // We must have found at least one face neighbor, namely the original element. + EXPECT_GE (neigh_num_neighbors, 1); + // The neighbor's neighbor tree must be the current tree + EXPECT_EQ (gtree_id, neigh_gneigh_tree); + // The neighbor's scheme must be the current scheme + EXPECT_EQ (scheme, neigh_neigh_scheme); + // The neighbor's orientation must be the orientation + EXPECT_EQ (orientation, neigh_orientation); + + // We now (try to) find the original element among the neighbors. + // If it does not exist there was an error. + // If it exists we check that dual face and index were computed correctly. + + int position_of_original_element = -1; + bool found_original = false; + for (int ineighneigh = 0; ineighneigh < neigh_num_neighbors && !found_original; ++ineighneigh) { + // Check that the neighbor of the neighbor element is the original element + const t8_element_t *neigh_of_neigh = neigh_neighbor_leaves[ineighneigh]; + if (scheme->t8_element_equal (element, neigh_of_neigh)) { + position_of_original_element = ineighneigh; + found_original = true; // Stop the for loop + } + } + // We must have found the original element among the neighbors. + ASSERT_TRUE (found_original) << "The original element was not a neighbor of its neighbor."; + + // Check that the dual face of the dual face is the original face + const int neigh_dual_face = neigh_dual_faces[position_of_original_element]; + EXPECT_EQ (neigh_dual_face, iface); + + // Check that the index is correct, i.e. when getting the neighbor neighbor element from the index + // we retrieve the original element. + const t8_locidx_t element_index = neigh_element_indices[position_of_original_element]; + EXPECT_GE (element_index, 0); + + if (element_index < num_local_elements) { + const t8_element_t *element_from_index = t8_forest_get_element (forest, element_index, NULL); + EXPECT_EQ (element_from_index, element) + << "Neighbor neighbor element at index " << element_index << " is not original element."; + } + // TODO: Check element index if original element is a ghost element + + // clean-up neighbor's neighbors + if (neigh_num_neighbors > 0) { + T8_FREE (neigh_neighbor_leaves); + T8_FREE (neigh_element_indices); + T8_FREE (neigh_dual_faces); + } } - } - - // clean-up original element neighbors - if (num_neighbors > 0) { - T8_FREE (neighbor_leaves); - T8_FREE (element_indices); - T8_FREE (dual_faces); - } - } - } - } -} -#if 0 -//// OLD CODE - - for (t8_locidx_t ielement = 0; ielement < t8_forest_get_tree_num_elements (forest, itree); ielement++) { - const t8_element_t *element = t8_forest_get_element_in_tree (forest, itree, ielement); - /* iterate over the faces */ - for (int face = 0; face < ts->t8_element_num_faces (element); face++) { - /* Get the eclass of the face neighbor and get the scheme */ - const t8_eclass_t neigh_class = t8_forest_element_neighbor_eclass (forest, itree, element, face); - t8_eclass_scheme_c *neigh_scheme = t8_forest_get_eclass_scheme (forest, neigh_class); - const int num_face_neighs = ts->t8_element_num_face_children (element, face); - t8_element_t **half_neighbors = T8_ALLOC (t8_element_t *, num_face_neighs); - ts->t8_element_new (num_face_neighs, half_neighbors); - t8_forest_element_half_face_neighbors (forest, itree, element, half_neighbors, neigh_scheme, face, - num_face_neighs, NULL); - /* allocate memory for element's neighbor and construct it */ - neigh_scheme->t8_element_new (1, &neighbor); - const t8_locidx_t neigh_tree - = t8_forest_element_face_neighbor (forest, itree, element, neighbor, neigh_scheme, face, &dual_face); - if (neigh_tree > 0) { - /* We now check whether the face children of neighbor are the half neighbors. */ - T8_ASSERT (num_face_neighs == neigh_scheme->t8_element_num_face_children (neighbor, dual_face)); - t8_element_t **neighbor_face_children = T8_ALLOC (t8_element_t *, num_face_neighs); - neigh_scheme->t8_element_new (num_face_neighs, neighbor_face_children); - int *child_ids = T8_ALLOC (int, num_face_neighs); - neigh_scheme->t8_element_children_at_face (neighbor, dual_face, neighbor_face_children, num_face_neighs, - child_ids); - /* Check that the children at face of the neighbor are the half neighbors of the element */ - for (int ineigh = 0; ineigh < num_face_neighs; ineigh++) { - EXPECT_ELEM_EQ (neigh_scheme, neighbor_face_children[ineigh], half_neighbors[ineigh]) - << "ineigh = " << ineigh << " face = " << face; + // clean-up original element neighbors + if (num_neighbors > 0) { + T8_FREE (neighbor_leaves); + T8_FREE (element_indices); + T8_FREE (dual_faces); } - neigh_scheme->t8_element_destroy (num_face_neighs, neighbor_face_children); - T8_FREE (child_ids); - T8_FREE (neighbor_face_children); } - neigh_scheme->t8_element_destroy (1, &neighbor); - neigh_scheme->t8_element_destroy (num_face_neighs, half_neighbors); - T8_FREE (half_neighbors); } } } - t8_forest_unref (&forest); - sc_array_reset (&owners); } -#endif INSTANTIATE_TEST_SUITE_P (t8_gtest_face_neighbors, forest_face_neighbors, AllCmeshsParam, pretty_print_base_example); From 9ab0b110f3f6eaf3338d1f10debc1118d6c8b8fd Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 28 Jan 2025 10:47:16 +0100 Subject: [PATCH 057/133] clean up custom assertion test header --- test/t8_gtest_custom_assertion.hxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/t8_gtest_custom_assertion.hxx b/test/t8_gtest_custom_assertion.hxx index e5cb271576..eeddd82f51 100644 --- a/test/t8_gtest_custom_assertion.hxx +++ b/test/t8_gtest_custom_assertion.hxx @@ -20,17 +20,17 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/** \file t8_gtest_custom_assertion.cxx +/** \file t8_gtest_custom_assertion.hxx * Provide customized GoogleTest functions for improved error-output */ +#ifndef T8_GTEST_CUSTOM_ASSERTION_HXX +#define T8_GTEST_CUSTOM_ASSERTION_HXX + #include #include #include -#ifndef CUSTOM_ASSERTION_HXX -#define CUSTOM_ASSERTION_HXX - /** * \brief Test two elements for equality and print the elements if they aren't equal * @@ -98,4 +98,4 @@ vec3_equality (const char *vec_1_expr, const char *vec_2_expr, const char *preci #define EXPECT_VEC3_EQ(vec_1, vec_2, precision) EXPECT_PRED_FORMAT3 (vec3_equality, (vec_1), (vec_2), (precision)) -#endif /* CUSTOM_ASSERTION_HXX */ +#endif /* T8_GTEST_CUSTOM_ASSERTION_HXX */ From 8d751e205dea9929f29ac22108018231f36f9a9c Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 28 Jan 2025 13:26:03 +0100 Subject: [PATCH 058/133] fix pointer arithmetic --- src/t8_forest/t8_forest.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index ab5d4f0a78..8fcf3d6d10 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -2034,8 +2034,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons } // All neighbor elements must be valid for (int ineigh = 0; ineigh < *num_neighbors; ++ineigh) { - T8_ASSERT (neigh_scheme->t8_element_is_valid (*pneighbor_leaves[ineigh])); - t8_debugf ("Face neighbor %p is valid.\n", *pneighbor_leaves[ineigh]); + T8_ASSERT (neigh_scheme->t8_element_is_valid ((*pneighbor_leaves)[ineigh])); + t8_debugf ("Face neighbor %p is valid.\n", (*pneighbor_leaves)[ineigh]); } #endif // T8_ENABLE_DEBUG From 13ec5cae23d42fd92a448e01ae1af99d7dadb366 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 28 Jan 2025 13:26:46 +0100 Subject: [PATCH 059/133] extend checks in test for adaptive forest --- test/t8_forest/t8_gtest_face_neighbors.cxx | 28 +++++++++++++++++----- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index c20eb0c899..db393ad2bc 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -176,13 +176,29 @@ TEST_P (forest_face_neighbors, test_face_neighbors) = is_root_boundary && t8_cmesh_tree_face_is_boundary (cmesh, cmesh_tree, tree_face); if (!is_boundary_element) { - if (!is_ghost) { - EXPECT_EQ (num_neighbors, 1) - << "Inner local element should have exactly 1 neighbor, has " << num_neighbors << "."; + if (!is_ghost) { // Local element + if (forest_is_uniform) { + // In a uniform forest we must have exactly 1 neighbor. + EXPECT_EQ (num_neighbors, 1) + << "Inner local element should have exactly 1 neighbor, has " << num_neighbors << "."; + } + else { + // In an adaptive forest we have 1 or more neighbors. + EXPECT_GE (num_neighbors, 1) + << "Inner local element should have at least 1 neighbor, has " << num_neighbors << "."; + } } - else { - EXPECT_TRUE (num_neighbors == 0 || num_neighbors == 1) - << "Inner ghost element should have exactly 1 or 0 neighbors, has " << num_neighbors << "."; + else { // Ghost element + if (forest_is_uniform) { + // In a uniform forest a ghost element has none or one neighbor. + EXPECT_TRUE (num_neighbors == 0 || num_neighbors == 1) + << "Inner ghost element should have exactly 1 or 0 neighbors, has " << num_neighbors << "."; + } + else { + // In an adaptive forest a ghost element has 0 or more neighbors. + EXPECT_GE (num_neighbors, 0) + << "Inner ghost element should have 0 or more neighbors, has " << num_neighbors << "."; + } } } else { From eab1b42491d03d099c52dc09b6c0fe70117750db Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 31 Jan 2025 10:51:57 +0100 Subject: [PATCH 060/133] temporarily skip adapted forest in test --- test/t8_forest/t8_gtest_face_neighbors.cxx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index db393ad2bc..5a458b4918 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -95,7 +95,14 @@ class forest_face_neighbors: public testing::TestWithParam TEST_P (forest_face_neighbors, test_face_neighbors) { /* iterate over all elements */ + bool forest_is_uniform = true; // The first forest is uniform. We set this to false at the end of the for loop. for (auto &forest : forests) { + if (!forest_is_uniform) { + // Currently, adaptive forest is not working properly due to a bug in the + // face neighbor computation. + // We hence currently skip the test for adaptive forests. + break; + } const t8_cmesh_t cmesh = t8_forest_get_cmesh (forest); const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); const t8_locidx_t num_ghost_trees = t8_forest_get_num_ghost_trees (forest); @@ -308,6 +315,7 @@ TEST_P (forest_face_neighbors, test_face_neighbors) } } } + forest_is_uniform = false; } } From 60e84ad67963abfb17a1249edf4c40093ebaee9e Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 31 Jan 2025 10:57:32 +0100 Subject: [PATCH 061/133] debugging output --- src/t8_forest/t8_forest.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 8fcf3d6d10..7a7560cb67 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1978,6 +1978,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_element_array_t face_leafs; const size_t face_leaf_count = last_desc_index - first_desc_index + 1; T8_ASSERT (face_leaf_count > 0); + t8_debugf ("Starting search with element indices %i to %i (including).\n", first_desc_index, last_desc_index); t8_element_array_init_view (&face_leafs, tree_leafs, first_desc_index, face_leaf_count); // Iterate over all leafs at the face and collect them as neighbors. const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); @@ -2006,6 +2007,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // total_num_neighbors temporarily counts all inserted neighbors, including this tree const int num_neighbors_current_tree = user_data.neighbors.size (); const int total_num_neighbors = *num_neighbors + num_neighbors_current_tree; + t8_debugf ("Found %i neighbors in tree. Adding up to %i total neighbors.\n", num_neighbors_current_tree, + total_num_neighbors); // Copy neighbor element pointers *pneighbor_leaves = T8_REALLOC (*pneighbor_leaves, t8_element_t *, total_num_neighbors); T8_ASSERT (*pneighbor_leaves != NULL); From b2f45d269575f5a0b420caad23f2ee4b91fe6b5a Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 31 Jan 2025 10:58:36 +0100 Subject: [PATCH 062/133] proper conversion of pointer output --- test/t8_forest/t8_gtest_face_neighbors.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index 5a458b4918..efaf45c145 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -219,7 +219,7 @@ TEST_P (forest_face_neighbors, test_face_neighbors) const int dual_face = dual_faces[ineigh]; const t8_locidx_t neigh_index = element_indices[ineigh]; - t8_debugf ("Checking neighbor element %p in (global) tree %li.\n", neighbor, gneigh_tree); + t8_debugf ("Checking neighbor element %p in (global) tree %li.\n", (void *) neighbor, gneigh_tree); t8_debugf ("dual face is %i, index is %i\n", dual_face, neigh_index); ASSERT_TRUE (neigh_scheme->t8_element_is_valid (neighbor)) From d5eeb2f7ab3f17e8c3c43655f5214493b887cde6 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 31 Jan 2025 11:03:50 +0100 Subject: [PATCH 063/133] comment --- src/t8_forest/t8_forest_private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t8_forest/t8_forest_private.h b/src/t8_forest/t8_forest_private.h index 0324d93e41..0201c2f7ec 100644 --- a/src/t8_forest/t8_forest_private.h +++ b/src/t8_forest/t8_forest_private.h @@ -329,7 +329,7 @@ t8_forest_element_owners_bounds (t8_forest_t forest, t8_gloidx_t gtreeid, const * on output a (better) bound. * * \note If on input \a lower >= \a upper, then the bounds are not changed by this - * algorithm. We interpret \a lower = \a such that the owner is unique and equals \a lower. + * algorithm. We interpret \a lower = \a upper such that the owner is unique and equals \a lower. * \note \a forest must be committed before calling this function. */ void From 5033098ed515e410b156b9d81c004972a5d31093 Mon Sep 17 00:00:00 2001 From: "Dreyer, Lukas" Date: Fri, 7 Feb 2025 16:13:12 +0100 Subject: [PATCH 064/133] leafs -> leaves --- src/t8_forest/t8_forest.cxx | 38 +++++++++++----------- test/t8_forest/t8_gtest_face_neighbors.cxx | 4 +-- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index d39146e05a..0b9c04ba73 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1692,7 +1692,7 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, t8_locidx_t tree_leaf_index, void *user_data) { // Output of iterate_faces: - // Array of indices in tree_leafs of all the face neighbor elements + // Array of indices in tree_leaves of all the face neighbor elements // Assign pneighbor_leaves // Assign dual_faces // Assign pelement_indices @@ -1840,7 +1840,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_element_t *nca_of_face_desc; scheme->element_new (neigh_class, 1, &nca_of_face_desc); - // The neighbor leafs could be distributed across a local tree and a ghost + // The neighbor leaves could be distributed across a local tree and a ghost // tree. We thus possibly need to search in two different arrays. // We store these in a vector and iterate over the entries. // The leaf arrays themself do not store any information about their tree, @@ -1869,9 +1869,9 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_debugf ("Adding local tree to search.\n"); if (0 <= local_neighbor_tree) { // The neighbor tree is a local tree and hence there may be local neighbor elements. - const t8_element_array_t *tree_leafs = t8_forest_tree_get_leaves (forest, local_neighbor_tree); - if (tree_leafs != nullptr) { - neighbor_leaf_array leaf_array (tree_leafs, false); + const t8_element_array_t *tree_leaves = t8_forest_tree_get_leaves (forest, local_neighbor_tree); + if (tree_leaves != nullptr) { + neighbor_leaf_array leaf_array (tree_leaves, false); leaf_arrays.push_back (&leaf_array); } } @@ -1886,9 +1886,9 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // The neighbor tree is also a ghost tree and face neighbors of our element might // be ghost elements. // We add the ghost elements of that tree to our search array. - const t8_element_array_t *ghost_leafs = t8_forest_ghost_get_tree_elements (forest, local_neighbor_ghost_treeid); - if (ghost_leafs != nullptr) { - neighbor_leaf_array leaf_array (ghost_leafs, true); + const t8_element_array_t *ghost_leaves = t8_forest_ghost_get_tree_elements (forest, local_neighbor_ghost_treeid); + if (ghost_leaves != nullptr) { + neighbor_leaf_array leaf_array (ghost_leaves, true); leaf_arrays.push_back (&leaf_array); } } @@ -1909,10 +1909,10 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons *pelement_indices = NULL; *dual_faces = NULL; for (auto &leaf_array : leaf_arrays) { - auto &tree_leafs = leaf_array->first; + auto &tree_leaves = leaf_array->first; const bool leaf_array_is_ghost = leaf_array->second; - const t8_locidx_t first_desc_search = t8_forest_bin_search_lower (tree_leafs, first_face_desc_id, maxlevel); - const t8_locidx_t last_desc_search = t8_forest_bin_search_lower (tree_leafs, last_face_desc_id, maxlevel); + const t8_locidx_t first_desc_search = t8_forest_bin_search_lower (tree_leaves, first_face_desc_id, maxlevel); + const t8_locidx_t last_desc_search = t8_forest_bin_search_lower (tree_leaves, last_face_desc_id, maxlevel); if (first_desc_search >= 0 || last_desc_search >= 0) { // There may be face neighbors in this leaf array. // The first descendant may not be in the leaf array, we then @@ -1921,8 +1921,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_locidx_t last_desc_index = last_desc_search; // Get the actual leaf elements that contain the first and last face desc - const t8_element_t *first_face_leaf = t8_element_array_index_locidx (tree_leafs, first_desc_index); - const t8_element_t *last_face_leaf = t8_element_array_index_locidx (tree_leafs, last_desc_index); + const t8_element_t *first_face_leaf = t8_element_array_index_locidx (tree_leaves, first_desc_index); + const t8_element_t *last_face_leaf = t8_element_array_index_locidx (tree_leaves, last_desc_index); // Compute their nearest common ancestor scheme->element_get_nca (neigh_class, first_face_leaf, last_face_leaf, nca_of_face_desc); /* Check whether the computed nca element does contain or is contained by the same level @@ -1942,23 +1942,23 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons SC_CHECK_ABORT (scheme_is_default_quad_hex, "Computing leaf face neighbors currently only works for default quad or hex schemes."); - // Restrict search array to the leafs from first to last face desc - t8_element_array_t face_leafs; + // Restrict search array to the leaves from first to last face desc + t8_element_array_t face_leaves; const size_t face_leaf_count = last_desc_index - first_desc_index + 1; T8_ASSERT (face_leaf_count > 0); t8_debugf ("Starting search with element indices %i to %i (including).\n", first_desc_index, last_desc_index); - t8_element_array_init_view (&face_leafs, tree_leafs, first_desc_index, face_leaf_count); - // Iterate over all leafs at the face and collect them as neighbors. + t8_element_array_init_view (&face_leaves, tree_leaves, first_desc_index, face_leaf_count); + // Iterate over all leaves at the face and collect them as neighbors. const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); // Compute the local or ghost tree id depending on whether this leaf array corresponds to a local // tree or ghost tree. const t8_locidx_t face_iterate_tree_id = leaf_array_is_ghost ? t8_forest_ghost_get_ghost_treeid (forest, *gneigh_tree) + num_local_trees : local_neighbor_tree; - t8_forest_iterate_faces (forest, face_iterate_tree_id, nca_of_face_desc, face_of_nca, &face_leafs, + t8_forest_iterate_faces (forest, face_iterate_tree_id, nca_of_face_desc, face_of_nca, &face_leaves, first_desc_index, t8_forest_leaf_face_neighbors_iterate, &user_data); // Output of iterate_faces: - // Array of indices in tree_leafs of all the face neighbor elements + // Array of indices in tree_leaves of all the face neighbor elements // Assign pneighbor_leaves // Assign dual_faces // Assign pelement_indices diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index fddaf9fc95..620006fd96 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -119,9 +119,9 @@ TEST_P (forest_face_neighbors, test_face_neighbors) : t8_forest_ghost_get_tree_elements (forest, ghost_tree_id); const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, itree); const t8_scheme *scheme = t8_forest_get_scheme (forest); - const t8_locidx_t num_leafs = t8_element_array_get_count (leaf_elements); + const t8_locidx_t num_leaves = t8_element_array_get_count (leaf_elements); const t8_locidx_t cmesh_tree = t8_forest_ltreeid_to_cmesh_ltreeid (forest, itree); - for (t8_locidx_t ileaf = 0; ileaf < num_leafs; ++ileaf, ++ielement_index) { + for (t8_locidx_t ileaf = 0; ileaf < num_leaves; ++ileaf, ++ielement_index) { // Iterate over each leaf element const t8_element_t *element = t8_element_array_index_locidx (leaf_elements, ileaf); const int num_faces = scheme->element_get_num_faces (tree_class, element); From 06de9731e7ebd3234dbfb23d77e1a4a683a5ca53 Mon Sep 17 00:00:00 2001 From: "Dreyer, Lukas" Date: Fri, 7 Feb 2025 16:18:28 +0100 Subject: [PATCH 065/133] fix CI --- src/t8_forest/t8_forest.cxx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 0b9c04ba73..43eb58678b 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1449,11 +1449,6 @@ t8_eclass_t t8_forest_element_neighbor_eclass (const t8_forest_t forest, const t8_locidx_t ltreeid, const t8_element_t *elem, const int face) { - t8_ctree_t coarse_tree; - int tree_face; - t8_locidx_t lcoarse_neighbor; - t8_cmesh_t cmesh; - /* Get a pointer to the tree to read its element class */ const t8_tree_t tree = t8_forest_get_tree (forest, ltreeid); const t8_eclass_t tree_class = tree->eclass; @@ -1710,7 +1705,7 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, struct t8_lfn_user_data *lfn_data = reinterpret_cast (user_data); // face is the face of the considered leaf neighbor element and thus the // corresponding dual face - t8_debugf ("Adding new face neighbor (leaf index %li) with dual face %i.\n", tree_leaf_index, face); + t8_debugf ("Adding new face neighbor (leaf index %i) with dual face %i.\n", tree_leaf_index, face); lfn_data->dual_faces.push_back (face); // Compute the index of the element const t8_locidx_t num_local_elements = t8_forest_get_local_num_elements (forest); @@ -1886,7 +1881,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // The neighbor tree is also a ghost tree and face neighbors of our element might // be ghost elements. // We add the ghost elements of that tree to our search array. - const t8_element_array_t *ghost_leaves = t8_forest_ghost_get_tree_elements (forest, local_neighbor_ghost_treeid); + const t8_element_array_t *ghost_leaves + = t8_forest_ghost_get_tree_elements (forest, local_neighbor_ghost_treeid); if (ghost_leaves != nullptr) { neighbor_leaf_array leaf_array (ghost_leaves, true); leaf_arrays.push_back (&leaf_array); @@ -2006,7 +2002,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // All neighbor elements must be valid for (int ineigh = 0; ineigh < *num_neighbors; ++ineigh) { T8_ASSERT (scheme->element_is_valid (neigh_class, (*pneighbor_leaves)[ineigh])); - t8_debugf ("Face neighbor %p is valid.\n", (*pneighbor_leaves)[ineigh]); + t8_debugf ("Face neighbor %p is valid.\n", (void *) (*pneighbor_leaves)[ineigh]); } #endif // T8_ENABLE_DEBUG From 74127201f50e858f14fd4e5040ce8f07a389fd8b Mon Sep 17 00:00:00 2001 From: "Dreyer, Lukas" Date: Fri, 7 Feb 2025 16:41:37 +0100 Subject: [PATCH 066/133] adapt test parameter --- test/t8_forest/t8_gtest_forest_face_normal.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/t8_forest/t8_gtest_forest_face_normal.cxx b/test/t8_forest/t8_gtest_forest_face_normal.cxx index 2a33194a4a..5d2e4661e5 100644 --- a/test/t8_forest/t8_gtest_forest_face_normal.cxx +++ b/test/t8_forest/t8_gtest_forest_face_normal.cxx @@ -114,4 +114,4 @@ TEST_P (class_forest_face_normal, back_and_forth) } INSTANTIATE_TEST_SUITE_P (t8_gtest_forest_face_normal, class_forest_face_normal, - testing::Combine (DefaultScheme, testing::Range (0, 2))); + testing::Combine(testing::Combine (testing::Range(0, 1), testing::Values(T8_ECLASS_QUAD, T8_ECLASS_HEX)), testing::Range (0, 2))); From cccd74b2bf869be647925fdd933afe03d5b9fc24 Mon Sep 17 00:00:00 2001 From: "Dreyer, Lukas" Date: Thu, 13 Feb 2025 10:26:39 +0100 Subject: [PATCH 067/133] indent --- test/t8_forest/t8_gtest_forest_face_normal.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/t8_forest/t8_gtest_forest_face_normal.cxx b/test/t8_forest/t8_gtest_forest_face_normal.cxx index 5d2e4661e5..2680d9e8bf 100644 --- a/test/t8_forest/t8_gtest_forest_face_normal.cxx +++ b/test/t8_forest/t8_gtest_forest_face_normal.cxx @@ -114,4 +114,6 @@ TEST_P (class_forest_face_normal, back_and_forth) } INSTANTIATE_TEST_SUITE_P (t8_gtest_forest_face_normal, class_forest_face_normal, - testing::Combine(testing::Combine (testing::Range(0, 1), testing::Values(T8_ECLASS_QUAD, T8_ECLASS_HEX)), testing::Range (0, 2))); + testing::Combine (testing::Combine (testing::Range (0, 1), + testing::Values (T8_ECLASS_QUAD, T8_ECLASS_HEX)), + testing::Range (0, 2))); From a7c83c6405aa039018d2116bc6a1e91849a6065d Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Mon, 17 Mar 2025 15:00:31 +0100 Subject: [PATCH 068/133] merge updates from fix-forest_leave_face_neigh --- src/t8_forest/t8_forest.cxx | 129 +++++++++++++++++++++++++++++------- 1 file changed, 105 insertions(+), 24 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index a62132f9f0..e39fb3560a 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1450,8 +1450,7 @@ t8_forest_element_neighbor_eclass (const t8_forest_t forest, const t8_locidx_t l const int face) { /* Get a pointer to the tree to read its element class */ - const t8_tree_t tree = t8_forest_get_tree (forest, ltreeid); - const t8_eclass_t tree_class = tree->eclass; + const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, ltreeid); const t8_scheme *scheme = t8_forest_get_scheme (forest); if (!scheme->element_is_root_boundary (tree_class, elem, face)) { /* The neighbor element is inside the current tree. */ @@ -1486,8 +1485,7 @@ t8_forest_element_face_neighbor (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eclass_t neigh_eclass, int face, int *neigh_face) { /* Get a pointer to the tree to read its element class */ - const t8_tree_t tree = t8_forest_get_tree (forest, ltreeid); - const t8_eclass_t eclass = tree->eclass; + const t8_eclass_t eclass = t8_forest_get_tree_class (forest, ltreeid); const t8_scheme *scheme = t8_forest_get_scheme (forest); if (neigh_eclass == eclass && scheme->element_get_face_neighbor_inside (eclass, elem, neigh, face, neigh_face)) { /* The neighbor was constructed and is inside the current tree. */ @@ -1804,16 +1802,21 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_element_t *same_level_neighbor; scheme->element_new (neigh_class, 1, &same_level_neighbor); int neigh_face; - *gneigh_tree = t8_forest_element_face_neighbor (forest, ltreeid, leaf_or_ghost, same_level_neighbor, neigh_class, - face, &neigh_face); + const t8_gloidx_t computed_gneigh_tree = t8_forest_element_face_neighbor ( + forest, ltreeid, leaf_or_ghost, same_level_neighbor, neigh_class, face, &neigh_face); - if (*gneigh_tree < 0) { + if (computed_gneigh_tree < 0) { // There is no face neighbor across this face scheme->element_destroy (neigh_class, 1, &same_level_neighbor); *dual_faces = NULL; *num_neighbors = 0; *pelement_indices = NULL; - *pneighbor_leaves = NULL; + if (pneighbor_leaves) { + *pneighbor_leaves = NULL; + } + if (gneigh_tree != NULL) { + *gneigh_tree = -1; + } return; } @@ -1852,12 +1855,12 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons int face_owners_lower_bound = 0; int face_owners_upper_bound = forest->mpisize - 1; const int mpirank = forest->mpirank; - t8_forest_element_owners_at_face_bounds (forest, *gneigh_tree, same_level_neighbor, neigh_class, neigh_face, + t8_forest_element_owners_at_face_bounds (forest, computed_gneigh_tree, same_level_neighbor, neigh_class, neigh_face, &face_owners_lower_bound, &face_owners_upper_bound); std::vector leaf_arrays; - const t8_locidx_t local_neighbor_tree = t8_forest_get_local_id (forest, *gneigh_tree); + const t8_locidx_t local_neighbor_tree = t8_forest_get_local_id (forest, computed_gneigh_tree); if (face_owners_lower_bound <= mpirank && mpirank <= face_owners_upper_bound) { // Add the local neighbor tree's elements to the search array. // Compute the local id of the neighbor tree and check if it is a local tree @@ -1866,8 +1869,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // The neighbor tree is a local tree and hence there may be local neighbor elements. const t8_element_array_t *tree_leaves = t8_forest_tree_get_leaves (forest, local_neighbor_tree); if (tree_leaves != nullptr) { - neighbor_leaf_array leaf_array (tree_leaves, false); - leaf_arrays.push_back (&leaf_array); + neighbor_leaf_array *leaf_array = new neighbor_leaf_array (tree_leaves, false); + leaf_arrays.push_back (leaf_array); } } } @@ -1876,7 +1879,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons if (face_owners_lower_bound != mpirank || face_owners_upper_bound != mpirank) { // Add the neighbor tree ghost elements to the search array t8_debugf ("Adding ghost tree to search.\n"); - const t8_locidx_t local_neighbor_ghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, *gneigh_tree); + const t8_locidx_t local_neighbor_ghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, computed_gneigh_tree); if (local_neighbor_ghost_treeid >= 0) { // The neighbor tree is also a ghost tree and face neighbors of our element might // be ghost elements. @@ -1884,8 +1887,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_element_array_t *ghost_leaves = t8_forest_ghost_get_tree_elements (forest, local_neighbor_ghost_treeid); if (ghost_leaves != nullptr) { - neighbor_leaf_array leaf_array (ghost_leaves, true); - leaf_arrays.push_back (&leaf_array); + neighbor_leaf_array *leaf_array = new neighbor_leaf_array (ghost_leaves, true); + leaf_arrays.push_back (leaf_array); } } } @@ -1901,12 +1904,16 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // This will trigger REALLOC to allocate the memory in the initial call. // Not setting them to NULL but keeping them possibly uninitialized, will // call REALLOC on uninitialized memory and result in memory errors. - *pneighbor_leaves = NULL; + if (pneighbor_leaves != NULL) { + *pneighbor_leaves = NULL; + } + *pelement_indices = NULL; *dual_faces = NULL; for (auto &leaf_array : leaf_arrays) { auto &tree_leaves = leaf_array->first; const bool leaf_array_is_ghost = leaf_array->second; + T8_ASSERT (tree_leaves != NULL); const t8_locidx_t first_desc_search = t8_forest_bin_search_lower (tree_leaves, first_face_desc_id, maxlevel); const t8_locidx_t last_desc_search = t8_forest_bin_search_lower (tree_leaves, last_face_desc_id, maxlevel); if (first_desc_search >= 0 || last_desc_search >= 0) { @@ -1949,7 +1956,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // Compute the local or ghost tree id depending on whether this leaf array corresponds to a local // tree or ghost tree. const t8_locidx_t face_iterate_tree_id - = leaf_array_is_ghost ? t8_forest_ghost_get_ghost_treeid (forest, *gneigh_tree) + num_local_trees + = leaf_array_is_ghost ? t8_forest_ghost_get_ghost_treeid (forest, computed_gneigh_tree) + num_local_trees : local_neighbor_tree; t8_forest_iterate_faces (forest, face_iterate_tree_id, nca_of_face_desc, face_of_nca, &face_leaves, first_desc_index, t8_forest_leaf_face_neighbors_iterate, &user_data); @@ -1965,6 +1972,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // neighbor data. // TODO: Since there is no other way, we copy them from the vectors. // This should be improved in the future to get around the copy. + // Indeed it would be more beneficial to just return const pointers to the actual internal leaves. // num_neighbors counts the already inserted neighbors before this tree // num_neighbors_current_tree counts the neighbors added in this tree @@ -1974,10 +1982,21 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_debugf ("Found %i neighbors in tree. Adding up to %i total neighbors.\n", num_neighbors_current_tree, total_num_neighbors); // Copy neighbor element pointers - *pneighbor_leaves = T8_REALLOC (*pneighbor_leaves, t8_element_t *, total_num_neighbors); - T8_ASSERT (*pneighbor_leaves != NULL); - memcpy (*pneighbor_leaves + *num_neighbors, user_data.neighbors.data () + *num_neighbors, - num_neighbors_current_tree * sizeof (t8_element_t *)); + if (pneighbor_leaves != NULL) { + // Note element_destroy call after this function on *pneighbor_leaves + // is compatible with using T8_REALLOC on *pneighbor_leaves. + // REALLOC moving the storage of the pointers. The pointers store the element storage. + // So the element storage allocated by t8_element_new is not affected by the call to REALLOC. + *pneighbor_leaves = T8_REALLOC (*pneighbor_leaves, t8_element_t *, total_num_neighbors); + scheme->element_new (eclass, num_neighbors_current_tree, *pneighbor_leaves + *num_neighbors); + T8_ASSERT (*pneighbor_leaves != NULL); + // Call element copy for each element + for (t8_locidx_t ielem = 0; ielem < num_neighbors_current_tree; ++ielem) { + t8_element_t *new_element = (*pneighbor_leaves)[ielem]; + const t8_element_t *forest_leaf = user_data.neighbors.data ()[*num_neighbors + ielem]; + scheme->element_copy (eclass, forest_leaf, new_element); + } + } // Copy element indices *pelement_indices = T8_REALLOC (*pelement_indices, t8_locidx_t, total_num_neighbors); T8_ASSERT (*pelement_indices != NULL); @@ -1991,6 +2010,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons *num_neighbors = total_num_neighbors; } } + // clean up memory allocated with new + delete leaf_array; } scheme->element_destroy (neigh_class, 1, &same_level_neighbor); #if T8_ENABLE_DEBUG @@ -2000,12 +2021,18 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons T8_ASSERT (*num_neighbors > 0); } // All neighbor elements must be valid - for (int ineigh = 0; ineigh < *num_neighbors; ++ineigh) { - T8_ASSERT (scheme->element_is_valid (neigh_class, (*pneighbor_leaves)[ineigh])); - t8_debugf ("Face neighbor %p is valid.\n", (void *) (*pneighbor_leaves)[ineigh]); + if (pneighbor_leaves != NULL) { + for (int ineigh = 0; ineigh < *num_neighbors; ++ineigh) { + T8_ASSERT (scheme->element_is_valid (neigh_class, (*pneighbor_leaves)[ineigh])); + t8_debugf ("Face neighbor %p is valid.\n", (void *) (*pneighbor_leaves)[ineigh]); + } } #endif // T8_ENABLE_DEBUG + if (gneigh_tree != NULL) { + *gneigh_tree = computed_gneigh_tree; + } + // clean-up scheme->element_destroy (eclass, 1, &nca_of_face_desc); #if 0 @@ -2252,6 +2279,60 @@ t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8 pelement_indices, pneigh_eclass, NULL, NULL); } +t8_locidx_t +t8_forest_same_level_leaf_face_neighbor_index (t8_forest_t forest, const t8_locidx_t element_index, + const int face_index, const t8_gloidx_t global_treeid, int *dual_face) +{ + const t8_locidx_t num_local_elements = t8_forest_get_local_num_elements (forest); +#if T8_ENABLE_DEBUG + const t8_locidx_t num_ghosts = t8_forest_get_num_ghosts (forest); + T8_ASSERT (0 <= element_index && element_index < num_local_elements + num_ghosts); +#endif + const bool is_local = element_index < num_local_elements; + + t8_locidx_t local_tree; + t8_locidx_t element_index_in_tree; + const t8_element_t *element; + if (is_local) { + local_tree = t8_forest_get_local_id (forest, global_treeid); + element_index_in_tree = element_index - t8_forest_get_tree_element_offset (forest, local_tree); + element = t8_forest_get_element_in_tree (forest, local_tree, element_index_in_tree); + } + else { + local_tree = t8_forest_ghost_get_ghost_treeid (forest, global_treeid); + const t8_locidx_t ghost_offset_in_tree = t8_forest_ghost_get_tree_element_offset (forest, local_tree); + element_index_in_tree = element_index - num_local_elements - ghost_offset_in_tree; + element = t8_forest_ghost_get_element (forest, local_tree, element_index_in_tree); + local_tree += t8_forest_get_num_local_trees (forest); + } + + int *dual_faces; + int num_neighbors = 0; + t8_locidx_t *element_indices; + t8_eclass_t neigh_class; + + t8_debugf ("Same level leaf neighbor for index %i. Which is %s element %i in tree %i.\n", element_index, + element_index < num_local_elements ? "local" : "ghost", element_index_in_tree, local_tree); + + t8_forest_leaf_face_neighbors (forest, local_tree, element, NULL, face_index, &dual_faces, &num_neighbors, + &element_indices, &neigh_class); + + T8_ASSERT (num_neighbors == 0 || num_neighbors == 1); + + if (num_neighbors == 0) { + *dual_face = -1; + return -1; + } + + *dual_face = dual_faces[0]; + const t8_locidx_t neigh_index = element_indices[0]; + + T8_FREE (element_indices); + T8_FREE (dual_faces); + + return neigh_index; +} + void t8_forest_print_all_leaf_neighbors (t8_forest_t forest) { From f66fa5cb24a4c3a3d893e54ea7c6220d56f3c6b2 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Mon, 17 Mar 2025 15:25:20 +0100 Subject: [PATCH 069/133] update test file from fix-forest_leave_face_neigh branch --- test/t8_forest/t8_gtest_face_neighbors.cxx | 23 ++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index 620006fd96..dc39f6b1b7 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -62,13 +62,13 @@ class forest_face_neighbors: public testing::TestWithParam (GetParam ()); - const t8_scheme *scheme = create_from_scheme_id (scheme_id); t8_cmesh_t cmesh = std::get<1> (GetParam ())->cmesh_create (); if (test_face_neighbors_skip_cmesh (cmesh)) { /* we skip empty cmeshes case */ t8_cmesh_unref (&cmesh); GTEST_SKIP (); } + const t8_scheme *scheme = create_from_scheme_id (scheme_id); const int level = 1; const int adapt_levels = 2; const int max_adapt_level = level + adapt_levels; @@ -215,6 +215,22 @@ TEST_P (forest_face_neighbors, test_face_neighbors) << "."; } + if (forest_is_uniform) { + ASSERT_TRUE (num_neighbors == 0 || num_neighbors == 1); + // Check the index computation function and that it computes the correct neighbor index. + int check_dual_face; + const t8_locidx_t check_same_level_index = t8_forest_same_level_leaf_face_neighbor_index ( + forest, ielement_index, iface, gtree_id, &check_dual_face); + + if (check_dual_face < 0) { + EXPECT_EQ (num_neighbors, 0); + } + if (check_dual_face >= 0) { + EXPECT_EQ (dual_faces[0], check_dual_face); + EXPECT_EQ (element_indices[0], check_same_level_index); + } + } + // Check that the neighbor of the neighbor is the original element. for (int ineigh = 0; ineigh < num_neighbors; ++ineigh) { const t8_element_t *neighbor = neighbor_leaves[ineigh]; @@ -224,9 +240,10 @@ TEST_P (forest_face_neighbors, test_face_neighbors) t8_debugf ("Checking neighbor element %p in (global) tree %li.\n", (void *) neighbor, gneigh_tree); t8_debugf ("dual face is %i, index is %i\n", dual_face, neigh_index); +#if T8_ENABLE_DEBUG ASSERT_TRUE (scheme->element_is_valid (neigh_class, neighbor)) << "Neighbor element " << ineigh << " is not valid"; - +#endif t8_locidx_t neigh_ltreeid_from_index; // Check that neighbor index correctly yields neighbor element. if (neigh_index < num_local_elements) { @@ -302,6 +319,7 @@ TEST_P (forest_face_neighbors, test_face_neighbors) // clean-up neighbor's neighbors if (neigh_num_neighbors > 0) { + scheme->element_destroy (neigh_class, neigh_num_neighbors, neigh_neighbor_leaves); T8_FREE (neigh_neighbor_leaves); T8_FREE (neigh_element_indices); T8_FREE (neigh_dual_faces); @@ -310,6 +328,7 @@ TEST_P (forest_face_neighbors, test_face_neighbors) // clean-up original element neighbors if (num_neighbors > 0) { + scheme->element_destroy (neigh_class, num_neighbors, neighbor_leaves); T8_FREE (neighbor_leaves); T8_FREE (element_indices); T8_FREE (dual_faces); From dc3105bf1027024cb2daecc5e0deb3e652ef841c Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Mon, 17 Mar 2025 15:27:06 +0100 Subject: [PATCH 070/133] add same level face neigh to header --- src/t8_forest/t8_forest_general.h | 44 ++++++++++++++++--------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index 800ebe677c..4269ea3c79 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -3,7 +3,7 @@ t8code is a C library to manage a collection (a forest) of multiple connected adaptive space-trees of general element classes in parallel. - Copyright (C) 2015 the developers + Copyright (C) 2024 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 @@ -568,14 +568,14 @@ t8_forest_leaf_face_orientation (t8_forest_t forest, const t8_locidx_t ltreeid, * 0, 1, ... num_local_el - 1 for local leaves and * num_local_el , ... , num_local_el + num_ghosts - 1 for ghosts. * \param [out] pneigh_eclass On output the eclass of the neighbor elements. - * \note If there are no face neighbors, then *neighbor_leaves = NULL, num_neighbors = 0, + * \note If there are no face neighbors, then *pneighbor_leaves = NULL, num_neighbors = 0, * and *pelement_indices = NULL on output. * \note \a forest must be committed before calling this function. * * \note Important! This routine allocates memory which must be freed. Do it like this: * * if (num_neighbors > 0) { - * scheme->element_destroy (pneigh_eclass, num_neighbors, neighbors); + * scheme->element_destroy (pneigh_eclass, num_neighbors, pneighbor_leaves); * T8_FREE (pneighbor_leaves); * T8_FREE (pelement_indices); * T8_FREE (dual_faces); @@ -607,14 +607,14 @@ t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8 * 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, + * \note If there are no face neighbors, then *pneighbor_leaves = NULL, num_neighbors = 0, * and *pelement_indices = NULL on output. * \note \a forest must be committed before calling this function. * * \note Important! This routine allocates memory which must be freed. Do it like this: * * if (num_neighbors > 0) { - * scheme->element_destroy (pneigh_eclass, num_neighbors, neighbors); + * scheme->element_destroy (pneigh_eclass, num_neighbors, pneighbor_leaves); * T8_FREE (pneighbor_leaves); * T8_FREE (pelement_indices); * T8_FREE (dual_faces); @@ -627,6 +627,24 @@ 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, t8_gloidx_t *gneigh_tree, int *orientation); +/** Given a leaf element or ghost index in "all local elements + ghosts" enumeration + * compute the index of the face neighbor of the element - provided that only one or no + * face neighbors exists. + * HANDLE WITH CARE. DO NOT CALL IF THE FOREST IS ADAPTED. + * + * \param[in] forest The forest. Must be committed. + * \param[in] element_index Index of an element in \a forest. Must have only one or no facen neighbors across the given face. + * 0 <= \a element_index < num_local_elements + num_ghosts + * \param[in] face_index Index of a face of \a element. + * \param[in] global_treeid Global index of the tree that contains \a element. + * \param[out] dual_face Return value, the dual_face index of the face neighbor. + * \return The index of the face neighbor leaf (local element or ghost). + * \note Do not call if you are unsure about the number of face neighbors. In particular if the forest is adapted and not uniform. + */ +t8_locidx_t +t8_forest_same_level_leaf_face_neighbor_index (t8_forest_t forest, const t8_locidx_t element_index, + const int face_index, const t8_gloidx_t global_treeid, int *dual_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 @@ -874,22 +892,6 @@ void t8_forest_element_points_inside (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, const double *points, int num_points, int *is_inside, const double tolerance); -/** Find the owner process of a given element. - * \param [in] forest The forest. - * \param [in] gtreeid The global id of the tree in which the element lies. - * \param [in] element The element to look for. - * \param [in] eclass The element class of the tree \a gtreeid. - * \return The mpirank of the process that owns \a element. - * \note The element must not exist in the forest, but an ancestor of its first - * descendant has to. If the element's owner is not unique, the owner of the element's - * first descendant is returned. - * \note \a forest must be committed before calling this function. - * \see t8_forest_element_find_owner_ext - * \see t8_forest_element_owners_bounds - */ -int -t8_forest_element_find_owner (t8_forest_t forest, t8_gloidx_t gtreeid, t8_element_t *element, t8_eclass_t eclass); - /* TODO: if set level and partition/adapt/balance all give NULL, then * refine uniformly and partition/adapt/balance the uniform forest. */ /** Build a uniformly refined forest on a coarse mesh. From 9e42acfa5a7ec89c3c08d665acd55588dd6507c1 Mon Sep 17 00:00:00 2001 From: "Dreyer, Lukas" Date: Fri, 28 Feb 2025 16:22:40 +0100 Subject: [PATCH 071/133] Move element_find_owner to public interface --- src/t8_forest/t8_forest_general.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index 4269ea3c79..fbc1ceb1ca 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -892,6 +892,22 @@ void t8_forest_element_points_inside (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, const double *points, int num_points, int *is_inside, const double tolerance); +/** Find the owner process of a given element. + * \param [in] forest The forest. + * \param [in] gtreeid The global id of the tree in which the element lies. + * \param [in] element The element to look for. + * \param [in] eclass The element class of the tree \a gtreeid. + * \return The mpirank of the process that owns \a element. + * \note The element must not exist in the forest, but an ancestor of its first + * descendant has to. If the element's owner is not unique, the owner of the element's + * first descendant is returned. + * \note \a forest must be committed before calling this function. + * \see t8_forest_element_find_owner_ext + * \see t8_forest_element_owners_bounds + */ +int +t8_forest_element_find_owner (t8_forest_t forest, t8_gloidx_t gtreeid, t8_element_t *element, t8_eclass_t eclass); + /* TODO: if set level and partition/adapt/balance all give NULL, then * refine uniformly and partition/adapt/balance the uniform forest. */ /** Build a uniformly refined forest on a coarse mesh. From 424e91846761b6222359c08e920d57961dbb1814 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 18 Mar 2025 13:35:12 +0100 Subject: [PATCH 072/133] Add TODO comment for adaptive bug and how to solve it --- src/t8_forest/t8_forest.cxx | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index e39fb3560a..4550902672 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1928,10 +1928,37 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_element_t *last_face_leaf = t8_element_array_index_locidx (tree_leaves, last_desc_index); // Compute their nearest common ancestor scheme->element_get_nca (neigh_class, first_face_leaf, last_face_leaf, nca_of_face_desc); + /* Check whether the computed nca element does contain or is contained by the same level * face neighbor. If not, then there are no face neighbors and we do not continue. * Otherwise, face neighbors exist and are descendants of nca_of_face_desc. */ + /* There may occur situations where a neighbor exists but the search returns an element + * that is not an ancestor of either first_face_leaf nor last_face_leaf. + * This might happen when computer face neighbors of ghosts. Consider the following situation + * + * __ __ + * | | | + * |__|__| p_1 The top elements belong to p_1 + * |G |_| p_0 The bottom elements belong to p_0 + * |__| + * + * If we compute the neighbor of ghost element G across the right face (face number 1) + * we should get the level 2 element in the bottom right. + * However, the search for the first face desc will return G, since the element array does not + * contain any elements that are descendants of the first face desc and have a smaller or equal linear + * index. + * + * TODO: Try this approach + * - build first and last desc from their id + * - compute their nca + * - search for nca + * - if not ancestor of nca (including not found) + * - add 1 to index (use 0 if element was not found) + * - if not ancestor -> no neighbor + * - else iterate faces + * */ + if (t8_forest_elements_are_ancestor (scheme, neigh_class, same_level_neighbor, nca_of_face_desc)) { const int face_of_nca = neigh_face; // TODO: Need to implement element function to compute face id of nca face. From 97300de4a326c953c930e52f222f7fd54b304f32 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 21 Mar 2025 13:57:45 +0100 Subject: [PATCH 073/133] update todo comment --- src/t8_forest/t8_forest.cxx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 4550902672..6c14ea9ea7 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1928,7 +1928,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_element_t *last_face_leaf = t8_element_array_index_locidx (tree_leaves, last_desc_index); // Compute their nearest common ancestor scheme->element_get_nca (neigh_class, first_face_leaf, last_face_leaf, nca_of_face_desc); - + /* Check whether the computed nca element does contain or is contained by the same level * face neighbor. If not, then there are no face neighbors and we do not continue. * Otherwise, face neighbors exist and are descendants of nca_of_face_desc. */ @@ -1951,12 +1951,13 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons * * TODO: Try this approach * - build first and last desc from their id - * - compute their nca - * - search for nca - * - if not ancestor of nca (including not found) - * - add 1 to index (use 0 if element was not found) - * - if not ancestor -> no neighbor - * - else iterate faces + * - do a "find ancestor/desc search" + * - if not ancestor of nca (including not found) + * - add 1 to index (use 0 if element was not found) + * - if not ancestor -> not found + * - if one is found then both must have been found + * - build nca + * - iterate faces * */ if (t8_forest_elements_are_ancestor (scheme, neigh_class, same_level_neighbor, nca_of_face_desc)) { From d74c6781096feb25e4826e66bd2d1bb4822ed88b Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 14 May 2025 13:20:08 +0200 Subject: [PATCH 074/133] Update is_ghost test to recent scheme changes --- test/t8_forest/t8_gtest_element_is_leaf.cxx | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/t8_forest/t8_gtest_element_is_leaf.cxx b/test/t8_forest/t8_gtest_element_is_leaf.cxx index 32a4f577ba..57b9a8946c 100644 --- a/test/t8_forest/t8_gtest_element_is_leaf.cxx +++ b/test/t8_forest/t8_gtest_element_is_leaf.cxx @@ -70,7 +70,7 @@ t8_test_adapt_first_child (t8_forest_t forest, [[maybe_unused]] t8_forest_t fore return 0; } -class element_is_leaf_or_ghost: public testing::TestWithParam> { +class element_is_leaf_or_ghost: public testing::TestWithParam> { protected: void SetUp () override @@ -87,7 +87,7 @@ class element_is_leaf_or_ghost: public testing::TestWithParamt8_element_new (1, ¬_ghost); + scheme->element_new (tree_class, 1, ¬_ghost); /* Iterate over all the tree's ghost elements, check whether the ghost * is correctly identified by t8_forest_element_is_ghost and t8_forest_element_is_leaf_or_ghost, * build its parent and its first child (if they exist), and verify @@ -180,19 +179,19 @@ t8_test_element_is_ghost_for_forest (t8_forest_t forest) EXPECT_TRUE (t8_forest_element_is_ghost (forest, ghost_element, ighost_tree)); EXPECT_TRUE (t8_forest_element_is_leaf_or_ghost (forest, ghost_element, ighost_tree, 1)); /* Compute parent and first child of element and check that they are not in the tree */ - const int element_level = scheme->t8_element_level (ghost_element); + const int element_level = scheme->element_get_level (tree_class, ghost_element); if (element_level > 0) { - scheme->t8_element_parent (ghost_element, not_ghost); + scheme->element_get_parent (tree_class, ghost_element, not_ghost); EXPECT_FALSE (t8_forest_element_is_ghost (forest, not_ghost, ighost_tree)); EXPECT_FALSE (t8_forest_element_is_leaf_or_ghost (forest, not_ghost, ighost_tree, 1)); } - if (element_level < scheme->t8_element_maxlevel ()) { - scheme->t8_element_child (ghost_element, 0, not_ghost); + if (element_level < scheme->get_maxlevel (tree_class)) { + scheme->element_get_child (tree_class, ghost_element, 0, not_ghost); EXPECT_FALSE (t8_forest_element_is_ghost (forest, not_ghost, ighost_tree)); EXPECT_FALSE (t8_forest_element_is_leaf_or_ghost (forest, not_ghost, ighost_tree, 1)); } } - scheme->t8_element_destroy (1, ¬_ghost); + scheme->element_destroy (tree_class, 1, ¬_ghost); } } @@ -220,5 +219,6 @@ auto pretty_print_level_and_cmesh_params }; INSTANTIATE_TEST_SUITE_P (t8_gtest_element_is_leaf_or_ghost, element_is_leaf_or_ghost, - testing::Combine (testing::Range (0, T8_IS_LEAF_MAX_LVL), AllCmeshsParam), + testing::Combine (AllSchemeCollections, testing::Range (0, T8_IS_LEAF_MAX_LVL), + AllCmeshsParam), pretty_print_level_and_cmesh_params); From 5c58879fbfe597cacc078ad9a03925a7eb2261c3 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 14 May 2025 13:21:00 +0200 Subject: [PATCH 075/133] New element_find function for element_array --- src/t8_data/t8_containers.cxx | 33 +++++++++++++++++++++++++++++++++ src/t8_data/t8_containers.h | 10 ++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/t8_data/t8_containers.cxx b/src/t8_data/t8_containers.cxx index e025c9ed6b..a3989ea098 100644 --- a/src/t8_data/t8_containers.cxx +++ b/src/t8_data/t8_containers.cxx @@ -326,6 +326,39 @@ t8_element_array_get_array_mutable (t8_element_array_t *element_array) return &element_array->array; } +t8_locidx_t +t8_element_array_find (const t8_element_array_t *element_array, const t8_element_t *element) +{ + /* In order to find the element, we need to compute its linear id. + * To do so, we need the scheme and the level of the element. */ + const t8_scheme *scheme = t8_element_array_get_scheme (element_array); + const t8_eclass_t tree_class = t8_element_array_get_tree_class (element_array); + const int element_level = scheme->element_get_level (tree_class, element); + /* Compute the linear id. */ + const t8_linearidx_t element_id = scheme->element_get_linear_id (tree_class, element, element_level); + /* Search for the element. + * The search returns the largest index i, + * such that the element at position i has a smaller id than the given one. + * If no such i exists, it returns -1. */ + const t8_locidx_t search_result = t8_forest_bin_search_lower (element_array, element_id, element_level); + if (search_result < 0) { + // The element was not found, we return -1. */ + return -1; + } + /* 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 (element_array, search_result); + T8_ASSERT (check_element != NULL); + if (scheme->element_is_equal (tree_class, element, check_element)) { + // The element was found at position search_result. We return it. + return search_result; + } + else { + // The element was not found, we return -1. */ + return -1; + } +} + void t8_element_array_reset (t8_element_array_t *element_array) { diff --git a/src/t8_data/t8_containers.h b/src/t8_data/t8_containers.h index b8ae00f6f5..e3c2fc0cc9 100644 --- a/src/t8_data/t8_containers.h +++ b/src/t8_data/t8_containers.h @@ -262,6 +262,16 @@ t8_element_array_get_array (const t8_element_array_t *element_array); sc_array_t * t8_element_array_get_array_mutable (t8_element_array_t *element_array); +/** Search for an element in an array. + * \param [in] element_array Array structure. + * \param [in] element Element to be found in \a element_array. + * The element must have been created with the scheme used in \a element_array. + * \return If \a element was found in \a element_array then the position in the array is returned. + * If the element is not found, -1 is returned. +*/ +t8_locidx_t +t8_element_array_find (const t8_element_array_t *element_array, const t8_element_t *element); + /** Sets the array count to zero and frees all elements. * \param [in,out] element_array Array structure to be reset. * \note Calling t8_element_array_init, then any array operations, From daf57839ef4ecb3c2487b6a1efc19c7ced39b9c5 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 14 May 2025 13:21:27 +0200 Subject: [PATCH 076/133] use element_array_find in is_leaf_or_ghost --- src/t8_forest/t8_forest.cxx | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index a4eeeef6fd..451f12727d 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -2016,27 +2016,9 @@ t8_forest_element_is_leaf_or_ghost (const t8_forest_t forest, const t8_element_t T8_ASSERT (elements != NULL); - /* In order to find the element, we need to compute its linear id. - * To do so, we need the scheme and the level of the element. */ - const t8_scheme *scheme = t8_element_array_get_scheme (elements); - const t8_eclass_t tree_class = t8_element_array_get_tree_class (elements); - const int element_level = scheme->element_get_level (tree_class, element); - /* Compute the linear id. */ - const t8_linearidx_t element_id = scheme->element_get_linear_id (tree_class, element, element_level); - /* Search for the element. - * The search returns the largest index i, - * such that the element at position i has a smaller id than the given one. - * If no such i exists, it returns -1. */ - const t8_locidx_t search_result = t8_forest_bin_search_lower (elements, element_id, element_level); - if (search_result < 0) { - /* The element was not found. */ - return 0; - } - /* 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); - return (scheme->element_is_equal (tree_class, element, check_element)); + // Search for the element in the array, return true if it was found, + // false if not. + return t8_element_array_find (elements, element) >= 0; } /* Check if an element is owned by a specific rank */ From 22862823e20753c903a8cf05481f72e31bd3548f Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 14 May 2025 13:59:43 +0200 Subject: [PATCH 077/133] add forest_private include --- src/t8_data/t8_containers.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/t8_data/t8_containers.cxx b/src/t8_data/t8_containers.cxx index a3989ea098..fe20b2864c 100644 --- a/src/t8_data/t8_containers.cxx +++ b/src/t8_data/t8_containers.cxx @@ -27,6 +27,7 @@ #include #include #include +#include T8_EXTERN_C_BEGIN (); From bd2dba3a63943c428adb3ebb456b19460c755780 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 14 May 2025 14:16:33 +0200 Subject: [PATCH 078/133] remove duplicate scheme creation --- test/t8_forest/t8_gtest_element_is_leaf.cxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/t8_forest/t8_gtest_element_is_leaf.cxx b/test/t8_forest/t8_gtest_element_is_leaf.cxx index 57b9a8946c..8043ff892f 100644 --- a/test/t8_forest/t8_gtest_element_is_leaf.cxx +++ b/test/t8_forest/t8_gtest_element_is_leaf.cxx @@ -86,8 +86,7 @@ class element_is_leaf_or_ghost: public testing::TestWithParam Date: Fri, 16 May 2025 14:09:37 +0200 Subject: [PATCH 079/133] change ifdef to if --- src/t8_forest/t8_forest_iterate.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index d67856efdc..f1e66b1153 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -109,7 +109,7 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme // In that case, element is a leaf but elem_count is not 1. bool is_leaf = t8_forest_element_is_leaf_or_ghost (forest, element, local_or_ghost_tree_id, tree_is_ghost); -#ifdef T8_ENABLE_DEBUG +#if T8_ENABLE_DEBUG if (!is_leaf) { /* Check whether element has smaller level than the first leaf */ const t8_element_t *leaf = t8_element_array_index_locidx (leaf_elements, 0); From 6c8241be363d502434cf6b47a00fdbc7587877ab Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 5 Jun 2025 12:33:16 +0200 Subject: [PATCH 080/133] update element/leaf function calls --- src/t8_forest/t8_forest.cxx | 20 ++++++++++---------- test/t8_forest/t8_gtest_face_neighbors.cxx | 11 ++++++----- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index c544d3c4c1..7f3880eab2 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1707,7 +1707,7 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, t8_debugf ("Adding new face neighbor (leaf index %i) with dual face %i.\n", tree_leaf_index, face); lfn_data->dual_faces.push_back (face); // Compute the index of the element - const t8_locidx_t num_local_elements = t8_forest_get_local_num_elements (forest); + const t8_locidx_t num_local_elements = t8_forest_get_local_num_leaf_elements (forest); const t8_locidx_t tree_offset = !is_ghost_tree ? t8_forest_get_tree_element_offset (forest, ltreeid) : t8_forest_ghost_get_tree_element_offset (forest, adjusted_tree_id) + num_local_elements; @@ -1716,10 +1716,10 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, // Add the pointer to the current element const t8_element_t *&pnew_element = lfn_data->neighbors.emplace_back (); if (!is_ghost_tree) { - pnew_element = t8_forest_get_element_in_tree (forest, ltreeid, tree_leaf_index); + pnew_element = t8_forest_get_leaf_element_in_tree (forest, ltreeid, tree_leaf_index); } else { - pnew_element = t8_forest_ghost_get_element (forest, adjusted_tree_id, tree_leaf_index); + pnew_element = t8_forest_ghost_get_leaf_element (forest, adjusted_tree_id, tree_leaf_index); } return 1; } @@ -1868,7 +1868,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_debugf ("Adding local tree to search.\n"); if (0 <= local_neighbor_tree) { // The neighbor tree is a local tree and hence there may be local neighbor elements. - const t8_element_array_t *tree_leaves = t8_forest_tree_get_leaves (forest, local_neighbor_tree); + const t8_element_array_t *tree_leaves = t8_forest_tree_get_leaf_elements (forest, local_neighbor_tree); if (tree_leaves != nullptr) { neighbor_leaf_array *leaf_array = new neighbor_leaf_array (tree_leaves, false); leaf_arrays.push_back (leaf_array); @@ -1886,7 +1886,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // be ghost elements. // We add the ghost elements of that tree to our search array. const t8_element_array_t *ghost_leaves - = t8_forest_ghost_get_tree_elements (forest, local_neighbor_ghost_treeid); + = t8_forest_ghost_get_tree_leaf_elements (forest, local_neighbor_ghost_treeid); if (ghost_leaves != nullptr) { neighbor_leaf_array *leaf_array = new neighbor_leaf_array (ghost_leaves, true); leaf_arrays.push_back (leaf_array); @@ -2302,14 +2302,14 @@ for (ineigh = 0; ineigh < num_children_at_face; ineigh++) { /* We check whether the element is really the element at this local id */ { t8_element_t *check_element; - check_element = t8_forest_ghost_get_element (forest, lghost_treeid, element_indices[ineigh]); + check_element = t8_forest_ghost_get_leaf_element (forest, lghost_treeid, element_indices[ineigh]); T8_ASSERT (neigh_scheme->t8_element_equal (check_element, neighbor_leaves[ineigh])); } #endif /* Add the element offset of previous ghosts to this index */ element_indices[ineigh] += t8_forest_ghost_get_tree_element_offset (forest, lghost_treeid); /* Add the number of all local elements to this index */ - element_indices[ineigh] += t8_forest_get_local_num_elements (forest); + element_indices[ineigh] += t8_forest_get_local_num_leaf_elements (forest); } } /* End for loop over neighbor leaves */ T8_FREE (owners); @@ -2335,7 +2335,7 @@ t8_locidx_t t8_forest_same_level_leaf_face_neighbor_index (t8_forest_t forest, const t8_locidx_t element_index, const int face_index, const t8_gloidx_t global_treeid, int *dual_face) { - const t8_locidx_t num_local_elements = t8_forest_get_local_num_elements (forest); + const t8_locidx_t num_local_elements = t8_forest_get_local_num_leaf_elements (forest); #if T8_ENABLE_DEBUG const t8_locidx_t num_ghosts = t8_forest_get_num_ghosts (forest); T8_ASSERT (0 <= element_index && element_index < num_local_elements + num_ghosts); @@ -2348,13 +2348,13 @@ t8_forest_same_level_leaf_face_neighbor_index (t8_forest_t forest, const t8_loci if (is_local) { local_tree = t8_forest_get_local_id (forest, global_treeid); element_index_in_tree = element_index - t8_forest_get_tree_element_offset (forest, local_tree); - element = t8_forest_get_element_in_tree (forest, local_tree, element_index_in_tree); + element = t8_forest_get_leaf_element_in_tree (forest, local_tree, element_index_in_tree); } else { local_tree = t8_forest_ghost_get_ghost_treeid (forest, global_treeid); const t8_locidx_t ghost_offset_in_tree = t8_forest_ghost_get_tree_element_offset (forest, local_tree); element_index_in_tree = element_index - num_local_elements - ghost_offset_in_tree; - element = t8_forest_ghost_get_element (forest, local_tree, element_index_in_tree); + element = t8_forest_ghost_get_leaf_element (forest, local_tree, element_index_in_tree); local_tree += t8_forest_get_num_local_trees (forest); } diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index 2cf47b5ec5..b0e1e70fe7 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -114,15 +114,16 @@ TEST_P (forest_face_neighbors, test_face_neighbors) } const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); const t8_locidx_t num_ghost_trees = t8_forest_get_num_ghost_trees (forest); - const t8_locidx_t num_local_elements = t8_forest_get_local_num_elements (forest); + const t8_locidx_t num_local_elements = t8_forest_get_local_num_leaf_elements (forest); t8_locidx_t ielement_index = 0; for (t8_locidx_t itree = 0; itree < num_local_trees + num_ghost_trees; itree++) { const t8_gloidx_t gtree_id = t8_forest_global_tree_id (forest, itree); const bool is_ghost = itree >= num_local_trees; const t8_locidx_t ghost_tree_id = itree - num_local_trees; /* Get the leaf element array */ - const t8_element_array_t *leaf_elements = !is_ghost ? t8_forest_get_tree_element_array (forest, itree) - : t8_forest_ghost_get_tree_elements (forest, ghost_tree_id); + const t8_element_array_t *leaf_elements = !is_ghost + ? t8_forest_get_tree_leaf_element_array (forest, itree) + : t8_forest_ghost_get_tree_leaf_elements (forest, ghost_tree_id); const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, itree); const t8_scheme *scheme = t8_forest_get_scheme (forest); const t8_locidx_t num_leaves = t8_element_array_get_count (leaf_elements); @@ -254,7 +255,7 @@ TEST_P (forest_face_neighbors, test_face_neighbors) // Check that neighbor index correctly yields neighbor element. if (neigh_index < num_local_elements) { const t8_element_t *neighbor_from_index - = t8_forest_get_element (forest, neigh_index, &neigh_ltreeid_from_index); + = t8_forest_get_leaf_element (forest, neigh_index, &neigh_ltreeid_from_index); EXPECT_TRUE (scheme->element_is_equal (neigh_class, neighbor_from_index, neighbor)); } // TODO: Check neighbor index if the element is a ghost element @@ -322,7 +323,7 @@ TEST_P (forest_face_neighbors, test_face_neighbors) EXPECT_GE (element_index, 0); if (element_index < num_local_elements) { - const t8_element_t *element_from_index = t8_forest_get_element (forest, element_index, NULL); + const t8_element_t *element_from_index = t8_forest_get_leaf_element (forest, element_index, NULL); EXPECT_EQ (element_from_index, element) << "Neighbor neighbor element at index " << element_index << " is not original element."; } From 504ade787678dbd5d7c59af378a8f4889f93fa48 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 5 Jun 2025 12:47:02 +0200 Subject: [PATCH 081/133] repair merge, is_leaf test reduce cmeshes --- test/t8_forest/t8_gtest_element_is_leaf.cxx | 23 ++++++--------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/test/t8_forest/t8_gtest_element_is_leaf.cxx b/test/t8_forest/t8_gtest_element_is_leaf.cxx index d5b329f42d..1ebcb0ec9c 100644 --- a/test/t8_forest/t8_gtest_element_is_leaf.cxx +++ b/test/t8_forest/t8_gtest_element_is_leaf.cxx @@ -45,7 +45,7 @@ #define T8_IS_LEAF_MAX_LVL 4 #endif -class element_is_leaf_or_ghost: public testing::TestWithParam> { +class element_is_leaf_or_ghost: public testing::TestWithParam, int>> { protected: void SetUp () override @@ -55,13 +55,7 @@ class element_is_leaf_or_ghost: public testing::TestWithParam (std::get<0> (GetParam ())); const int level = std::get<1> (GetParam ()); - t8_cmesh_t cmesh = std::get<2> (GetParam ())->cmesh_create (); - if (t8_cmesh_is_empty (cmesh)) { - /* forest_commit does not support empty cmeshes, we skip this case */ - scheme->unref (); - t8_cmesh_unref (&cmesh); - GTEST_SKIP (); - } + t8_cmesh_t cmesh = t8_cmesh_new_from_class (tree_class, sc_MPI_COMM_WORLD); forest = t8_forest_new_uniform (cmesh, scheme, level, 1, sc_MPI_COMM_WORLD); t8_forest_ref (forest); @@ -173,7 +167,7 @@ t8_test_element_is_ghost_for_forest (t8_forest_t forest) const t8_locidx_t num_ghost_trees = t8_forest_get_num_ghost_trees (forest); const t8_scheme *scheme = t8_forest_get_scheme (forest); for (t8_locidx_t ighost_tree = 0; ighost_tree < num_ghost_trees; ++ighost_tree) { - const t8_locidx_t num_elements_in_tree = t8_forest_ghost_tree_num_elements (forest, ighost_tree); + const t8_locidx_t num_elements_in_tree = t8_forest_ghost_tree_num_leaf_elements (forest, ighost_tree); const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, ighost_tree); /* Allocate memory to build a non-ghost element. */ t8_element_t *not_ghost; @@ -183,7 +177,7 @@ t8_test_element_is_ghost_for_forest (t8_forest_t forest) * build its parent and its first child (if they exist), and verify * that t8_forest_element_is_leaf and t8_forest_element_is_leaf_or_ghost returns false. */ for (t8_locidx_t ielement = 0; ielement < num_elements_in_tree; ++ielement) { - const t8_element_t *ghost_element = t8_forest_ghost_get_element (forest, ighost_tree, ielement); + const t8_element_t *ghost_element = t8_forest_ghost_get_leaf_element (forest, ighost_tree, ielement); EXPECT_TRUE (t8_forest_element_is_ghost (forest, ghost_element, ighost_tree)); EXPECT_TRUE (t8_forest_element_is_leaf_or_ghost (forest, ghost_element, ighost_tree, 1)); /* Compute parent and first child of element and check that they are not in the tree */ @@ -233,14 +227,9 @@ auto pretty_print_eclass_scheme_and_level return scheme + "_" + eclass + level; }; -INSTANTIATE_TEST_SUITE_P (t8_gtest_element_is_leaf, element_is_leaf, - testing::Combine (AllSchemes, testing::Range (0, T8_IS_LEAF_MAX_LVL)), - pretty_print_eclass_scheme_and_level); - INSTANTIATE_TEST_SUITE_P (t8_gtest_element_is_leaf_or_ghost_hybrid, element_is_leaf_or_ghost_hybrid, AllSchemeCollections, print_scheme); INSTANTIATE_TEST_SUITE_P (t8_gtest_element_is_leaf_or_ghost, element_is_leaf_or_ghost, - testing::Combine (AllSchemeCollections, testing::Range (0, T8_IS_LEAF_MAX_LVL), - AllCmeshsParam), - pretty_print_level_and_cmesh_params); + testing::Combine (AllSchemes, testing::Range (0, T8_IS_LEAF_MAX_LVL)), + pretty_print_eclass_scheme_and_level); From b78f5eaa20f57b35aa35279e3ef1d4aebe0e1378 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 12:20:58 +0200 Subject: [PATCH 082/133] improve comments --- src/t8_forest/t8_forest.cxx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 57b856480b..d74f8c5891 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1901,7 +1901,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons /* There may occur situations where a neighbor exists but the search returns an element * that is not an ancestor of either first_face_leaf nor last_face_leaf. - * This might happen when computer face neighbors of ghosts. Consider the following situation + * This might happen when computing face neighbors of ghosts. Consider the following situation * * __ __ * | | | @@ -1909,7 +1909,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons * |G |_| p_0 The bottom elements belong to p_0 * |__| * - * If we compute the neighbor of ghost element G across the right face (face number 1) + * If p_1 computes the neighbor of ghost element G across the right face (face number 1) * we should get the level 2 element in the bottom right. * However, the search for the first face desc will return G, since the element array does not * contain any elements that are descendants of the first face desc and have a smaller or equal linear @@ -1921,9 +1921,11 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons * - if not ancestor of nca (including not found) * - add 1 to index (use 0 if element was not found) * - if not ancestor -> not found - * - if one is found then both must have been found - * - build nca - * - iterate faces + * - If for both no anc/desc is found, then there are no neighbors. + * - If only for one an anc/desc is found, continue the face iteration with the found element. + * - If for both an anc/desc is found, then: + * - build nca + * - iterate faces (nca) * */ if (t8_forest_elements_are_ancestor (scheme, neigh_class, same_level_neighbor, nca_of_face_desc)) { From fa49f00747e04b8840915f932350cdc338168374 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 12:21:24 +0200 Subject: [PATCH 083/133] Change TODO comment to updated strategy - i am very sure that this will work now --- src/t8_forest/t8_forest.cxx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index d74f8c5891..a2be94a5e3 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1721,14 +1721,15 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons { /* We compute all face neighbor leaf elements of E via the following strategy: * - Compute the same level face neighbor N - * - Compute the first and last face descendants FD, LD, of N * - The neighbor tree could be a local tree or ghost (or both), * for each variant get the leaf array of the neighbor tree and search in it: - * - Search for FD and LD in the leaf array and get indices of the nearest matching leaf elements. - * - For the matching leaf elements, compute their nca (nearest common ancestor) - that is the finest element that contains both of them. - * This nca will contain all face neighbors (in the local tree or ghost tree). - * - Use the nca as a starting point for a recursive search across its corresponding face. - * Each element found on the face is a matching leaf face neighbor of E. + * - Search for the first leaf element L overlapping with N. + * If it exists, it is either an ancestor or descendant of N (or N itself which is included in both definitions). + * If it does not exist, there are not leaf face neighbors in this tree. + * - If L is an ancestor of N (i.e. Level(L) <= Level(N)) then L is the only face neighbor. + * - Otherwise (Level(L) > Level (N)) we use a recursive face search across N's neighbor face, + * adding all leaf elements on the face to the face neighbors. + * This search will require L (more precise its position in the tree leaf array) as input. **/ T8_ASSERT (t8_forest_is_committed (forest)); From 357331c0e4ac060cac2404a1626a08e288a6ae1b Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 12:41:48 +0200 Subject: [PATCH 084/133] update lfn computation to new strategy via same level neighbor. Now only ancestor search missing --- src/t8_forest/t8_forest.cxx | 102 ++++++------------------------------ 1 file changed, 17 insertions(+), 85 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index a2be94a5e3..bc722a8d35 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1768,9 +1768,12 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // Compute the same level face neighbor t8_element_t *same_level_neighbor; scheme->element_new (neigh_class, 1, &same_level_neighbor); - int neigh_face; + int same_level_neighbor_dual_face; const t8_gloidx_t computed_gneigh_tree = t8_forest_element_face_neighbor ( - forest, ltreeid, leaf_or_ghost, same_level_neighbor, neigh_class, face, &neigh_face); + forest, ltreeid, leaf_or_ghost, same_level_neighbor, neigh_class, face, &same_level_neighbor_dual_face); + const int element_level = scheme->element_get_level (eclass, leaf_or_ghost); + const t8_locidx_t same_level_neighbor_index + = scheme->element_get_linear_id (neigh_class, same_level_neighbor, element_level); if (computed_gneigh_tree < 0) { // There is no face neighbor across this face @@ -1787,24 +1790,6 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons return; } - const int maxlevel = scheme->get_maxlevel (neigh_class); - - // Compute the first and last face descendant of the neighbor to compute their ids - t8_element_t *first_face_desc; - t8_element_t *last_face_desc; - scheme->element_new (neigh_class, 1, &first_face_desc); - scheme->element_new (neigh_class, 1, &last_face_desc); - scheme->element_get_first_descendant_face (neigh_class, same_level_neighbor, neigh_face, first_face_desc, maxlevel); - scheme->element_get_last_descendant_face (neigh_class, same_level_neighbor, neigh_face, last_face_desc, maxlevel); - const t8_linearidx_t first_face_desc_id = scheme->element_get_linear_id (neigh_class, first_face_desc, maxlevel); - const t8_linearidx_t last_face_desc_id = scheme->element_get_linear_id (neigh_class, last_face_desc, maxlevel); - // same level neighbor, first and last face desc not needed anymore, free memory - scheme->element_destroy (neigh_class, 1, &first_face_desc); - scheme->element_destroy (neigh_class, 1, &last_face_desc); - // Allocate memory for the nca of first and last face desc - t8_element_t *nca_of_face_desc; - scheme->element_new (neigh_class, 1, &nca_of_face_desc); - // The neighbor leaves could be distributed across a local tree and a ghost // tree. We thus possibly need to search in two different arrays. // We store these in a vector and iterate over the entries. @@ -1822,7 +1807,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons int face_owners_lower_bound = 0; int face_owners_upper_bound = forest->mpisize - 1; const int mpirank = forest->mpirank; - t8_forest_element_owners_at_face_bounds (forest, computed_gneigh_tree, same_level_neighbor, neigh_class, neigh_face, + t8_forest_element_owners_at_face_bounds (forest, computed_gneigh_tree, same_level_neighbor, neigh_class, same_level_neighbor_dual_face, &face_owners_lower_bound, &face_owners_upper_bound); std::vector leaf_arrays; @@ -1872,6 +1857,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons // Not setting them to NULL but keeping them possibly uninitialized, will // call REALLOC on uninitialized memory and result in memory errors. if (pneighbor_leaves != NULL) { + /* Only set *pneighbor_leaves if a computation is desired. */ *pneighbor_leaves = NULL; } @@ -1881,73 +1867,22 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons auto &tree_leaves = leaf_array->first; const bool leaf_array_is_ghost = leaf_array->second; T8_ASSERT (tree_leaves != NULL); - const t8_locidx_t first_desc_search = t8_forest_bin_search_lower (tree_leaves, first_face_desc_id, maxlevel); - const t8_locidx_t last_desc_search = t8_forest_bin_search_lower (tree_leaves, last_face_desc_id, maxlevel); - if (first_desc_search >= 0 || last_desc_search >= 0) { + // TODO: Use anc/desc search here + const t8_locidx_t first_leaf_index = t8_forest_bin_search_lower (tree_leaves, same_level_neighbor_index, element_level); + if (first_leaf_index >= 0) { // There may be face neighbors in this leaf array. - // The first descendant may not be in the leaf array, we then - // start with the first leaf. - const t8_locidx_t first_desc_index = SC_MAX (0, first_desc_search); - const t8_locidx_t last_desc_index = last_desc_search; - - // Get the actual leaf elements that contain the first and last face desc - const t8_element_t *first_face_leaf = t8_element_array_index_locidx (tree_leaves, first_desc_index); - const t8_element_t *last_face_leaf = t8_element_array_index_locidx (tree_leaves, last_desc_index); - // Compute their nearest common ancestor - scheme->element_get_nca (neigh_class, first_face_leaf, last_face_leaf, nca_of_face_desc); - - /* Check whether the computed nca element does contain or is contained by the same level - * face neighbor. If not, then there are no face neighbors and we do not continue. - * Otherwise, face neighbors exist and are descendants of nca_of_face_desc. */ - - /* There may occur situations where a neighbor exists but the search returns an element - * that is not an ancestor of either first_face_leaf nor last_face_leaf. - * This might happen when computing face neighbors of ghosts. Consider the following situation - * - * __ __ - * | | | - * |__|__| p_1 The top elements belong to p_1 - * |G |_| p_0 The bottom elements belong to p_0 - * |__| - * - * If p_1 computes the neighbor of ghost element G across the right face (face number 1) - * we should get the level 2 element in the bottom right. - * However, the search for the first face desc will return G, since the element array does not - * contain any elements that are descendants of the first face desc and have a smaller or equal linear - * index. - * - * TODO: Try this approach - * - build first and last desc from their id - * - do a "find ancestor/desc search" - * - if not ancestor of nca (including not found) - * - add 1 to index (use 0 if element was not found) - * - if not ancestor -> not found - * - If for both no anc/desc is found, then there are no neighbors. - * - If only for one an anc/desc is found, continue the face iteration with the found element. - * - If for both an anc/desc is found, then: - * - build nca - * - iterate faces (nca) - * */ - if (t8_forest_elements_are_ancestor (scheme, neigh_class, same_level_neighbor, nca_of_face_desc)) { - const int face_of_nca = neigh_face; - // TODO: Need to implement element function to compute face id of nca face. - // Input: Element A and face f, Element B that is ancestor or successor of A, and - // shares face f (f is a subface of a face of B or B has a subface of f) - // Output: The face id of the corresponding ancestor/descendant face of B - // - // Currently we hardcode this algorithm for quads. In that case the face id of B is always f. - const bool scheme_is_default_quad_hex - = t8_eclass_scheme_is_default (scheme, eclass) && (eclass == T8_ECLASS_QUAD || eclass == T8_ECLASS_HEX); - SC_CHECK_ABORT (scheme_is_default_quad_hex, - "Computing leaf face neighbors currently only works for default quad or hex schemes."); - // Restrict search array to the leaves from first to last face desc + +#if 0 + // TODO: We can optimize the runtime by restricting the search array to the + // descendants of the same level neighbor. t8_element_array_t face_leaves; const size_t face_leaf_count = last_desc_index - first_desc_index + 1; T8_ASSERT (face_leaf_count > 0); t8_debugf ("Starting search with element indices %i to %i (including).\n", first_desc_index, last_desc_index); t8_element_array_init_view (&face_leaves, tree_leaves, first_desc_index, face_leaf_count); +#endif // Iterate over all leaves at the face and collect them as neighbors. const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); // Compute the local or ghost tree id depending on whether this leaf array corresponds to a local @@ -1955,8 +1890,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_locidx_t face_iterate_tree_id = leaf_array_is_ghost ? t8_forest_ghost_get_ghost_treeid (forest, computed_gneigh_tree) + num_local_trees : local_neighbor_tree; - t8_forest_iterate_faces (forest, face_iterate_tree_id, nca_of_face_desc, face_of_nca, &face_leaves, - first_desc_index, t8_forest_leaf_face_neighbors_iterate, &user_data); + t8_forest_iterate_faces (forest, face_iterate_tree_id, same_level_neighbor, same_level_neighbor_dual_face, tree_leaves, + first_leaf_index, t8_forest_leaf_face_neighbors_iterate, &user_data); // Output of iterate_faces: // Array of indices in tree_leaves of all the face neighbor elements // Assign pneighbor_leaves @@ -2005,7 +1940,6 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons memcpy (*dual_faces + *num_neighbors, user_data.dual_faces.data () + *num_neighbors, num_neighbors_current_tree * sizeof (int)); *num_neighbors = total_num_neighbors; - } } // clean up memory allocated with new delete leaf_array; @@ -2030,8 +1964,6 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons *gneigh_tree = computed_gneigh_tree; } - // clean-up - scheme->element_destroy (eclass, 1, &nca_of_face_desc); #if 0 t8_locidx_t lneigh_treeid = -1; t8_locidx_t lghost_treeid = -1, *element_indices, element_index; From 52eafd4584f2bc1309fdae13fba3ad2e06482b03 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 12:42:32 +0200 Subject: [PATCH 085/133] indent --- src/t8_forest/t8_forest.cxx | 122 ++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index bc722a8d35..ea2587abc5 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1807,8 +1807,9 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons int face_owners_lower_bound = 0; int face_owners_upper_bound = forest->mpisize - 1; const int mpirank = forest->mpirank; - t8_forest_element_owners_at_face_bounds (forest, computed_gneigh_tree, same_level_neighbor, neigh_class, same_level_neighbor_dual_face, - &face_owners_lower_bound, &face_owners_upper_bound); + t8_forest_element_owners_at_face_bounds (forest, computed_gneigh_tree, same_level_neighbor, neigh_class, + same_level_neighbor_dual_face, &face_owners_lower_bound, + &face_owners_upper_bound); std::vector leaf_arrays; @@ -1868,12 +1869,11 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const bool leaf_array_is_ghost = leaf_array->second; T8_ASSERT (tree_leaves != NULL); // TODO: Use anc/desc search here - const t8_locidx_t first_leaf_index = t8_forest_bin_search_lower (tree_leaves, same_level_neighbor_index, element_level); + const t8_locidx_t first_leaf_index + = t8_forest_bin_search_lower (tree_leaves, same_level_neighbor_index, element_level); if (first_leaf_index >= 0) { // There may be face neighbors in this leaf array. - - #if 0 // TODO: We can optimize the runtime by restricting the search array to the // descendants of the same level neighbor. @@ -1883,63 +1883,63 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_debugf ("Starting search with element indices %i to %i (including).\n", first_desc_index, last_desc_index); t8_element_array_init_view (&face_leaves, tree_leaves, first_desc_index, face_leaf_count); #endif - // Iterate over all leaves at the face and collect them as neighbors. - const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); - // Compute the local or ghost tree id depending on whether this leaf array corresponds to a local - // tree or ghost tree. - const t8_locidx_t face_iterate_tree_id - = leaf_array_is_ghost ? t8_forest_ghost_get_ghost_treeid (forest, computed_gneigh_tree) + num_local_trees - : local_neighbor_tree; - t8_forest_iterate_faces (forest, face_iterate_tree_id, same_level_neighbor, same_level_neighbor_dual_face, tree_leaves, - first_leaf_index, t8_forest_leaf_face_neighbors_iterate, &user_data); - // Output of iterate_faces: - // Array of indices in tree_leaves of all the face neighbor elements - // Assign pneighbor_leaves - // Assign dual_faces - // Assign pelement_indices - // (all as growing std::vectors, resp t8_element_array) - - // - // After the iteration is finished we collected all - // neighbor data. - // TODO: Since there is no other way, we copy them from the vectors. - // This should be improved in the future to get around the copy. - // Indeed it would be more beneficial to just return const pointers to the actual internal leaves. - - // num_neighbors counts the already inserted neighbors before this tree - // num_neighbors_current_tree counts the neighbors added in this tree - // total_num_neighbors temporarily counts all inserted neighbors, including this tree - const int num_neighbors_current_tree = user_data.neighbors.size (); - const int total_num_neighbors = *num_neighbors + num_neighbors_current_tree; - t8_debugf ("Found %i neighbors in tree. Adding up to %i total neighbors.\n", num_neighbors_current_tree, - total_num_neighbors); - // Copy neighbor element pointers - if (pneighbor_leaves != NULL) { - // Note element_destroy call after this function on *pneighbor_leaves - // is compatible with using T8_REALLOC on *pneighbor_leaves. - // REALLOC moving the storage of the pointers. The pointers store the element storage. - // So the element storage allocated by t8_element_new is not affected by the call to REALLOC. - *pneighbor_leaves = T8_REALLOC (*pneighbor_leaves, t8_element_t *, total_num_neighbors); - scheme->element_new (eclass, num_neighbors_current_tree, *pneighbor_leaves + *num_neighbors); - T8_ASSERT (*pneighbor_leaves != NULL); - // Call element copy for each element - for (t8_locidx_t ielem = 0; ielem < num_neighbors_current_tree; ++ielem) { - t8_element_t *new_element = (*pneighbor_leaves)[ielem]; - const t8_element_t *forest_leaf = user_data.neighbors.data ()[*num_neighbors + ielem]; - scheme->element_copy (eclass, forest_leaf, new_element); - } + // Iterate over all leaves at the face and collect them as neighbors. + const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); + // Compute the local or ghost tree id depending on whether this leaf array corresponds to a local + // tree or ghost tree. + const t8_locidx_t face_iterate_tree_id + = leaf_array_is_ghost ? t8_forest_ghost_get_ghost_treeid (forest, computed_gneigh_tree) + num_local_trees + : local_neighbor_tree; + t8_forest_iterate_faces (forest, face_iterate_tree_id, same_level_neighbor, same_level_neighbor_dual_face, + tree_leaves, first_leaf_index, t8_forest_leaf_face_neighbors_iterate, &user_data); + // Output of iterate_faces: + // Array of indices in tree_leaves of all the face neighbor elements + // Assign pneighbor_leaves + // Assign dual_faces + // Assign pelement_indices + // (all as growing std::vectors, resp t8_element_array) + + // + // After the iteration is finished we collected all + // neighbor data. + // TODO: Since there is no other way, we copy them from the vectors. + // This should be improved in the future to get around the copy. + // Indeed it would be more beneficial to just return const pointers to the actual internal leaves. + + // num_neighbors counts the already inserted neighbors before this tree + // num_neighbors_current_tree counts the neighbors added in this tree + // total_num_neighbors temporarily counts all inserted neighbors, including this tree + const int num_neighbors_current_tree = user_data.neighbors.size (); + const int total_num_neighbors = *num_neighbors + num_neighbors_current_tree; + t8_debugf ("Found %i neighbors in tree. Adding up to %i total neighbors.\n", num_neighbors_current_tree, + total_num_neighbors); + // Copy neighbor element pointers + if (pneighbor_leaves != NULL) { + // Note element_destroy call after this function on *pneighbor_leaves + // is compatible with using T8_REALLOC on *pneighbor_leaves. + // REALLOC moving the storage of the pointers. The pointers store the element storage. + // So the element storage allocated by t8_element_new is not affected by the call to REALLOC. + *pneighbor_leaves = T8_REALLOC (*pneighbor_leaves, t8_element_t *, total_num_neighbors); + scheme->element_new (eclass, num_neighbors_current_tree, *pneighbor_leaves + *num_neighbors); + T8_ASSERT (*pneighbor_leaves != NULL); + // Call element copy for each element + for (t8_locidx_t ielem = 0; ielem < num_neighbors_current_tree; ++ielem) { + t8_element_t *new_element = (*pneighbor_leaves)[ielem]; + const t8_element_t *forest_leaf = user_data.neighbors.data ()[*num_neighbors + ielem]; + scheme->element_copy (eclass, forest_leaf, new_element); } - // Copy element indices - *pelement_indices = T8_REALLOC (*pelement_indices, t8_locidx_t, total_num_neighbors); - T8_ASSERT (*pelement_indices != NULL); - memcpy (*pelement_indices + *num_neighbors, user_data.element_indices.data () + *num_neighbors, - num_neighbors_current_tree * sizeof (t8_locidx_t)); - // Copy dual face - *dual_faces = T8_REALLOC (*dual_faces, int, total_num_neighbors); - T8_ASSERT (*dual_faces != NULL); - memcpy (*dual_faces + *num_neighbors, user_data.dual_faces.data () + *num_neighbors, - num_neighbors_current_tree * sizeof (int)); - *num_neighbors = total_num_neighbors; + } + // Copy element indices + *pelement_indices = T8_REALLOC (*pelement_indices, t8_locidx_t, total_num_neighbors); + T8_ASSERT (*pelement_indices != NULL); + memcpy (*pelement_indices + *num_neighbors, user_data.element_indices.data () + *num_neighbors, + num_neighbors_current_tree * sizeof (t8_locidx_t)); + // Copy dual face + *dual_faces = T8_REALLOC (*dual_faces, int, total_num_neighbors); + T8_ASSERT (*dual_faces != NULL); + memcpy (*dual_faces + *num_neighbors, user_data.dual_faces.data () + *num_neighbors, + num_neighbors_current_tree * sizeof (int)); + *num_neighbors = total_num_neighbors; } // clean up memory allocated with new delete leaf_array; From 3943b653cfc54de347f52b21e6f3e61707443cb6 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 12:43:04 +0200 Subject: [PATCH 086/133] Change parameter of iterate_faces to const*const --- src/t8_forest/t8_forest.cxx | 2 +- src/t8_forest/t8_forest_iterate.cxx | 2 +- src/t8_forest/t8_forest_iterate.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index ea2587abc5..2cbb33c54c 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1647,7 +1647,7 @@ struct t8_lfn_user_data static int t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, - int is_leaf, const t8_element_array_t *leaf_elements, + int is_leaf, const t8_element_array_t *const leaf_elements, t8_locidx_t tree_leaf_index, void *user_data) { // Output of iterate_faces: diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index 8eddd2c670..0e937b7d56 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -79,7 +79,7 @@ t8_forest_split_array (const t8_element_t *element, const t8_element_array_t *le void t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, - const t8_element_array_t *leaf_elements, t8_locidx_t tree_lindex_of_first_leaf, + const t8_element_array_t *const leaf_elements, t8_locidx_t tree_lindex_of_first_leaf, t8_forest_iterate_face_fn callback, void *user_data) { diff --git a/src/t8_forest/t8_forest_iterate.h b/src/t8_forest/t8_forest_iterate.h index 047dc460a1..f10bf92b8e 100644 --- a/src/t8_forest/t8_forest_iterate.h +++ b/src/t8_forest/t8_forest_iterate.h @@ -154,7 +154,7 @@ t8_forest_split_array (const t8_element_t *element, const t8_element_array_t *le * If it returns false, the current element is not traversed further */ void t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, - const t8_element_array_t *leaf_elements, t8_locidx_t tree_lindex_of_first_leaf, + const t8_element_array_t *const leaf_elements, t8_locidx_t tree_lindex_of_first_leaf, t8_forest_iterate_face_fn callback, void *user_data); /* Perform a top-down search of the forest, executing a callback on each From 3223c6301a6f479fc2bcc45c637e3d3eccb891f5 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 15:13:43 +0200 Subject: [PATCH 087/133] Add a comment to bin_search --- src/t8_forest/t8_forest_private.cxx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/t8_forest/t8_forest_private.cxx b/src/t8_forest/t8_forest_private.cxx index ce3e91879d..66fe4ebcd6 100644 --- a/src/t8_forest/t8_forest_private.cxx +++ b/src/t8_forest/t8_forest_private.cxx @@ -57,6 +57,14 @@ t8_forest_get_tree_leaf_element_array_mutable (const t8_forest_t forest, t8_loci return (t8_element_array_t *) t8_forest_get_tree_leaf_element_array (forest, ltreeid); } +/* TODO: does the search fail when element_level is smaller then levels in the array? + For example entering the search with the root element or a level 1 element + and the array contains much finer elements. + Will it still return the largest index, or just any index? + */ +/* TODO: This may be implementable with std::partition_point, which would yield an easier implementation. + Need to check. + */ /** \brief Search for a linear element id (at level element_level) in a sorted array of * elements. If the element does not exist, return the largest index i * such that the element at position i has a smaller id than the given one. From a3422fcf58ed2b65db600c8ee35fde554bb19966 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 15:14:32 +0200 Subject: [PATCH 088/133] Implement t8_forest_bin_search_upper --- src/t8_forest/t8_forest_private.cxx | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/t8_forest/t8_forest_private.cxx b/src/t8_forest/t8_forest_private.cxx index 66fe4ebcd6..682f01b4de 100644 --- a/src/t8_forest/t8_forest_private.cxx +++ b/src/t8_forest/t8_forest_private.cxx @@ -101,4 +101,49 @@ t8_forest_bin_search_lower (const t8_element_array_t *elements, const t8_lineari return elem_iter.get_current_index () - 1; } +/** \brief Search for a linear element id (at level element_level) in a sorted array of + * elements. If the element does not exist, return the smallest index i + * such that the element at position i has a larger id than the given one. + * If no such i exists, return -1. + */ +t8_locidx_t +t8_forest_bin_search_upper (const t8_element_array_t *elements, const t8_linearidx_t element_id, + const int element_level) +{ + const t8_scheme *scheme = t8_element_array_get_scheme (elements); + const t8_eclass_t tree_class = t8_element_array_get_tree_class (elements); + /* At first, we check whether any element has smaller id than the + * given one. */ + const t8_locidx_t num_elements = t8_element_array_get_count (elements); + if (num_elements == 0) { + /* This array is empty. */ + return -1; + } + const t8_element_t *query = t8_element_array_index_int (elements, num_elements - 1); + const t8_linearidx_t query_id = scheme->element_get_linear_id (tree_class, query, element_level); + if (query_id < element_id) { + /* No element has id larger than the given one. */ + return -1; + } + + /* We search for the first element E in the array, where element_id > ID(E) is false. + Thus, E is the first element with ID(E) >= element_id . */ + auto elem_iter + = std::lower_bound (t8_element_array_begin (elements), t8_element_array_end (elements), element_id, + [&element_level, &scheme, &tree_class] (const t8_linearidx_t element_id_, + const t8_element_array_iterator::value_type &elem_ptr) { + return (element_id_ > scheme->element_get_linear_id (tree_class, elem_ptr, element_level)); + }); + + /* In case we do not find an element that is greater than the given element_id, the binary search returns + * the end-iterator of the element array. */ + if (elem_iter == t8_element_array_end (elements)) { + // No element was found. + return -1; + } + else { + return elem_iter.get_current_index () + } +} + T8_EXTERN_C_END (); From 5584c0f00bc4f455f8c70aa537841d3a99b1535a Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 15:15:05 +0200 Subject: [PATCH 089/133] Implement t8_forest_element_is_ancestor --- src/t8_forest/t8_forest_private.cxx | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/t8_forest/t8_forest_private.cxx b/src/t8_forest/t8_forest_private.cxx index 682f01b4de..8f52cd6c0f 100644 --- a/src/t8_forest/t8_forest_private.cxx +++ b/src/t8_forest/t8_forest_private.cxx @@ -146,4 +146,42 @@ t8_forest_bin_search_upper (const t8_element_array_t *elements, const t8_lineari } } +/** Query whether one element is an ancestor of the other. + * An element A is ancestor of an element B if A == B or if B can + * be obtained from A via successive refinement. + * \param [in] scheme A scheme. + * \param [in] eclass An eclass. + * \param [in] element_A An element of class \a eclass in scheme \a scheme. + * \param [in] element_B An element of class \a eclass in scheme \a scheme. + * \return True if and only if \a element_A is an ancestor of \a element_B. +*/ +// TODO: Move this function to the scheme class. +static bool +t8_forest_element_is_ancestor (const t8_scheme *scheme, t8_eclass_t eclass, const t8_element_t *element_A, + const t8_element_t *element_B) +{ + /* A is ancestor of B if and only if it has smaller or equal level and + restricted to A's level, B has the same id as A. + + level(A) <= level(B) and ID(A,level(A)) == ID(B,level(B)) + */ + T8_ASSERT (scheme->element_is_valid (eclass, element_A)); + T8_ASSERT (scheme->element_is_valid (eclass, element_B)); + + const int level_A = scheme->element_get_level (element_A); + const int level_B = scheme->element_get_level (element_B); + + if (level_A > level_B) { + /* element A is finer than element B and thus cannot be + * an ancestor of B. */ + return false; + } + + const t8_locidx_t id_A = scheme->element_get_linear_id (element_A, level_A); + const t8_locidx_t id_B = scheme->element_get_linear_id (element_B, level_A); + + // If both elements have the same linear ID and level_A then A is an ancestor of B. + return id_A == id_B; +} + T8_EXTERN_C_END (); From 5d1564a0f7e278342c71035490ae796cd63b8682 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 15:15:22 +0200 Subject: [PATCH 090/133] Add t8_forest_bin_search_first_descendant_ancenstor --- src/t8_forest/t8_forest_private.cxx | 74 +++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/t8_forest/t8_forest_private.cxx b/src/t8_forest/t8_forest_private.cxx index 8f52cd6c0f..4849350fb9 100644 --- a/src/t8_forest/t8_forest_private.cxx +++ b/src/t8_forest/t8_forest_private.cxx @@ -184,4 +184,78 @@ t8_forest_element_is_ancestor (const t8_scheme *scheme, t8_eclass_t eclass, cons return id_A == id_B; } +/** \brief Search for a linear element id (at level element_level) in a sorted array of + * elements. If the element does not exist, return the first index i such that + * the element at position i is an ancestor or descendant of the element corresponding to the element id. + * If no such i exists, return -1. + */ +t8_locidx_t +t8_forest_bin_search_first_descendant_ancenstor (const t8_element_array_t *elements, const t8_element_t *element, + const t8_element_t *element_found) +{ + /* This search works as follows: + + Let E denote the element with element_id at level L. + If an ancestor or descendant of E exists in the array then they are either: + A: The search result of t8_forest_bin_search_lower + B: The search result of t8_forest_bin_search_upper + + Let ID(element,level) denote the linear id of an element at a given level. + + Case A: There is an element F in the array that is E itself or an ancestor of E (i.e. level(F) <= level(E)). + In that case + ID(E,L) >= ID(F,L) and there can be no element with id in between (since it would also be an ancestor of E). + Then F will be the search result of t8_forest_bin_search_lower + Case B: There is an element F in the array that is a descendant of E and it has the smallest index in the array of all descendants. + Then + ID(E,L) = ID(F,L) + and also + ID(E,L) = ID(D,L) for all other descendants of E. + But since F is the first it will be the search result of t8_forest_bin_search_upper. + Case C: There is no descendant or ancestor of E in the array. In both cases t8_forest_bin_search_lower and + t8_forest_bin_search_upper may find elements but the results will not be ancestors/descendants of E. + + From this, we determine the following algorithm: + + 1. Query t8_forest_bin_search_lower with N. + 2. If no element was found, or the resulting element is not an ancestor of N. + 3. Query t8_forest_bin_search_upper with N. + 3. If an element was found and it is a descendant of N, we found our element. + 4. If not, no element was found. + */ + + /* Compute the element's level and linear id. In order to do so, + * we first need the scheme and eclass. */ + const t8_scheme *scheme = t8_element_array_get_scheme (elements); + const t8_eclass eclass = t8_element_array_get_tree_class (elements); + const int element_level = scheme->element_get_level (eclass, element); + const t8_linearidx_t element_id = scheme->element_get_linear_id (eclass, element, element_level); + + const t8_locidx_t search_pos_lower = t8_forest_bin_search_lower (elements, element_id, element_level); + + /* Get the element at the current position. */ + if (search_pos_lower >= 0) { + element_found = t8_element_array_index_locidx (elements, search_pos_lower); + const bool is_ancestor = t8_forest_element_is_ancestor (scheme, eclass, element_found, element); + if (is_ancestor) { + /* The element at this position is an ancestor or descendant. */ + return search_pos_lower; + } + } + /* t8_forest_bin_search_lower did not return a result or an ancestor. */ + + const t8_locidx_t search_pos_upper = t8_forest_bin_search_upper (elements, element_id, element_level); + if (search_pos_upper >= 0) { + element_found = t8_element_array_index_locidx (elements, search_pos_upper); + const bool is_descendant = t8_forest_element_is_ancestor (scheme, eclass, element, element_found); + if (is_descendant) { + /* The element at this position is an ancestor or descendant. */ + return search_pos_upper; + } + } + // No ancestor or descendant was found + element_found = nullptr; + return -1; +} + T8_EXTERN_C_END (); From ca8989ed815f8bc053159acdba0897fa66713bd2 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 15:44:13 +0200 Subject: [PATCH 091/133] fix bin_search_upper --- src/t8_forest/t8_forest_private.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/t8_forest/t8_forest_private.cxx b/src/t8_forest/t8_forest_private.cxx index 4849350fb9..83b1ae4fff 100644 --- a/src/t8_forest/t8_forest_private.cxx +++ b/src/t8_forest/t8_forest_private.cxx @@ -130,8 +130,8 @@ t8_forest_bin_search_upper (const t8_element_array_t *elements, const t8_lineari Thus, E is the first element with ID(E) >= element_id . */ auto elem_iter = std::lower_bound (t8_element_array_begin (elements), t8_element_array_end (elements), element_id, - [&element_level, &scheme, &tree_class] (const t8_linearidx_t element_id_, - const t8_element_array_iterator::value_type &elem_ptr) { + [&element_level, &scheme, &tree_class] (const t8_element_array_iterator::value_type &elem_ptr, + const t8_linearidx_t element_id_) { return (element_id_ > scheme->element_get_linear_id (tree_class, elem_ptr, element_level)); }); @@ -142,7 +142,7 @@ t8_forest_bin_search_upper (const t8_element_array_t *elements, const t8_lineari return -1; } else { - return elem_iter.get_current_index () + return elem_iter.get_current_index (); } } From d50a3d818f5f46cc6437d6e9a057501846c73248 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 15:44:29 +0200 Subject: [PATCH 092/133] fix element_is_ancestor --- src/t8_forest/t8_forest_private.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/t8_forest/t8_forest_private.cxx b/src/t8_forest/t8_forest_private.cxx index 83b1ae4fff..0d0d2b63e8 100644 --- a/src/t8_forest/t8_forest_private.cxx +++ b/src/t8_forest/t8_forest_private.cxx @@ -168,8 +168,8 @@ t8_forest_element_is_ancestor (const t8_scheme *scheme, t8_eclass_t eclass, cons T8_ASSERT (scheme->element_is_valid (eclass, element_A)); T8_ASSERT (scheme->element_is_valid (eclass, element_B)); - const int level_A = scheme->element_get_level (element_A); - const int level_B = scheme->element_get_level (element_B); + const int level_A = scheme->element_get_level (eclass, element_A); + const int level_B = scheme->element_get_level (eclass, element_B); if (level_A > level_B) { /* element A is finer than element B and thus cannot be @@ -177,8 +177,8 @@ t8_forest_element_is_ancestor (const t8_scheme *scheme, t8_eclass_t eclass, cons return false; } - const t8_locidx_t id_A = scheme->element_get_linear_id (element_A, level_A); - const t8_locidx_t id_B = scheme->element_get_linear_id (element_B, level_A); + const t8_linearidx_t id_A = scheme->element_get_linear_id (eclass, element_A, level_A); + const t8_linearidx_t id_B = scheme->element_get_linear_id (eclass, element_B, level_A); // If both elements have the same linear ID and level_A then A is an ancestor of B. return id_A == id_B; From b700bd773d077d1228182c18d99524e62e64ca7c Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 16:15:19 +0200 Subject: [PATCH 093/133] document bin_search_lower --- src/t8_forest/t8_forest_private.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/t8_forest/t8_forest_private.h b/src/t8_forest/t8_forest_private.h index 170aa2f13a..40e0fdb750 100644 --- a/src/t8_forest/t8_forest_private.h +++ b/src/t8_forest/t8_forest_private.h @@ -196,10 +196,11 @@ t8_forest_get_tree_leaf_element_array_mutable (const t8_forest_t forest, t8_loci * elements. If the element does not exist, return the largest index i * such that the element at position i has a smaller id than the given one. * If no such i exists, return -1. - * \param [in] elements The array of elements. + * \param [in] elements An array of elements. Must be sorted according to linear id at maximum level. + * Must correspond to a valid refinement (i.e. contain no duplicate elements or elements and their descendants). * \param [in] element_id The linear id of the element to search for. * \param [in] element_level The level of the element to search for. Thus, the level at which \a element_id was computed. - * \return The index \a i of an element with the linear_id \a element_id in \a elements if it exists. + * \return The largest index \a i of an element with linear_id smaller than or equal to \a element_id in \a elements if it exists. * -1 if no such element was found in \a elements. */ t8_locidx_t From b28d548ef3a88ca4f7f60820716e9f77c1e1e645 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 16:15:41 +0200 Subject: [PATCH 094/133] document bin_search_upper --- src/t8_forest/t8_forest_private.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/t8_forest/t8_forest_private.h b/src/t8_forest/t8_forest_private.h index 40e0fdb750..8db9d41b1a 100644 --- a/src/t8_forest/t8_forest_private.h +++ b/src/t8_forest/t8_forest_private.h @@ -207,6 +207,21 @@ t8_locidx_t t8_forest_bin_search_lower (const t8_element_array_t *elements, const t8_linearidx_t element_id, const int element_level); +/** \brief Search for a linear element id (at level element_level) in a sorted array of + * elements. If the element does not exist, return the smallest index i + * such that the element at position i has a larger id than the given one. + * If no such i exists, return -1. + * \param [in] elements An array of elements. Must be sorted according to linear id at maximum level. + * Must correspond to a valid refinement (i.e. contain no duplicate elements or elements and their descendants). + * \param [in] element_id The linear id of the element to search for. + * \param [in] element_level The level of the element to search for. Thus, the level at which \a element_id was computed. + * \return The smallest index \a i of an element with linear_id larger than or equal to \a element_id in \a elements if it exists. + * -1 if no such element was found in \a elements. + */ +t8_locidx_t +t8_forest_bin_search_upper (const t8_element_array_t *elements, const t8_linearidx_t element_id, + const int element_level); + /** Find the owner process of a given element, deprecated version. * Use t8_forest_element_find_owner instead. * \param [in] forest The forest. From 67d62654e97ac5ae0eb855837fdf4aab21e1c03b Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 16:16:05 +0200 Subject: [PATCH 095/133] Add declaration of +t8_forest_bin_search_first_descendant_ancenstor. docstring still missing --- src/t8_forest/t8_forest_private.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/t8_forest/t8_forest_private.h b/src/t8_forest/t8_forest_private.h index 8db9d41b1a..4de2da3d6b 100644 --- a/src/t8_forest/t8_forest_private.h +++ b/src/t8_forest/t8_forest_private.h @@ -222,6 +222,12 @@ t8_locidx_t t8_forest_bin_search_upper (const t8_element_array_t *elements, const t8_linearidx_t element_id, const int element_level); +/** \brief TODO: document + */ +t8_locidx_t +t8_forest_bin_search_first_descendant_ancenstor (const t8_element_array_t *elements, const t8_element_t *element, + const t8_element_t *element_found); + /** Find the owner process of a given element, deprecated version. * Use t8_forest_element_find_owner instead. * \param [in] forest The forest. From 9920bb83dfebd7d011075c361350d113203f5b0b Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 16:43:04 +0200 Subject: [PATCH 096/133] remove old elements_are_ancestor --- src/t8_forest/t8_forest.cxx | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 2cbb33c54c..0fa16dc766 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1689,30 +1689,6 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, return 1; } -/** Compute whether two elements are ancestor of each other. - * TODO: Move the function inside the element module */ -static bool -t8_forest_elements_are_ancestor (const t8_scheme *scheme, t8_eclass_t eclass, const t8_element_t *element_A, - const t8_element_t *element_B) -{ - T8_ASSERT (scheme->element_is_valid (eclass, element_A)); - T8_ASSERT (scheme->element_is_valid (eclass, element_B)); - - t8_element_t *nca; - scheme->element_new (eclass, 1, &nca); - scheme->element_get_nca (eclass, element_A, element_B, nca); - - const bool nca_is_A = scheme->element_is_equal (eclass, element_A, nca); - if (!nca_is_A) { - if (!scheme->element_is_equal (eclass, element_B, nca)) { - scheme->element_destroy (eclass, 1, &nca); - return false; - } - } - scheme->element_destroy (eclass, 1, &nca); - return true; -} - void t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf_or_ghost, t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], int *num_neighbors, From 8d7112e924b28c9c9797f89766a4af613a475ed9 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 16:43:34 +0200 Subject: [PATCH 097/133] Add new lfn computation via first descendant of same level neighbor --- src/t8_forest/t8_forest.cxx | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 0fa16dc766..2d5e1ac78a 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1747,9 +1747,6 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons int same_level_neighbor_dual_face; const t8_gloidx_t computed_gneigh_tree = t8_forest_element_face_neighbor ( forest, ltreeid, leaf_or_ghost, same_level_neighbor, neigh_class, face, &same_level_neighbor_dual_face); - const int element_level = scheme->element_get_level (eclass, leaf_or_ghost); - const t8_locidx_t same_level_neighbor_index - = scheme->element_get_linear_id (neigh_class, same_level_neighbor, element_level); if (computed_gneigh_tree < 0) { // There is no face neighbor across this face @@ -1845,8 +1842,34 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const bool leaf_array_is_ghost = leaf_array->second; T8_ASSERT (tree_leaves != NULL); // TODO: Use anc/desc search here + const t8_element_t *first_descendant; const t8_locidx_t first_leaf_index - = t8_forest_bin_search_lower (tree_leaves, same_level_neighbor_index, element_level); + = t8_forest_bin_search_first_descendant_ancenstor (tree_leaves, same_level_neighbor, &first_descendant); + const int neighbor_level = scheme->element_get_level (neigh_class, same_level_neighbor); + const int first_desc_level = scheme->element_get_level (neigh_class, first_descendant); + /* If the same level neighbor is coarser than the first found leaf, then + * we iterate over the faces of the same level neighbor. + * Otherwise, there is only one face neighbor, the first_descendant. + * We will do the iteration over the first_descendant nevertheless, but it will stop immediately. + */ + const bool neighbor_unique = first_desc_level <= neighbor_level; + const t8_element_t *search_this_element = neighbor_unique ? first_descendant : same_level_neighbor; + t8_debugf ("[H] Starting face search. neigh level %i, desc level %i\n", neighbor_level, first_desc_level); + if (neighbor_unique) { + t8_debugf ("[H] Starting search with first desc."); + // We need to update the dual face. + // TODO: Add function to calculate the dual face upward in the refinement hierarchy. + SC_CHECK_ABORT ( + scheme->check_eclass_scheme_type (eclass) + || scheme->check_eclass_scheme_type (eclass) + || scheme->check_eclass_scheme_type (eclass) + || scheme->check_eclass_scheme_type (eclass) + || scheme->check_eclass_scheme_type> (eclass) + || scheme->check_eclass_scheme_type> (eclass) + || scheme->check_eclass_scheme_type> (eclass) + || scheme->check_eclass_scheme_type> (eclass), + "Face neighbor computation currently only possible for default or standalone vertex/line/quad/hex scheme."); + } if (first_leaf_index >= 0) { // There may be face neighbors in this leaf array. @@ -1866,7 +1889,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_locidx_t face_iterate_tree_id = leaf_array_is_ghost ? t8_forest_ghost_get_ghost_treeid (forest, computed_gneigh_tree) + num_local_trees : local_neighbor_tree; - t8_forest_iterate_faces (forest, face_iterate_tree_id, same_level_neighbor, same_level_neighbor_dual_face, + t8_forest_iterate_faces (forest, face_iterate_tree_id, search_this_element, same_level_neighbor_dual_face, tree_leaves, first_leaf_index, t8_forest_leaf_face_neighbors_iterate, &user_data); // Output of iterate_faces: // Array of indices in tree_leaves of all the face neighbor elements From fa981d3bc4e7828829240441a68c026a3e1f7bc9 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 16:43:46 +0200 Subject: [PATCH 098/133] docstring --- src/t8_forest/t8_forest_iterate.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/t8_forest/t8_forest_iterate.h b/src/t8_forest/t8_forest_iterate.h index f10bf92b8e..65f32b2caf 100644 --- a/src/t8_forest/t8_forest_iterate.h +++ b/src/t8_forest/t8_forest_iterate.h @@ -152,6 +152,10 @@ t8_forest_split_array (const t8_element_t *element, const t8_element_array_t *le * - (index + 1) */ /* Top-down iteration and callback is called on each intermediate level. * If it returns false, the current element is not traversed further */ +/** + \param [in] tree_lindex_of_first_leaf Index of the first leaf of \a element in \a leaf_elements. + The corresponding leaf does not necessarily lie on the face of \a element. +*/ void t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, const t8_element_array_t *const leaf_elements, t8_locidx_t tree_lindex_of_first_leaf, From d43ed6e6e908d9c5ad5f52d137de18c589d66e0c Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 16:44:09 +0200 Subject: [PATCH 099/133] t8_forest_bin_search_first_descendant_ancenstor fix parameter --- src/t8_forest/t8_forest_private.cxx | 12 ++++++------ src/t8_forest/t8_forest_private.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/t8_forest/t8_forest_private.cxx b/src/t8_forest/t8_forest_private.cxx index 0d0d2b63e8..42334955f8 100644 --- a/src/t8_forest/t8_forest_private.cxx +++ b/src/t8_forest/t8_forest_private.cxx @@ -191,7 +191,7 @@ t8_forest_element_is_ancestor (const t8_scheme *scheme, t8_eclass_t eclass, cons */ t8_locidx_t t8_forest_bin_search_first_descendant_ancenstor (const t8_element_array_t *elements, const t8_element_t *element, - const t8_element_t *element_found) + const t8_element_t **element_found) { /* This search works as follows: @@ -235,8 +235,8 @@ t8_forest_bin_search_first_descendant_ancenstor (const t8_element_array_t *eleme /* Get the element at the current position. */ if (search_pos_lower >= 0) { - element_found = t8_element_array_index_locidx (elements, search_pos_lower); - const bool is_ancestor = t8_forest_element_is_ancestor (scheme, eclass, element_found, element); + *element_found = t8_element_array_index_locidx (elements, search_pos_lower); + const bool is_ancestor = t8_forest_element_is_ancestor (scheme, eclass, *element_found, element); if (is_ancestor) { /* The element at this position is an ancestor or descendant. */ return search_pos_lower; @@ -246,15 +246,15 @@ t8_forest_bin_search_first_descendant_ancenstor (const t8_element_array_t *eleme const t8_locidx_t search_pos_upper = t8_forest_bin_search_upper (elements, element_id, element_level); if (search_pos_upper >= 0) { - element_found = t8_element_array_index_locidx (elements, search_pos_upper); - const bool is_descendant = t8_forest_element_is_ancestor (scheme, eclass, element, element_found); + *element_found = t8_element_array_index_locidx (elements, search_pos_upper); + const bool is_descendant = t8_forest_element_is_ancestor (scheme, eclass, element, *element_found); if (is_descendant) { /* The element at this position is an ancestor or descendant. */ return search_pos_upper; } } // No ancestor or descendant was found - element_found = nullptr; + *element_found = nullptr; return -1; } diff --git a/src/t8_forest/t8_forest_private.h b/src/t8_forest/t8_forest_private.h index 180977a63b..4d2f982eed 100644 --- a/src/t8_forest/t8_forest_private.h +++ b/src/t8_forest/t8_forest_private.h @@ -226,7 +226,7 @@ t8_forest_bin_search_upper (const t8_element_array_t *elements, const t8_lineari */ t8_locidx_t t8_forest_bin_search_first_descendant_ancenstor (const t8_element_array_t *elements, const t8_element_t *element, - const t8_element_t *element_found); + const t8_element_t **element_found); /** Find the owner process of a given element, deprecated version. * Use t8_forest_element_find_owner instead. From fb0cc9e7273b865a4cc219c944ed0af519695dc4 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 6 Jun 2025 16:44:09 +0200 Subject: [PATCH 100/133] t8_forest_bin_search_first_descendant_ancenstor fix parameter --- src/t8_forest/t8_forest_private.cxx | 12 ++++++------ src/t8_forest/t8_forest_private.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/t8_forest/t8_forest_private.cxx b/src/t8_forest/t8_forest_private.cxx index 0d0d2b63e8..42334955f8 100644 --- a/src/t8_forest/t8_forest_private.cxx +++ b/src/t8_forest/t8_forest_private.cxx @@ -191,7 +191,7 @@ t8_forest_element_is_ancestor (const t8_scheme *scheme, t8_eclass_t eclass, cons */ t8_locidx_t t8_forest_bin_search_first_descendant_ancenstor (const t8_element_array_t *elements, const t8_element_t *element, - const t8_element_t *element_found) + const t8_element_t **element_found) { /* This search works as follows: @@ -235,8 +235,8 @@ t8_forest_bin_search_first_descendant_ancenstor (const t8_element_array_t *eleme /* Get the element at the current position. */ if (search_pos_lower >= 0) { - element_found = t8_element_array_index_locidx (elements, search_pos_lower); - const bool is_ancestor = t8_forest_element_is_ancestor (scheme, eclass, element_found, element); + *element_found = t8_element_array_index_locidx (elements, search_pos_lower); + const bool is_ancestor = t8_forest_element_is_ancestor (scheme, eclass, *element_found, element); if (is_ancestor) { /* The element at this position is an ancestor or descendant. */ return search_pos_lower; @@ -246,15 +246,15 @@ t8_forest_bin_search_first_descendant_ancenstor (const t8_element_array_t *eleme const t8_locidx_t search_pos_upper = t8_forest_bin_search_upper (elements, element_id, element_level); if (search_pos_upper >= 0) { - element_found = t8_element_array_index_locidx (elements, search_pos_upper); - const bool is_descendant = t8_forest_element_is_ancestor (scheme, eclass, element, element_found); + *element_found = t8_element_array_index_locidx (elements, search_pos_upper); + const bool is_descendant = t8_forest_element_is_ancestor (scheme, eclass, element, *element_found); if (is_descendant) { /* The element at this position is an ancestor or descendant. */ return search_pos_upper; } } // No ancestor or descendant was found - element_found = nullptr; + *element_found = nullptr; return -1; } diff --git a/src/t8_forest/t8_forest_private.h b/src/t8_forest/t8_forest_private.h index 4de2da3d6b..50321113dc 100644 --- a/src/t8_forest/t8_forest_private.h +++ b/src/t8_forest/t8_forest_private.h @@ -226,7 +226,7 @@ t8_forest_bin_search_upper (const t8_element_array_t *elements, const t8_lineari */ t8_locidx_t t8_forest_bin_search_first_descendant_ancenstor (const t8_element_array_t *elements, const t8_element_t *element, - const t8_element_t *element_found); + const t8_element_t **element_found); /** Find the owner process of a given element, deprecated version. * Use t8_forest_element_find_owner instead. From 3ca4e23ae16df19cf91d936dc961e712f1d08a15 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Tue, 10 Jun 2025 12:08:24 +0200 Subject: [PATCH 101/133] restrict search to the descendant of the neighbor element --- src/t8_forest/t8_forest.cxx | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 2d5e1ac78a..02091463a8 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1873,15 +1873,32 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons if (first_leaf_index >= 0) { // There may be face neighbors in this leaf array. -#if 0 - // TODO: We can optimize the runtime by restricting the search array to the - // descendants of the same level neighbor. - t8_element_array_t face_leaves; - const size_t face_leaf_count = last_desc_index - first_desc_index + 1; - T8_ASSERT (face_leaf_count > 0); - t8_debugf ("Starting search with element indices %i to %i (including).\n", first_desc_index, last_desc_index); - t8_element_array_init_view (&face_leaves, tree_leaves, first_desc_index, face_leaf_count); -#endif + // We need to restrict the array such that it contains only elements inside the search element. + // Thus, we create a new view containing all elements starting at first_leaf_index. + t8_element_array_t reduced_leaves; + if (neighbor_unique) { + t8_debugf ("Starting search with element indices %i to %i (including).\n", first_leaf_index, first_leaf_index); + t8_element_array_init_view (&reduced_leaves, tree_leaves, first_leaf_index, 1); + } + else { + /* We need to compute the first element that is not longer contain in the same_level_neighbor. + * To do so, we compute the first ancestor of the successor of the same_level_neighbor */ + t8_element_t *successor; + scheme->element_new (neigh_class, 1, &successor); + scheme->element_construct_successor (neigh_class, same_level_neighbor, successor); + const t8_element_t *first_succ_desc; + const t8_locidx_t first_succ_desc_index + = t8_forest_bin_search_first_descendant_ancenstor (tree_leaves, successor, &first_succ_desc); + scheme->element_destroy (neigh_class, 1, &successor); + const t8_locidx_t leaf_count = t8_element_array_get_count (tree_leaves); + const t8_locidx_t last_search_element_index + = first_succ_desc_index < 0 ? leaf_count - 1 : first_succ_desc_index; + const size_t reduced_leaf_count = last_search_element_index - first_leaf_index; + T8_ASSERT (reduced_leaf_count > 0); + t8_debugf ("Starting search with element indices %i to %i (including).\n", first_leaf_index, + last_search_element_index); + t8_element_array_init_view (&reduced_leaves, tree_leaves, first_leaf_index, reduced_leaf_count); + } // Iterate over all leaves at the face and collect them as neighbors. const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); // Compute the local or ghost tree id depending on whether this leaf array corresponds to a local @@ -1890,7 +1907,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons = leaf_array_is_ghost ? t8_forest_ghost_get_ghost_treeid (forest, computed_gneigh_tree) + num_local_trees : local_neighbor_tree; t8_forest_iterate_faces (forest, face_iterate_tree_id, search_this_element, same_level_neighbor_dual_face, - tree_leaves, first_leaf_index, t8_forest_leaf_face_neighbors_iterate, &user_data); + &reduced_leaves, first_leaf_index, t8_forest_leaf_face_neighbors_iterate, &user_data); // Output of iterate_faces: // Array of indices in tree_leaves of all the face neighbor elements // Assign pneighbor_leaves From f168141dd0ffff01151df3efbc4ed8496f111177 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Mon, 23 Jun 2025 11:20:35 +0200 Subject: [PATCH 102/133] Documentation --- src/t8_forest/t8_forest_private.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/t8_forest/t8_forest_private.h b/src/t8_forest/t8_forest_private.h index 50321113dc..85993541f1 100644 --- a/src/t8_forest/t8_forest_private.h +++ b/src/t8_forest/t8_forest_private.h @@ -192,7 +192,7 @@ t8_forest_get_tree_leaf_element_array (t8_forest_t forest, t8_locidx_t ltreeid); t8_element_array_t * t8_forest_get_tree_leaf_element_array_mutable (const t8_forest_t forest, t8_locidx_t ltreeid); -/** Search for a linear element id (at forest->maxlevel) in a sorted array of +/** Search for a linear element id in a sorted array of * elements. If the element does not exist, return the largest index i * such that the element at position i has a smaller id than the given one. * If no such i exists, return -1. @@ -222,7 +222,15 @@ t8_locidx_t t8_forest_bin_search_upper (const t8_element_array_t *elements, const t8_linearidx_t element_id, const int element_level); -/** \brief TODO: document +/** \brief Search for the first descendant or ancestor of an element in a sorted array of elements. + * \param [in] elements An array of elements. Must be sorted according to linear id at maximum level. + * Must correspond to a valid refinement (i.e. contain no duplicate elements or elements and their descendants). + * \param [in] element The element to search for. + * \param [in] element_found On return either a descendant or ancestor of \a element in \a elements if it exists. NULL if no + * such element exists in \a elements. + * \return The smallest index \a i such that elements[i] (= \a element_found) is an ancestor or a descendant of \a element. + * -1 if no such element was found in \a elements. + * \note \a element is ancestor and descendant of itself, so if \a element is contained in \a elements then it will be found by this function. */ t8_locidx_t t8_forest_bin_search_first_descendant_ancenstor (const t8_element_array_t *elements, const t8_element_t *element, From 511cbefdcbe316a198b16884e9d138a4d271f78c Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Mon, 23 Jun 2025 11:27:34 +0200 Subject: [PATCH 103/133] typo --- src/t8_forest/t8_forest_private.cxx | 4 ++-- src/t8_forest/t8_forest_private.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/t8_forest/t8_forest_private.cxx b/src/t8_forest/t8_forest_private.cxx index 42334955f8..1dde5aa78d 100644 --- a/src/t8_forest/t8_forest_private.cxx +++ b/src/t8_forest/t8_forest_private.cxx @@ -190,8 +190,8 @@ t8_forest_element_is_ancestor (const t8_scheme *scheme, t8_eclass_t eclass, cons * If no such i exists, return -1. */ t8_locidx_t -t8_forest_bin_search_first_descendant_ancenstor (const t8_element_array_t *elements, const t8_element_t *element, - const t8_element_t **element_found) +t8_forest_bin_search_first_descendant_ancestor (const t8_element_array_t *elements, const t8_element_t *element, + const t8_element_t **element_found) { /* This search works as follows: diff --git a/src/t8_forest/t8_forest_private.h b/src/t8_forest/t8_forest_private.h index 85993541f1..46e01fc55d 100644 --- a/src/t8_forest/t8_forest_private.h +++ b/src/t8_forest/t8_forest_private.h @@ -233,8 +233,8 @@ t8_forest_bin_search_upper (const t8_element_array_t *elements, const t8_lineari * \note \a element is ancestor and descendant of itself, so if \a element is contained in \a elements then it will be found by this function. */ t8_locidx_t -t8_forest_bin_search_first_descendant_ancenstor (const t8_element_array_t *elements, const t8_element_t *element, - const t8_element_t **element_found); +t8_forest_bin_search_first_descendant_ancestor (const t8_element_array_t *elements, const t8_element_t *element, + const t8_element_t **element_found); /** Find the owner process of a given element, deprecated version. * Use t8_forest_element_find_owner instead. From 488a6bbe9759df79a5d71cf00cdbc328f6f88355 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 2 Jul 2025 10:10:36 +0200 Subject: [PATCH 104/133] Change docstring Co-authored-by: David Knapp --- src/t8_forest/t8_forest_private.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t8_forest/t8_forest_private.cxx b/src/t8_forest/t8_forest_private.cxx index 1dde5aa78d..a0a3d73bb3 100644 --- a/src/t8_forest/t8_forest_private.cxx +++ b/src/t8_forest/t8_forest_private.cxx @@ -146,7 +146,7 @@ t8_forest_bin_search_upper (const t8_element_array_t *elements, const t8_lineari } } -/** Query whether one element is an ancestor of the other. +/** Query whether element A is an ancestor of the element B. * An element A is ancestor of an element B if A == B or if B can * be obtained from A via successive refinement. * \param [in] scheme A scheme. From 30bbb699029cab8ff2d92c97a396c6b60462e90e Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 2 Jul 2025 10:11:25 +0200 Subject: [PATCH 105/133] Add const to parameter Co-authored-by: David Knapp --- src/t8_forest/t8_forest_private.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t8_forest/t8_forest_private.cxx b/src/t8_forest/t8_forest_private.cxx index a0a3d73bb3..436cc4ae45 100644 --- a/src/t8_forest/t8_forest_private.cxx +++ b/src/t8_forest/t8_forest_private.cxx @@ -157,7 +157,7 @@ t8_forest_bin_search_upper (const t8_element_array_t *elements, const t8_lineari */ // TODO: Move this function to the scheme class. static bool -t8_forest_element_is_ancestor (const t8_scheme *scheme, t8_eclass_t eclass, const t8_element_t *element_A, +t8_forest_element_is_ancestor (const t8_scheme *scheme, const t8_eclass_t eclass, const t8_element_t *element_A, const t8_element_t *element_B) { /* A is ancestor of B if and only if it has smaller or equal level and From 0b3c46f3bfc12e571008e400c8cd426922bd9816 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 2 Jul 2025 11:53:18 +0200 Subject: [PATCH 106/133] typo --- test/t8_forest/t8_gtest_face_neighbors.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index b0e1e70fe7..2dbbbf57cd 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -157,7 +157,7 @@ TEST_P (forest_face_neighbors, test_face_neighbors) if (gneigh_tree < 0) { // If there is no neighbor tree then there cannot be any face neighbors. // Note that there can also be no face neighbors computed if a neighbor tree exists, but - // the element is a ghost and the neighbor would is neither a local element nor ghost. + // the element is a ghost and the neighbor element is neither a local element nor ghost. ASSERT_EQ (num_neighbors, 0); } if (num_neighbors == 0) { From 56b3e51d9cbfe7da2807bc925409b88bd3c55aaa Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 2 Jul 2025 11:53:45 +0200 Subject: [PATCH 107/133] Add debug output to remove later --- test/t8_forest/t8_gtest_face_neighbors.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_face_neighbors.cxx index 2dbbbf57cd..851027a5bf 100644 --- a/test/t8_forest/t8_gtest_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_face_neighbors.cxx @@ -104,6 +104,7 @@ TEST_P (forest_face_neighbors, test_face_neighbors) if (t8_cmesh_get_tree_geometry (cmesh, 0) != NULL) { // Debug vtk output, only if cmesh has a registered geometry t8_forest_write_vtk (forest, "debug_face_neigh"); + t8_debugf ("writing forest to \'debug_face_neigh\'"); } #endif if (!forest_is_uniform) { @@ -246,6 +247,7 @@ TEST_P (forest_face_neighbors, test_face_neighbors) t8_debugf ("Checking neighbor element %p in (global) tree %li.\n", (void *) neighbor, gneigh_tree); t8_debugf ("dual face is %i, index is %i\n", dual_face, neigh_index); + scheme->element_debug_print (neigh_class, neighbor); #if T8_ENABLE_DEBUG ASSERT_TRUE (scheme->element_is_valid (neigh_class, neighbor)) @@ -302,9 +304,12 @@ TEST_P (forest_face_neighbors, test_face_neighbors) int position_of_original_element = -1; bool found_original = false; + t8_debugf ("Checking all %i neighbors of neighbor for original element:\n", neigh_num_neighbors); for (int ineighneigh = 0; ineighneigh < neigh_num_neighbors && !found_original; ++ineighneigh) { // Check that the neighbor of the neighbor element is the original element const t8_element_t *neigh_of_neigh = neigh_neighbor_leaves[ineighneigh]; + t8_debugf ("Checking neighbor of neighbor #%i:\n", ineighneigh); + scheme->element_debug_print (tree_class, neigh_of_neigh); if (scheme->element_is_equal (tree_class, element, neigh_of_neigh)) { position_of_original_element = ineighneigh; found_original = true; // Stop the for loop From 132a09c7931f635fd47dced92f8508dfa4277545 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 2 Jul 2025 11:54:48 +0200 Subject: [PATCH 108/133] rename face neighbor test t8 t8_gtest_leaf_face_neighbors --- test/CMakeLists.txt | 2 +- ...test_face_neighbors.cxx => t8_gtest_leaf_face_neighbors.cxx} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename test/t8_forest/{t8_gtest_face_neighbors.cxx => t8_gtest_leaf_face_neighbors.cxx} (100%) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9629480472..56b866e722 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -139,7 +139,7 @@ add_t8_cpp_test( NAME t8_gtest_half_neighbors_parallel SOURCES t8_forest/t8 add_t8_cpp_test( NAME t8_gtest_find_owner_parallel SOURCES t8_forest/t8_gtest_find_owner.cxx ) add_t8_cpp_test( NAME t8_gtest_user_data_parallel SOURCES t8_forest/t8_gtest_user_data.cxx ) add_t8_cpp_test( NAME t8_gtest_transform_serial SOURCES t8_forest/t8_gtest_transform.cxx ) -add_t8_cpp_test( NAME t8_gtest_face_neighbors_parallel SOURCES t8_forest/t8_gtest_face_neighbors.cxx t8_gtest_adapt_callbacks.cxx ) +add_t8_cpp_test( NAME t8_gtest_leaf_face_neighbors_parallel SOURCES t8_forest/t8_gtest_leaf_face_neighbors.cxx t8_gtest_adapt_callbacks.cxx ) add_t8_cpp_test( NAME t8_gtest_ghost_exchange_parallel SOURCES t8_forest/t8_gtest_ghost_exchange.cxx ) add_t8_cpp_test( NAME t8_gtest_ghost_delete_parallel SOURCES t8_forest/t8_gtest_ghost_delete.cxx ) add_t8_cpp_test( NAME t8_gtest_ghost_and_owner_parallel SOURCES t8_forest/t8_gtest_ghost_and_owner.cxx ) diff --git a/test/t8_forest/t8_gtest_face_neighbors.cxx b/test/t8_forest/t8_gtest_leaf_face_neighbors.cxx similarity index 100% rename from test/t8_forest/t8_gtest_face_neighbors.cxx rename to test/t8_forest/t8_gtest_leaf_face_neighbors.cxx From 7e4090afacfcfa78a73ecb2993e9f6603bb4d9f8 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 2 Jul 2025 12:00:45 +0200 Subject: [PATCH 109/133] Set no neighbors found return values in separate function --- src/t8_forest/t8_forest.cxx | 46 +++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 02091463a8..fdb5309939 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1689,6 +1689,26 @@ t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, return 1; } +/* + Set the proper return values of the leaf face neighbor computation + in case that no neighbors are found. + */ +static void +t8_forest_leaf_face_neighbors_set_no_neighbor_return_value (t8_element_t **pneighbor_leaves[], int *dual_faces[], + int *num_neighbors, t8_locidx_t **pelement_indices, + t8_gloidx_t *gneigh_tree) +{ + *dual_faces = NULL; + *num_neighbors = 0; + *pelement_indices = NULL; + if (pneighbor_leaves) { + *pneighbor_leaves = NULL; + } + if (gneigh_tree != NULL) { + *gneigh_tree = -1; + } +} + void t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf_or_ghost, t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], int *num_neighbors, @@ -1709,6 +1729,9 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons **/ T8_ASSERT (t8_forest_is_committed (forest)); + T8_ASSERT (pelement_indices != NULL); + T8_ASSERT (dual_faces != NULL); + T8_ASSERT (num_neighbors != NULL); #if T8_ENABLE_DEBUG const bool tree_is_local = t8_forest_tree_is_local (forest, ltreeid); @@ -1747,19 +1770,13 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons int same_level_neighbor_dual_face; const t8_gloidx_t computed_gneigh_tree = t8_forest_element_face_neighbor ( forest, ltreeid, leaf_or_ghost, same_level_neighbor, neigh_class, face, &same_level_neighbor_dual_face); + t8_debugf ("Computed same level neighbor with dual face %i\n", same_level_neighbor_dual_face); if (computed_gneigh_tree < 0) { // There is no face neighbor across this face scheme->element_destroy (neigh_class, 1, &same_level_neighbor); - *dual_faces = NULL; - *num_neighbors = 0; - *pelement_indices = NULL; - if (pneighbor_leaves) { - *pneighbor_leaves = NULL; - } - if (gneigh_tree != NULL) { - *gneigh_tree = -1; - } + t8_forest_leaf_face_neighbors_set_no_neighbor_return_value (pneighbor_leaves, dual_faces, num_neighbors, + pelement_indices, gneigh_tree); return; } @@ -1972,9 +1989,20 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons for (int ineigh = 0; ineigh < *num_neighbors; ++ineigh) { T8_ASSERT (scheme->element_is_valid (neigh_class, (*pneighbor_leaves)[ineigh])); t8_debugf ("Face neighbor %p is valid.\n", (void *) (*pneighbor_leaves)[ineigh]); + scheme->element_debug_print (neigh_class, (*pneighbor_leaves)[ineigh]); } } #endif // T8_ENABLE_DEBUG + // If no neighbors are found, set the proper return values. + if (*num_neighbors == 0) { + t8_debugf ("Found no neighbors\n"); + t8_forest_leaf_face_neighbors_set_no_neighbor_return_value (pneighbor_leaves, dual_faces, num_neighbors, + pelement_indices, gneigh_tree); + T8_ASSERT (*dual_faces == NULL); + T8_ASSERT (*pelement_indices == NULL); + T8_ASSERT (pneighbor_leaves == NULL || *pneighbor_leaves == NULL); + T8_ASSERT (gneigh_tree == NULL || *gneigh_tree == -1); + } if (gneigh_tree != NULL) { *gneigh_tree = computed_gneigh_tree; From 220f246a3564308cb3ecd07d794cc829e5e08c07 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 2 Jul 2025 12:01:21 +0200 Subject: [PATCH 110/133] only compute level if elements exist --- src/t8_forest/t8_forest.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index fdb5309939..5eaa3085b3 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1862,8 +1862,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_element_t *first_descendant; const t8_locidx_t first_leaf_index = t8_forest_bin_search_first_descendant_ancenstor (tree_leaves, same_level_neighbor, &first_descendant); - const int neighbor_level = scheme->element_get_level (neigh_class, same_level_neighbor); - const int first_desc_level = scheme->element_get_level (neigh_class, first_descendant); + const int neighbor_level = first_leaf_index < 0 ? -1 : scheme->element_get_level (neigh_class, same_level_neighbor); + const int first_desc_level = first_leaf_index < 0 ? -1 : scheme->element_get_level (neigh_class, first_descendant); /* If the same level neighbor is coarser than the first found leaf, then * we iterate over the faces of the same level neighbor. * Otherwise, there is only one face neighbor, the first_descendant. From 60e0a328c116a3b547e6f96371395a3da2f885f8 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 2 Jul 2025 12:01:45 +0200 Subject: [PATCH 111/133] Correct off by one error --- src/t8_forest/t8_forest.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 5eaa3085b3..48eabe88b2 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1910,7 +1910,7 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons const t8_locidx_t leaf_count = t8_element_array_get_count (tree_leaves); const t8_locidx_t last_search_element_index = first_succ_desc_index < 0 ? leaf_count - 1 : first_succ_desc_index; - const size_t reduced_leaf_count = last_search_element_index - first_leaf_index; + const size_t reduced_leaf_count = last_search_element_index - first_leaf_index + 1; T8_ASSERT (reduced_leaf_count > 0); t8_debugf ("Starting search with element indices %i to %i (including).\n", first_leaf_index, last_search_element_index); From 2e4039ff1dceaeb9e69039910a513c8e1deb494a Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 2 Jul 2025 12:03:00 +0200 Subject: [PATCH 112/133] debug out --- src/t8_forest/t8_forest_iterate.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index 0e937b7d56..d76070f903 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -82,7 +82,8 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme const t8_element_array_t *const leaf_elements, t8_locidx_t tree_lindex_of_first_leaf, t8_forest_iterate_face_fn callback, void *user_data) { - + t8_debugf ("Entering t8_forest_iterate_faces with leaf_index %i and %i total leafs.\n", tree_lindex_of_first_leaf, + t8_element_array_get_count (leaf_elements)); T8_ASSERT (t8_forest_is_committed (forest)); const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); const t8_locidx_t num_ghost_trees = t8_forest_get_num_ghost_trees (forest); From 50a6d66eb8353f4d1da8e2d2e47c950ae71af7ca Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 2 Jul 2025 12:03:24 +0200 Subject: [PATCH 113/133] Edit docstring for t8_forest_iterate_faces --- src/t8_forest/t8_forest_iterate.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/t8_forest/t8_forest_iterate.h b/src/t8_forest/t8_forest_iterate.h index 65f32b2caf..b5e7df8769 100644 --- a/src/t8_forest/t8_forest_iterate.h +++ b/src/t8_forest/t8_forest_iterate.h @@ -153,8 +153,9 @@ t8_forest_split_array (const t8_element_t *element, const t8_element_array_t *le /* Top-down iteration and callback is called on each intermediate level. * If it returns false, the current element is not traversed further */ /** - \param [in] tree_lindex_of_first_leaf Index of the first leaf of \a element in \a leaf_elements. + \param [in] tree_lindex_of_first_leaf Index of the first leaf of \a element in the tree's leafs. The corresponding leaf does not necessarily lie on the face of \a element. + \note \a tree_lindex_of_first_leaf is not an index in \a leaf_elements. \a leaf_elements may only be a part of the tree's leafs. */ void t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, From 3a69df1b8f35d8a1d532a416d1d4933944c16ba2 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 2 Jul 2025 12:05:46 +0200 Subject: [PATCH 114/133] Add adapt_callback linkage to t8_gtest_element_is_leaf_serial test --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 56b866e722..c99807915e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -146,7 +146,7 @@ add_t8_cpp_test( NAME t8_gtest_ghost_and_owner_parallel SOURCES t8_forest/t8 add_t8_cpp_test( NAME t8_gtest_balance_parallel SOURCES t8_forest/t8_gtest_balance.cxx ) add_t8_cpp_test( NAME t8_gtest_forest_commit_parallel SOURCES t8_forest/t8_gtest_forest_commit.cxx ) add_t8_cpp_test( NAME t8_gtest_forest_face_normal_serial SOURCES t8_forest/t8_gtest_forest_face_normal.cxx ) -add_t8_cpp_test( NAME t8_gtest_element_is_leaf_serial SOURCES t8_forest/t8_gtest_element_is_leaf.cxx ) +add_t8_cpp_test( NAME t8_gtest_element_is_leaf_serial SOURCES t8_forest/t8_gtest_element_is_leaf.cxx t8_gtest_adapt_callbacks.cxx ) add_t8_cpp_test( NAME t8_gtest_partition_data_parallel SOURCES t8_forest/t8_gtest_partition_data.cxx ) add_t8_cpp_test( NAME t8_gtest_permute_hole_serial SOURCES t8_forest_incomplete/t8_gtest_permute_hole.cxx ) add_t8_cpp_test( NAME t8_gtest_recursive_serial SOURCES t8_forest_incomplete/t8_gtest_recursive.cxx ) From 70f4a37344ac1c3e351d2f211fd2d1d23e4372cd Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 2 Jul 2025 15:14:56 +0200 Subject: [PATCH 115/133] Add element_is_ancestor function to the scheme interface and the default scheme --- src/t8_forest/t8_forest_private.cxx | 42 +------------------ .../t8_default_common/t8_default_common.hxx | 36 ++++++++++++++++ src/t8_schemes/t8_scheme.hxx | 16 +++++++ 3 files changed, 54 insertions(+), 40 deletions(-) diff --git a/src/t8_forest/t8_forest_private.cxx b/src/t8_forest/t8_forest_private.cxx index 436cc4ae45..49d3367eda 100644 --- a/src/t8_forest/t8_forest_private.cxx +++ b/src/t8_forest/t8_forest_private.cxx @@ -146,44 +146,6 @@ t8_forest_bin_search_upper (const t8_element_array_t *elements, const t8_lineari } } -/** Query whether element A is an ancestor of the element B. - * An element A is ancestor of an element B if A == B or if B can - * be obtained from A via successive refinement. - * \param [in] scheme A scheme. - * \param [in] eclass An eclass. - * \param [in] element_A An element of class \a eclass in scheme \a scheme. - * \param [in] element_B An element of class \a eclass in scheme \a scheme. - * \return True if and only if \a element_A is an ancestor of \a element_B. -*/ -// TODO: Move this function to the scheme class. -static bool -t8_forest_element_is_ancestor (const t8_scheme *scheme, const t8_eclass_t eclass, const t8_element_t *element_A, - const t8_element_t *element_B) -{ - /* A is ancestor of B if and only if it has smaller or equal level and - restricted to A's level, B has the same id as A. - - level(A) <= level(B) and ID(A,level(A)) == ID(B,level(B)) - */ - T8_ASSERT (scheme->element_is_valid (eclass, element_A)); - T8_ASSERT (scheme->element_is_valid (eclass, element_B)); - - const int level_A = scheme->element_get_level (eclass, element_A); - const int level_B = scheme->element_get_level (eclass, element_B); - - if (level_A > level_B) { - /* element A is finer than element B and thus cannot be - * an ancestor of B. */ - return false; - } - - const t8_linearidx_t id_A = scheme->element_get_linear_id (eclass, element_A, level_A); - const t8_linearidx_t id_B = scheme->element_get_linear_id (eclass, element_B, level_A); - - // If both elements have the same linear ID and level_A then A is an ancestor of B. - return id_A == id_B; -} - /** \brief Search for a linear element id (at level element_level) in a sorted array of * elements. If the element does not exist, return the first index i such that * the element at position i is an ancestor or descendant of the element corresponding to the element id. @@ -236,7 +198,7 @@ t8_forest_bin_search_first_descendant_ancestor (const t8_element_array_t *elemen /* Get the element at the current position. */ if (search_pos_lower >= 0) { *element_found = t8_element_array_index_locidx (elements, search_pos_lower); - const bool is_ancestor = t8_forest_element_is_ancestor (scheme, eclass, *element_found, element); + const bool is_ancestor = scheme->element_is_ancestor (eclass, *element_found, element); if (is_ancestor) { /* The element at this position is an ancestor or descendant. */ return search_pos_lower; @@ -247,7 +209,7 @@ t8_forest_bin_search_first_descendant_ancestor (const t8_element_array_t *elemen const t8_locidx_t search_pos_upper = t8_forest_bin_search_upper (elements, element_id, element_level); if (search_pos_upper >= 0) { *element_found = t8_element_array_index_locidx (elements, search_pos_upper); - const bool is_descendant = t8_forest_element_is_ancestor (scheme, eclass, element, *element_found); + const bool is_descendant = scheme->element_is_ancestor (eclass, element, *element_found); if (is_descendant) { /* The element at this position is an ancestor or descendant. */ return search_pos_upper; diff --git a/src/t8_schemes/t8_default/t8_default_common/t8_default_common.hxx b/src/t8_schemes/t8_default/t8_default_common/t8_default_common.hxx index 7e1b87cc3c..1e54ec3826 100644 --- a/src/t8_schemes/t8_default/t8_default_common/t8_default_common.hxx +++ b/src/t8_schemes/t8_default/t8_default_common/t8_default_common.hxx @@ -200,6 +200,42 @@ class t8_default_scheme_common: public t8_crtp_operatorunderlying ().element_is_valid (element_A)); + T8_ASSERT (this->underlying ().element_is_valid (element_B)); + + const int level_A = this->underlying ().element_get_level (element_A); + const int level_B = this->underlying ().element_get_level (element_B); + + if (level_A > level_B) { + /* element A is finer than element B and thus cannot be + * an ancestor of B. */ + return false; + } + + const t8_linearidx_t id_A = this->underlying ().element_get_linear_id (element_A, level_A); + const t8_linearidx_t id_B = this->underlying ().element_get_linear_id (element_B, level_A); + + // If both elements have the same linear ID at level_A then A is an ancestor of B. + return id_A == id_B; + } + /** Allocate space for a bunch of elements. * \param [in] length The number of elements to allocate. * \param [out] elem The elements to allocate. diff --git a/src/t8_schemes/t8_scheme.hxx b/src/t8_schemes/t8_scheme.hxx index f97d835729..a41f64b2ca 100644 --- a/src/t8_schemes/t8_scheme.hxx +++ b/src/t8_schemes/t8_scheme.hxx @@ -493,6 +493,22 @@ class t8_scheme { eclass_schemes[tree_class]); }; + /** Query whether element A is an ancestor of the element B. + * An element A is ancestor of an element B if A == B or if B can + * be obtained from A via successive refinement. + * \param [in] scheme A scheme. + * \param [in] eclass An eclass. + * \param [in] element_A An element of class \a eclass in scheme \a scheme. + * \param [in] element_B An element of class \a eclass in scheme \a scheme. + * \return True if and only if \a element_A is an ancestor of \a element_B. + */ + inline bool + element_is_ancestor (const t8_eclass_t tree_class, const t8_element_t *element_A, const t8_element_t *element_B) const + { + return std::visit ([&] (auto &&scheme) { return scheme.element_is_ancestor (element_A, element_B); }, + eclass_schemes[tree_class]); + } + /** Query whether a given set of elements is a family or not. * \param [in] tree_class The eclass of the current tree. * \param [in] fam An array of as many elements as an element of class From f758a8bb5bb44cc566250f69ef43508d999078de Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Wed, 2 Jul 2025 15:15:37 +0200 Subject: [PATCH 116/133] Add a non-implementation i.e. abort of element_is_ancestor to the standalone scheme --- .../t8_standalone_implementation.hxx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx b/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx index 0a36d2c1ff..8e1c78c108 100644 --- a/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx +++ b/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx @@ -610,6 +610,21 @@ struct t8_standalone_scheme return 1; } + /** Query whether element A is an ancestor of the element B. + * An element A is ancestor of an element B if A == B or if B can + * be obtained from A via successive refinement. + * \param [in] scheme A scheme. + * \param [in] eclass An eclass. + * \param [in] element_A An element of class \a eclass in scheme \a scheme. + * \param [in] element_B An element of class \a eclass in scheme \a scheme. + * \return True if and only if \a element_A is an ancestor of \a element_B. + */ + static constexpr bool + element_is_ancestor (const t8_element_t *element_A, const t8_element_t *element_B) noexcept + { + SC_ABORT ("element_is_ancestor is currently not implemented for the standalone scheme."); + } + /** Compute the nearest common ancestor of two elements. That is, * the element with highest level that still has both given elements as * descendants. From 18fe45a6bc8cd131c888e2e3227e24e75b5bf950 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 3 Jul 2025 15:03:44 +0200 Subject: [PATCH 117/133] Implement standalone version of is_ancestor --- .../t8_standalone_implementation.hxx | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx b/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx index 8e1c78c108..1eb314dd9f 100644 --- a/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx +++ b/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx @@ -610,6 +610,8 @@ struct t8_standalone_scheme return 1; } + // Note to devs: element_is_ancestor currently cannot be static + // since it uses the non-static function element_new /** Query whether element A is an ancestor of the element B. * An element A is ancestor of an element B if A == B or if B can * be obtained from A via successive refinement. @@ -619,10 +621,43 @@ struct t8_standalone_scheme * \param [in] element_B An element of class \a eclass in scheme \a scheme. * \return True if and only if \a element_A is an ancestor of \a element_B. */ - static constexpr bool - element_is_ancestor (const t8_element_t *element_A, const t8_element_t *element_B) noexcept - { - SC_ABORT ("element_is_ancestor is currently not implemented for the standalone scheme."); + constexpr bool + element_is_ancestor (const t8_element_t *element_A, const t8_element_t *element_B) const noexcept + { + /* We compute whether A is an ancestor of B by + + - If level(A) > level(B) then A cannot be an ancestor. + - Otherwise compute the ancestor of B at level(A) + - Compare the computed ancestor with A. + */ + T8_ASSERT (element_is_valid (element_A)); + T8_ASSERT (element_is_valid (element_B)); + + const t8_standalone_element *el_B = (const t8_standalone_element *) element_B; + + const int level_A = element_get_level (element_A); + const int level_B = element_get_level (element_B); + + if (level_A > level_B) { + // A is finer than B and thus cannot be an ancestor. + return false; + } + + // Compute the ancestor of B at level_A and compare it with A + t8_element_t *ancestor; + element_new (1, &ancestor); + + t8_standalone_element *ancestor_casted = (t8_standalone_element *) ancestor; + + element_get_ancestor (el_B, level_A, ancestor_casted); + + const bool is_ancestor = element_is_equal (ancestor, element_A); + + element_destroy (1, &ancestor); + + // Return true if A == ancestor + // Return false if A != ancestor + return is_ancestor; } /** Compute the nearest common ancestor of two elements. That is, From 0d8b09a2d4d46a2c21a661b0f1ec18eb226f2823 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 3 Jul 2025 15:22:23 +0200 Subject: [PATCH 118/133] Add an is_ancestor test --- test/CMakeLists.txt | 1 + test/t8_schemes/t8_gtest_is_ancestor.cxx | 94 ++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 test/t8_schemes/t8_gtest_is_ancestor.cxx diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2a2381bfe9..89dd6d9b72 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -173,6 +173,7 @@ add_t8_cpp_test( NAME t8_gtest_pyra_connectivity_serial SOURCES t8_schemes/t add_t8_cpp_test( NAME t8_gtest_face_neigh_serial SOURCES t8_schemes/t8_gtest_face_neigh.cxx ) add_t8_cpp_test( NAME t8_gtest_get_linear_id_serial SOURCES t8_schemes/t8_gtest_get_linear_id.cxx ) add_t8_cpp_test( NAME t8_gtest_ancestor_serial SOURCES t8_schemes/t8_gtest_ancestor.cxx ) +add_t8_cpp_test( NAME t8_gtest_is_ancestor_serial SOURCES t8_schemes/t8_gtest_is_ancestor.cxx ) add_t8_cpp_test( NAME t8_gtest_ancestor_id_serial SOURCES t8_schemes/t8_gtest_ancestor_id.cxx ) add_t8_cpp_test( NAME t8_gtest_element_count_leaves_serial SOURCES t8_schemes/t8_gtest_element_count_leaves.cxx ) add_t8_cpp_test( NAME t8_gtest_element_ref_coords_serial SOURCES t8_schemes/t8_gtest_element_ref_coords.cxx ) diff --git a/test/t8_schemes/t8_gtest_is_ancestor.cxx b/test/t8_schemes/t8_gtest_is_ancestor.cxx new file mode 100644 index 0000000000..3f12cd7bf8 --- /dev/null +++ b/test/t8_schemes/t8_gtest_is_ancestor.cxx @@ -0,0 +1,94 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_gtest_is_ancestor.cxx + * This test checks the element_is_ancestor function of the scheme interface. + * Starting from an element we build up all its ancestor up to root level and + * test whether the element_is_ancestor function returns true. + * We also test sone cases where the function returns false, namely when putting + * in an element and an ancestor in reverse order. + * + * This test is modified from the ancestor_id test. + */ + +#include +#include +#include +#include +#include "t8_gtest_dfs_base.hxx" +#include + +class class_is_ancestor: public TestDFS { + void + check_element () override + { + t8_element_t *ancestor; + scheme->element_new (eclass, 1, &ancestor); + /* Get level of current element */ + const int level = scheme->element_get_level (eclass, element); + + /* Iterate over all levels above the current element and check + if ancestor id corresponds with the child id of elem, parent, grandparent, ... */ + for (int levels_above_elem = 0; levels_above_elem < level; levels_above_elem++) { + const int ancestor_level = level - levels_above_elem; + /* Compute the ancestor by iteratively computing the parent */ + scheme->element_copy (eclass, element, ancestor); + for (int level_diff = 0; level_diff < levels_above_elem; level_diff++) { + scheme->element_get_parent (eclass, ancestor, ancestor); + } + // Check whether element_is_ancestor correctly identifies our candidate as an ancestor + EXPECT_TRUE (scheme->element_is_ancestor (eclass, ancestor, element)); + // We check that element_is_ancestor properly returns false by + // checking that element is not an ancestor of our candidate if their levels do not agree. + if (ancestor_level != level) { + EXPECT_FALSE (scheme->element_is_ancestor (eclass, element, ancestor)); + } + } + scheme->element_destroy (eclass, 1, &ancestor); + } + + protected: + void + SetUp () override + { + /* Setup DFS test */ + dfs_test_setup (); + } + void + TearDown () override + { + /* Destroy DFS test */ + dfs_test_teardown (); + } +}; + +TEST_P (class_is_ancestor, t8_recursive_dfs_is_ancestor) +{ +#if T8CODE_TEST_LEVEL >= 1 + const int maxlvl = 4; +#else + const int maxlvl = 6; +#endif + check_recursive_dfs_to_max_lvl (maxlvl); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_is_ancestor, class_is_ancestor, AllSchemes, print_all_schemes); From 9acef23f00f84d420fec3d45e08fd78e7411b0a7 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 3 Jul 2025 15:34:44 +0200 Subject: [PATCH 119/133] Add an is_ancestor assertion to nca computation --- src/t8_schemes/t8_scheme.hxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/t8_schemes/t8_scheme.hxx b/src/t8_schemes/t8_scheme.hxx index a41f64b2ca..ec09fd8dc2 100644 --- a/src/t8_schemes/t8_scheme.hxx +++ b/src/t8_schemes/t8_scheme.hxx @@ -537,8 +537,9 @@ class t8_scheme { element_get_nca (const t8_eclass_t tree_class, const t8_element_t *elem1, const t8_element_t *elem2, t8_element_t *const nca) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_nca (elem1, elem2, nca); }, - eclass_schemes[tree_class]); + std::visit ([&] (auto &&scheme) { return scheme.element_get_nca (elem1, elem2, nca); }, eclass_schemes[tree_class]); + T8_ASSERT (element_is_ancestor (tree_class, nca, elem1)); + T8_ASSERT (element_is_ancestor (tree_class, nca, elem2)); }; /** Compute the shape of the face of an element. From f6c751bffbfe38084b988d8db38ab48375e4f2e6 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 3 Jul 2025 15:37:10 +0200 Subject: [PATCH 120/133] Add is_ancestor check to nca unit test --- test/t8_schemes/t8_gtest_nca.cxx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/t8_schemes/t8_gtest_nca.cxx b/test/t8_schemes/t8_gtest_nca.cxx index 5e19cc5816..a0263ac90c 100644 --- a/test/t8_schemes/t8_gtest_nca.cxx +++ b/test/t8_schemes/t8_gtest_nca.cxx @@ -80,6 +80,10 @@ TEST_P (nca, nca_check_shallow) scheme->element_get_nca (tree_class, desc_a, desc_b, check); /*expect equality */ EXPECT_ELEM_EQ (scheme, tree_class, check, correct_nca); + // Check agains element_is_ancestor. This adds another test layer + // to both element_get_nca and element_is_ancestor. + EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, desc_a)); + EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, desc_b)); } } } @@ -124,6 +128,10 @@ TEST_P (nca, nca_check_deep) /* Expect equality of correct_nca and check for every other class */ EXPECT_ELEM_EQ (scheme, tree_class, correct_nca, check); } + // Check agains element_is_ancestor. This adds another test layer + // to both element_get_nca and element_is_ancestor. + EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, desc_a)); + EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, desc_b)); } } } @@ -178,6 +186,10 @@ t8_recursive_nca_check (t8_element_t *check_nca, t8_element_t *desc_a, t8_elemen for (j = 0; j < num_children_b; j++) { scheme->element_get_child (tree_class, parent_b, j, desc_b); scheme->element_get_nca (tree_class, desc_a, desc_b, check); + // Check agains element_is_ancestor. This adds another test layer + // to both element_get_nca and element_is_ancestor. + EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, desc_a)); + EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, desc_b)); if (!scheme->element_is_equal (tree_class, check_nca, check)) { level_a = scheme->element_get_level (tree_class, desc_a); @@ -306,6 +318,10 @@ TEST_P (nca, recursive_check_higher_level) scheme->element_get_nca (tree_class, parent_a, parent_b, check); EXPECT_ELEM_EQ (scheme, tree_class, parent_a, check); EXPECT_ELEM_EQ (scheme, tree_class, parent_b, check); + // Check agains element_is_ancestor. This adds another test layer + // to both element_get_nca and element_is_ancestor. + EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, parent_a)); + EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, parent_b)); } } } From 35402c73028788cf4ecd2eba1651e107be124516 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Thu, 3 Jul 2025 15:42:23 +0200 Subject: [PATCH 121/133] typos --- test/t8_schemes/t8_gtest_nca.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/t8_schemes/t8_gtest_nca.cxx b/test/t8_schemes/t8_gtest_nca.cxx index a0263ac90c..32b0876623 100644 --- a/test/t8_schemes/t8_gtest_nca.cxx +++ b/test/t8_schemes/t8_gtest_nca.cxx @@ -80,7 +80,7 @@ TEST_P (nca, nca_check_shallow) scheme->element_get_nca (tree_class, desc_a, desc_b, check); /*expect equality */ EXPECT_ELEM_EQ (scheme, tree_class, check, correct_nca); - // Check agains element_is_ancestor. This adds another test layer + // Check against element_is_ancestor. This adds another test layer // to both element_get_nca and element_is_ancestor. EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, desc_a)); EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, desc_b)); @@ -128,7 +128,7 @@ TEST_P (nca, nca_check_deep) /* Expect equality of correct_nca and check for every other class */ EXPECT_ELEM_EQ (scheme, tree_class, correct_nca, check); } - // Check agains element_is_ancestor. This adds another test layer + // Check against element_is_ancestor. This adds another test layer // to both element_get_nca and element_is_ancestor. EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, desc_a)); EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, desc_b)); @@ -186,7 +186,7 @@ t8_recursive_nca_check (t8_element_t *check_nca, t8_element_t *desc_a, t8_elemen for (j = 0; j < num_children_b; j++) { scheme->element_get_child (tree_class, parent_b, j, desc_b); scheme->element_get_nca (tree_class, desc_a, desc_b, check); - // Check agains element_is_ancestor. This adds another test layer + // Check against element_is_ancestor. This adds another test layer // to both element_get_nca and element_is_ancestor. EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, desc_a)); EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, desc_b)); @@ -318,7 +318,7 @@ TEST_P (nca, recursive_check_higher_level) scheme->element_get_nca (tree_class, parent_a, parent_b, check); EXPECT_ELEM_EQ (scheme, tree_class, parent_a, check); EXPECT_ELEM_EQ (scheme, tree_class, parent_b, check); - // Check agains element_is_ancestor. This adds another test layer + // Check against element_is_ancestor. This adds another test layer // to both element_get_nca and element_is_ancestor. EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, parent_a)); EXPECT_TRUE (scheme->element_is_ancestor (tree_class, check, parent_b)); From 83c39ac34b20b629535b633dfd9b73645083f1f5 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 4 Jul 2025 11:53:45 +0200 Subject: [PATCH 122/133] Fix search range computation by computing last element --- src/t8_forest/t8_forest.cxx | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index ccf718c9b7..b5b909698b 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1898,18 +1898,30 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_element_array_init_view (&reduced_leaves, tree_leaves, first_leaf_index, 1); } else { - /* We need to compute the first element that is not longer contain in the same_level_neighbor. - * To do so, we compute the first ancestor of the successor of the same_level_neighbor */ + /* We need to compute the first element that is not longer contained in the same_level_neighbor. + * To do so, we compute the successor of the same_level_neighbor and do + * a lower search for it in the leaf array. The found element is either the successor (which we can check) + * or the last leaf that is a descendant of same_level_neighbor. */ t8_element_t *successor; scheme->element_new (neigh_class, 1, &successor); scheme->element_construct_successor (neigh_class, same_level_neighbor, successor); const t8_element_t *first_succ_desc; - const t8_locidx_t first_succ_desc_index + t8_locidx_t first_index_of_non_descendant = t8_forest_bin_search_first_descendant_ancestor (tree_leaves, successor, &first_succ_desc); - scheme->element_destroy (neigh_class, 1, &successor); const t8_locidx_t leaf_count = t8_element_array_get_count (tree_leaves); + // If the successor is not contained in the leaf_array, + // we must use the index of the first element after the successor. + // We can compute this index with an upper search. + // Note the we could not do an upper search on same + // TODO REPLACE WITH lower search on successor + if (first_index_of_non_descendant < 0) { + const int successor_level = scheme->element_get_level (neigh_class, successor); + const t8_linearidx_t successor_id = scheme->element_get_linear_id (neigh_class, successor, successor_level); + first_index_of_non_descendant = t8_forest_bin_search_upper (tree_leaves, successor_id, successor_level); + } + scheme->element_destroy (neigh_class, 1, &successor); const t8_locidx_t last_search_element_index - = first_succ_desc_index < 0 ? leaf_count - 1 : first_succ_desc_index; + = first_index_of_non_descendant < 0 ? leaf_count - 1 : first_index_of_non_descendant - 1; const size_t reduced_leaf_count = last_search_element_index - first_leaf_index + 1; T8_ASSERT (reduced_leaf_count > 0); t8_debugf ("Starting search with element indices %i to %i (including).\n", first_leaf_index, From 8bc7c1eacb13c62fb4af8a92e316f7feca071cc3 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 4 Jul 2025 11:54:02 +0200 Subject: [PATCH 123/133] only reallocate memory if neighbors are found --- src/t8_forest/t8_forest.cxx | 50 +++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index b5b909698b..f7c800213b 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1959,32 +1959,34 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_debugf ("Found %i neighbors in tree. Adding up to %i total neighbors.\n", num_neighbors_current_tree, total_num_neighbors); // Copy neighbor element pointers - if (pneighbor_leaves != NULL) { - // Note element_destroy call after this function on *pneighbor_leaves - // is compatible with using T8_REALLOC on *pneighbor_leaves. - // REALLOC moving the storage of the pointers. The pointers store the element storage. - // So the element storage allocated by t8_element_new is not affected by the call to REALLOC. - *pneighbor_leaves = T8_REALLOC (*pneighbor_leaves, t8_element_t *, total_num_neighbors); - scheme->element_new (eclass, num_neighbors_current_tree, *pneighbor_leaves + *num_neighbors); - T8_ASSERT (*pneighbor_leaves != NULL); - // Call element copy for each element - for (t8_locidx_t ielem = 0; ielem < num_neighbors_current_tree; ++ielem) { - t8_element_t *new_element = (*pneighbor_leaves)[ielem]; - const t8_element_t *forest_leaf = user_data.neighbors.data ()[*num_neighbors + ielem]; - scheme->element_copy (eclass, forest_leaf, new_element); + if (num_neighbors_current_tree > 0) { + if (pneighbor_leaves != NULL) { + // Note element_destroy call after this function on *pneighbor_leaves + // is compatible with using T8_REALLOC on *pneighbor_leaves. + // REALLOC moving the storage of the pointers. The pointers store the element storage. + // So the element storage allocated by t8_element_new is not affected by the call to REALLOC. + *pneighbor_leaves = T8_REALLOC (*pneighbor_leaves, t8_element_t *, total_num_neighbors); + scheme->element_new (eclass, num_neighbors_current_tree, *pneighbor_leaves + *num_neighbors); + T8_ASSERT (*pneighbor_leaves != NULL); + // Call element copy for each element + for (t8_locidx_t ielem = 0; ielem < num_neighbors_current_tree; ++ielem) { + t8_element_t *new_element = (*pneighbor_leaves)[ielem]; + const t8_element_t *forest_leaf = user_data.neighbors.data ()[*num_neighbors + ielem]; + scheme->element_copy (eclass, forest_leaf, new_element); + } } + // Copy element indices + *pelement_indices = T8_REALLOC (*pelement_indices, t8_locidx_t, total_num_neighbors); + T8_ASSERT (*pelement_indices != NULL); + memcpy (*pelement_indices + *num_neighbors, user_data.element_indices.data () + *num_neighbors, + num_neighbors_current_tree * sizeof (t8_locidx_t)); + // Copy dual face + *dual_faces = T8_REALLOC (*dual_faces, int, total_num_neighbors); + T8_ASSERT (*dual_faces != NULL); + memcpy (*dual_faces + *num_neighbors, user_data.dual_faces.data () + *num_neighbors, + num_neighbors_current_tree * sizeof (int)); + *num_neighbors = total_num_neighbors; } - // Copy element indices - *pelement_indices = T8_REALLOC (*pelement_indices, t8_locidx_t, total_num_neighbors); - T8_ASSERT (*pelement_indices != NULL); - memcpy (*pelement_indices + *num_neighbors, user_data.element_indices.data () + *num_neighbors, - num_neighbors_current_tree * sizeof (t8_locidx_t)); - // Copy dual face - *dual_faces = T8_REALLOC (*dual_faces, int, total_num_neighbors); - T8_ASSERT (*dual_faces != NULL); - memcpy (*dual_faces + *num_neighbors, user_data.dual_faces.data () + *num_neighbors, - num_neighbors_current_tree * sizeof (int)); - *num_neighbors = total_num_neighbors; } // clean up memory allocated with new delete leaf_array; From 00ac942f3cd946f43509b672ae1de12d6cf11756 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 4 Jul 2025 11:55:02 +0200 Subject: [PATCH 124/133] additional debug check that all leafs to search for are descendants of the element --- src/t8_forest/t8_forest_iterate.cxx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index d76070f903..631ccf6a70 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -116,6 +116,12 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme const t8_element_t *leaf = t8_element_array_index_locidx (leaf_elements, 0); T8_ASSERT (t8_forest_element_is_leaf_or_ghost (forest, leaf, local_or_ghost_tree_id, tree_is_ghost)); T8_ASSERT (scheme->element_get_level (eclass, element) < scheme->element_get_level (eclass, leaf)); + + // Verify that all leafs in leaf_elements are descendants of element + for (t8_locidx_t ileaf_index = 0; ileaf_index < elem_count; ++ileaf_index) { + const t8_element_t *ileaf = t8_element_array_index_locidx (leaf_elements, ileaf_index); + T8_ASSERT (scheme->element_is_ancestor (eclass, element, ileaf)); + } } #endif From e4a9fb29cf6faea8f008c8dd9de2ae23b797c94b Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 4 Jul 2025 12:08:27 +0200 Subject: [PATCH 125/133] Simplify the search for the last element in search range --- src/t8_forest/t8_forest.cxx | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index f7c800213b..5a42c363f6 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1900,28 +1900,23 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons else { /* We need to compute the first element that is not longer contained in the same_level_neighbor. * To do so, we compute the successor of the same_level_neighbor and do - * a lower search for it in the leaf array. The found element is either the successor (which we can check) - * or the last leaf that is a descendant of same_level_neighbor. */ + * an upper search for it in the leaf array. + * The found element (if existing) is the first leaf that is not a descendant of same_level_neighbor. */ t8_element_t *successor; scheme->element_new (neigh_class, 1, &successor); scheme->element_construct_successor (neigh_class, same_level_neighbor, successor); - const t8_element_t *first_succ_desc; - t8_locidx_t first_index_of_non_descendant - = t8_forest_bin_search_first_descendant_ancestor (tree_leaves, successor, &first_succ_desc); - const t8_locidx_t leaf_count = t8_element_array_get_count (tree_leaves); - // If the successor is not contained in the leaf_array, - // we must use the index of the first element after the successor. - // We can compute this index with an upper search. - // Note the we could not do an upper search on same - // TODO REPLACE WITH lower search on successor - if (first_index_of_non_descendant < 0) { - const int successor_level = scheme->element_get_level (neigh_class, successor); - const t8_linearidx_t successor_id = scheme->element_get_linear_id (neigh_class, successor, successor_level); - first_index_of_non_descendant = t8_forest_bin_search_upper (tree_leaves, successor_id, successor_level); - } + const int successor_level = scheme->element_get_level (neigh_class, successor); + const t8_linearidx_t successor_id = scheme->element_get_linear_id (neigh_class, successor, successor_level); scheme->element_destroy (neigh_class, 1, &successor); + const t8_locidx_t upper_search_for_successor + = t8_forest_bin_search_upper (tree_leaves, successor_id, successor_level); + const t8_locidx_t leaf_count = t8_element_array_get_count (tree_leaves); + // The first index of a non descendant is the found element or the end of the array + // if no element was found. + // The last index in our search range is 1 less. const t8_locidx_t last_search_element_index - = first_index_of_non_descendant < 0 ? leaf_count - 1 : first_index_of_non_descendant - 1; + = upper_search_for_successor < 0 ? leaf_count - 1 : upper_search_for_successor - 1; + const size_t reduced_leaf_count = last_search_element_index - first_leaf_index + 1; T8_ASSERT (reduced_leaf_count > 0); t8_debugf ("Starting search with element indices %i to %i (including).\n", first_leaf_index, From 71a5a993a0de5cd857177caa9688d149a9bd3462 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 4 Jul 2025 12:28:42 +0200 Subject: [PATCH 126/133] remove old code --- src/t8_forest/t8_forest.cxx | 257 ------------------------------------ 1 file changed, 257 deletions(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 5a42c363f6..0fef6af898 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -2016,263 +2016,6 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons if (gneigh_tree != NULL) { *gneigh_tree = computed_gneigh_tree; } - -#if 0 -t8_locidx_t lneigh_treeid = -1; - t8_locidx_t lghost_treeid = -1, *element_indices, element_index; - const t8_element_t *ancestor; - t8_element_t **neighbor_leaves; - t8_linearidx_t neigh_id; - int num_children_at_face, at_maxlevel; - int ineigh, *owners, different_owners, have_ghosts; -// Compute first face desc and last face desc -// Search for them in the element array -// if both not found -> no neighbor -// otherwise take 0 if first not found. Last must have been found (check) -// Compute NCA of search results -// Use NCA as start for face it. -// If first_found = last_found, there is only one neighbor and that is NCA -// Otherwise NCA will be a descendant of neighbor. - -/* If we are at the maximum refinement level, we compute the neighbor instead */ -at_maxlevel = ts->t8_element_level (leaf_or_ghost) == t8_forest_get_maxlevel (forest); -// TODO (ghosts): There is some duplicated code in the if/else block. Get rid of it -if (at_maxlevel) { - num_children_at_face = 1; - neighbor_leaves = *pneighbor_leaves = T8_ALLOC (t8_element_t *, 1); - *dual_faces = T8_ALLOC (int, 1); - neigh_scheme->t8_element_new (num_children_at_face, neighbor_leaves); - /* Compute neighbor element and global treeid of the neighbor */ - // TODO (ghosts): Extend this to ghosts - gneigh_treeid = t8_forest_element_face_neighbor (forest, ltreeid, leaf_or_ghost, neighbor_leaves[0], neigh_scheme, - face, *dual_faces); -} -else { - /* Allocate neighbor element */ - num_children_at_face = ts->t8_element_num_face_children (leaf_or_ghost, face); - neighbor_leaves = *pneighbor_leaves = T8_ALLOC (t8_element_t *, num_children_at_face); - *dual_faces = T8_ALLOC (int, num_children_at_face); - neigh_scheme->t8_element_new (num_children_at_face, neighbor_leaves); - /* Compute neighbor elements and global treeid of the neighbor */ - // TODO (ghosts): Extend this to ghosts - gneigh_treeid = t8_forest_element_half_face_neighbors (forest, ltreeid, leaf_or_ghost, neighbor_leaves, neigh_scheme, - face, num_children_at_face, *dual_faces); -} -if (gneigh_tree) { - *gneigh_tree = gneigh_treeid; -} -if (gneigh_treeid < 0) { - /* There exists no face neighbor across this face, we return with this info */ - neigh_scheme->t8_element_destroy (num_children_at_face, neighbor_leaves); - T8_FREE (neighbor_leaves); - T8_FREE (*dual_faces); - *dual_faces = NULL; - *num_neighbors = 0; - *pelement_indices = NULL; - *pneighbor_leaves = NULL; - return; -} -T8_ASSERT (gneigh_treeid >= 0 && gneigh_treeid < forest->global_num_trees); -/* We have computed the half face neighbor elements, we now compute their owners, - * if they differ, we know that the half face neighbors are the neighbor leaves. - * If the owners do not differ, we have to check if the neighbor leaf is their - * parent or grandparent. */ -owners = T8_ALLOC (int, num_children_at_face); -different_owners = 0; -have_ghosts = 0; -for (ineigh = 0; ineigh < num_children_at_face; ineigh++) { - /* At first, we check whether the current rank owns the neighbor, since - * this is a constant time check and it is the most common case */ - if (t8_forest_element_check_owner (forest, neighbor_leaves[ineigh], gneigh_treeid, neigh_class, forest->mpirank, - at_maxlevel)) { - owners[ineigh] = forest->mpirank; - /* The neighbor tree is also a local tree. we store its local treeid */ - lneigh_treeid = t8_forest_get_local_id (forest, gneigh_treeid); - } - else { - owners[ineigh] = t8_forest_element_find_owner (forest, gneigh_treeid, neighbor_leaves[ineigh], neigh_class); - /* Store that at least one neighbor is a ghost */ - have_ghosts = 1; - } - if (ineigh > 0) { - /* Check if all owners are the same for all neighbors or not */ - different_owners = different_owners || (owners[ineigh] != owners[ineigh - 1]); - } -} -if (have_ghosts) { - /* At least one neighbor is a ghost, we compute the ghost treeid of the neighbor - * tree. */ - // TODO (ghosts): If input is a ghost, this call may not find a tree since the neighbor may not be an element or ghost. - // In that case, we cannot return any element for this neighbor - lghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, gneigh_treeid); - T8_ASSERT (lghost_treeid >= 0); -} -/* TODO (ghosts): Maybe we do not need to compute the owners. It suffices to know - * whether the neighbor is owned by mpirank or not. */ - -// TODO (ghost): Definitely check whether we can improve this whole owner logic -if (!different_owners) { - // TODO (ghost): This is more complex when the neighbor is neither element nor ghost, since a subset - // could still be elements or ghosts. In that case we must identify those somehow. It is not - // enough to query neighbor_leaves[0] - /* The face neighbors belong to the same process, we thus need to determine - * if they are leaves or their parent or grandparent. */ - neigh_id = scheme->element_get_linear_id (*pneigh_eclass, neighbor_leaves[0], forest->maxlevel); - if (owners[0] != forest->mpirank) { - /* The elements are ghost elements of the same owner */ - // TODO (ghost): If the ghost tree does not exist since the input was a ghost and the neighbor is neither - // element or ghost, we cannot do the call here - const t8_element_array_t *element_array = t8_forest_ghost_get_tree_leaf_elements (forest, lghost_treeid); - /* Find the index in element_array of the leaf ancestor of the first neighbor. - * This is either the neighbor itself or its parent, or its grandparent */ - element_index = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); - // TODO (ghost): If the input is a ghost and the neighbor is neither element nor ghost, this search may not find anything - T8_ASSERT (element_index >= 0); - - /* Get the element */ - ancestor = t8_forest_ghost_get_leaf_element (forest, lghost_treeid, element_index); - /* Add the number of ghost elements on previous ghost trees and the number of local elements. */ - element_index += t8_forest_ghost_get_tree_element_offset (forest, lghost_treeid); - element_index += t8_forest_get_local_num_leaf_elements (forest); - T8_ASSERT (forest->local_num_elements <= element_index - && element_index < forest->local_num_elements + t8_forest_get_num_ghosts (forest)); - } - /* TEMP COMMENT - * GHOSTS REWORK CONTINUE GOING THROUGH AND PLANNING FROM HERE - */ - else { - /* the elements are local elements */ - const t8_element_array_t *element_array = t8_forest_get_tree_leaf_element_array (forest, lneigh_treeid); - /* Find the index in element_array of the leaf ancestor of the first neighbor. - * This is either the neighbor itself or its parent, or its grandparent */ - element_index = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); - /* Get the element */ - ancestor = t8_forest_get_tree_leaf_element (t8_forest_get_tree (forest, lneigh_treeid), element_index); - /* Add the element offset of this tree to the index */ - element_index += t8_forest_get_tree_element_offset (forest, lneigh_treeid); - } - if (neigh_scheme->t8_element_compare (ancestor, neighbor_leaves[0]) < 0) { - /* ancestor is a real ancestor, and thus the neighbor is either the parent - * or the grandparent of the half neighbors. We can return it and the indices. */ - /* We need to determine the dual face */ - if (neigh_scheme->t8_element_level (ancestor) == ts->t8_element_level (leaf_or_ghost)) { - /* The ancestor is the same-level neighbor of leaf */ - if (!at_maxlevel) { - /* its dual face is the face of the parent of the first neighbor leaf */ - *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); - } - } - else { - /* The ancestor is the parent of the parent */ - T8_ASSERT (neigh_scheme->t8_element_level (ancestor) == ts->t8_element_level (leaf_or_ghost) - 1); - - *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); - if (!at_maxlevel) { - /* We need to compute the dual face of the grandparent. */ - /* Construct the parent of the grand child */ - neigh_scheme->t8_element_parent (neighbor_leaves[0], neighbor_leaves[0]); - /* Compute the face id of the parent's face */ - *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); - } - } - - /* free memory */ - neigh_scheme->t8_element_destroy (num_children_at_face - 1, neighbor_leaves + 1); - /* copy the ancestor */ - neigh_scheme->t8_element_copy (ancestor, neighbor_leaves[0]); - /* set return values */ - *num_neighbors = 1; - *pelement_indices = T8_ALLOC (t8_locidx_t, 1); - (*pelement_indices)[0] = element_index; - - T8_FREE (owners); - return; - } -} // if (different owners) - -/* The leaves are the face neighbors that we are looking for. */ -/* The face neighbors either belong to different processes and thus must be leaves - * in the forest, or the ancestor leaf of the first half neighbor is the half - * neighbor itself and thus all half neighbors must be leaves. - * Since the forest is balanced, we found all neighbor leaves. - * It remains to compute their local ids */ -*num_neighbors = num_children_at_face; -*pelement_indices = T8_ALLOC (t8_locidx_t, num_children_at_face); -element_indices = *pelement_indices; -for (ineigh = 0; ineigh < num_children_at_face; ineigh++) { - /* Compute the linear id at maxlevel of the neighbor leaf */ - neigh_id = neigh_scheme->t8_element_get_linear_id (neighbor_leaves[ineigh], forest->maxlevel); - /* Get a pointer to the element array in which the neighbor lies and search for the element's index in this array. - * This is either the local leaf array of the local tree or the corresponding leaf array in the ghost structure */ - if (owners[ineigh] == forest->mpirank) { - /* The neighbor is a local leaf */ - const t8_element_array_t *element_array = t8_forest_get_tree_leaf_element_array (forest, lneigh_treeid); - /* Find the index of the neighbor in the array */ - element_indices[ineigh] = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); - T8_ASSERT (element_indices[ineigh] >= 0); - /* We have to add the tree's element offset to the index found to get the actual local element id */ - element_indices[ineigh] += t8_forest_get_tree_element_offset (forest, lneigh_treeid); -#if T8_ENABLE_DEBUG - /* We check whether the element is really the element at this local id */ - { - t8_locidx_t check_ltreeid; - const t8_element_t *check_element - = t8_forest_get_leaf_element (forest, element_indices[ineigh], &check_ltreeid); - T8_ASSERT (check_ltreeid == lneigh_treeid); - T8_ASSERT (scheme->element_is_equal (*pneigh_eclass, check_element, neighbor_leaves[ineigh])); - } -#endif - } - else { - /* The neighbor is a ghost */ - const t8_element_array_t *element_array = t8_forest_ghost_get_tree_leaf_elements (forest, lghost_treeid); - /* Find the index of the neighbor in the array */ - element_indices[ineigh] = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); - -#if T8_ENABLE_DEBUG - /* We check whether the element is really the element at this local id */ - { - t8_element_t *check_element; - check_element = t8_forest_ghost_get_leaf_element (forest, lghost_treeid, element_indices[ineigh]); - T8_ASSERT (scheme->element_is_equal (*pneigh_eclass, check_element, neighbor_leaves[ineigh])); - } -#endif - /* Add the element offset of previous ghosts to this index */ - element_indices[ineigh] += t8_forest_ghost_get_tree_element_offset (forest, lghost_treeid); - /* Add the number of all local elements to this index */ - element_indices[ineigh] += t8_forest_get_local_num_leaf_elements (forest); - } - } /* End for loop over neighbor leaves */ - T8_FREE (owners); - } - else { - /* The neighbor is a ghost */ - const t8_element_array_t *element_array = t8_forest_ghost_get_tree_elements (forest, lghost_treeid); - /* Find the index of the neighbor in the array */ - element_indices[ineigh] = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); - -#if T8_ENABLE_DEBUG - /* We check whether the element is really the element at this local id */ - { - t8_element_t *check_element; - check_element = t8_forest_ghost_get_leaf_element (forest, lghost_treeid, element_indices[ineigh]); - T8_ASSERT (neigh_scheme->t8_element_equal (check_element, neighbor_leaves[ineigh])); - } -#endif - /* Add the element offset of previous ghosts to this index */ - element_indices[ineigh] += t8_forest_ghost_get_tree_element_offset (forest, lghost_treeid); - /* Add the number of all local elements to this index */ - element_indices[ineigh] += t8_forest_get_local_num_leaf_elements (forest); - } -} /* End for loop over neighbor leaves */ -T8_FREE (owners); -} -else -{ - /* TODO: implement unbalanced version */ - SC_ABORT ("Computing leaf face neighbors is only supported for balanced forests.\n"); -} -#endif // if 0 } void From c2a2270be685c12c6b0fe3db3c10b6c99f628158 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 4 Jul 2025 12:38:55 +0200 Subject: [PATCH 127/133] indent --- example/forest/t8_test_face_iterate.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/example/forest/t8_test_face_iterate.cxx b/example/forest/t8_test_face_iterate.cxx index 48f45c8aca..a358f6f247 100644 --- a/example/forest/t8_test_face_iterate.cxx +++ b/example/forest/t8_test_face_iterate.cxx @@ -90,8 +90,9 @@ t8_test_fiterate (t8_forest_t forest) const t8_locidx_t num_tree_elements = is_ghost ? t8_forest_ghost_tree_num_leaf_elements (forest, ghost_tree_id) : t8_forest_get_tree_num_leaf_elements (forest, itree); // Get all leaf elements - const t8_element_array_t *leaf_elements = !is_ghost ? t8_forest_tree_get_leaf_elements (forest, itree) - : t8_forest_ghost_get_tree_leaf_elements (forest, ghost_tree_id); + const t8_element_array_t *leaf_elements = !is_ghost + ? t8_forest_tree_get_leaf_elements (forest, itree) + : t8_forest_ghost_get_tree_leaf_elements (forest, ghost_tree_id); // Get the first and last element const t8_element_t *first_el = (const t8_element_t *) t8_element_array_index_locidx (leaf_elements, 0); const t8_element_t *last_el From c3bd2b3551929c4a1fa6bff4f412133e90202d71 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 4 Jul 2025 12:41:48 +0200 Subject: [PATCH 128/133] fix debug out file format warnings --- src/t8_forest/t8_forest_iterate.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index 631ccf6a70..68e9450a1d 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -82,7 +82,7 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme const t8_element_array_t *const leaf_elements, t8_locidx_t tree_lindex_of_first_leaf, t8_forest_iterate_face_fn callback, void *user_data) { - t8_debugf ("Entering t8_forest_iterate_faces with leaf_index %i and %i total leafs.\n", tree_lindex_of_first_leaf, + t8_debugf ("Entering t8_forest_iterate_faces with leaf_index %i and %li total leafs.\n", tree_lindex_of_first_leaf, t8_element_array_get_count (leaf_elements)); T8_ASSERT (t8_forest_is_committed (forest)); const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); @@ -118,7 +118,7 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme T8_ASSERT (scheme->element_get_level (eclass, element) < scheme->element_get_level (eclass, leaf)); // Verify that all leafs in leaf_elements are descendants of element - for (t8_locidx_t ileaf_index = 0; ileaf_index < elem_count; ++ileaf_index) { + for (t8_locidx_t ileaf_index = 0; (size_t) ileaf_index < elem_count; ++ileaf_index) { const t8_element_t *ileaf = t8_element_array_index_locidx (leaf_elements, ileaf_index); T8_ASSERT (scheme->element_is_ancestor (eclass, element, ileaf)); } @@ -155,6 +155,7 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme T8_ASSERT (child_indices[iface] < num_children + 1); const size_t indexa = split_offsets[child_indices[iface]]; /* first leaf of this face child */ const size_t indexb = split_offsets[child_indices[iface] + 1]; /* first leaf of next child */ + t8_debugf ("Computed indices for face child %i: %zd %zd\n", iface, indexa, indexb); if (indexa < indexb) { /* There exist leaves of this face child in leaf_elements, * we construct an array of these leaves */ From 3fc80e673ecc7bbd972ae2cb09381da6acd93b9a Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 4 Jul 2025 12:49:38 +0200 Subject: [PATCH 129/133] Add unused parameter flag --- src/t8_forest/t8_forest.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 0fef6af898..96b127900f 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1647,7 +1647,7 @@ struct t8_lfn_user_data static int t8_forest_leaf_face_neighbors_iterate (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, - int is_leaf, const t8_element_array_t *const leaf_elements, + int is_leaf, [[maybe_unused]] const t8_element_array_t *const leaf_elements, t8_locidx_t tree_leaf_index, void *user_data) { // Output of iterate_faces: From fc749faccf3b40efa5a98f429f45a1750c38abfa Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 4 Jul 2025 13:00:15 +0200 Subject: [PATCH 130/133] add unused flags to adapt callback --- test/t8_gtest_adapt_callbacks.cxx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/t8_gtest_adapt_callbacks.cxx b/test/t8_gtest_adapt_callbacks.cxx index 44dfed5197..17795e055d 100644 --- a/test/t8_gtest_adapt_callbacks.cxx +++ b/test/t8_gtest_adapt_callbacks.cxx @@ -34,9 +34,10 @@ * This integer is the maximum refinement level. */ int -t8_test_adapt_first_child (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, - const t8_eclass_t eclass, t8_locidx_t lelement_id, const t8_scheme *scheme, - const int is_family, const int num_elements, t8_element_t *elements[]) +t8_test_adapt_first_child (t8_forest_t forest, [[maybe_unused]] t8_forest_t forest_from, + [[maybe_unused]] t8_locidx_t which_tree, const t8_eclass_t eclass, + [[maybe_unused]] t8_locidx_t lelement_id, const t8_scheme *scheme, const int is_family, + const int num_elements, t8_element_t *elements[]) { T8_ASSERT (!is_family || (is_family && num_elements == scheme->element_get_num_children (eclass, elements[0]))); From e5b68b732b5cb0e22973e77f0326233c946ce994 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 4 Jul 2025 13:01:54 +0200 Subject: [PATCH 131/133] typos --- src/t8_forest/t8_forest_iterate.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/t8_forest/t8_forest_iterate.h b/src/t8_forest/t8_forest_iterate.h index b5e7df8769..fdd7f50329 100644 --- a/src/t8_forest/t8_forest_iterate.h +++ b/src/t8_forest/t8_forest_iterate.h @@ -153,9 +153,9 @@ t8_forest_split_array (const t8_element_t *element, const t8_element_array_t *le /* Top-down iteration and callback is called on each intermediate level. * If it returns false, the current element is not traversed further */ /** - \param [in] tree_lindex_of_first_leaf Index of the first leaf of \a element in the tree's leafs. + \param [in] tree_lindex_of_first_leaf Index of the first leaf of \a element in the tree's leaves. The corresponding leaf does not necessarily lie on the face of \a element. - \note \a tree_lindex_of_first_leaf is not an index in \a leaf_elements. \a leaf_elements may only be a part of the tree's leafs. + \note \a tree_lindex_of_first_leaf is not an index in \a leaf_elements. \a leaf_elements may only be a part of the tree's leaves. */ void t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, From 9676881e9be9ee6028c346754ef8c64907f68953 Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 4 Jul 2025 13:03:13 +0200 Subject: [PATCH 132/133] typos --- src/t8_forest/t8_forest_iterate.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index 68e9450a1d..1553391913 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -82,7 +82,7 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme const t8_element_array_t *const leaf_elements, t8_locidx_t tree_lindex_of_first_leaf, t8_forest_iterate_face_fn callback, void *user_data) { - t8_debugf ("Entering t8_forest_iterate_faces with leaf_index %i and %li total leafs.\n", tree_lindex_of_first_leaf, + t8_debugf ("Entering t8_forest_iterate_faces with leaf_index %i and %li total leaves.\n", tree_lindex_of_first_leaf, t8_element_array_get_count (leaf_elements)); T8_ASSERT (t8_forest_is_committed (forest)); const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); @@ -117,7 +117,7 @@ t8_forest_iterate_faces (t8_forest_t forest, t8_locidx_t ltreeid, const t8_eleme T8_ASSERT (t8_forest_element_is_leaf_or_ghost (forest, leaf, local_or_ghost_tree_id, tree_is_ghost)); T8_ASSERT (scheme->element_get_level (eclass, element) < scheme->element_get_level (eclass, leaf)); - // Verify that all leafs in leaf_elements are descendants of element + // Verify that all leaves in leaf_elements are descendants of element for (t8_locidx_t ileaf_index = 0; (size_t) ileaf_index < elem_count; ++ileaf_index) { const t8_element_t *ileaf = t8_element_array_index_locidx (leaf_elements, ileaf_index); T8_ASSERT (scheme->element_is_ancestor (eclass, element, ileaf)); From d1c8bd9a25eedb2464e1d546e0927c4094e397eb Mon Sep 17 00:00:00 2001 From: Johannes Holke Date: Fri, 4 Jul 2025 13:25:59 +0200 Subject: [PATCH 133/133] add unused flag --- example/forest/t8_test_face_iterate.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example/forest/t8_test_face_iterate.cxx b/example/forest/t8_test_face_iterate.cxx index a358f6f247..d72a493647 100644 --- a/example/forest/t8_test_face_iterate.cxx +++ b/example/forest/t8_test_face_iterate.cxx @@ -43,7 +43,8 @@ typedef struct static int t8_test_fiterate_callback (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, int is_leaf, - const t8_element_array_t *leaf_elements, t8_locidx_t leaf_index, void *user_data) + [[maybe_unused]] const t8_element_array_t *leaf_elements, t8_locidx_t leaf_index, + void *user_data) { if (is_leaf) { t8_test_fiterate_udata_t *test_user_data = (t8_test_fiterate_udata_t *) user_data;