diff --git a/src/t8_forest/t8_forest.c b/src/t8_forest/t8_forest.c index 66e1bafec0..4ed0e660ec 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); @@ -1433,6 +1442,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 52ef17e086..fc84e70a3d 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); } @@ -1153,86 +1156,155 @@ 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_mutable (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_mutable (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); - /* 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); + 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*/ - 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; + 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); + } - if (!is_empty) { + /* True if the forest has no elements */ + 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); + + 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; 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_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_mutable (telements, 0); + t8_element_t *element = t8_element_array_index_locidx_mutable (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_mutable (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_mutable (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 */ diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index 02ea23227b..d4c38715ef 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 @@ -840,6 +843,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 f43e244631..f3fd00b4ce 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 a49c3df9da..b40ab1e610 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -71,6 +71,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 \ @@ -270,6 +271,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 @@ -526,6 +531,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) @@ -648,6 +657,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..961e8ef547 --- /dev/null +++ b/test/t8_forest/t8_gtest_forest_new_from_part_cmesh.cxx @@ -0,0 +1,234 @@ +/* + 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 +#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_two_one) +{ + 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 / 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++) { + const 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. + */ +} + +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