From 4a6b447d069ae2a8d61ec59de22cb18393d82e71 Mon Sep 17 00:00:00 2001 From: "Dreyer, Lukas" Date: Mon, 25 Mar 2024 08:52:27 +0100 Subject: [PATCH 1/5] Introduce populate functionality according to cmesh borders --- src/t8_forest/t8_forest.c | 55 +++++- src/t8_forest/t8_forest_cxx.cxx | 158 +++++++++++++----- src/t8_forest/t8_forest_general.h | 7 + src/t8_forest/t8_forest_private.h | 3 + src/t8_forest/t8_forest_types.h | 4 +- test/Makefile.am | 10 ++ .../t8_gtest_forest_new_from_part_cmesh.cxx | 125 ++++++++++++++ 7 files changed, 311 insertions(+), 51 deletions(-) create mode 100644 test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx diff --git a/src/t8_forest/t8_forest.c b/src/t8_forest/t8_forest.c index 7a0a6c397c..cf1692bb20 100644 --- a/src/t8_forest/t8_forest.c +++ b/src/t8_forest/t8_forest.c @@ -60,6 +60,7 @@ t8_forest_init (t8_forest_t *pforest) forest->maxlevel_existing = -1; forest->stats_computed = 0; forest->incomplete_trees = -1; + forest->set_initial_partition_according_to_cmesh = 0; } int @@ -139,6 +140,13 @@ t8_forest_set_level (t8_forest_t forest, int level) forest->set_level = level; } +void +t8_forest_set_uniform_partition_from_cmesh (t8_forest_t forest, t8_cmesh_t cmesh, sc_MPI_Comm comm) +{ + t8_forest_set_cmesh (forest, cmesh, comm); + forest->set_initial_partition_according_to_cmesh = 1; +} + void t8_forest_set_copy (t8_forest_t forest, const t8_forest_t set_from) { @@ -381,11 +389,10 @@ t8_forest_refines_irregular (t8_forest_t forest) * \param[in] forest The forest to populate */ static void -t8_forest_populate_irregular (t8_forest_t forest) +t8_forest_populate_irregular (t8_forest_t forest, int repartition) { t8_forest_t forest_zero; t8_forest_t forest_tmp; - t8_forest_t forest_tmp_partition; t8_cmesh_ref (forest->cmesh); t8_scheme_cxx_ref (forest->scheme_cxx); /* We start with a level 0 uniform refinement */ @@ -400,16 +407,15 @@ t8_forest_populate_irregular (t8_forest_t forest) t8_forest_init (&forest_tmp); t8_forest_set_level (forest_tmp, i); t8_forest_set_adapt (forest_tmp, forest_zero, t8_forest_refine_everything, 0); + if (!forest->set_initial_partition_according_to_cmesh) { + t8_forest_set_partition (forest_tmp, forest_zero, 0); + } t8_forest_commit (forest_tmp); - /* Partition the forest to even the load */ - t8_forest_init (&forest_tmp_partition); - t8_forest_set_partition (forest_tmp_partition, forest_tmp, 0); - t8_forest_commit (forest_tmp_partition); - forest_zero = forest_tmp_partition; + forest_zero = forest_tmp; } /* Copy all elements over to the original forest. */ t8_forest_copy_trees (forest, forest_zero, 1); - t8_forest_unref (&forest_tmp_partition); + t8_forest_unref (&forest_tmp); /* same as forest zero */ } void @@ -450,12 +456,15 @@ t8_forest_commit (t8_forest_t forest) mpiret = sc_MPI_Comm_rank (forest->mpicomm, &forest->mpirank); SC_CHECK_MPI (mpiret); /* Compute the maximum allowed refinement level */ + t8_debugf ("compute maxlevel of forest:\n"); t8_forest_compute_maxlevel (forest); + t8_debugf ("computed maxlevel: %i\n", forest->maxlevel); T8_ASSERT (forest->set_level <= forest->maxlevel); /* populate a new forest with tree and quadrant objects */ if (t8_forest_refines_irregular (forest) && forest->set_level > 0) { /* On root level we will also use the normal algorithm */ - t8_forest_populate_irregular (forest); + t8_forest_populate_irregular (forest, 1); + // t8_forest_populate_irregular (forest, !forest->set_initial_partition_according_to_cmesh); } else { t8_forest_populate (forest); @@ -1431,6 +1440,34 @@ t8_forest_new_uniform (t8_cmesh_t cmesh, t8_scheme_cxx_t *scheme, const int leve return forest; } +t8_forest_t +t8_forest_new_uniform_with_cmesh_partition (t8_cmesh_t cmesh, t8_scheme_cxx_t *scheme, const int level, + const int do_face_ghost, sc_MPI_Comm comm) +{ + t8_forest_t forest; + + T8_ASSERT (t8_cmesh_is_committed (cmesh)); + T8_ASSERT (scheme != NULL); + T8_ASSERT (0 <= level); + + /* Initialize the forest */ + t8_forest_init (&forest); + /* Set the cmesh, scheme and level */ + t8_forest_set_uniform_partition_from_cmesh (forest, cmesh, comm); + t8_forest_set_scheme (forest, scheme); + t8_forest_set_level (forest, level); + if (do_face_ghost) { + t8_forest_set_ghost (forest, 1, T8_GHOST_FACES); + } + + /* commit the forest */ + t8_forest_commit (forest); + t8_global_productionf ("Constructed uniform forest with %lli global elements.\n", + (long long) forest->global_num_elements); + + return forest; +} + t8_forest_t t8_forest_new_adapt (t8_forest_t forest_from, t8_forest_adapt_t adapt_fn, int recursive, int do_face_ghost, void *user_data) diff --git a/src/t8_forest/t8_forest_cxx.cxx b/src/t8_forest/t8_forest_cxx.cxx index 0981dfd492..3659a24d9c 100644 --- a/src/t8_forest/t8_forest_cxx.cxx +++ b/src/t8_forest/t8_forest_cxx.cxx @@ -192,6 +192,9 @@ t8_forest_compute_maxlevel (t8_forest_t forest) } } } + int global_maxlevel; + sc_MPI_Allreduce (&forest->maxlevel, &global_maxlevel, 1, sc_MPI_INT, sc_MPI_MAX, forest->mpicomm); + forest->maxlevel = global_maxlevel; T8_ASSERT (forest->maxlevel >= 0); t8_debugf ("Computed maxlevel %i\n", forest->maxlevel); } @@ -1137,86 +1140,159 @@ t8_forest_compute_desc (t8_forest_t forest) } } +void +t8_forest_populate_according_to_cmesh (t8_forest_t forest) +{ + SC_CHECK_ABORT (!forest->cmesh->first_tree_shared, "Cmesh needs to be partitioned without shared elements \n"); + forest->first_local_tree = t8_cmesh_get_first_treeid (forest->cmesh); + int num_local_trees = t8_cmesh_get_num_local_trees (forest->cmesh); + forest->last_local_tree = forest->first_local_tree + num_local_trees - 1; + + forest->global_num_elements = forest->local_num_elements = 0; + int is_empty = (num_local_trees == 0); + t8_linearidx_t count_elements; + /* create only the non-empty tree objects */ + if (is_empty) { + t8_debugf ("is empty\n"); + /* This processor is empty + * we still set the tree array to store 0 as the number of trees here */ + forest->trees = sc_array_new (sizeof (t8_tree_struct_t)); + count_elements = 0; + /* Set the first local tree larger than the last local tree to + * indicate empty forest */ + forest->first_local_tree = forest->last_local_tree + 1; + } + else { + t8_debugf ("has %i trees\n", num_local_trees); + /* for each tree, allocate elements */ + T8_ASSERT (num_local_trees == forest->last_local_tree - forest->first_local_tree + 1); + + forest->trees = sc_array_new_count (sizeof (t8_tree_struct_t), num_local_trees); + t8_gloidx_t first_ctree = t8_cmesh_get_first_treeid (forest->cmesh); + count_elements = 0; /** apparently this cannot be in the for loop initialisation. */ + for (t8_gloidx_t jt = forest->first_local_tree; jt <= forest->last_local_tree; jt++) { + t8_tree_t tree = (t8_tree_t) t8_sc_array_index_locidx (forest->trees, jt - forest->first_local_tree); + t8_eclass_t tree_class = tree->eclass = t8_cmesh_get_tree_class (forest->cmesh, jt - first_ctree); + tree->elements_offset = count_elements; + t8_eclass_scheme_c *eclass_scheme = forest->scheme_cxx->eclass_schemes[tree_class]; + T8_ASSERT (eclass_scheme != NULL); + t8_element_array_t *telements = &tree->elements; + /* calculate first and last element on this tree */ + t8_locidx_t start = 0; + t8_locidx_t end = eclass_scheme->t8_element_count_leaves_from_root (forest->set_level); + t8_debugf ("end: %i\n", end); + /* Allocate elements for this processor. */ + t8_element_array_init_size (telements, eclass_scheme, end - start); + t8_element_t *element = t8_element_array_index_locidx (telements, 0); + eclass_scheme->t8_element_set_linear_id (element, forest->set_level, start); + count_elements++; + for (t8_locidx_t et = start + 1; et < end; et++, count_elements++) { + t8_element_t *element_succ = t8_element_array_index_locidx (telements, et - start); + T8_ASSERT (eclass_scheme->t8_element_level (element) == forest->set_level); + eclass_scheme->t8_element_successor (element, element_succ); + element = element_succ; + } + t8_debugf ("count_elements in loop:%li\n", count_elements); + } + t8_debugf ("count_elements:%li\n", count_elements); + } + forest->local_num_elements = count_elements; + /* TODO: if no tree has pyramid type we can optimize this to global_num_elements = global_num_trees * 2^(dim*level) */ + t8_forest_comm_global_num_elements (forest); + /* TODO: figure out global_first_position, global_first_quadrant without comm */ +} + /* Create the elements on this process given a uniform partition of the coarse mesh. */ void t8_forest_populate (t8_forest_t forest) { - t8_gloidx_t child_in_tree_begin; - t8_gloidx_t child_in_tree_end; - t8_locidx_t count_elements; - t8_locidx_t num_tree_elements; - t8_locidx_t num_local_trees; - t8_gloidx_t jt, first_ctree; - t8_gloidx_t start, end, et; - t8_tree_t tree; - t8_element_t *element, *element_succ; - t8_element_array_t *telements; - t8_eclass_t tree_class; - t8_eclass_scheme_c *eclass_scheme; - t8_gloidx_t cmesh_first_tree, cmesh_last_tree; - int is_empty; - SC_CHECK_ABORT (forest->set_level <= forest->maxlevel, "Given refinement level exceeds the maximum.\n"); - /* TODO: create trees and quadrants according to uniform refinement */ - t8_cmesh_uniform_bounds (forest->cmesh, forest->set_level, forest->scheme_cxx, &forest->first_local_tree, - &child_in_tree_begin, &forest->last_local_tree, &child_in_tree_end, NULL); + + t8_gloidx_t cmesh_first_tree = t8_cmesh_get_first_treeid (forest->cmesh); + t8_gloidx_t cmesh_last_tree = cmesh_first_tree + t8_cmesh_get_num_local_trees (forest->cmesh) - 1; + t8_gloidx_t + child_in_tree_begin; /** does not get filled when the cmesh does not contain any shared elements between processes */ + t8_gloidx_t child_in_tree_end; /** see above*/ + + if (forest->set_initial_partition_according_to_cmesh) { + SC_CHECK_ABORT (!forest->cmesh->first_tree_shared, "Cmesh needs to be partitioned without shared elements for a " + "forest that is partitioned in the same way as the cmesh \n"); + forest->first_local_tree = cmesh_first_tree; + forest->last_local_tree = cmesh_last_tree; + } + else { + t8_cmesh_uniform_bounds (forest->cmesh, forest->set_level, forest->scheme_cxx, &forest->first_local_tree, + &child_in_tree_begin, &forest->last_local_tree, &child_in_tree_end, NULL); + } /* True if the forest has no elements */ - is_empty = forest->first_local_tree > forest->last_local_tree - || (forest->first_local_tree == forest->last_local_tree && child_in_tree_begin >= child_in_tree_end); + int is_empty + = forest->first_local_tree > forest->last_local_tree + || (!forest->set_initial_partition_according_to_cmesh && forest->first_local_tree == forest->last_local_tree + && child_in_tree_begin >= child_in_tree_end); - cmesh_first_tree = t8_cmesh_get_first_treeid (forest->cmesh); - cmesh_last_tree = cmesh_first_tree + t8_cmesh_get_num_local_trees (forest->cmesh) - 1; + t8_debugf ("first: %i, last: %i, is_empty: %i\n", forest->first_local_tree, forest->last_local_tree, is_empty); - if (!is_empty) { + if (!is_empty && !forest->set_initial_partition_according_to_cmesh) { SC_CHECK_ABORT (forest->first_local_tree >= cmesh_first_tree && forest->last_local_tree <= cmesh_last_tree, - "cmesh partition does not match the planned forest partition"); + "cmesh partition does not match the planned uniform equally distributed forest partition"); } forest->global_num_elements = forest->local_num_elements = 0; /* create only the non-empty tree objects */ if (is_empty) { + forest->local_num_elements = 0; /* This processor is empty * we still set the tree array to store 0 as the number of trees here */ forest->trees = sc_array_new (sizeof (t8_tree_struct_t)); - count_elements = 0; /* Set the first local tree larger than the last local tree to - * indicate empty forest */ + * indicate empty forest, this is sometimes used instead of the more accurate check that local_num_element = 0. TODO: check that line can be removed */ forest->first_local_tree = forest->last_local_tree + 1; } else { /* for each tree, allocate elements */ - num_local_trees = forest->last_local_tree - forest->first_local_tree + 1; + t8_locidx_t num_local_trees = forest->last_local_tree - forest->first_local_tree + 1; + t8_debugf ("-----num_local_trees:%i\n", num_local_trees); forest->trees = sc_array_new_count (sizeof (t8_tree_struct_t), num_local_trees); - first_ctree = t8_cmesh_get_first_treeid (forest->cmesh); - for (jt = forest->first_local_tree, count_elements = 0; jt <= forest->last_local_tree; jt++) { - tree = (t8_tree_t) t8_sc_array_index_locidx (forest->trees, jt - forest->first_local_tree); - tree_class = tree->eclass = t8_cmesh_get_tree_class (forest->cmesh, jt - first_ctree); + t8_gloidx_t first_ctree = t8_cmesh_get_first_treeid (forest->cmesh); + t8_locidx_t count_elements = 0; + for (t8_gloidx_t jt = forest->first_local_tree; jt <= forest->last_local_tree; jt++) { + t8_debugf ("look at local tree %i\n", jt); + t8_tree_t tree = (t8_tree_t) t8_sc_array_index_locidx (forest->trees, jt - forest->first_local_tree); + t8_eclass_t tree_class = tree->eclass = t8_cmesh_get_tree_class (forest->cmesh, jt - first_ctree); tree->elements_offset = count_elements; - eclass_scheme = forest->scheme_cxx->eclass_schemes[tree_class]; + t8_eclass_scheme_c *eclass_scheme = forest->scheme_cxx->eclass_schemes[tree_class]; T8_ASSERT (eclass_scheme != NULL); - telements = &tree->elements; + t8_element_array_t *telements = &tree->elements; /* calculate first and last element on this tree */ - start = (jt == forest->first_local_tree) ? child_in_tree_begin : 0; - end = (jt == forest->last_local_tree) ? child_in_tree_end - : eclass_scheme->t8_element_count_leaves_from_root (forest->set_level); + t8_locidx_t start, end, num_tree_elements; + if (forest->set_initial_partition_according_to_cmesh) { + start = 0; + end = eclass_scheme->t8_element_count_leaves_from_root (forest->set_level); + } + else { + start = (jt == forest->first_local_tree) ? child_in_tree_begin : 0; + end = (jt == forest->last_local_tree) ? child_in_tree_end + : eclass_scheme->t8_element_count_leaves_from_root (forest->set_level); + } num_tree_elements = end - start; + t8_debugf ("num_tree_elements: %i \n", num_tree_elements); T8_ASSERT (num_tree_elements > 0); /* Allocate elements for this processor. */ t8_element_array_init_size (telements, eclass_scheme, num_tree_elements); - element = t8_element_array_index_locidx (telements, 0); + t8_element_t *element = t8_element_array_index_locidx (telements, 0); eclass_scheme->t8_element_set_linear_id (element, forest->set_level, start); count_elements++; - for (et = start + 1; et < end; et++, count_elements++) { - element_succ = t8_element_array_index_locidx (telements, et - start); + for (t8_locidx_t et = start + 1; et < end; et++, count_elements++) { + t8_element_t *element_succ = t8_element_array_index_locidx (telements, et - start); T8_ASSERT (eclass_scheme->t8_element_level (element) == forest->set_level); eclass_scheme->t8_element_successor (element, element_succ); /* TODO: process elements here */ element = element_succ; } } + forest->local_num_elements = count_elements; } - forest->local_num_elements = count_elements; /* TODO: if no tree has pyramid type we can optimize this to global_num_elements = global_num_trees * 2^(dim*level) */ t8_forest_comm_global_num_elements (forest); /* TODO: figure out global_first_position, global_first_quadrant without comm */ @@ -1237,7 +1313,7 @@ t8_forest_tree_shared (t8_forest_t forest, int first_or_last) T8_ASSERT (first_or_last == 0 || first_or_last == 1); T8_ASSERT (forest != NULL); T8_ASSERT (forest->first_local_tree > -1); - T8_ASSERT (forest->first_local_tree < forest->global_num_trees); + T8_ASSERT (forest->first_local_tree < forest->global_num_trees || forest->local_num_elements == 0); T8_ASSERT (forest->last_local_tree < forest->global_num_trees); #if T8_ENABLE_DEBUG if (forest->first_local_tree == 0 && forest->last_local_tree == -1) { diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index 9672f3db3f..01aa043940 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -213,6 +213,9 @@ t8_forest_set_scheme (t8_forest_t forest, t8_scheme_cxx_t *scheme); void t8_forest_set_level (t8_forest_t forest, int level); +void +t8_forest_set_uniform_partition_from_cmesh (t8_forest_t forest, t8_cmesh_t cmesh, sc_MPI_Comm comm); + /** Set a forest as source for copying on committing. * By default, the forest takes ownership of the source \b from such that it will * be destroyed on calling \ref t8_forest_commit. To keep ownership of \b @@ -800,6 +803,10 @@ t8_forest_t t8_forest_new_uniform (t8_cmesh_t cmesh, t8_scheme_cxx_t *scheme, const int level, const int do_face_ghost, sc_MPI_Comm comm); +t8_forest_t +t8_forest_new_uniform_with_cmesh_partition (t8_cmesh_t cmesh, t8_scheme_cxx_t *scheme, const int level, + const int do_face_ghost, sc_MPI_Comm comm); + /** Build a adapted forest from another forest. * \param [in] forest_from The forest to refine * \param [in] adapt_fn Adapt function to use diff --git a/src/t8_forest/t8_forest_private.h b/src/t8_forest/t8_forest_private.h index 07353af61d..0f4ded2dda 100644 --- a/src/t8_forest/t8_forest_private.h +++ b/src/t8_forest/t8_forest_private.h @@ -68,6 +68,9 @@ t8_forest_compute_desc (t8_forest_t forest); void t8_forest_populate (t8_forest_t forest); +void +t8_forest_populate_according_to_cmesh (t8_forest_t forest); + /** Return the eclass scheme of a given element class associated to a forest. * This function does not check whether the given forest is committed, use with * caution and only if you are sure that the eclass_scheme was set. diff --git a/src/t8_forest/t8_forest_types.h b/src/t8_forest/t8_forest_types.h index 10f4c84b72..c3e3936ad5 100644 --- a/src/t8_forest/t8_forest_types.h +++ b/src/t8_forest/t8_forest_types.h @@ -69,7 +69,9 @@ typedef struct t8_forest { t8_refcount_t rc; /**< Reference counter. */ - int set_level; /**< Level to use in new construction. */ + int set_level; /**< Level to use in new construction. */ + int + set_initial_partition_according_to_cmesh; /** Decide whether to partition the elements according to a given cmesh without shared elements or equally */ int set_for_coarsening; /**< Change partition to allow for one round of coarsening */ diff --git a/test/Makefile.am b/test/Makefile.am index b5873e584a..4ac369122c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -57,6 +57,7 @@ t8code_googletest_programs = \ test/t8_schemes/t8_gtest_face_descendant \ test/t8_geometry/t8_gtest_point_inside \ test/t8_forest/t8_gtest_user_data \ + test/t8_forest/t8_gtest_forest_new_from_part_cmesh \ test/t8_forest/t8_gtest_transform \ test/t8_forest/t8_gtest_ghost_exchange \ test/t8_forest/t8_gtest_ghost_delete \ @@ -251,6 +252,10 @@ test_t8_forest_t8_gtest_user_data_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_forest/t8_gtest_user_data.cxx +test_t8_forest_t8_gtest_forest_new_from_part_cmesh_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx + test_t8_forest_t8_gtest_transform_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_forest/t8_gtest_transform.cxx @@ -499,6 +504,10 @@ test_t8_forest_t8_gtest_user_data_LDADD = $(t8_gtest_target_ld_add) test_t8_forest_t8_gtest_user_data_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_forest_t8_gtest_user_data_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_forest_t8_gtest_forest_new_from_part_cmesh_LDADD = $(t8_gtest_target_ld_add) +test_t8_forest_t8_gtest_forest_new_from_part_cmesh_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_forest_t8_gtest_forest_new_from_part_cmesh_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_forest_t8_gtest_transform_LDADD = $(t8_gtest_target_ld_add) test_t8_forest_t8_gtest_transform_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_forest_t8_gtest_transform_CPPFLAGS = $(t8_gtest_target_cpp_flags) @@ -616,6 +625,7 @@ test_t8_forest_t8_gtest_forest_face_normal_CPPFLAGS += $(t8_gtest_target_mpi_cpp test_t8_schemes_t8_gtest_face_descendant_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_geometry_t8_gtest_point_inside_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_user_data_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_forest_t8_gtest_forest_new_from_part_cmesh_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_transform_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_ghost_exchange_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_ghost_delete_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) diff --git a/test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx b/test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx new file mode 100644 index 0000000000..c952115920 --- /dev/null +++ b/test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx @@ -0,0 +1,125 @@ +/* + 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) 2015 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 "test/t8_cmesh_generator/t8_cmesh_example_sets.hxx" +#include + +TEST (t8_forest_new_from_partition, all_elems_on_root) +{ + t8_cmesh_t cmesh; + t8_cmesh_init (&cmesh); + /* create a line cmesh */ + int mpirank, mpisize; + sc_MPI_Comm comm = sc_MPI_COMM_WORLD; + sc_MPI_Comm_size (comm, &mpisize); + sc_MPI_Comm_rank (comm, &mpirank); + int face_knowledge = 3; + int num_trees = 2; + if (mpirank == 0) { + t8_gloidx_t first = num_trees * mpirank; + t8_gloidx_t second = num_trees * mpirank + 1; + t8_gloidx_t third = num_trees * mpirank + 2; + double vertices[9] = { (double) first, 0, 0, (double) second, 0, 0, (double) third, 0, 0 }; + + t8_cmesh_set_tree_class (cmesh, first, T8_ECLASS_LINE); + t8_cmesh_set_tree_class (cmesh, second, T8_ECLASS_LINE); + t8_cmesh_set_tree_vertices (cmesh, first, vertices, 2); + t8_cmesh_set_tree_vertices (cmesh, second, vertices + 3, 2); + // t8_cmesh_set_join (cmesh, first, second, 1, 0, 0); + t8_cmesh_set_partition_range (cmesh, face_knowledge, 0, 1); + } + else { + t8_cmesh_set_dimension (cmesh, 1); + t8_cmesh_set_partition_range (cmesh, face_knowledge, 2, 1); + } + t8_cmesh_commit (cmesh, comm); + EXPECT_EQ (t8_cmesh_get_num_local_trees (cmesh), (mpirank == 0) ? 2 : 0); + EXPECT_EQ (t8_cmesh_get_num_trees (cmesh), 2); + t8_scheme_cxx_t *scheme = t8_scheme_new_default_cxx (); + int level = 3; + t8_forest_t forest = t8_forest_new_uniform_with_cmesh_partition (cmesh, scheme, level, 0, comm); + EXPECT_EQ (t8_forest_get_local_num_elements (forest), (mpirank == 0) ? 2 << level : 0); + EXPECT_EQ (t8_forest_get_global_num_elements (forest), 2 << level); + t8_forest_unref (&forest); +} + +TEST (t8_forest_new_from_partition, alternating_one_two) +{ + t8_cmesh_t cmesh; + t8_cmesh_init (&cmesh); + /* create a line cmesh */ + int mpirank, mpisize; + sc_MPI_Comm comm = sc_MPI_COMM_WORLD; + sc_MPI_Comm_size (comm, &mpisize); + sc_MPI_Comm_rank (comm, &mpirank); + int face_knowledge = 3; + int num_trees_before = 3 * ((mpirank - 1) / 2) + 2 * (mpirank % 2); + t8_debugf ("num_trees_before:%i\n", num_trees_before); + t8_gloidx_t first = num_trees_before; + t8_gloidx_t second = num_trees_before + 1; + t8_gloidx_t third = num_trees_before + 2; + double vertices[9] = { (double) first, 0, 0, (double) second, 0, 0, (double) third, 0, 0 }; + t8_cmesh_set_tree_class (cmesh, first, T8_ECLASS_LINE); + t8_cmesh_set_tree_vertices (cmesh, first, vertices, 2); + if (mpirank % 2 == 0) { + t8_cmesh_set_tree_class (cmesh, second, T8_ECLASS_LINE); + t8_cmesh_set_tree_vertices (cmesh, second, vertices + 3, 2); + } + t8_cmesh_set_partition_range (cmesh, face_knowledge, num_trees_before, num_trees_before + 1 - mpirank % 2); + t8_cmesh_commit (cmesh, comm); + int expected_local_trees = 2 - mpirank % 2; + int expected_global_trees = 3 * (mpisize / 2) + 2 * (mpisize % 2); + EXPECT_EQ (t8_cmesh_get_num_local_trees (cmesh), expected_local_trees); + EXPECT_EQ (t8_cmesh_get_num_trees (cmesh), expected_global_trees); + t8_scheme_cxx_t *scheme = t8_scheme_new_default_cxx (); + int level = 3; + t8_forest_t forest = t8_forest_new_uniform_with_cmesh_partition (cmesh, scheme, level, 0, comm); + EXPECT_EQ (t8_forest_get_local_num_elements (forest), expected_local_trees << level); + EXPECT_EQ (t8_forest_get_global_num_elements (forest), expected_global_trees << level); + + t8_eclass_scheme_c *line_scheme = t8_forest_get_eclass_scheme (forest, T8_ECLASS_LINE); + + for (t8_locidx_t itree = 0; itree < t8_forest_get_num_local_trees (forest); itree++) { + t8_tree_t tree = t8_forest_get_tree (forest, itree); + for (t8_locidx_t ielement = 0; ielement < t8_forest_get_tree_element_count (tree); ielement++) { + t8_element_t *element = t8_forest_get_tree_element (tree, ielement); + EXPECT_EQ (line_scheme->t8_element_level (element), level); + } + } + t8_forest_unref (&forest); +} + +TEST (t8_forest_new_from_partition, empty_test) +{ + /** TODO: + * hybrid + * parameterized above over eclass + * Look in cmesh examples, if there are any than can be constructed partitioned imbalanced. + */ +} From a576755707b1a32cc57a38496e60192e41e53478 Mon Sep 17 00:00:00 2001 From: "Dreyer, Lukas" Date: Mon, 25 Mar 2024 09:02:55 +0100 Subject: [PATCH 2/5] Remove debug print statements with warnings --- src/t8_forest/t8_forest_cxx.cxx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/t8_forest/t8_forest_cxx.cxx b/src/t8_forest/t8_forest_cxx.cxx index 3659a24d9c..38723e8420 100644 --- a/src/t8_forest/t8_forest_cxx.cxx +++ b/src/t8_forest/t8_forest_cxx.cxx @@ -1231,8 +1231,6 @@ t8_forest_populate (t8_forest_t forest) || (!forest->set_initial_partition_according_to_cmesh && forest->first_local_tree == forest->last_local_tree && child_in_tree_begin >= child_in_tree_end); - t8_debugf ("first: %i, last: %i, is_empty: %i\n", forest->first_local_tree, forest->last_local_tree, is_empty); - if (!is_empty && !forest->set_initial_partition_according_to_cmesh) { SC_CHECK_ABORT (forest->first_local_tree >= cmesh_first_tree && forest->last_local_tree <= cmesh_last_tree, "cmesh partition does not match the planned uniform equally distributed forest partition"); @@ -1252,12 +1250,10 @@ t8_forest_populate (t8_forest_t forest) else { /* for each tree, allocate elements */ t8_locidx_t num_local_trees = forest->last_local_tree - forest->first_local_tree + 1; - t8_debugf ("-----num_local_trees:%i\n", num_local_trees); forest->trees = sc_array_new_count (sizeof (t8_tree_struct_t), num_local_trees); t8_gloidx_t first_ctree = t8_cmesh_get_first_treeid (forest->cmesh); t8_locidx_t count_elements = 0; for (t8_gloidx_t jt = forest->first_local_tree; jt <= forest->last_local_tree; jt++) { - t8_debugf ("look at local tree %i\n", jt); t8_tree_t tree = (t8_tree_t) t8_sc_array_index_locidx (forest->trees, jt - forest->first_local_tree); t8_eclass_t tree_class = tree->eclass = t8_cmesh_get_tree_class (forest->cmesh, jt - first_ctree); tree->elements_offset = count_elements; From aca7d182f600deaa44e862b3d898626cb18d3411 Mon Sep 17 00:00:00 2001 From: "Dreyer, Lukas" Date: Mon, 25 Mar 2024 16:46:28 +0100 Subject: [PATCH 3/5] Correct test offsets --- test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx b/test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx index c952115920..c65dcd8f38 100644 --- a/test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx +++ b/test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx @@ -69,7 +69,7 @@ TEST (t8_forest_new_from_partition, all_elems_on_root) t8_forest_unref (&forest); } -TEST (t8_forest_new_from_partition, alternating_one_two) +TEST (t8_forest_new_from_partition, alternating_two_one) { t8_cmesh_t cmesh; t8_cmesh_init (&cmesh); @@ -79,7 +79,7 @@ TEST (t8_forest_new_from_partition, alternating_one_two) sc_MPI_Comm_size (comm, &mpisize); sc_MPI_Comm_rank (comm, &mpirank); int face_knowledge = 3; - int num_trees_before = 3 * ((mpirank - 1) / 2) + 2 * (mpirank % 2); + int num_trees_before = 3 * (mpirank / 2) + 2 * (mpirank % 2); t8_debugf ("num_trees_before:%i\n", num_trees_before); t8_gloidx_t first = num_trees_before; t8_gloidx_t second = num_trees_before + 1; From f4a73e4cf05e30b60f19b885d2004496419b880d Mon Sep 17 00:00:00 2001 From: "Dreyer, Lukas" Date: Mon, 25 Mar 2024 18:56:20 +0100 Subject: [PATCH 4/5] Fix global to local offset for lagrange geometry --- .../t8_geometry_implementations/t8_geometry_lagrange.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/t8_geometry/t8_geometry_implementations/t8_geometry_lagrange.cxx b/src/t8_geometry/t8_geometry_implementations/t8_geometry_lagrange.cxx index 4bd8b06d13..bc82ee1230 100644 --- a/src/t8_geometry/t8_geometry_implementations/t8_geometry_lagrange.cxx +++ b/src/t8_geometry/t8_geometry_implementations/t8_geometry_lagrange.cxx @@ -71,7 +71,8 @@ inline void t8_geometry_lagrange::t8_geom_load_tree_data (t8_cmesh_t cmesh, t8_gloidx_t gtreeid) { t8_geometry_with_vertices::t8_geom_load_tree_data (cmesh, gtreeid); - degree = (const int *) t8_cmesh_get_attribute (cmesh, t8_get_package_id (), T8_CMESH_LAGRANGE_POLY_DEGREE, gtreeid); + degree = (const int *) t8_cmesh_get_attribute (cmesh, t8_get_package_id (), T8_CMESH_LAGRANGE_POLY_DEGREE, + t8_cmesh_get_local_id (cmesh, gtreeid)); T8_ASSERT (degree != NULL); } From d96024c545c6dbfdbf6a99941af75d67c8516f29 Mon Sep 17 00:00:00 2001 From: "Dreyer, Lukas" Date: Mon, 25 Mar 2024 18:58:06 +0100 Subject: [PATCH 5/5] Add test for quads with lagrange geometry --- .../t8_gtest_forest_new_from_part_cmesh.cxx | 113 +++++++++++++++++- 1 file changed, 111 insertions(+), 2 deletions(-) diff --git a/test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx b/test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx index c65dcd8f38..91a9c7201a 100644 --- a/test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx +++ b/test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx @@ -22,14 +22,14 @@ #include #include -#include +#include #include #include #include #include #include "test/t8_cmesh_generator/t8_cmesh_example_sets.hxx" #include - +#include TEST (t8_forest_new_from_partition, all_elems_on_root) { t8_cmesh_t cmesh; @@ -123,3 +123,112 @@ TEST (t8_forest_new_from_partition, empty_test) * Look in cmesh examples, if there are any than can be constructed partitioned imbalanced. */ } + +TEST (t8_forest_new_from_partition, one_two_quad) +{ + /* Current process */ + int rank, size; + sc_MPI_Comm_size (sc_MPI_COMM_WORLD, &size); + sc_MPI_Comm_rank (sc_MPI_COMM_WORLD, &rank); + + /* Geometry */ + int degree = 2; + std::vector> cells; + cells.push_back (std::vector { -1, 1.2, 0, 0, 1.2, 0, -1, 3, 0, 0, 3, 0, -1, 2.1, + 0, 0, 2.1, 0, -0.5, 1.2, 0, -0.5, 3, 0, -0.5, 2.1, 0 }); + cells.push_back (std::vector { 0, 1.2, 0, 2, 1.5, 0, 0, 3, 0, 2, 3, 0, 0, 2.1, + 0, 1.7, 2.1, 0, 1, 1.55, 0, 1, 3, 0, 0.9, 2.1, 0 }); + cells.push_back ( + std::vector { 2, 1.5, 0, 4, 0, 0, 2, 3, 0, 4, 3, 0, 1.7, 2.1, 0, 4, 1.5, 0, 3, 1, 0, 3, 3, 0, 3, 2, 0 }); + + /* Create a partitioned cmesh */ + t8_cmesh_t cmesh; + t8_cmesh_init (&cmesh); + if (size != 2) + SC_ABORT ("Program must be run on two MPI processes.\n"); + else { + if (rank == 0) { + t8_cmesh_set_tree_class (cmesh, 0, T8_ECLASS_QUAD); + t8_cmesh_set_tree_class (cmesh, 1, T8_ECLASS_QUAD); + t8_cmesh_set_tree_vertices (cmesh, 0, cells[0].data (), cells[0].size ()); + t8_cmesh_set_tree_vertices (cmesh, 1, cells[1].data (), cells[1].size ()); + t8_cmesh_set_attribute (cmesh, 0, t8_get_package_id (), T8_CMESH_LAGRANGE_POLY_DEGREE, °ree, sizeof (int), 1); + t8_cmesh_set_attribute (cmesh, 1, t8_get_package_id (), T8_CMESH_LAGRANGE_POLY_DEGREE, °ree, sizeof (int), 1); + t8_cmesh_set_join (cmesh, 0, 1, 1, 0, 0); + t8_cmesh_set_partition_range (cmesh, 3, 0, 0); + } + else if (rank == 1) { + for (size_t itree = 0; itree < 3; ++itree) { + t8_cmesh_set_tree_class (cmesh, itree, T8_ECLASS_QUAD); + t8_cmesh_set_tree_vertices (cmesh, itree, cells[itree].data (), cells[itree].size ()); + t8_cmesh_set_attribute (cmesh, itree, t8_get_package_id (), T8_CMESH_LAGRANGE_POLY_DEGREE, °ree, + sizeof (int), 1); + } + t8_cmesh_set_join (cmesh, 0, 1, 1, 0, 0); + t8_cmesh_set_join (cmesh, 1, 2, 1, 0, 0); + t8_cmesh_set_partition_range (cmesh, 3, 1, 2); + } + } + t8_cmesh_register_geometry (cmesh, 2); + t8_cmesh_commit (cmesh, sc_MPI_COMM_WORLD); + t8_forest_t forest + = t8_forest_new_uniform_with_cmesh_partition (cmesh, t8_scheme_new_default_cxx (), 0, 0, sc_MPI_COMM_WORLD); + // The following line still works, because the partition "by chance" matches the partition, that t8code expects from + // an equidistributed uniform mesh + // t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_default_cxx (), 0, 1, sc_MPI_COMM_WORLD); + t8_forest_unref (&forest); +} + +TEST (t8_forest_new_from_partition, two_one_quad) +{ + /* Current process */ + int rank, size; + sc_MPI_Comm_size (sc_MPI_COMM_WORLD, &size); + sc_MPI_Comm_rank (sc_MPI_COMM_WORLD, &rank); + + /* Geometry */ + int degree = 2; + std::vector> cells; + cells.push_back (std::vector { -1, 1.2, 0, 0, 1.2, 0, -1, 3, 0, 0, 3, 0, -1, 2.1, + 0, 0, 2.1, 0, -0.5, 1.2, 0, -0.5, 3, 0, -0.5, 2.1, 0 }); + cells.push_back (std::vector { 0, 1.2, 0, 2, 1.5, 0, 0, 3, 0, 2, 3, 0, 0, 2.1, + 0, 1.7, 2.1, 0, 1, 1.55, 0, 1, 3, 0, 0.9, 2.1, 0 }); + cells.push_back ( + std::vector { 2, 1.5, 0, 4, 0, 0, 2, 3, 0, 4, 3, 0, 1.7, 2.1, 0, 4, 1.5, 0, 3, 1, 0, 3, 3, 0, 3, 2, 0 }); + + /* Create a partitioned cmesh */ + t8_cmesh_t cmesh; + t8_cmesh_init (&cmesh); + if (size != 2) + SC_ABORT ("Program must be run on two MPI processes.\n"); + else { + if (rank == 0) { + for (size_t itree = 0; itree < 3; ++itree) { + t8_cmesh_set_tree_class (cmesh, itree, T8_ECLASS_QUAD); + t8_cmesh_set_tree_vertices (cmesh, itree, cells[itree].data (), cells[itree].size ()); + t8_cmesh_set_attribute (cmesh, itree, t8_get_package_id (), T8_CMESH_LAGRANGE_POLY_DEGREE, °ree, + sizeof (int), 1); + } + t8_cmesh_set_join (cmesh, 0, 1, 1, 0, 0); + t8_cmesh_set_join (cmesh, 1, 2, 1, 0, 0); + t8_cmesh_set_partition_range (cmesh, 3, 0, 1); + } + else if (rank == 1) { + t8_cmesh_set_tree_class (cmesh, 1, T8_ECLASS_QUAD); + t8_cmesh_set_tree_class (cmesh, 2, T8_ECLASS_QUAD); + t8_cmesh_set_tree_vertices (cmesh, 1, cells[1].data (), cells[1].size ()); + t8_cmesh_set_tree_vertices (cmesh, 2, cells[2].data (), cells[2].size ()); + t8_cmesh_set_attribute (cmesh, 1, t8_get_package_id (), T8_CMESH_LAGRANGE_POLY_DEGREE, °ree, sizeof (int), 1); + t8_cmesh_set_attribute (cmesh, 2, t8_get_package_id (), T8_CMESH_LAGRANGE_POLY_DEGREE, °ree, sizeof (int), 1); + t8_cmesh_set_join (cmesh, 1, 2, 1, 0, 0); + t8_cmesh_set_partition_range (cmesh, 3, 2, 2); + } + } + t8_cmesh_register_geometry (cmesh, 2); + t8_cmesh_commit (cmesh, sc_MPI_COMM_WORLD); + t8_forest_t forest + = t8_forest_new_uniform_with_cmesh_partition (cmesh, t8_scheme_new_default_cxx (), 0, 0, sc_MPI_COMM_WORLD); + // The following line fails, as the refinement cmesh partitioned required from t8code does not match the provided partition. + // t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_default_cxx (), 0, 1, sc_MPI_COMM_WORLD); + t8_forest_unref (&forest); +} \ No newline at end of file