diff --git a/benchmarks/t8_time_forest_partition.cxx b/benchmarks/t8_time_forest_partition.cxx index c0ce560d7c..798b5fecc8 100644 --- a/benchmarks/t8_time_forest_partition.cxx +++ b/benchmarks/t8_time_forest_partition.cxx @@ -179,7 +179,7 @@ t8_time_forest_cmesh_mshfile (t8_cmesh_t cmesh, const char *vtu_prefix, sc_MPI_C /* partition the adapted forest */ t8_forest_init (&forest_partition); /* partition the adapted forest */ - t8_forest_set_partition (forest_partition, forest_adapt, 0); + t8_forest_set_partition (forest_partition, forest_adapt, 0, nullptr); /* If desired, create ghost elements and balance */ t8_forest_set_profiling (forest_partition, 1); diff --git a/benchmarks/t8_time_prism_adapt.cxx b/benchmarks/t8_time_prism_adapt.cxx index 2d7784fe07..3b32c5ec4d 100644 --- a/benchmarks/t8_time_prism_adapt.cxx +++ b/benchmarks/t8_time_prism_adapt.cxx @@ -128,7 +128,7 @@ t8_time_refine (int start_level, int end_level, [[maybe_unused]] int create_fore } forest_partition = forest_adapt; /* partition the adapted forest */ - t8_forest_set_partition (forest_partition, NULL, 0); + t8_forest_set_partition (forest_partition, NULL, 0, nullptr); /* enable profiling for the partitioned forest */ t8_forest_set_profiling (forest_partition, 1); /* if desired do balance */ @@ -181,7 +181,7 @@ main (int argc, char **argv) if (sreturnA > BUFSIZ || sreturnB > BUFSIZ) { /* The usage string or help message was truncated */ - /* Note: gcc >= 7.1 prints a warning if we + /* Note: gcc >= 7.1 prints a warning if we * do not check the return value of snprintf. */ t8_debugf ("Warning: Truncated usage string and help message to '%s' and '%s'\n", usage, help); } diff --git a/example/advect/t8_advection.cxx b/example/advect/t8_advection.cxx index 0853141fff..f7b76dd53e 100644 --- a/example/advect/t8_advection.cxx +++ b/example/advect/t8_advection.cxx @@ -783,7 +783,7 @@ t8_advect_problem_partition (t8_advect_problem_t *problem, int measure_time) /* Enable profiling to measure runtime */ t8_forest_set_profiling (forest_partition, 1); /* Partition the forest and create ghosts */ - t8_forest_set_partition (forest_partition, problem->forest, 0); + t8_forest_set_partition (forest_partition, problem->forest, 0, nullptr); t8_forest_set_ghost (forest_partition, 1, T8_GHOST_FACES); t8_forest_commit (forest_partition); diff --git a/example/forest/t8_test_face_iterate.cxx b/example/forest/t8_test_face_iterate.cxx index d354094aa7..27e0ce1d1c 100644 --- a/example/forest/t8_test_face_iterate.cxx +++ b/example/forest/t8_test_face_iterate.cxx @@ -138,7 +138,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_partition (forest_partition, forest_adapt, 0, nullptr); 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) { @@ -212,7 +212,7 @@ main (int argc, char **argv) if (sreturnA > BUFSIZ || sreturnB > BUFSIZ) { /* The usage string or help message was truncated */ - /* Note: gcc >= 7.1 prints a warning if we + /* Note: gcc >= 7.1 prints a warning if we * do not check the return value of snprintf. */ t8_debugf ("Warning: Truncated usage string and help message to '%s' and '%s'\n", usage, help); } diff --git a/example/forest/t8_test_ghost.cxx b/example/forest/t8_test_ghost.cxx index e1a8bdd215..7730d4f895 100644 --- a/example/forest/t8_test_ghost.cxx +++ b/example/forest/t8_test_ghost.cxx @@ -189,7 +189,7 @@ t8_test_ghost_refine_and_partition (t8_cmesh_t cmesh, const int level, sc_MPI_Co } /* Set the forest for partitioning */ - t8_forest_set_partition (forest_ghost, forest, 0); + t8_forest_set_partition (forest_ghost, forest, 0, nullptr); /* Activate ghost creation */ t8_forest_set_ghost_ext (forest_ghost, 1, T8_GHOST_FACES, ghost_version); /* Activate timers */ @@ -303,7 +303,7 @@ main (int argc, char **argv) if (sreturnA > BUFSIZ || sreturnB > BUFSIZ) { /* The usage string or help message was truncated */ - /* Note: gcc >= 7.1 prints a warning if we + /* Note: gcc >= 7.1 prints a warning if we * do not check the return value of snprintf. */ t8_debugf ("Warning: Truncated usage string and help message to '%s' and '%s'\n", usage, help); } diff --git a/example/forest/t8_test_ghost_large_level_diff.cxx b/example/forest/t8_test_ghost_large_level_diff.cxx index 08115b1393..e79993a6e3 100644 --- a/example/forest/t8_test_ghost_large_level_diff.cxx +++ b/example/forest/t8_test_ghost_large_level_diff.cxx @@ -179,7 +179,7 @@ t8_ghost_large_level_diff (const char *prefix, int dim, int level, int refine, i /* Partition */ t8_forest_init (&forest_partition); - t8_forest_set_partition (forest_partition, forest_adapt, 0); + t8_forest_set_partition (forest_partition, forest_adapt, 0, nullptr); t8_forest_set_ghost_ext (forest_partition, 1, T8_GHOST_FACES, 3); t8_forest_set_profiling (forest_partition, 1); t8_forest_commit (forest_partition); @@ -219,7 +219,7 @@ main (int argc, char *argv[]) if (sreturn >= BUFSIZ) { /* The help message was truncated */ - /* Note: gcc >= 7.1 prints a warning if we + /* Note: gcc >= 7.1 prints a warning if we * do not check the return value of snprintf. */ t8_debugf ("Warning: Truncated help message to '%s'\n", help); } diff --git a/example/remove/t8_example_empty_trees.cxx b/example/remove/t8_example_empty_trees.cxx index e2a95c3fd7..1c5e487b73 100644 --- a/example/remove/t8_example_empty_trees.cxx +++ b/example/remove/t8_example_empty_trees.cxx @@ -66,7 +66,7 @@ t8_strip_of_quads (t8_gloidx_t num_trees, t8_gloidx_t empty_tree, const char **v t8_forest_t forest_adapt; t8_forest_init (&forest_adapt); t8_forest_set_adapt (forest_adapt, forest, t8_adapt_remove, 0); - t8_forest_set_partition (forest_adapt, NULL, 0); + t8_forest_set_partition (forest_adapt, NULL, 0, nullptr); t8_forest_set_user_data (forest_adapt, &empty_tree); t8_forest_commit (forest_adapt); @@ -115,7 +115,7 @@ main (int argc, char **argv) if (sreturnA > BUFSIZ || sreturnB > BUFSIZ) { /* The usage string or help message was truncated */ - /* Note: gcc >= 7.1 prints a warning if we + /* Note: gcc >= 7.1 prints a warning if we * do not check the return value of snprintf. */ t8_debugf ("Warning: Truncated usage string and help message to '%s' and '%s'\n", usage, help); } diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index af4377baf6..986ecb0b41 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -79,7 +79,7 @@ t8_forest_is_incomplete_family (const t8_forest_t forest, const t8_locidx_t ltre scheme->element_new (tree_class, 1, &element_parent_current); scheme->element_new (tree_class, 1, &element_compare); - /* We first assume that we have an (in)complete family with the size of array elements. + /* We first assume that we have an (in)complete family with the size of array elements. * In the following we try to disprove this. */ int family_size = elements_size; @@ -88,9 +88,9 @@ t8_forest_is_incomplete_family (const t8_forest_t forest, const t8_locidx_t ltre const int child_id_current = scheme->element_get_child_id (tree_class, elements[0]); scheme->element_get_parent (tree_class, elements[0], element_parent_current); - /* Elements of the current family could already be passed, so that + /* Elements of the current family could already be passed, so that * the element/family currently under consideration can no longer be coarsened. - * Also, there may be successors of a hypothetical previous family member + * Also, there may be successors of a hypothetical previous family member * that would be overlapped after coarsening. * */ if (child_id_current > 0 && el_considered > 0) { @@ -132,12 +132,12 @@ t8_forest_is_incomplete_family (const t8_forest_t forest, const t8_locidx_t ltre T8_ASSERT (family_size > 0); T8_ASSERT (family_size >= 0 && family_size <= elements_size); - /* There may be successors of a hypothetical later family member (with index + /* There may be successors of a hypothetical later family member (with index * family_size in this family) that would be overlapped after coarsening. */ if (family_size < elements_size) { /* Get level of element after last element of current possible family */ const int level = scheme->element_get_level (tree_class, elements[family_size]); - /* Only elements with higher level then level of current element, can get + /* Only elements with higher level then level of current element, can get * potentially be overlapped. */ if (level > level_current) { /* Compare ancestors */ @@ -160,7 +160,7 @@ t8_forest_is_incomplete_family (const t8_forest_t forest, const t8_locidx_t ltre const int num_siblings = scheme->element_get_num_siblings (tree_class, elements[0]); T8_ASSERT (family_size <= num_siblings); /* If the first/last element at a process boundary is not the first/last - * element of a possible family, we are not guaranteed to consider all + * element of a possible family, we are not guaranteed to consider all * family members.*/ if (el_considered == 0 && child_id_current > 0 && ltree_id == 0 && forest->mpirank > 0) { return 0; @@ -296,9 +296,9 @@ t8_forest_no_overlap ([[maybe_unused]] t8_forest_t forest) * More detailed: * Let e_a and e_b be two elements. * If the level of e_a is equal to the level of the nca of e_a and e_b, - * then e_b is a descendant of e_a. + * then e_b is a descendant of e_a. * If the level of e_b is equal to the level of the nca of e_a and e_b, - * then e_a is a descendant of e_b. + * then e_a is a descendant of e_b. * Thus e_a and e_b overlap in both cases. * Note: If e_a equals e_b, e_a is the descendant of e_b and vice versa. * */ @@ -1297,9 +1297,9 @@ t8_forest_tree_shared ([[maybe_unused]] t8_forest_t forest, [[maybe_unused]] int else { SC_ABORT ("For incomplete trees the method t8_forest_last_tree_shared aka " "t8_forest_tree_shared(forest, 1) is not implemented.\n"); - /* TODO: If last_local_tree is 0 of the current process and it gets 0 as the - * first_local_tree of the bigger process, then it cannot be said whether - * the tree with id 0 is shared or not, since the bigger process could also + /* TODO: If last_local_tree is 0 of the current process and it gets 0 as the + * first_local_tree of the bigger process, then it cannot be said whether + * the tree with id 0 is shared or not, since the bigger process could also * carry an empty forest. */ } /* If global_neighbour_tree_idx == forest->first_local_tree tree is shared */ @@ -1952,7 +1952,7 @@ t8_forest_element_is_leaf (const t8_forest_t forest, const t8_element_t *element T8_ASSERT (t8_forest_is_committed (forest)); T8_ASSERT (t8_forest_tree_is_local (forest, local_tree)); - /* We get the array of the tree's elements and then search in the array of elements for our + /* We get the array of the tree's elements and then search in the array of elements for our * element candidate. */ /* Get the array */ const t8_element_array_t *elements = t8_forest_get_tree_leaf_element_array (forest, local_tree); @@ -1974,7 +1974,7 @@ t8_forest_element_is_leaf (const t8_forest_t forest, const t8_element_t *element /* The element was not found. */ return 0; } - /* An element was found but it may not be the candidate element. + /* An element was found but it may not be the candidate element. * To identify whether the element was found, we compare these two. */ const t8_element_t *check_element = t8_element_array_index_locidx (elements, search_result); T8_ASSERT (check_element != NULL); @@ -2315,7 +2315,7 @@ t8_forest_element_find_owner_old (t8_forest_t forest, t8_gloidx_t gtreeid, t8_el return proc; } else { - /* Get the next owning process. Its first descendant is in fact an element of the tree. + /* Get the next owning process. Its first descendant is in fact an element of the tree. * If it is bigger than the descendant we look for, then proc is the owning process of element. */ proc_next = *(int *) sc_array_index (owners_of_tree, 1); if (*(t8_linearidx_t *) t8_shmem_array_index (forest->global_first_desc, (size_t) proc_next) @@ -2798,7 +2798,8 @@ t8_forest_set_copy (t8_forest_t forest, const t8_forest_t set_from) } void -t8_forest_set_partition (t8_forest_t forest, const t8_forest_t set_from, int set_for_coarsening) +t8_forest_set_partition (t8_forest_t forest, const t8_forest_t set_from, int set_for_coarsening, + t8_weight_fcn_t *weight_callback) { T8_ASSERT (forest != NULL); T8_ASSERT (forest->rc.refcount > 0); @@ -2808,6 +2809,7 @@ t8_forest_set_partition (t8_forest_t forest, const t8_forest_t set_from, int set T8_ASSERT (forest->scheme == NULL); forest->set_for_coarsening = set_for_coarsening; + forest->weight_function = weight_callback; if (set_from != NULL) { /* If set_from = NULL, we assume a previous forest_from was set */ @@ -2956,9 +2958,9 @@ t8_forest_comm_global_num_leaf_elements (t8_forest_t forest) * Check if any tree in a forest refines irregularly. * An irregular refining tree is a tree with an element that does not * refine into 2^dim children. For example the default implementation - * of pyramids. + * of pyramids. * \note This function is MPI collective - * + * * \param[in] forest The forest to check * \return Non-zero if any tree refines irregular */ @@ -3148,7 +3150,8 @@ t8_forest_commit (t8_forest_t forest) /* forest_partition should not change ownership of forest->set_from */ t8_forest_ref (forest->set_from); } - t8_forest_set_partition (forest_partition, forest->set_from, forest->set_for_coarsening); + t8_forest_set_partition (forest_partition, forest->set_from, forest->set_for_coarsening, + forest->weight_function); /* activate profiling, if this forest has profiling */ t8_forest_set_profiling (forest_partition, forest->profile != NULL); /* Commit the partitioned forest */ @@ -3169,7 +3172,7 @@ t8_forest_commit (t8_forest_t forest) /* Initialize the trees array of the forest */ forest->trees = sc_array_new (sizeof (t8_tree_struct_t)); /* partition the forest */ - t8_forest_partition (forest); + t8_forest_partition (forest, forest->weight_function); } } if (forest->from_method & T8_FOREST_FROM_BALANCE) { diff --git a/src/t8_forest/t8_forest_balance.cxx b/src/t8_forest/t8_forest_balance.cxx index 7381628049..50086a03ab 100644 --- a/src/t8_forest/t8_forest_balance.cxx +++ b/src/t8_forest/t8_forest_balance.cxx @@ -245,7 +245,7 @@ t8_forest_balance (t8_forest_t forest, int repartition) t8_forest_init (&forest_partition); /* Update the maximum occurring level */ forest_partition->maxlevel_existing = forest_temp->maxlevel_existing; - t8_forest_set_partition (forest_partition, forest_temp, 0); + t8_forest_set_partition (forest_partition, forest_temp, 0, nullptr); t8_forest_set_ghost (forest_partition, 1, T8_GHOST_FACES); /* If profiling is enabled, measure partition runtimes */ if (forest->profile != NULL) { diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index 0815f75041..afa90aba18 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -48,11 +48,18 @@ typedef enum { T8_GHOST_VERTICES /**< Consider all vertex (codimension 3) and edge and face neighbors. */ } t8_ghost_type_t; -/** This typedef is needed as a helper construct to +/** This typedef is needed as a helper construct to * properly be able to define a function that returns * a pointer to a void fun(void) function. \see t8_forest_get_user_function. */ typedef void (*t8_generic_function_pointer) (void); + +/** + * The prototype a weight function for the partition algorithm. + * The function should be pure, and return a positive weight given a forest, a local tree index and an element index within the local tree + */ +typedef double (t8_weight_fcn_t) (t8_forest_t, t8_locidx_t, t8_locidx_t); + T8_EXTERN_C_BEGIN (); /** Callback function prototype to replace one set of elements with another. @@ -77,12 +84,12 @@ T8_EXTERN_C_BEGIN (); * \param [in] first_incoming The tree local index of the first incoming element. * 0 <= first_incom < new_which_tree->num_elements * - * If an element is being refined, \a refine and \a num_outgoing will be 1 and + * If an element is being refined, \a refine and \a num_outgoing will be 1 and * \a num_incoming will be the number of children. - * If a family is being coarsened, \a refine will be -1, \a num_outgoing will be - * the number of family members and \a num_incoming will be 1. - * If an element is being removed, \a refine and \a num_outgoing will be 1 and - * \a num_incoming will be 0. + * If a family is being coarsened, \a refine will be -1, \a num_outgoing will be + * the number of family members and \a num_incoming will be 1. + * If an element is being removed, \a refine and \a num_outgoing will be 1 and + * \a num_incoming will be 0. * Else \a refine will be 0 and \a num_outgoing and \a num_incoming will both be 1. * \see t8_forest_iterate_replace */ @@ -96,7 +103,7 @@ typedef void (*t8_forest_replace_t) (t8_forest_t forest_old, t8_forest_t forest_ * form a family and we decide whether this family should be coarsened * or only the first element should be refined. * Otherwise \a is_family must equal zero and we consider the first entry - * of the element array for refinement. + * of the element array for refinement. * Entries of the element array beyond the first \a num_elements are undefined. * \param [in] forest The forest to which the new elements belong. * \param [in] forest_from The forest that is adapted. @@ -121,7 +128,7 @@ typedef int (*t8_forest_adapt_t) (t8_forest_t forest, t8_forest_t forest_from, t /** Create a new forest with reference count one. * This forest needs to be specialized with the t8_forest_set_* calls. - * Currently it is mandatory to either call the functions \see t8_forest_set_mpicomm, + * Currently it is mandatory to either call the functions \see t8_forest_set_mpicomm, * \ref t8_forest_set_cmesh, and \ref t8_forest_set_scheme, * or to call one of \ref t8_forest_set_copy, \ref t8_forest_set_adapt, or * \ref t8_forest_set_partition. It is illegal to mix these calls, or to @@ -159,7 +166,7 @@ t8_forest_is_committed (t8_forest_t forest); * \param [in] forest The forest to consider. * \return True if \a forest has no elements which are inside each other. * \note This function is collective, but only checks local overlapping on each process. - * \see t8_forest_partition_test_boundary_element if you also want to test for + * \see t8_forest_partition_test_boundary_element if you also want to test for * global overlap across the process boundaries. */ int @@ -313,6 +320,7 @@ t8_forest_get_user_function (const t8_forest_t forest); * \param [in] set_for_coarsening CURRENTLY DISABLED. If true, then the partitions * are choose such that coarsening an element once is a process local * operation. + * \param [in] weight_callback A callback function defining element weights for the partitioning. If null, all the elements are assumed to have the same weight. Must be free of side-effects. * \note This setting can be combined with \ref t8_forest_set_adapt and \ref * t8_forest_set_balance. The order in which these operations are executed is always * 1) Adapt 2) Partition 3) Balance. @@ -322,7 +330,8 @@ t8_forest_get_user_function (const t8_forest_t forest); * this setting. */ void -t8_forest_set_partition (t8_forest_t forest, const t8_forest_t set_from, int set_for_coarsening); +t8_forest_set_partition (t8_forest_t forest, const t8_forest_t set_from, int set_for_coarsening, + t8_weight_fcn_t *weight_callback); /** Set a source forest to be balanced during commit. * A forest is said to be balanced if each element has face neighbors of level @@ -379,8 +388,8 @@ t8_forest_set_ghost_ext (t8_forest_t forest, int do_ghost, t8_ghost_type_t ghost /** * Use assertions and document that the forest_set (..., from) and - * set_load are mutually exclusive. - * + * set_load are mutually exclusive. + * * TODO: Unused function -> remove? */ void @@ -452,7 +461,7 @@ t8_forest_get_eclass (const t8_forest_t forest, const t8_locidx_t ltreeid); /** * Check whether a given tree id belongs to a local tree in a forest. - * + * * \param [in] forest The forest. * \param [in] local_tree A tree id. * \return True if and only if the id \a local_tree belongs to a local tree of \a forest. @@ -522,7 +531,7 @@ t8_forest_get_coarse_tree (t8_forest_t forest, t8_locidx_t ltreeid); /** * Query whether a given element is a leaf in a forest. - * + * * \param [in] forest The forest. * \param [in] element An element of a local tree in \a forest. * \param [in] local_tree A local tree id of \a forest. @@ -585,7 +594,7 @@ t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8 t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], int *num_neighbors, t8_locidx_t **pelement_indices, t8_eclass_t *pneigh_eclass, int forest_is_balanced); -/** Like \ref t8_forest_leaf_face_neighbors but also provides information about the global neighbors and the orientation. +/** Like \ref t8_forest_leaf_face_neighbors but also provides information about the global neighbors and the orientation. * \param [in] forest The forest. Must have a valid ghost layer. * \param [in] ltreeid A local tree id. * \param [in] leaf A leaf in tree \a ltreeid of \a forest. @@ -603,8 +612,8 @@ t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8 * \param [in] forest_is_balanced True if we know that \a forest is balanced, false * otherwise. * \param [out] gneigh_tree The global tree IDs of the neighbor trees. - * \param [out] orientation If not NULL on input, the face orientation is computed and stored here. - * Thus, if the face connection is an inter-tree connection the orientation of the tree-to-tree connection is stored. + * \param [out] orientation If not NULL on input, the face orientation is computed and stored here. + * Thus, if the face connection is an inter-tree connection the orientation of the tree-to-tree connection is stored. * Otherwise, the value 0 is stored. * All other parameters and behavior are identical to \ref t8_forest_leaf_face_neighbors. * \note If there are no face neighbors, then *neighbor_leaves = NULL, num_neighbors = 0, @@ -854,7 +863,7 @@ t8_forest_element_face_neighbor (t8_forest_t forest, t8_locidx_t ltreeid, const /** * TODO: Can be removed since it is unused. - * + * * \param[in] forest The forest. */ void @@ -862,16 +871,16 @@ t8_forest_iterate (t8_forest_t forest); /** Query whether a batch of points lies inside an element. For bilinearly interpolated elements. * \note For 2D quadrilateral elements this function is only an approximation. It is correct - * if the four vertices lie in the same plane, but it may produce only approximate results if + * if the four vertices lie in the same plane, but it may produce only approximate results if * the vertices do not lie in the same plane. * \param [in] forest The forest. * \param [in] ltreeid The forest local id of the tree in which the element is. * \param [in] element The element. * \param [in] points 3-dimensional coordinates of the points to check * \param [in] num_points The number of points to check - * \param [in, out] is_inside An array of length \a num_points, filled with 0/1 on output. True (non-zero) if a \a point - * lies within an \a element, false otherwise. The return value is also true if the point - * lies on the element boundary. Thus, this function may return true for different leaf + * \param [in, out] is_inside An array of length \a num_points, filled with 0/1 on output. True (non-zero) if a \a point + * lies within an \a element, false otherwise. The return value is also true if the point + * lies on the element boundary. Thus, this function may return true for different leaf * elements, if they are neighbors and the point lies on the common boundary. * \param [in] tolerance Tolerance that we allow the point to not exactly match the element. * If this value is larger we detect more points. diff --git a/src/t8_forest/t8_forest_partition.cxx b/src/t8_forest/t8_forest_partition.cxx index 3708f32119..83d1d6085f 100644 --- a/src/t8_forest/t8_forest_partition.cxx +++ b/src/t8_forest/t8_forest_partition.cxx @@ -32,7 +32,7 @@ T8_EXTERN_C_BEGIN (); /** * For each tree that we send elements from to other processes, - * we send the information stored in this struct to the other process + * we send the information stored in this struct to the other process */ typedef struct { @@ -211,7 +211,7 @@ t8_forest_partition_test_boundary_element ([[maybe_unused]] const t8_forest_t fo return; } if (forest->mpirank == forest->mpisize - 1) { - /* The last process can only share a tree with process rank-1. + /* The last process can only share a tree with process rank-1. * However, this is already tested by process rank-1. */ return; } @@ -220,7 +220,7 @@ t8_forest_partition_test_boundary_element ([[maybe_unused]] const t8_forest_t fo /* The first tree on process rank+1 is not shared with current rank, nothing to do */ return; } - /* The first tree on process rank+1 may be shared but empty. + /* The first tree on process rank+1 may be shared but empty. * Thus, the first descendant id of rank+1 is not of the first local tree. */ if (local_tree_num_elements == 0) { /* check if first not shared tree of process rank+1 contains elements */ @@ -430,48 +430,102 @@ t8_forest_partition_create_tree_offsets (t8_forest_t forest) } } -/* Calculate the new element_offset for forest from - * the element in forest->set_from assuming a partition without element weights */ +// Compute forest->element_offsets according to the weight function, if provided static void -t8_forest_partition_compute_new_offset (t8_forest_t forest) +t8_forest_partition_compute_new_offset (t8_forest_t forest, t8_weight_fcn_t *weight_fcn) { - t8_forest_t forest_from; - sc_MPI_Comm comm; - t8_gloidx_t new_first_element_id; - int i, mpiret, mpisize; - T8_ASSERT (t8_forest_is_initialized (forest)); T8_ASSERT (forest->set_from != NULL); + T8_ASSERT (forest->element_offsets == NULL); - forest_from = forest->set_from; - comm = forest->mpicomm; + t8_forest_t forest_from = forest->set_from; + sc_MPI_Comm comm = forest_from->mpicomm; + int const mpirank = forest_from->mpirank; + int const mpisize = forest_from->mpisize; + t8_gloidx_t const global_num_leaf_elements = forest_from->global_num_leaf_elements; - T8_ASSERT (forest->element_offsets == NULL); - /* Set the shmem array type to comm */ + /* Initialize the shmem array */ t8_shmem_init (comm); t8_shmem_set_type (comm, T8_SHMEM_BEST_TYPE); - /* Initialize the shmem array */ - t8_shmem_array_init (&forest->element_offsets, sizeof (t8_gloidx_t), forest->mpisize + 1, comm); - mpiret = sc_MPI_Comm_size (comm, &mpisize); - SC_CHECK_MPI (mpiret); + t8_shmem_array_init (&forest->element_offsets, sizeof (t8_gloidx_t), mpisize + 1, comm); - if (t8_shmem_array_start_writing (forest->element_offsets)) { - if (forest_from->global_num_leaf_elements > 0) { + if (global_num_leaf_elements == 0) { + if (t8_shmem_array_start_writing (forest->element_offsets)) { t8_gloidx_t *element_offsets = t8_shmem_array_get_gloidx_array_for_writing (forest->element_offsets); - for (i = 0; i < mpisize; i++) { - /* Calculate the first element index for each process. We convert to doubles to prevent overflow */ - new_first_element_id = (((double) i * (long double) forest_from->global_num_leaf_elements) / (double) mpisize); - T8_ASSERT (0 <= new_first_element_id && new_first_element_id < forest_from->global_num_leaf_elements); - element_offsets[i] = new_first_element_id; - } - element_offsets[forest->mpisize] = forest->global_num_leaf_elements; + std::fill_n (element_offsets, mpisize + 1, t8_gloidx_t { 0 }); } - else { + t8_shmem_array_end_writing (forest->element_offsets); + return; + } + + // If no weight function is provided, compute the offsets solely based on the number of elements. + if (not weight_fcn) { + if (t8_shmem_array_start_writing (forest->element_offsets)) { t8_gloidx_t *element_offsets = t8_shmem_array_get_gloidx_array_for_writing (forest->element_offsets); - for (i = 0; i <= mpisize; i++) { - element_offsets[i] = 0; + for (int i = 0; i < mpisize; ++i) { + element_offsets[i] = std::floor (static_cast (global_num_leaf_elements) * i / mpisize); + } + element_offsets[mpisize] = global_num_leaf_elements; + } + t8_shmem_array_end_writing (forest->element_offsets); + return; + } +// Weighted load balancing: +// ------------------------ + double const partition_weight = [&] () { // sum of the weights on the local partition + double local_sum = 0.; + for (t8_locidx_t ltreeid = 0; ltreeid < t8_forest_get_num_local_trees (forest_from); ++ltreeid) { + for (t8_locidx_t ielm = 0; ielm < t8_forest_get_tree_num_leaf_elements (forest_from, ltreeid); ++ielm) { + local_sum += weight_fcn (forest_from, ltreeid, ielm); } } + return local_sum; + }(); + + double const partition_weight_offset = [&] () { // partial sum of the partition weights of all lower-rank processes (excluding the local rank) + double local_offset = 0.; + double local_partition_weight = partition_weight; // because MPI does not like const variables + sc_MPI_Exscan (&local_partition_weight, &local_offset, 1, sc_MPI_DOUBLE, sc_MPI_SUM, comm); + return mpirank > 0 ? local_offset : 0; // because the result of MPI_Exscan is undefined on rank 0 + }(); + + double const forest_weight = [&] () { // complete sum of the partition weights + double total_weight = partition_weight_offset + partition_weight; + sc_MPI_Bcast (&total_weight, 1, sc_MPI_DOUBLE, mpisize - 1, comm); + return total_weight; + }(); + + // The [rank_begin, rank_end) slice of the new offsets land in the local partition + int const rank_begin = std::ceil (mpisize * partition_weight_offset / forest_weight); + int const rank_end = std::ceil (mpisize * (partition_weight_offset + partition_weight) / forest_weight); + std::vector local_offsets (rank_end - rank_begin, 0); + + double accumulated_weight = partition_weight_offset; + t8_gloidx_t global_elm_idx = t8_forest_get_first_local_leaf_element_id (forest_from); + int i = rank_begin; + + for (t8_locidx_t ltreeid = 0; ltreeid < t8_forest_get_num_local_trees (forest_from); ++ltreeid) { + for (t8_locidx_t ielm = 0; ielm < t8_forest_get_tree_num_leaf_elements (forest_from, ltreeid); ++ielm) { + T8_ASSERT (0 <= global_elm_idx && global_elm_idx < global_num_leaf_elements); + accumulated_weight += weight_fcn (forest_from, ltreeid, ielm); + while (accumulated_weight + > forest_weight * i / mpisize) { // there may be empty partitions, hence while and not if + T8_ASSERT (rank_begin <= i && i < rank_end); + local_offsets[i - rank_begin] = global_elm_idx; + ++i; + } + ++global_elm_idx; + } + } + T8_ASSERT (i == rank_end); // i.e. local_offsets has been filled properly + + t8_shmem_array_allgatherv (local_offsets.data (), local_offsets.size (), T8_MPI_GLOIDX, forest->element_offsets, + T8_MPI_GLOIDX, comm); + + if (t8_shmem_array_start_writing (forest->element_offsets)) { + t8_gloidx_t *element_offsets = t8_shmem_array_get_gloidx_array_for_writing (forest->element_offsets); + element_offsets[0] = 0; + element_offsets[mpisize] = global_num_leaf_elements; } t8_shmem_array_end_writing (forest->element_offsets); } @@ -1196,7 +1250,7 @@ t8_forest_partition_given (t8_forest_t forest, const int send_data, const sc_arr * Currently the elements are distributed evenly (each element has the same weight). */ void -t8_forest_partition (t8_forest_t forest) +t8_forest_partition (t8_forest_t forest, t8_weight_fcn_t *weight_callback) { t8_forest_t forest_from; int create_offset_from = 0; @@ -1226,7 +1280,7 @@ t8_forest_partition (t8_forest_t forest) /* TODO: if offsets already exist on forest_from, check it for consistency */ /* We now calculate the new element offsets */ - t8_forest_partition_compute_new_offset (forest); + t8_forest_partition_compute_new_offset (forest, weight_callback); t8_forest_partition_given (forest, 0, NULL, NULL); T8_ASSERT ((size_t) t8_forest_get_num_local_trees (forest_from) == forest_from->trees->elem_count); diff --git a/src/t8_forest/t8_forest_partition.h b/src/t8_forest/t8_forest_partition.h index d5da1ab8b1..190119fe9b 100644 --- a/src/t8_forest/t8_forest_partition.h +++ b/src/t8_forest/t8_forest_partition.h @@ -36,11 +36,14 @@ T8_EXTERN_C_BEGIN (); /** * Populate a forest with the partitioned elements of forest->set_from. * Currently the elements are distributed evenly (each element has the same weight). - * + * * \param [in,out] forest The forest. + * \param [in] weight_callback A callback function defining element weights for the partitioning + * \pre \a weight_callback must be free of side-effects, the behavior is undefined otherwise + * \note If \a weight_callback is null, then all the elements are assumed to have the same weight */ void -t8_forest_partition (t8_forest_t forest); +t8_forest_partition (t8_forest_t forest, t8_weight_fcn_t *weight_callback); /** Create the element_offset array of a partitioned forest. * \param [in,out] forest The forest. @@ -76,13 +79,13 @@ t8_forest_partition_create_first_desc (t8_forest_t forest); void t8_forest_partition_create_tree_offsets (t8_forest_t forest); -/** \brief Re-Partition an array accordingly to a partitioned forest. - * +/** \brief Re-Partition an array accordingly to a partitioned forest. + * * \param[in] forest_from The forest before the partitioning step. * \param[in] forest_to The partitioned forest of \a forest_from. * \param[in] data_in A pointer to an sc_array_t holding data (one value per element) accordingly to \a forest_from. * \param[in,out] data_out A pointer to an already allocated sc_array_t capable of holding data accordingly to \a forest_to. - * + * * \note \a data_in has to be of size equal to the number of local elements of \a forest_from * \a data_out has to be already allocated and has to be of size equal to the number of local elements of \a forest_to. */ diff --git a/src/t8_forest/t8_forest_types.h b/src/t8_forest/t8_forest_types.h index 712358881d..285352860e 100644 --- a/src/t8_forest/t8_forest_types.h +++ b/src/t8_forest/t8_forest_types.h @@ -46,7 +46,7 @@ typedef struct t8_forest_ghost *t8_forest_ghost_t; /**< Defined below */ * The latter 3 can be combined, in which case the order is * 1. Adapt, 2. Partition, 3. Balance. * We store the methods in an int8_t and use these defines to - * distinguish between them. + * distinguish between them. */ typedef int8_t t8_forest_from_t; @@ -69,9 +69,10 @@ typedef struct t8_forest { t8_refcount_t rc; /**< Reference counter. */ - int set_level; /**< Level to use in new construction. */ - int set_for_coarsening; /**< Change partition to allow + int set_level; /**< Level to use in new construction. */ + int set_for_coarsening; /**< Change partition to allow for one round of coarsening */ + t8_weight_fcn_t *weight_function; /**< Pointer to user defined element weight function. Can be null. */ sc_MPI_Comm mpicomm; /**< MPI communicator to use. */ t8_cmesh_t cmesh; /**< Coarse mesh to use. */ @@ -83,7 +84,7 @@ typedef struct t8_forest int dimension; /**< Dimension inferred from \b cmesh. */ int incomplete_trees; /**< Flag to check whether the forest has (potential) incomplete trees. A tree is incomplete if an element has been removed from it. - Once an element got removed, the flag sets to 1 (true) and stays. + Once an element got removed, the flag sets to 1 (true) and stays. For a committed forest this flag is either true on all ranks or false on all ranks. */ @@ -108,8 +109,8 @@ typedef struct t8_forest int mpisize; /**< Number of MPI processes. */ int mpirank; /**< Number of this MPI process. */ - t8_gloidx_t first_local_tree; /**< The global index of the first local tree on this process. - If first_local_tree is larger than last_local_tree then + t8_gloidx_t first_local_tree; /**< The global index of the first local tree on this process. + If first_local_tree is larger than last_local_tree then this processor/forest is empty. See https://github.com/DLR-AMR/t8code/wiki/Tree-indexing */ t8_gloidx_t last_local_tree; /**< The global index of the last local tree on this process. @@ -190,9 +191,9 @@ typedef struct t8_profile } t8_profile_struct_t; -/** +/** * This struct stores various information about a forest's ghost elements and ghost trees. - * + * */ typedef struct t8_forest_ghost { diff --git a/test/t8_forest/t8_gtest_forest_commit.cxx b/test/t8_forest/t8_gtest_forest_commit.cxx index 4137d38545..cac9241a74 100644 --- a/test/t8_forest/t8_gtest_forest_commit.cxx +++ b/test/t8_forest/t8_gtest_forest_commit.cxx @@ -104,7 +104,7 @@ t8_test_forest_commit_abp (t8_forest_t forest, int maxlevel) #if T8_TEST_LEVEL_INT < 2 t8_forest_set_balance (forest_ada_bal_par, NULL, 0); #endif - t8_forest_set_partition (forest_ada_bal_par, NULL, 0); + t8_forest_set_partition (forest_ada_bal_par, NULL, 0, nullptr); t8_forest_commit (forest_ada_bal_par); return forest_ada_bal_par; @@ -114,32 +114,29 @@ t8_test_forest_commit_abp (t8_forest_t forest, int maxlevel) static t8_forest_t t8_test_forest_commit_abp_3step (t8_forest_t forest, int maxlevel) { + /* adapt the forest */ t8_forest_t forest_adapt; - t8_forest_t forest_partition; -#if T8_TEST_LEVEL_INT < 2 - t8_forest_t forest_balance; - t8_forest_init (&forest_balance); -#endif - t8_forest_init (&forest_adapt); - t8_forest_init (&forest_partition); - - /* adapt the forest */ t8_forest_set_user_data (forest_adapt, &maxlevel); t8_forest_set_adapt (forest_adapt, forest, t8_test_adapt_balance, 1); t8_forest_commit (forest_adapt); #if T8_TEST_LEVEL_INT < 2 /* balance the forest */ + t8_forest_t forest_balance; + t8_forest_init (&forest_balance); t8_forest_set_balance (forest_balance, forest_adapt, 0); t8_forest_commit (forest_balance); #endif - /* partition the forest */ + /* partition the forest with a unit weight function */ + t8_forest_t forest_partition; + t8_forest_init (&forest_partition); + auto weight_fcn = [] (t8_forest_t, t8_locidx_t, t8_locidx_t) -> double { return 1; }; #if T8_TEST_LEVEL_INT < 2 - t8_forest_set_partition (forest_partition, forest_balance, 0); + t8_forest_set_partition (forest_partition, forest_balance, 0, weight_fcn); #else - t8_forest_set_partition (forest_partition, forest_adapt, 0); + t8_forest_set_partition (forest_partition, forest_adapt, 0, weight_fcn); #endif t8_forest_commit (forest_partition); @@ -148,7 +145,6 @@ t8_test_forest_commit_abp_3step (t8_forest_t forest, int maxlevel) TEST_P (forest_commit, test_forest_commit) { - t8_forest_t forest; t8_forest_t forest_ada_bal_part; t8_forest_t forest_abp_3part; diff --git a/test/t8_forest/t8_gtest_partition_data.cxx b/test/t8_forest/t8_gtest_partition_data.cxx index f68f1ef6b8..62366be7fc 100644 --- a/test/t8_forest/t8_gtest_partition_data.cxx +++ b/test/t8_forest/t8_gtest_partition_data.cxx @@ -254,7 +254,7 @@ TEST_P (t8_test_partition_data_test, test_partition_data) t8_forest_t partitioned_forest; t8_forest_init (&partitioned_forest); const int partition_for_coarsening = 0; - t8_forest_set_partition (partitioned_forest, initial_forest, partition_for_coarsening); + t8_forest_set_partition (partitioned_forest, initial_forest, partition_for_coarsening, nullptr); t8_forest_commit (partitioned_forest); /* Test the exemplary partition_data with some arithmetic data types as well as with a custom struct. */ diff --git a/test/t8_forest_incomplete/t8_gtest_empty_global_tree.cxx b/test/t8_forest_incomplete/t8_gtest_empty_global_tree.cxx index dc8489bc16..9db23eca1b 100644 --- a/test/t8_forest_incomplete/t8_gtest_empty_global_tree.cxx +++ b/test/t8_forest_incomplete/t8_gtest_empty_global_tree.cxx @@ -28,11 +28,11 @@ #include #include -/** In this test, we are given a forest with 3 global trees. - * We adapt the forest so that all 6 compositions of empty - * global trees are the result of it. +/** In this test, we are given a forest with 3 global trees. + * We adapt the forest so that all 6 compositions of empty + * global trees are the result of it. * Therefore, \a testcase runs from 0 to 5. - * We do this twice. Once we partition the forest in the same call. + * We do this twice. Once we partition the forest in the same call. * The second time, we do the adapting and partitioning separately. * The two resulting forests must be equal. * */ @@ -115,11 +115,11 @@ t8_adapt_forest (t8_forest_t forest_from, t8_forest_adapt_t adapt_fn, int do_ada if (do_adapt) { t8_forest_set_adapt (forest_new, forest_from, adapt_fn, 0); if (do_partition) { - t8_forest_set_partition (forest_new, NULL, 0); + t8_forest_set_partition (forest_new, NULL, 0, nullptr); } } else if (do_partition) { - t8_forest_set_partition (forest_new, forest_from, 0); + t8_forest_set_partition (forest_new, forest_from, 0, nullptr); } if (user_data != NULL) { t8_forest_set_user_data (forest_new, user_data); diff --git a/test/t8_forest_incomplete/t8_gtest_empty_local_tree.cxx b/test/t8_forest_incomplete/t8_gtest_empty_local_tree.cxx index 80316d8e1b..e5165bef08 100644 --- a/test/t8_forest_incomplete/t8_gtest_empty_local_tree.cxx +++ b/test/t8_forest_incomplete/t8_gtest_empty_local_tree.cxx @@ -31,11 +31,11 @@ #define MAX_NUM_RANKS 8 -/* In this test, a partitioned forest with one global tree and at - * least so many elements, such that each process has at least one +/* In this test, a partitioned forest with one global tree and at + * least so many elements, such that each process has at least one * local element is given. Let x be the number of mpi ranks. * There are 2^x many ways to empty these x local trees. - * + * * Example: * x = 3 * instances - binary representation @@ -43,13 +43,13 @@ * 1 - 0 1 * 2 - 1 0 * 3 - 1 1 - * We remove all elements from rank with id i if the i`th bit + * We remove all elements from rank with id i if the i`th bit * in the current instances is 0. - * + * * Note, this test runs only on two to maxmal 8 ranks. - * - * We adapt the given forest twice. - * The first time, we partition the forest in the same call. + * + * We adapt the given forest twice. + * The first time, we partition the forest in the same call. * The second time, we do the adapting and partitioning separately. * The two resulting forests must be equal. */ @@ -82,7 +82,7 @@ class DISABLED_local_tree: public testing::TestWithParam { t8_forest_t forest; }; -/** This structure contains a bitset with all +/** This structure contains a bitset with all * local trees on all processes to be removed. */ struct t8_trees_to_remove @@ -90,7 +90,7 @@ struct t8_trees_to_remove std::bitset remove; }; -/** Remove every element of rank i if the i`th bit in +/** Remove every element of rank i if the i`th bit in * the current instance \a remove is 0. */ static int t8_adapt_remove (t8_forest_t forest, t8_forest_t forest_from, [[maybe_unused]] t8_locidx_t which_tree, @@ -114,11 +114,11 @@ t8_adapt_forest (t8_forest_t forest_from, t8_forest_adapt_t adapt_fn, int do_ada if (do_adapt) { t8_forest_set_adapt (forest_new, forest_from, adapt_fn, 0); if (do_partition) { - t8_forest_set_partition (forest_new, NULL, 0); + t8_forest_set_partition (forest_new, NULL, 0, nullptr); } } else if (do_partition) { - t8_forest_set_partition (forest_new, forest_from, 0); + t8_forest_set_partition (forest_new, forest_from, 0, nullptr); } if (user_data != NULL) { t8_forest_set_user_data (forest_new, user_data); diff --git a/test/t8_forest_incomplete/t8_gtest_iterate_replace.cxx b/test/t8_forest_incomplete/t8_gtest_iterate_replace.cxx index 2f9ce5e483..f97d59273f 100644 --- a/test/t8_forest_incomplete/t8_gtest_iterate_replace.cxx +++ b/test/t8_forest_incomplete/t8_gtest_iterate_replace.cxx @@ -71,7 +71,7 @@ struct t8_return_data int *callbacks; }; -/** Inside the callback of iterate_replace we compare \a refine +/** Inside the callback of iterate_replace we compare \a refine * with the according return value of the callback of forest_adapt. * If true, we check the parameter \a num_outgoing, \a first_outgoing * \a num_incoming and \a first_incoming for correctness. */ @@ -210,11 +210,11 @@ t8_adapt_forest (t8_forest_t forest_from, t8_forest_adapt_t adapt_fn, int do_ada if (do_adapt) { t8_forest_set_adapt (forest_new, forest_from, adapt_fn, 0); if (do_partition) { - t8_forest_set_partition (forest_new, NULL, 0); + t8_forest_set_partition (forest_new, NULL, 0, nullptr); } } else if (do_partition) { - t8_forest_set_partition (forest_new, forest_from, 0); + t8_forest_set_partition (forest_new, forest_from, 0, nullptr); } if (user_data != NULL) { t8_forest_set_user_data (forest_new, user_data); diff --git a/test/t8_schemes/t8_gtest_element_ref_coords.cxx b/test/t8_schemes/t8_gtest_element_ref_coords.cxx index 6fba562446..f944fd53bb 100644 --- a/test/t8_schemes/t8_gtest_element_ref_coords.cxx +++ b/test/t8_schemes/t8_gtest_element_ref_coords.cxx @@ -234,7 +234,7 @@ class class_ref_coords: public testing::TestWithParam /* std::string */ #include /* std::array */ -/* We use this data to control to which level the elements at which +/* We use this data to control to which level the elements at which * geometry get refined. */ struct t8_naca_geometry_adapt_data { @@ -61,7 +61,7 @@ struct t8_naca_geometry_adapt_data int *levels; /** Array with refinement levels */ }; -/** +/** * The adaptation callback function. This function will be called once for each element * and the return value decides whether this element should be refined or not. * return > 0 -> This element should get refined. @@ -72,11 +72,11 @@ struct t8_naca_geometry_adapt_data * return > 0 -> The first element should get refined. * return = 0 -> The first element should not get refined. * return < 0 -> The whole family should get coarsened. - * + * * In this case, the function retrieves the geometry information of the tree the element belongs to. * Based on that the function looks whether the tree is linked to a specific geometry * and if this element touches this geometry. If true, it returns 1. Otherwise it returns 0. - * + * * \param [in] forest The current forest that is in construction. * \param [in] forest_from The forest from which we adapt the current forest (in our case, the uniform forest) * \param [in] which_tree The process local id of the current tree. @@ -130,9 +130,9 @@ t8_naca_geometry_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8 return 0; } -/** +/** * The geometry refinement function. Here, we refine all elements, which touch certain geometries. - * + * * \param [in] forest The forest that has to be refined * \param [in] fileprefix The prefix of the msh and brep file. * Influences only the vtu file name. @@ -172,7 +172,7 @@ t8_naca_geometry_refinement (t8_forest_t forest, const std::string &fileprefix, geometries.data (), /* Array with geometry indices */ levels.data () /* Array with refinement levels */ }; - /* Adapt and balance the forest. + /* Adapt and balance the forest. * Note, that we have to hand the adapt data to the forest before the commit. */ t8_forest_init (&forest_new); t8_forest_set_adapt (forest_new, forest, t8_naca_geometry_adapt_callback, 1); @@ -204,7 +204,7 @@ struct t8_naca_plane_adapt_data int rlevel; /* The max refinement level */ }; -/** +/** * The adaptation callback function. This function will be called once for each element * and the return value decides whether this element should be refined or not. * return > 0 -> This element should get refined. @@ -215,12 +215,12 @@ struct t8_naca_plane_adapt_data * return > 0 -> The first element should get refined. * return = 0 -> The first element should not get refined. * return < 0 -> The whole family should get coarsened. - * + * * In this case the function checks whether the element or family is in a certain proximity to a refinement plane. * If true and the element does not have a max level, 1 is returned. If a family of elements - * is too far away and the level is not below or equal the min level threshold -1 is returned. + * is too far away and the level is not below or equal the min level threshold -1 is returned. * Otherwise 0 is returned. - * + * * \param [in] forest The current forest that is in construction. * \param [in] forest_from The forest from which we adapt the current forest (in our case, the uniform forest) * \param [in] which_tree The process local id of the current tree. @@ -264,10 +264,10 @@ t8_naca_plane_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8_lo return 0; } -/** +/** * The plane refinement function. Here we create a refinement loop, which moves a plane * through the mesh and refines it near the plane. - * + * * \param [in] forest The forest which has to be refined. * \param [in] fileprefix The prefix of the msh and brep file. * Influences only the vtu file name. @@ -300,12 +300,12 @@ t8_naca_plane_refinement (t8_forest_t forest, const std::string &fileprefix, int /* Moving plane loop */ while (adapt_data.t < steps) { - /* Adapt and balance the forest. + /* Adapt and balance the forest. * Note, that we have to hand the adapt data to the forest before the commit. */ t8_forest_init (&forest_new); t8_forest_set_adapt (forest_new, forest, t8_naca_plane_adapt_callback, 1); t8_forest_set_user_data (forest_new, &adapt_data); - t8_forest_set_partition (forest_new, forest, 0); + t8_forest_set_partition (forest_new, forest, 0, nullptr); t8_forest_set_balance (forest_new, forest, 0); t8_forest_commit (forest_new); diff --git a/tutorials/general/t8_step4_partition_balance_ghost.cxx b/tutorials/general/t8_step4_partition_balance_ghost.cxx index a7d97f858a..48190b0acb 100644 --- a/tutorials/general/t8_step4_partition_balance_ghost.cxx +++ b/tutorials/general/t8_step4_partition_balance_ghost.cxx @@ -24,30 +24,30 @@ * * This is step4 of the t8code tutorials. * After generating a coarse mesh (step1), building a uniform forest - * on it (step2) and adapting this forest (step3) + * on it (step2) and adapting this forest (step3) * we will now learn how to control the forest creation in more detail, * how to partition and balance a forest and how to generate a layer of ghost elements. - * + * * Partition: Each forest is distributed among the MPI processes. Partitioning a forest means - * to change this distribution in order to maintain a load balance. After we + * to change this distribution in order to maintain a load balance. After we * applied partition to a forest the new forest will have equal numbers of elements * on each process (+- 1). * In our example we start with a uniform forest from a cmesh. This forest is constructed - * such that each process has the same number of elements. + * such that each process has the same number of elements. * We then adapt this forest, thus refining and coarsening its elements. This changes the * number of elements on each process and will almost always result in a load imbalance. - * You can verify this yourself by printing the process local number on each process for the + * You can verify this yourself by printing the process local number on each process for the * adapted forest (for example with t8_productionf). * In order to reestablish a load balance, we will construct a new forest from the adapted * one via the partition feature. - * - * Ghost: Many applications require a layer of ghost elements. Ghost element of a process are + * + * Ghost: Many applications require a layer of ghost elements. Ghost element of a process are * those elements that are not local to this process but have a (face) neighbor element that is. * Telling a forest to create a ghost layer will gather all necessary information and give * us access to the ghost elements. * t8code does not create a layer of ghost elements by default, thus our initial uniform forest * and the adapted forest will not have one. - * + * * Balance: A forest fulfills the balance property if (and only if) for each element its (face) neighbors * have refinement level at most +1 or -1 in comparison to the element's level. * The balance property is often broken after adaptation. The Balance algorithm iterates through @@ -65,7 +65,7 @@ * Note that balance changes the local number of elements and thus may also change the load balance * and hence require repartitioning. * Balance is usually the most expensive of t8code's mesh manipulation algorithms. - * + * * How you can experiment here: * Partition: * - Test the program with different numbers of processes and compare the local @@ -79,7 +79,7 @@ * on treeid to only view those elements with treeid = -1. * Balance: * - View the unbalanced and balanced forest vtu files and compare them. - * You can color the elements by level. + * You can color the elements by level. * - Exercise: Apply balance to forest_adapt and not to the twice adapted forest. * - Set the no_repartition flag of t8_forest_set_balance to true and observe how the * partition of the resulting forest changes. @@ -100,25 +100,25 @@ T8_EXTERN_C_BEGIN (); * However, t8code offers us more control over the creation of forests. * For example we can control whether or not a forest should have a ghost layer, * be balanced/partitioned from another forest, etc. - * + * * Usually, there are three steps involved in creating a forest: * 1. Initialize the forest with t8_forest_init. - * This function will prepare a forest by setting default values for its members and + * This function will prepare a forest by setting default values for its members and * initializing necessary structures. * 2. Set all properties that the forest should have. * Here we can for example set a cmesh and refinement scheme or specify that the * forest should be adapted/partitioned/balanced from another forest etc. * See the t8_forest_set functions in t8_forest.h * The order in which you call the set functions does not matter. - * 3. Commit the forest with t8_forest_commit. + * 3. Commit the forest with t8_forest_commit. * In this step the forest is actually created after setting all - * desired properties. The forest cannot be changed after it was committed. - * + * desired properties. The forest cannot be changed after it was committed. + * * The t8_forest_new functions are just wrappers around this process. */ /* In this function we create a new forest that repartitions a given forest - * and has a layer of ghost elements. + * and has a layer of ghost elements. */ static t8_forest_t t8_step4_partition_ghost (t8_forest_t forest) @@ -135,17 +135,17 @@ t8_step4_partition_ghost (t8_forest_t forest) * This will change the distribution of the forest elements among the processes * in such a way that afterwards each process has the same number of elements * (+- 1 if the number of elements is not divisible by the number of processes). - * + * * The third 0 argument is the flag 'partition_for_coarsening' which is currently not * implemented. Once it is, this will ensure that a family of elements will not be split * across multiple processes and thus one level coarsening is always possible (see also the * comments on coarsening in t8_step3). */ - t8_forest_set_partition (new_forest, forest, 0); + t8_forest_set_partition (new_forest, forest, 0, nullptr); /* Tell the new_forest to create a ghost layer. * This will gather those face neighbor elements of process local element that reside * on a different process. - * + * * We currently support ghost mode T8_GHOST_FACES that creates face neighbor ghost elements * and will in future also support other modes for edge/vertex neighbor ghost elements. */ @@ -156,7 +156,7 @@ t8_step4_partition_ghost (t8_forest_t forest) return new_forest; } -/* In this function we adapt a forest as in step3 and balance it. +/* In this function we adapt a forest as in step3 and balance it. * In our main program the input forest is already adapted and then the resulting twice adapted forest will be unbalanced. */ static t8_forest_t diff --git a/tutorials/general/t8_step5_element_data.cxx b/tutorials/general/t8_step5_element_data.cxx index a496799497..fbef32940f 100644 --- a/tutorials/general/t8_step5_element_data.cxx +++ b/tutorials/general/t8_step5_element_data.cxx @@ -23,22 +23,22 @@ /* See also: https://github.com/DLR-AMR/t8code/wiki/Step-5---Store-element-data * * This is step5 of the t8code tutorials. - * In the following we will store data in the individual elements of our forest. - * To do this, we will again create a uniform forest, which will get adapted as in step4, + * In the following we will store data in the individual elements of our forest. + * To do this, we will again create a uniform forest, which will get adapted as in step4, * with the difference that we partition, balance and create ghost elements all in the same step. - * After adapting the forest we will learn how to build a data array and gather data for + * After adapting the forest we will learn how to build a data array and gather data for * the local elements. Furthermore, we exchange the data values of the ghost elements and * output the volume data to vtu. * * How you can experiment here: * - Look at the paraview output files of the adapted forest. - * You can apply a clip filter to look into the cube. Also you can apply (in addition) + * You can apply a clip filter to look into the cube. Also you can apply (in addition) * the threshold filter to display only elements with certain properties. * But at first you may just want to enter the tooltip selection mode 'Hover Cells On' * to display cell information when hover over them. * - Change the adaptation criterion as you wish to adapt elements or families as desired. * - Store even more data per element, for instance the coordinates of its midpoint. - * You can again apply the threshold filter to your new data. Don't forget to write the + * You can again apply the threshold filter to your new data. Don't forget to write the * data into the output file. * */ @@ -79,7 +79,7 @@ t8_step5_build_forest (sc_MPI_Comm comm, int level) t8_forest_init (&forest_apbg); t8_forest_set_user_data (forest_apbg, &adapt_data); t8_forest_set_adapt (forest_apbg, forest, t8_step3_adapt_callback, 0); - t8_forest_set_partition (forest_apbg, NULL, 0); + t8_forest_set_partition (forest_apbg, NULL, 0, nullptr); t8_forest_set_balance (forest_apbg, NULL, 0); t8_forest_set_ghost (forest_apbg, 1, T8_GHOST_FACES); t8_forest_commit (forest_apbg); @@ -104,7 +104,7 @@ t8_step5_create_element_data (t8_forest_t forest) /* Now we need to build an array of our data that is as long as the number * of elements plus the number of ghosts. You can use any allocator such as - * new, malloc or the t8code provide allocation macro T8_ALLOC. + * new, malloc or the t8code provide allocation macro T8_ALLOC. * Note that in the latter case you need * to use T8_FREE in order to free the memory. */ @@ -185,7 +185,7 @@ t8_step5_exchange_ghost_data (t8_forest_t forest, struct t8_step5_data_per_eleme } /* Write the forest as vtu and also write the element's volumes in the file. - * + * * t8code supports writing element based data to vtu as long as its stored * as doubles. Each of the data fields to write has to be provided in its own * array of length num_local_elements. @@ -215,7 +215,7 @@ t8_step5_output_data_to_vtu (t8_forest_t forest, struct t8_step5_data_per_elemen } { /* To write user defined data, we need the extended output function t8_forest_vtk_write_file - * from t8_forest_vtk.h. Despite writing user data, it also offers more control over which + * from t8_forest_vtk.h. Despite writing user data, it also offers more control over which * properties of the forest to write. */ int write_treeid = 1; int write_mpirank = 1; diff --git a/tutorials/general/t8_step5_element_data_c_interface.c b/tutorials/general/t8_step5_element_data_c_interface.c index fb98104d7d..378e4e686d 100644 --- a/tutorials/general/t8_step5_element_data_c_interface.c +++ b/tutorials/general/t8_step5_element_data_c_interface.c @@ -23,22 +23,22 @@ /* See also: https://github.com/DLR-AMR/t8code/wiki/Step-5---Store-element-data * * This is step5 of the t8code tutorials using the C interface of t8code. - * In the following we will store data in the individual elements of our forest. - * To do this, we will again create a uniform forest, which will get adapted as in step4, + * In the following we will store data in the individual elements of our forest. + * To do this, we will again create a uniform forest, which will get adapted as in step4, * with the difference that we partition, balance and create ghost elements all in the same step. - * After adapting the forest we will learn how to build a data array and gather data for + * After adapting the forest we will learn how to build a data array and gather data for * the local elements. Furthermore, we exchange the data values of the ghost elements and * output the volume data to vtu. * * How you can experiment here: * - Look at the paraview output files of the adapted forest. - * You can apply a clip filter to look into the cube. Also you can apply (in addition) + * You can apply a clip filter to look into the cube. Also you can apply (in addition) * the threshold filter to display only elements with certain properties. * But at first you may just want to enter the tooltip selection mode 'Hover Cells On' * to display cell information when hover over them. * - Change the adaptation criterion as you wish to adapt elements or families as desired. * - Store even more data per element, for instance the coordinates of its midpoint. - * You can again apply the threshold filter to your new data. Don't forget to write the + * You can again apply the threshold filter to your new data. Don't forget to write the * data into the output file. * */ @@ -81,7 +81,7 @@ t8_step5_build_forest (sc_MPI_Comm comm, int level) t8_forest_init (&forest_apbg); t8_forest_set_user_data (forest_apbg, &adapt_data); t8_forest_set_adapt (forest_apbg, forest, t8_step3_adapt_callback, 0); - t8_forest_set_partition (forest_apbg, NULL, 0); + t8_forest_set_partition (forest_apbg, NULL, 0, nullptr); t8_forest_set_balance (forest_apbg, NULL, 0); t8_forest_set_ghost (forest_apbg, 1, T8_GHOST_FACES); t8_forest_commit (forest_apbg); @@ -106,7 +106,7 @@ t8_step5_create_element_data (t8_forest_t forest) /* Now we need to build an array of our data that is as long as the number * of elements plus the number of ghosts. You can use any allocator such as - * new, malloc or the t8code provide allocation macro T8_ALLOC. + * new, malloc or the t8code provide allocation macro T8_ALLOC. * Note that in the latter case you need * to use T8_FREE in order to free the memory. */ @@ -188,7 +188,7 @@ t8_step5_exchange_ghost_data (t8_forest_t forest, struct t8_step5_data_per_eleme } /* Write the forest as vtu and also write the element's volumes in the file. - * + * * t8code supports writing element based data to vtu as long as its stored * as doubles. Each of the data fields to write has to be provided in its own * array of length num_local_elements. @@ -218,7 +218,7 @@ t8_step5_output_data_to_vtu (t8_forest_t forest, struct t8_step5_data_per_elemen } { /* To write user defined data, we need to extended output function t8_forest_vtk_write_file - * from t8_forest_vtk.h. Despite writing user data, it also offers more control over which + * from t8_forest_vtk.h. Despite writing user data, it also offers more control over which * properties of the forest to write. */ int write_treeid = 1; int write_mpirank = 1; diff --git a/tutorials/general/t8_step6_stencil.cxx b/tutorials/general/t8_step6_stencil.cxx index 7bb63d0fb5..1e2cfb2223 100644 --- a/tutorials/general/t8_step6_stencil.cxx +++ b/tutorials/general/t8_step6_stencil.cxx @@ -23,10 +23,10 @@ /* See also: https://github.com/DLR-AMR/t8code/wiki/Step-6-Computing-stencils * * This is step6 of the t8code tutorials using the C++ interface of t8code. - * In the following we will store data in the individual elements of our forest. - * To do this, we will create a uniform forest in 2D, which will get adapted, + * In the following we will store data in the individual elements of our forest. + * To do this, we will create a uniform forest in 2D, which will get adapted, * partitioned, balanced and create ghost elements all in one go. - * After adapting the forest we build a data array and gather data for + * After adapting the forest we build a data array and gather data for * the local elements. Next, we exchange the data values of the ghost elements and compute * various stencils resp. finite differences. Finally, vtu files are stored with three * custom data fields. @@ -98,7 +98,7 @@ t8_step6_build_forest (sc_MPI_Comm comm, int dim, int level) t8_forest_init (&forest_apbg); t8_forest_set_user_data (forest_apbg, &adapt_data); t8_forest_set_adapt (forest_apbg, forest, t8_step3_adapt_callback, 0); - t8_forest_set_partition (forest_apbg, NULL, 0); + t8_forest_set_partition (forest_apbg, NULL, 0, nullptr); t8_forest_set_balance (forest_apbg, NULL, 0); t8_forest_set_ghost (forest_apbg, 1, T8_GHOST_FACES); t8_forest_commit (forest_apbg); @@ -299,7 +299,7 @@ t8_step6_exchange_ghost_data (t8_forest_t forest, struct data_per_element *data) } /* Write the forest as vtu and also write the element's volumes in the file. - * + * * t8code supports writing element based data to vtu as long as its stored * as doubles. Each of the data fields to write has to be provided in its own * array of length num_local_elements.