diff --git a/Makefile.am b/Makefile.am index f0872791e7..a7c4ff8ff3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -74,6 +74,7 @@ DISTCLEANFILES += \ # non-recursive build include src/Makefile.am include src/t8_schemes/t8_default/Makefile.am +include src/t8_schemes/t8_transition/Makefile.am include test/Makefile.am include example/Makefile.am include tutorials/Makefile.am diff --git a/example/Makefile.am b/example/Makefile.am index 6080d75f4b..1d77724811 100644 --- a/example/Makefile.am +++ b/example/Makefile.am @@ -13,4 +13,6 @@ include example/geometry/Makefile.am include example/common/Makefile.am include example/version/Makefile.am -include example/remove/Makefile.am \ No newline at end of file +include example/remove/Makefile.am + +include example/transition/Makefile.am \ No newline at end of file diff --git a/example/transition/Makefile.am b/example/transition/Makefile.am new file mode 100644 index 0000000000..3427e14ad4 --- /dev/null +++ b/example/transition/Makefile.am @@ -0,0 +1,16 @@ +# This file is part of t8code +# Non-recursive Makefile.am in example/transition +# Included from toplevel directory + +bin_PROGRAMS += \ + example/transition/t8_transition_global_hex \ + example/transition/t8_transition_basic_hex \ + example/transition/t8_transition_forest \ + example/transition/t8_transition_multiple_trees_hex \ + example/transition/t8_transition_LFN_hex + +example_transition_t8_transition_global_hex_SOURCES = example/transition/t8_transition_global_hex.cxx +example_transition_t8_transition_basic_hex_SOURCES = example/transition/t8_transition_basic_hex.cxx +example_transition_t8_transition_forest_SOURCES = example/transition/t8_transition_forest.cxx +example_transition_t8_transition_multiple_trees_hex_SOURCES = example/transition/t8_transition_multiple_trees_hex.cxx +example_transition_t8_transition_LFN_hex_SOURCES = example/transition/t8_transition_LFN_hex.cxx diff --git a/example/transition/t8_transition_LFN_hex.cxx b/example/transition/t8_transition_LFN_hex.cxx new file mode 100644 index 0000000000..f118abe0ba --- /dev/null +++ b/example/transition/t8_transition_LFN_hex.cxx @@ -0,0 +1,243 @@ +/* + 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 types 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. +*/ + +/* Description: + * This is the example file for refinement with transitioning. In this testcase, we are able to + * (i) refine a mesh according to some refinement criterion and use transition cells to make the mesh conformal + * (ii) use multiple adaptation steps in which the refinement criterion changes (e.g. the geometry) + * (iii) decide, whether we want to check the LFN function for each mesh + * (iv) decide, whether we want to get statistics printed out, regarding # of elements in the meshes and runtime infos of the several functions or other debugging information + */ + +/* to switch between the default quad scheme and the transition implementation */ +#include "t8_eclass.h" +#include "t8_forest/t8_forest_types.h" + +#include "t8_forest/t8_forest_general.h" +#include +#include +#include +#include +#include // to write vtk +#include +#include +#include /* for cmesh initialization via for example t8_cmesh_new_hypercube */ +#include +#include + +/* In this example, the left side of a unit cube with initial level 2 is refined to construct an adapted and transitioned forest. */ + +/* Refinement criterion: All elements with x-coordinate smaller than 0.5 are being refined. All other elements remain unchanged. */ +int +t8_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, t8_locidx_t lelement_id, + t8_eclass_scheme_c *ts, const int is_family, const int num_elements, t8_element_t *elements[]) +{ + int child_id = ts->t8_element_child_id (elements[0]); + if (child_id == 1) { + return 1; + } + return 0; +} + +/* adapt, balance, transition and partition a given forest in one step */ +static t8_forest_t +t8_test_forest_commit_abpt (t8_forest_t forest) +{ + t8_forest_t forest_ada_bal_tra_par; + + /* Adapt, balance and partition the uniform forest */ + t8_forest_init (&forest_ada_bal_tra_par); + t8_forest_set_adapt (forest_ada_bal_tra_par, forest, t8_adapt_callback, 0); + t8_forest_set_balance (forest_ada_bal_tra_par, NULL, 0); + t8_forest_set_transition (forest_ada_bal_tra_par, NULL, 0); + t8_forest_set_partition (forest_ada_bal_tra_par, NULL, 0); + t8_forest_commit (forest_ada_bal_tra_par); + + return forest_ada_bal_tra_par; +} + +/* Compute neighbors of all elements in all trees at all faces */ +void +t8_LFN_test (t8_forest_t forest_adapt) +{ + t8_debugf ("~~~~~~~~~~ Into the LFN test function. ~~~~~~~~~~\n"); + + /* Collecting data of the adapted forest */ + const t8_element_t *current_element; + t8_tree_t current_tree; + t8_locidx_t forest_is_balanced = 1; + t8_element_t **neighbor_leaves; + t8_locidx_t *element_indices; + t8_eclass_scheme_c *neigh_scheme; + t8_eclass_t eclass; + t8_eclass_scheme_c *ts; + + int *dual_faces; + int num_neighbors; + int face_id; + int local_num_trees + = t8_forest_get_num_local_trees (forest_adapt); /* get the number of trees, this process knows about */ + int current_tree_num_elements; + int subelement_count = 0; + int LFN_call_count = 0; + int tree_count; + int elem_count; + int neighbor_count; + + for (tree_count = 0; tree_count < local_num_trees; ++tree_count) { + eclass = t8_forest_get_tree_class (forest_adapt, tree_count); + ts = t8_forest_get_eclass_scheme (forest_adapt, eclass); + + /* get the number of elements in the current tree */ + current_tree = t8_forest_get_tree (forest_adapt, tree_count); + current_tree_num_elements = t8_forest_get_tree_element_count (current_tree); + + for (elem_count = 0; elem_count < current_tree_num_elements; ++elem_count) { + + /* determining the current element according to the given tree id and element id within the tree */ + current_element = t8_forest_get_element_in_tree (forest_adapt, tree_count, elem_count); + + if (ts->t8_element_is_subelement (current_element)) { + subelement_count++; + } + + /* print current element */ +#if T8_ENABLE_DEBUG + t8_productionf ("\n\n________________" + "\nCurrent element: local elem index of this process: %i of %i (without ghosts)\n", + elem_count, t8_forest_get_local_num_elements (forest_adapt)); + ts->t8_element_debug_print (current_element); +#endif + + for (face_id = 0; face_id < ts->t8_element_num_faces (current_element); ++face_id) { + LFN_call_count++; + t8_forest_leaf_face_neighbors (forest_adapt, tree_count, current_element, &neighbor_leaves, face_id, + &dual_faces, &num_neighbors, &element_indices, &neigh_scheme, + forest_is_balanced); + /* free memory if neighbors exist */ + if (num_neighbors > 0) { + + /* print all neighbor elements */ + for (neighbor_count = 0; neighbor_count < num_neighbors; neighbor_count++) { +#if T8_ENABLE_DEBUG + t8_productionf ("\n_________" + "\nNeighbor: %i of %i at face %i: (dual face: %i | local index %i of %i (with ghosts) | " + "ghost, if >= %i):\n", + neighbor_count + 1, num_neighbors, face_id, dual_faces[neighbor_count], + element_indices[neighbor_count], + t8_forest_get_local_num_elements (forest_adapt) + t8_forest_get_num_ghosts (forest_adapt), + t8_forest_get_local_num_elements (forest_adapt) - 1); + ts->t8_element_debug_print (neighbor_leaves[neighbor_count]); +#endif + } + + neigh_scheme->t8_element_destroy (num_neighbors, neighbor_leaves); + + T8_FREE (element_indices); + T8_FREE (neighbor_leaves); + T8_FREE (dual_faces); + } + else { +#if T8_ENABLE_DEBUG + /* no neighbor in this case */ + t8_productionf ("\n_________" + "\nNeighbor: at face %i: There is no neighbor (domain boundary).\n", + face_id); +#endif + } + } /* end of face loop */ + } /* end of element loop */ + } /* end of tree loop */ + + T8_ASSERT (subelement_count == t8_forest_get_local_num_subelements (forest_adapt)); + + t8_debugf ("~~~~~~~~~~ The LFN test function finished successful ~~~~~~~~~~\n"); +} /* end of t8_LFN_test */ + +/* Initializing, adapting balancing and transitioning a forest */ +static void +t8_transition_global (void) +{ + /* At the moment, subelements are only implemented for hexes and quads */ + t8_eclass_t eclass + = T8_ECLASS_HEX; /* depending on the include file, this will be the transitioned or default hex implementation */ + t8_forest_t forest; + t8_forest_t forest_adapt; + t8_cmesh_t cmesh; + char filename[BUFSIZ]; + + /* refinement setting */ + int level = 2; /* initial uniform refinement level */ + + t8_locidx_t polygons_x = 2; + t8_locidx_t polygons_y = 1; + t8_locidx_t polygons_z = 1; + + const double boundary[24] = { 0, 0, 0, 2, 0, 0, 0, 1, 0, 2, 1, 0, 0, 0, 1, 2, 0, 1, 0, 1, 1, 2, 1, 1 }; + + t8_scheme_cxx_t *scheme = t8_scheme_new_transition_hex_cxx (); + + /* construct a multiple tree hex cmesh */ + // cmesh = t8_cmesh_new_hypercube_pad (eclass, sc_MPI_COMM_WORLD, boundary, polygons_x, polygons_y, polygons_z, 0); + + cmesh = t8_cmesh_new_hypercube (eclass, sc_MPI_COMM_WORLD, 0, 0, 0); + /* Create a uniformly refined forest */ + forest = t8_forest_new_uniform (cmesh, scheme, level, 0, sc_MPI_COMM_WORLD); + + t8_forest_write_vtk (forest, "forest_global_hex"); + + for (int adaptation_count = 1; adaptation_count <= 1; ++adaptation_count) { + + forest_adapt = t8_test_forest_commit_abpt (forest); + + t8_LFN_test (forest_adapt); + + t8_debugf ("---------------ROUND %i ---------------------------\n\n", adaptation_count); + + forest = forest_adapt; + } + t8_forest_write_vtk (forest, "transition_global_hex"); + + t8_forest_unref (&forest_adapt); + +} /* end of t8_transition_global */ + +int +main (int argc, char **argv) +{ + int mpiret; + + mpiret = sc_MPI_Init (&argc, &argv); + SC_CHECK_MPI (mpiret); + + sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT); + t8_init (SC_LP_DEFAULT); + + t8_transition_global (); + + sc_finalize (); + mpiret = sc_MPI_Finalize (); + + SC_CHECK_MPI (mpiret); + + return 0; +} diff --git a/example/transition/t8_transition_basic_hex.cxx b/example/transition/t8_transition_basic_hex.cxx new file mode 100644 index 0000000000..9348b47515 --- /dev/null +++ b/example/transition/t8_transition_basic_hex.cxx @@ -0,0 +1,262 @@ +/* + 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 types 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. +*/ + +/* This is step3 of the t8code tutorials. + * After generating a coarse mesh (step1) and building a uniform forest + * on it (step2), we will now adapt (= refine and coarsen) the forest + * according to our own criterion. + * + * The geometry (coarse mesh) is again a cube, this time modelled with + * 6 tetrahedra, 6 prisms and 4 cubes. + * We refine an element if its midpoint is within a sphere of given radius + * around the point (0.5, 0.5, 1) and we coarsen outside of a given radius. + * We will use non-recursive refinement, that means that the refinement level + * of any element will change by at most +-1. + * + * How you can experiment here: + * - Look at the paraview output files of the unifomr and the adapted forest. + * For the adapted forest you can apply a slice filter to look into the cube. + * - Run the program with different process numbers. You should see that refining is + * independent of the number of processes, but coarsening is not. + * This is due to the face that a family can only be coarsened if it is completely + * local to a single process and the distribution among the process may break this property. + * - Change the midpoint coordinates and the radii. + * - Change the adaptation criterion such that elements inside the sphere are coarsened + * and elements outside are refined. + * - Use t8_productionf to print the local number of elements on each process. + * Notice, that the uniform forest is evenly distributed, but that the adapted forest + * is not. This is due to the fact that we do not repartition our forest here. + * - Add a maximum refinement level to the adapt_data struct and use non-recursive refinement. + * Do not refine an element if it has reached the maximum level. (Hint: ts->t8_element_level) + */ + +#include /* General t8code header, always include this. */ +#include /* cmesh definition and basic interface. */ +#include /* A collection of exemplary cmeshes */ +#include /* forest definition and basic interface. */ +#include /* save forest */ +#include /* geometrical information of the forest */ +#include +#include +#include /* default refinement scheme. */ +#include +#include "t8_eclass.h" +#include + +// T8_EXTERN_C_BEGIN (); + +/* TODO (JM): Copied this function from `t8_transition_local.cxx`. Adapt to your needs. */ +#ifdef T8_ENABLE_DEBUG +static int +t8_check_coordinates (double *coords) +{ + /* The initial hex_element is the unit hex with vertices (0,0,0), (1,0,0), (0,1,0), (1,1,0), (0,0,1) ,(1,0,1), (0,1,1) and (1,1,1). + * We know that therefore, all children (even our subelements) will have vertices with coordinates 0, 0.5 or 1. */ + double eps = 1e-126; /* testing up to float precision */ + if ((fabs (coords[0] - 0.0) < eps || fabs (coords[0] - 0.5) < eps || fabs (coords[0] - 1.0) < eps) + && (fabs (coords[1] - 0.0) < eps || fabs (coords[1] - 0.5) < eps || fabs (coords[1] - 1.0) < eps) + && (fabs (coords[2] - 0.0) < eps || fabs (coords[2] - 0.5) < eps || fabs (coords[2] - 1.0) < eps)) { + return true; + } + return false; +} +#endif + +static void +t8_test_hex_local (t8_element_t *hex_element, t8_eclass_scheme_c *class_scheme) +{ + t8_debugf ("~~~~~~~~~~ Into the t8_test_hex_local function ~~~~~~~~~~\n"); + + t8_element_t *parent; + int num_children, num_vertices; + int child_id; + double coords[3]; + + /* Allocate enough memory for hex children */ + num_children = class_scheme->t8_element_num_children (hex_element); + t8_element_t **children = T8_ALLOC (t8_element_t *, num_children); + class_scheme->t8_element_new (num_children, children); + + /* Create all subelements for the given type from the initial hex element. */ + class_scheme->t8_element_children (hex_element, P8EST_CHILDREN, children); + + /* transition cell must be a family of subelements */ + T8_ASSERT (class_scheme->t8_element_is_family (children)); + + t8_debugf ("The children array consists of %i elements, whose IDs range from 0 to %i.\n", num_children, + num_children - 1); + + /* Iterate through all subelements and determine their vertex coordinates */ + for (child_id = 0; child_id < num_children; ++child_id) { + /* All children should be standard hex elements here */ + T8_ASSERT (!class_scheme->t8_element_is_subelement (children[child_id])); + +#if T8_ENABLE_DEBUG + /* Print the current subelement */ + class_scheme->t8_element_debug_print (children[child_id]); +#endif + + /* determine the shape of the subelement and use it to determine the number of vertices it has (pyramid -> 5 vertices) */ + const t8_element_shape_t shape = class_scheme->t8_element_shape (children[child_id]); + + num_vertices = t8_eclass_num_vertices[shape]; + t8_debugf ("Num vertices %i \n", num_vertices); + T8_ASSERT (num_vertices == class_scheme->t8_element_num_corners (children[child_id])); + + /* Iterate over all vertices of the subelement and determine their coordinates */ + int vertex_count; + for (vertex_count = 0; vertex_count < num_vertices; ++vertex_count) { + class_scheme->t8_element_vertex_reference_coords (children[child_id], vertex_count, coords); + t8_debugf ("Child ID: %d; Vertex: %d; Ref cords in [0,1]^2: (%lf,%lf,%lf)\n", child_id, vertex_count, coords[0], + coords[1], coords[2]); + T8_ASSERT (t8_check_coordinates (coords)); + } /* end of vertex loop */ + } /* end of subelement loop */ + + /* coarsen the transition cell back to its parent, which must be equal to the initial quad_element */ + class_scheme->t8_element_new (1, &parent); + class_scheme->t8_element_parent (children[0], parent); + T8_ASSERT (class_scheme->t8_element_compare (hex_element, parent) == 0); + + /* free memory */ + class_scheme->t8_element_destroy (1, &parent); + class_scheme->t8_element_destroy (num_children, children); + T8_FREE (children); + + t8_debugf ("~~~~~~~~~~ The t8_test_hex_local function finished successful ~~~~~~~~~~\n"); +} + +static void +t8_transition_local (t8_eclass_t eclass) +{ + t8_debugf ("~~~~~~~~~~ Into the t8_transition_local function ~~~~~~~~~~\n"); + + t8_scheme_cxx_t *ts = t8_scheme_new_transition_hex_cxx (); + t8_eclass_scheme_c *class_scheme; + t8_element_t *hex_element, *parent; + int subelement_id; + double coords[3]; + int num_subelements; + int num_vertices; + + /* At the moment, subelements are only implemented for the quad and hex scheme. */ + T8_ASSERT (eclass = T8_ECLASS_HEX); + + class_scheme = ts->eclass_schemes[eclass]; + + /* Allocate memory for a new hex element and initialize it */ + class_scheme->t8_element_new (1, &hex_element); + class_scheme->t8_element_set_linear_id (hex_element, 0, 0); + T8_ASSERT (class_scheme->t8_element_is_valid (hex_element)); + + /* First, validate some element functions for this hex element */ + t8_test_hex_local (hex_element, class_scheme); + + /* Make checks for all transition types */ + int type; + for (type = 1; type <= T8_SUB_HEX_MAX_TRANSITION_TYPE; type++) { + /* Allocate enough memory for subelements of the given type and initialize them */ + + num_subelements = class_scheme->t8_element_get_number_of_subelements (type); + + t8_element_t **transition_cell = T8_ALLOC (t8_element_t *, num_subelements); + class_scheme->t8_element_new (num_subelements, transition_cell); + + /* Create all subelements for the given type from the initial hex element. */ + class_scheme->t8_element_to_transition_cell (hex_element, type, transition_cell); + + /* transition cell must be a family of subelements */ + T8_ASSERT (class_scheme->t8_element_is_family (transition_cell)); + + t8_debugf ("The given type is type %i.\n", type); + t8_debugf ("The transition cell of type %i consists of %i subelements, whose IDs range from 0 to %i.\n", type, + num_subelements, num_subelements - 1); + + /* Iterate through all subelements and determine their vertex coordinates */ + for (subelement_id = 0; subelement_id < num_subelements; ++subelement_id) { + /* All elements in a transition cell are subelements */ + T8_ASSERT (class_scheme->t8_element_is_subelement (transition_cell[subelement_id])); + +#if T8_ENABLE_DEBUG + /* Print the current subelement */ + class_scheme->t8_element_debug_print (transition_cell[subelement_id]); +#endif + + /* determine the shape of the subelement and use it to determine the number of vertices it has (triangle -> 3 vertices) */ + const t8_element_shape_t shape = class_scheme->t8_element_shape (transition_cell[subelement_id]); + num_vertices = t8_eclass_num_vertices[shape]; + T8_ASSERT (num_vertices == class_scheme->t8_element_num_corners (transition_cell[subelement_id])); + T8_ASSERT (num_vertices == class_scheme->t8_element_num_faces (transition_cell[subelement_id])); + + /* Iterate over all vertices of the subelement and determine their coordinates */ + int vertex_count; + for (vertex_count = 0; vertex_count < num_vertices; ++vertex_count) { + class_scheme->t8_element_vertex_reference_coords (transition_cell[subelement_id], vertex_count, coords); + t8_debugf ("Subelement ID: %d; Vertex: %d; Ref cords in [0,1]^3: (%lf,%lf,%lf)\n", subelement_id, vertex_count, + coords[0], coords[1], coords[2]); + T8_ASSERT (t8_check_coordinates (coords)); + } /* end of vertex loop */ + } /* end of subelement loop */ + + /* coarsen the transition cell back to its parent, which must be equal to the initial quad_element */ + class_scheme->t8_element_new (1, &parent); + class_scheme->t8_element_parent (transition_cell[0], parent); + T8_ASSERT (class_scheme->t8_element_compare (hex_element, parent) == 0); + + /* free memory */ + class_scheme->t8_element_destroy (1, &parent); + class_scheme->t8_element_destroy (num_subelements, transition_cell); + T8_FREE (transition_cell); + + } /* end of transition type loop */ + + /* free more memory */ + class_scheme->t8_element_destroy (1, &hex_element); + t8_scheme_cxx_unref (&ts); + + t8_debugf ("~~~~~~~~~~ The t8_transition_local function finished successful ~~~~~~~~~~\n"); + +} /* end of t8_transition_local */ + +int +main (int argc, char **argv) +{ + int mpiret; + /* Initialize MPI. This has to happen before we initialize sc or t8code. */ + mpiret = sc_MPI_Init (&argc, &argv); + + SC_CHECK_MPI (mpiret); + /* Initialize the sc library, has to happen before we initialize t8code. */ + sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_ESSENTIAL); + + t8_init (SC_LP_DEFAULT); + + t8_transition_local (T8_ECLASS_HEX); + + sc_finalize (); + + mpiret = sc_MPI_Finalize (); + + SC_CHECK_MPI (mpiret); + + return 0; +} diff --git a/example/transition/t8_transition_forest.cxx b/example/transition/t8_transition_forest.cxx new file mode 100644 index 0000000000..a23ef6bd7f --- /dev/null +++ b/example/transition/t8_transition_forest.cxx @@ -0,0 +1,197 @@ +/* + 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 /* A collection of exemplary cmeshes */ +#include +#include +#include /* geometrical information of the forest */ +#include +#include +#include +#include +#include +#include + +/* In this test, we adapt, balance and partition a uniform forest. + * We do this in two ways: + * 1st All operations are performed in one single call to t8_forest_commit + * 2nd Each intermediate step is performed in a separate commit + * + * After these two forests are created, we check for equality. + */ + +/* Adapt a forest such that always the first child of a + * tree is refined and no other elements. This results in a highly + * imbalanced forest. */ +static int +t8_test_adapt_balance (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, t8_locidx_t lelement_id, + t8_eclass_scheme_c *ts, const int is_family, const int num_elements, t8_element_t *elements[]) +{ + T8_ASSERT (!is_family || (is_family && num_elements == ts->t8_element_num_children (elements[0]))); + + int level = ts->t8_element_level (elements[0]); + + /* we set a maximum refinement level as forest user data */ + int maxlevel = *(int *) t8_forest_get_user_data (forest); + // if (level >= maxlevel) { + // /* Do not refine after the maxlevel */ + // return 0; + // } + int child_id = ts->t8_element_child_id (elements[0]); + if (child_id == 1) { + return 1; + } + return 0; + // if (lelement_id == 0 || lelement_id == 5){ + // return 1; + // } + // return 0; +} + +/* adapt, balance and partition a given forest in one step */ +static t8_forest_t +t8_test_forest_commit_abp (t8_forest_t forest, int maxlevel) +{ + t8_debugf ("---------start t8_test_forest_commit_abp ---------------------\n"); + + t8_forest_t forest_ada_bal_par; + + /* Adapt, balance and partition the uniform forest */ + t8_forest_init (&forest_ada_bal_par); + /* Set user data for adapt */ + t8_forest_set_user_data (forest_ada_bal_par, &maxlevel); + t8_forest_set_adapt (forest_ada_bal_par, forest, t8_test_adapt_balance, 0); + t8_forest_set_balance (forest_ada_bal_par, NULL, 0); + t8_forest_set_transition (forest_ada_bal_par, NULL, 0); + t8_forest_set_partition (forest_ada_bal_par, NULL, 0); + t8_forest_commit (forest_ada_bal_par); + + t8_debugf ("---------finsish t8_test_forest_commit_abp ---------------------\n"); + return forest_ada_bal_par; +} + +/* adapt, balance and partition a given forest in 3 steps */ +static t8_forest_t +t8_test_forest_commit_abp_3step (t8_forest_t forest, int maxlevel) +{ + t8_debugf ("---------start t8_test_forest_commit_abtp_3step ---------------------\n"); + + t8_forest_t forest_adapt; + t8_forest_t forest_balance; + t8_forest_t forest_partition; + t8_forest_t forest_transition; + + t8_forest_init (&forest_adapt); + t8_forest_init (&forest_balance); + t8_forest_init (&forest_partition); + t8_forest_init (&forest_transition); + + /* adapt the forest */ + t8_forest_set_user_data (forest_adapt, &maxlevel); + t8_forest_set_adapt (forest_adapt, forest, t8_test_adapt_balance, 0); + t8_forest_commit (forest_adapt); + + /* balance the forest */ + t8_forest_set_balance (forest_balance, forest_adapt, 0); + t8_forest_commit (forest_balance); + + /* transition the forest */ + t8_forest_set_transition (forest_transition, forest_balance, 0); + t8_forest_commit (forest_transition); + + /* partrition the forest */ + t8_forest_set_partition (forest_partition, forest_transition, 0); + t8_forest_commit (forest_partition); + t8_debugf ("---------finsish t8_test_forest_commit_abtp_3step ---------------------\n"); + return forest_partition; +} + +void +t8_transition_commit () +{ + t8_cmesh_t cmesh; + t8_forest_t forest; + t8_forest_t forest_ada_bal_tra_part; + t8_forest_t forest_abtp_3part; + + /* construct a single tree hex cmesh */ + cmesh = t8_cmesh_new_hypercube (T8_ECLASS_HEX, sc_MPI_COMM_WORLD, 0, 0, 0); + + t8_scheme_cxx_t *scheme = t8_scheme_new_transition_hex_cxx (); + + int maxlevel = 2; + for (int level = 2; level <= maxlevel; level++) { + /* ref the cmesh since we reuse it */ + t8_cmesh_ref (cmesh); + t8_debugf ("Testing forest commit level %i\n", level); + + /* Create a uniformly refined forest */ + forest = t8_forest_new_uniform (cmesh, scheme, level, 0, sc_MPI_COMM_WORLD); + + /* We need to use forest twice, so we ref it */ + t8_forest_ref (forest); + /* Adapt, balance, transition and partition the forest */ + + forest_ada_bal_tra_part = t8_test_forest_commit_abp (forest, maxlevel); + t8_forest_write_vtk (forest_ada_bal_tra_part, "transition_forest"); + + /* Adapt, balance, transition and partition the forest using three separate steps */ + forest_abtp_3part = t8_test_forest_commit_abp_3step (forest, maxlevel); + + // ASSERT_TRUE (t8_forest_is_equal (forest_abtp_3part, forest_ada_bal_tra_part)) << "The forests are not equal"; + t8_scheme_cxx_ref (scheme); + + t8_forest_unref (&forest_ada_bal_tra_part); + t8_forest_unref (&forest_abtp_3part); + } + t8_cmesh_unref (&cmesh); + t8_scheme_cxx_unref (&scheme); + + t8_debugf ("Done testing forest commit."); +} + +int +main (int argc, char **argv) +{ + int mpiret; + /* Initialize MPI. This has to happen before we initialize sc or t8code. */ + mpiret = sc_MPI_Init (&argc, &argv); + + SC_CHECK_MPI (mpiret); + /* Initialize the sc library, has to happen before we initialize t8code. */ + sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_ESSENTIAL); + + t8_init (SC_LP_DEFAULT); + + t8_transition_commit (); + + sc_finalize (); + + mpiret = sc_MPI_Finalize (); + + SC_CHECK_MPI (mpiret); + + return 0; +} \ No newline at end of file diff --git a/example/transition/t8_transition_global_hex.cxx b/example/transition/t8_transition_global_hex.cxx new file mode 100644 index 0000000000..8ccab41a56 --- /dev/null +++ b/example/transition/t8_transition_global_hex.cxx @@ -0,0 +1,144 @@ +/* + 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 types 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. +*/ + +/* Description: + * This is the example file for refinement with transitioning. In this testcase, we are able to + * (i) refine a mesh according to some refinement criterion and use transition cells to make the mesh conformal + * (ii) use multiple adaptation steps in which the refinement criterion changes (e.g. the geometry) + * (iii) decide, whether we want to check the LFN function for each mesh + * (iv) decide, whether we want to get statistics printed out, regarding # of elements in the meshes and runtime infos of the several functions or other debugging information + */ + +/* to switch between the default quad scheme and the transition implementation */ +#include "t8_eclass.h" +#include "t8_forest/t8_forest_types.h" + +#include "t8_forest/t8_forest_general.h" +#include +#include +#include +#include +#include // to write vtk +#include +#include +#include /* for cmesh initialization via for example t8_cmesh_new_hypercube */ +#include +#include + +/* In this example, the left side of a unit cube with initial level 2 is refined to construct an adapted and transitioned forest. */ + +/* Refinement criterion: All elements with x-coordinate smaller than 0.5 are being refined. All other elements remain unchanged. */ +int +t8_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, t8_locidx_t lelement_id, + t8_eclass_scheme_c *ts, const int is_family, const int num_elements, t8_element_t *elements[]) +{ + // double coords[3] = {0.0,0.0,0.0}; + // ts->t8_element_vertex_reference_coords(elements[0], 0, coords); + // if (coords[0]* P8EST_ROOT_LEN < 0.5 ){ + // return 1; + // } + int child_id = ts->t8_element_child_id (elements[0]); + if (child_id == 1) { + return 1; + } + return 0; + // else if (coords[0] > 0.5){ + // return -1; + // } + // return 0; +} + +/* adapt, balance, transition and partition a given forest in one step */ +static t8_forest_t +t8_test_forest_commit_abpt (t8_forest_t forest) +{ + t8_forest_t forest_ada_bal_tra_par; + + /* Adapt, balance and partition the uniform forest */ + t8_forest_init (&forest_ada_bal_tra_par); + t8_forest_set_adapt (forest_ada_bal_tra_par, forest, t8_adapt_callback, 0); + t8_forest_set_balance (forest_ada_bal_tra_par, NULL, 0); + t8_forest_set_transition (forest_ada_bal_tra_par, NULL, 0); + t8_forest_set_partition (forest_ada_bal_tra_par, NULL, 0); + t8_forest_commit (forest_ada_bal_tra_par); + + return forest_ada_bal_tra_par; +} + +/* Initializing, adapting balancing and transitioning a forest */ +static void +t8_transition_global (void) +{ + /* At the moment, subelements are only implemented for T8_ECLASS_HEX and quads */ + t8_eclass_t eclass + = T8_ECLASS_HEX; /* depending on the include file, this will be the transitioned or default hex implementation */ + t8_forest_t forest; + t8_forest_t forest_adapt; + t8_cmesh_t cmesh; + char filename[BUFSIZ]; + + /* refinement setting */ + int level = 2; /* initial uniform refinement level */ + + t8_scheme_cxx_t *scheme = t8_scheme_new_transition_hex_cxx (); + + /* construct a single tree hex cmesh */ + cmesh = t8_cmesh_new_hypercube (eclass, sc_MPI_COMM_WORLD, 0, 0, 0); + /* Create a uniformly refined forest */ + forest = t8_forest_new_uniform (cmesh, scheme, level, 0, sc_MPI_COMM_WORLD); + + t8_forest_write_vtk (forest, "forest_global_hex"); + + for (int adaptation_count = 1; adaptation_count <= 2; ++adaptation_count) { + + forest_adapt = t8_test_forest_commit_abpt (forest); + + t8_debugf ("---------------ROUND %i ---------------------------\n\n", adaptation_count); + + forest = forest_adapt; + } + t8_forest_write_vtk (forest, "transition_global_hex"); + + t8_forest_unref (&forest_adapt); + +} /* end of t8_transition_global */ + +int +main (int argc, char **argv) +{ + int mpiret; + + mpiret = sc_MPI_Init (&argc, &argv); + SC_CHECK_MPI (mpiret); + + sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT); + t8_init (SC_LP_DEFAULT); + + t8_transition_global (); + + sc_finalize (); + mpiret = sc_MPI_Finalize (); + + SC_CHECK_MPI (mpiret); + + return 0; +} diff --git a/example/transition/t8_transition_multiple_trees_hex.cxx b/example/transition/t8_transition_multiple_trees_hex.cxx new file mode 100644 index 0000000000..8da9e534c2 --- /dev/null +++ b/example/transition/t8_transition_multiple_trees_hex.cxx @@ -0,0 +1,142 @@ +/* + 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 types 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. +*/ + +/* Description: + * This is the example file for refinement with transitioning. In this testcase, we are able to + * (i) refine a mesh according to some refinement criterion and use transition cells to make the mesh conformal + * (ii) use multiple adaptation steps in which the refinement criterion changes (e.g. the geometry) + * (iii) decide, whether we want to check the LFN function for each mesh + * (iv) decide, whether we want to get statistics printed out, regarding # of elements in the meshes and runtime infos of the several functions or other debugging information + */ + +/* to switch between the default quad scheme and the transition implementation */ +#include "t8_eclass.h" +#include "t8_forest/t8_forest_types.h" + +#include "t8_forest/t8_forest_general.h" +#include +#include +#include +#include +#include // to write vtk +#include +#include +#include /* for cmesh initialization via for example t8_cmesh_new_hypercube */ +#include +#include + +/* In this example, the left side of a unit cube with initial level 2 is refined to construct an adapted and transitioned forest. */ + +/* Refinement criterion: All elements with x-coordinate smaller than 0.5 are being refined. All other elements remain unchanged. */ +int +t8_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, t8_locidx_t lelement_id, + t8_eclass_scheme_c *ts, const int is_family, const int num_elements, t8_element_t *elements[]) +{ + int child_id = ts->t8_element_child_id (elements[0]); + if (child_id == 1) { + return 1; + } + return 0; +} + +/* adapt, balance, transition and partition a given forest in one step */ +static t8_forest_t +t8_test_forest_commit_abpt (t8_forest_t forest) +{ + t8_forest_t forest_ada_bal_tra_par; + + /* Adapt, balance and partition the uniform forest */ + t8_forest_init (&forest_ada_bal_tra_par); + t8_forest_set_adapt (forest_ada_bal_tra_par, forest, t8_adapt_callback, 0); + t8_forest_set_balance (forest_ada_bal_tra_par, NULL, 0); + t8_forest_set_transition (forest_ada_bal_tra_par, NULL, 0); + t8_forest_set_partition (forest_ada_bal_tra_par, NULL, 0); + t8_forest_commit (forest_ada_bal_tra_par); + + return forest_ada_bal_tra_par; +} + +/* Initializing, adapting balancing and transitioning a forest */ +static void +t8_transition_global (void) +{ + /* At the moment, subelements are only implemented for hexes and quads */ + t8_eclass_t eclass + = T8_ECLASS_HEX; /* depending on the include file, this will be the transitioned or default hex implementation */ + t8_forest_t forest; + t8_forest_t forest_adapt; + t8_cmesh_t cmesh; + char filename[BUFSIZ]; + + /* refinement setting */ + int level = 2; /* initial uniform refinement level */ + + t8_locidx_t polygons_x = 2; + t8_locidx_t polygons_y = 1; + t8_locidx_t polygons_z = 1; + + const double boundary[24] = { 0, 0, 0, 2, 0, 0, 0, 1, 0, 2, 1, 0, 0, 0, 1, 2, 0, 1, 0, 1, 1, 2, 1, 1 }; + + t8_scheme_cxx_t *scheme = t8_scheme_new_transition_hex_cxx (); + + /* construct a multiple tree hex cmesh */ + cmesh = t8_cmesh_new_hypercube_pad (eclass, sc_MPI_COMM_WORLD, boundary, polygons_x, polygons_y, polygons_z, 0); + + /* Create a uniformly refined forest */ + forest = t8_forest_new_uniform (cmesh, scheme, level, 0, sc_MPI_COMM_WORLD); + + t8_forest_write_vtk (forest, "forest_global_hex"); + + for (int adaptation_count = 1; adaptation_count <= 2; ++adaptation_count) { + + forest_adapt = t8_test_forest_commit_abpt (forest); + + t8_debugf ("---------------ROUND %i ---------------------------\n\n", adaptation_count); + + forest = forest_adapt; + } + t8_forest_write_vtk (forest, "transition_global_hex"); + + t8_forest_unref (&forest_adapt); + +} /* end of t8_transition_global */ + +int +main (int argc, char **argv) +{ + int mpiret; + + mpiret = sc_MPI_Init (&argc, &argv); + SC_CHECK_MPI (mpiret); + + sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT); + t8_init (SC_LP_DEFAULT); + + t8_transition_global (); + + sc_finalize (); + mpiret = sc_MPI_Finalize (); + + SC_CHECK_MPI (mpiret); + + return 0; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0dcea760ef..ad5e732208 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -111,6 +111,7 @@ target_sources( T8 PRIVATE t8_forest/t8_forest_iterate.cxx t8_forest/t8_forest_balance.cxx t8_forest/t8_forest_netcdf.cxx + t8_forest/t8_forest_transition.cxx t8_geometry/t8_geometry.cxx t8_geometry/t8_geometry_helpers.c t8_geometry/t8_geometry_base.cxx @@ -144,6 +145,10 @@ target_sources( T8 PRIVATE t8_schemes/t8_default/t8_default_tri/t8_dtri_connectivity.c t8_schemes/t8_default/t8_default_vertex/t8_default_vertex.cxx t8_schemes/t8_default/t8_default_vertex/t8_dvertex_bits.c + t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex.cxx + t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex_cxx.cxx + t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad.cxx + t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad_cxx.cxx t8_vtk/t8_vtk_polydata.cxx t8_vtk/t8_vtk_unstructured.cxx t8_vtk/t8_vtk_parallel.cxx diff --git a/src/Makefile.am b/src/Makefile.am index 191cccad5b..912261011d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -102,6 +102,7 @@ libt8_internal_headers = \ src/t8_vtk/t8_vtk_parallel.hxx \ src/t8_forest/t8_forest_ghost.h \ src/t8_forest/t8_forest_balance.h src/t8_forest/t8_forest_types.h \ + src/t8_forest/t8_forest_transition.h \ src/t8_forest/t8_forest_private.h \ src/t8_windows.h \ src/t8_vtk/t8_vtk_writer_helper.hxx @@ -141,6 +142,7 @@ libt8_compiled_sources = \ src/t8_forest/t8_forest_ghost.cxx src/t8_forest/t8_forest_iterate.cxx \ src/t8_version.c \ src/t8_vtk.c src/t8_forest/t8_forest_balance.cxx \ + src/t8_forest/t8_forest_transition.cxx \ src/t8_forest/t8_forest_netcdf.cxx \ src/t8_element_shape.c \ src/t8_netcdf.c \ diff --git a/src/t8_element.hxx b/src/t8_element.hxx index 53effbff9f..cfaae07ab5 100644 --- a/src/t8_element.hxx +++ b/src/t8_element.hxx @@ -738,6 +738,113 @@ struct t8_eclass_scheme * \param [in, out] position the position of the first byte that is not already packed * \param [in] comm MPI Communicator */ + /*-----------------------------------Functions for transition schemes------------------------------------------------*/ + + /** Return the subelement id of the neighbor subelement of elem (possibly not subelement) at face elem_face + * that is a sibling of the subelement neigh. + * \param [in] elem a given element (possibly subelement) + * \param [in] neigh a random subelement (pseudoneighbor) in a transition cell from which we assume that it owns the real neighbor of elem + * \param [in] elem_face a given face number of element elem + * \return the subelement id of the real subelement neighbor of element elem, which is a sibling of neigh. + */ + virtual int + t8_element_find_neighbor_in_transition_cell (const t8_element_t *elem, const t8_element_t *neigh, int elem_face) + = 0; + + /** Return the transition type of an element + * \param [in] elem A valid element + * \return the transition type of elem (0 if elem is no subelement) + */ + virtual int + t8_element_get_transition_type (const t8_element *elem) + = 0; + + /** Check whether the neighbors of an element at a specific face are siblings + * \param [in] elem A valid element + * \param [in] elem_face A valid face + * \return true if the neighbor of elem at face elem_face is a sibling. + */ + virtual int + t8_element_neighbor_is_sibling (const t8_element *elem, const int elem_face) const + = 0; + + /** Check whether the neighbors of an element at a specific face are siblings + * \param [in] elem A valid element + * \param [in] elem_face A valid face + * \return return the number of sibling neighbors at a given face. + */ + virtual int + t8_element_get_num_sibling_neighbors_at_face (const t8_element *elem, const int elem_face) const + = 0; + + /** Construct all sibling neighbors of elem at face in hex scheme. */ + virtual void + + t8_element_get_sibling_neighbor_in_transition_cell_hex (const t8_element_t *elem, const int face, + const int num_neighbors, t8_element_t *neighbor_at_face[], + int *neigh_face) + = 0; + + /** Construct all sibling neighbors of elem at face in quad scheme. */ + virtual void + + t8_element_get_sibling_neighbor_in_transition_cell (const t8_element_t *elem, const int face, const int num_neighbors, + t8_element_t *neighbor_at_face[], int *neigh_face[]) + = 0; + /** Return 1 if the eclass scheme has an implementation for subelements, which is conformal. */ + virtual int + t8_element_transition_scheme_is_conformal (void) + = 0; + /** Return zero refine value for schemes that do not have a transition implementation. + * \param [in] elem A valid element + * \return Integer, used as the refine value during transition adaptation. + */ + virtual int + t8_element_get_transition_refine_identifier (void) const + = 0; + + /** Check whether a given element is a subelement + * \param [in] elem A valid element + * \return true if elem is a subelement + */ + virtual int + t8_element_is_subelement (const t8_element *elem) const + = 0; + + /** Return the number of subelements in a transition cell of type transition_type + * \param [in] transition_type The subelement type as an integer + * \return the number of subelements, this transition cell consists of + */ + virtual int + t8_element_get_number_of_subelements (int transition_type) const + = 0; + + /** Return the subelement id of a given element. + * \param [in] elem A valid element + * \return the subelement id of elem (0 if elem is no subelement) + */ + virtual int + t8_element_get_subelement_id (const t8_element *elem) const + = 0; + + /** This function refines a parent element into subelements. + * Depending on the subelement type, the number of subelements + * to fill the parent element, can differ. + * \param [in] elem A valid element + * \param [in] type The subelement type + * \param [out] subelements An array of all subelements of the parent quad element elem + */ + virtual void + t8_element_to_transition_cell (const t8_element_t *elem, int type, t8_element_t *subelements[]) + = 0; + + /** Return 1 if the eclass scheme has an implementation for subelements. Return 0 otherwise. */ + virtual int + t8_element_scheme_supports_transitioning (void) + = 0; + + /* --------------------------End functions of transition scheme---------------------*/ + virtual void t8_element_MPI_Pack (t8_element_t **const elements, const unsigned int count, void *send_buffer, int buffer_size, int *position, sc_MPI_Comm comm) const diff --git a/src/t8_element_c_interface.cxx b/src/t8_element_c_interface.cxx index 88d57e5f65..943155b9a8 100644 --- a/src/t8_element_c_interface.cxx +++ b/src/t8_element_c_interface.cxx @@ -46,6 +46,14 @@ t8_element_refines_irregular (const t8_eclass_scheme_c *ts) return ts->t8_element_refines_irregular (); } +int +t8_element_scheme_supports_transitioning (t8_eclass_scheme_c *ts) +{ + T8_ASSERT (ts != NULL); + + return ts->t8_element_scheme_supports_transitioning (); +} + int t8_element_maxlevel (const t8_eclass_scheme_c *ts) { diff --git a/src/t8_element_c_interface.h b/src/t8_element_c_interface.h index f1fb8cea52..f1a5c524e9 100644 --- a/src/t8_element_c_interface.h +++ b/src/t8_element_c_interface.h @@ -49,6 +49,10 @@ t8_element_size (const t8_eclass_scheme_c *ts); int t8_element_refines_irregular (const t8_eclass_scheme_c *ts); +/** Returns true if the given eclass scheme has an implementation for subelements */ +int +t8_element_scheme_supports_transitioning (t8_eclass_scheme_c *ts); + /** Return the maximum allowed level for any element of a given class. * \param [in] ts Implementation of a class scheme. * \return The maximum allowed level for elements of class \b ts. diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index c65c2dc14d..a7f5ea9823 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -186,6 +187,7 @@ t8_forest_compute_maxlevel (t8_forest_t forest) /* If there are trees of this class, compute the maxlevel of the class */ ts = t8_forest_get_eclass_scheme_before_commit (forest, (t8_eclass_t) eclass_it); maxlevel = ts->t8_element_maxlevel (); + /* Compute the minimum of this level and the stored maxlevel */ if (forest->maxlevel == -1) { forest->maxlevel = maxlevel; @@ -1426,6 +1428,8 @@ t8_forest_copy_trees (t8_forest_t forest, t8_forest_t from, int copy_elements) } forest->first_local_tree = from->first_local_tree; forest->last_local_tree = from->last_local_tree; + forest->is_transitioned = from->is_transitioned; + if (copy_elements) { forest->local_num_elements = from->local_num_elements; forest->global_num_elements = from->global_num_elements; @@ -1706,6 +1710,515 @@ t8_forest_element_half_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, return neighbor_tree; } +/* This function does not need a declaration and is only called by LFN_transitioned. + * When entering this function, a neighbor transition cell is found and some additional + * subelement-specific functions are applied to return the right subelement neighbor of leaf. */ +static void +t8_forest_get_transition_cell_face_neighbor (t8_forest_t forest, const t8_element_t *pseudo_neighbor, + const t8_element_t *leaf, t8_locidx_t element_index, int face, + t8_locidx_t lneigh_treeid, t8_element_t **neighbor_leaves, + t8_locidx_t **pelement_indices, int neighbor_is_ghost, int *owners, + t8_locidx_t lghost_treeid, t8_locidx_t ghost_element_index, + t8_eclass_scheme_c *ts, t8_eclass_scheme_c *neigh_scheme, + int *num_neighbors) +{ + /* Consider the following situation: + * + * this is not yet implemented + * x - - - - - x - - - - - x x - - - - - x + * | | \ pnei / | / | \ pnei / | + * | | \ / | / | \ / | + * | leaf f| nei x | / f| nei x | + * | | / | \ | / leaf | / | \ | + * | | / | \ | / | / | \ | + * x - - - - - x - - x - - x x - - - - - x - - x - - x + * ts == neigh_scheme ts != neigh_scheme + * + * We are looking for the neighbor (nei) of leaf at f. + * Given is a random element (pnei), called pseudo_neighbor, of the neighboring transition cell. + * The aim of this function is to identify the real subelement neighbor (nei) of leaf at f. + * This function is neither implemented for hybrid meshes, nor for nonconformal meshes - we expect exactly one subelement neighbor. */ + + const t8_element_t *neighbor; + t8_locidx_t pseudo_neighbor_index; + int pseudo_neighbor_sub_id; + int leaf_neighbor_sub_id; + + T8_ASSERT (ts == neigh_scheme); + T8_ASSERT (forest->is_transitioned); + T8_ASSERT (neigh_scheme->t8_element_is_subelement (pseudo_neighbor)); + T8_ASSERT (element_index < forest->global_num_elements); + + pseudo_neighbor_index = element_index; + pseudo_neighbor_sub_id = neigh_scheme->t8_element_get_subelement_id (pseudo_neighbor); + + /* Get the subelement id of the real neighbor of leaf. + * This function will determine the sub_id of the real neighbor, given leaf, face and the pseudo_neighbor. + * This is why ts == neigh_scheme must be true. */ + //It is only valid to call this function if the face of elem is point outwards, thus face = 4. + t8_debugf ("face %i\n", face); + t8_debugf ("elem is subelement %i\n", ts->t8_element_is_subelement (leaf)); + t8_debugf ("neigh is subelement %i\n", ts->t8_element_is_subelement (pseudo_neighbor)); + + leaf_neighbor_sub_id = neigh_scheme->t8_element_find_neighbor_in_transition_cell (leaf, pseudo_neighbor, face); + + if (!neighbor_is_ghost) { + /* adjust the index of the pseudo neighbor to equal the index of the real neighbor. + * The following computation should be true for every subelement scheme and does not require + * a separate t8_element function. The important - and subelement-scheme-dependent - + * aspect is the computation of leaf_neighbor_sub_id above. */ + element_index = pseudo_neighbor_index - pseudo_neighbor_sub_id + leaf_neighbor_sub_id; + + /* get the real neighbor */ + neighbor = t8_forest_get_tree_element (t8_forest_get_tree (forest, lneigh_treeid), + element_index - t8_forest_get_tree_element_offset (forest, lneigh_treeid)); + + /* free memory */ + neigh_scheme->t8_element_destroy (0, neighbor_leaves + 1); + /* copy the neighbor */ + neigh_scheme->t8_element_copy (neighbor, neighbor_leaves[0]); + + /* set return values */ + *pelement_indices = T8_ALLOC (t8_locidx_t, 1); + (*pelement_indices)[0] = element_index; + + T8_FREE (owners); + + *num_neighbors = 1; + + return; + } + + /* the neighbor subelement is a ghost */ + int search_direction = (leaf_neighbor_sub_id > pseudo_neighbor_sub_id) ? 1 : -1; + int id_diff_abs = (leaf_neighbor_sub_id - pseudo_neighbor_sub_id) * search_direction; + + bool neighbor_found = false; + int subelement_count = 0; + + while (!neighbor_found) { + T8_ASSERT (ghost_element_index >= 0); /* check, that ghost_element_index has been set before */ + + const t8_element_t *check_neighbor; + check_neighbor + = t8_forest_ghost_get_element (forest, lghost_treeid, ghost_element_index + search_direction * subelement_count); + + if (neigh_scheme->t8_element_get_subelement_id (check_neighbor) == leaf_neighbor_sub_id) { + /* both elements should be siblings or equal */ + T8_ASSERT (neigh_scheme->t8_element_get_transition_type (check_neighbor) + == neigh_scheme->t8_element_get_transition_type (pseudo_neighbor)); + + /* adjust element index */ + element_index = element_index + search_direction * subelement_count; + + /* free memory */ + neigh_scheme->t8_element_destroy (0, neighbor_leaves + 1); + /* copy the neighbor */ + neigh_scheme->t8_element_copy (check_neighbor, neighbor_leaves[0]); + + /* set return values */ + *pelement_indices = T8_ALLOC (t8_locidx_t, 1); + (*pelement_indices)[0] = element_index; + + T8_FREE (owners); + + /* neighbor found */ + neighbor_found = true; + } + + if (subelement_count + > id_diff_abs) { /* we assume that the real neighbor is a sibling subelement and hence we should find it here */ + SC_ABORT ("Ghost subelement neighbor not found!\n"); + } + + subelement_count++; + } /* end while-loop to identify ghost-subelement neighbor */ + + *num_neighbors = 1; + + return; +} + +/* This is the transitioned version of the LFN function. It does not need a declaration + * as it is only called by LFN if the input forest is transitioned. + * Note: there is a lot of code duplication compared to LFN, but it might be better to separate the transitioned LFN + * from the standard LFN for now. */ +void +t8_forest_leaf_face_neighbors_transitioned (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *leaf, + t8_element_t **pneighbor_leaves[], int face, int *dual_faces[], + int *num_neighbors, t8_locidx_t **pelement_indices, + t8_eclass_scheme_c **pneigh_scheme, int forest_is_balanced) +{ + /* Consider the following transitioned forest: + * + * forest + * f1 + * x - - x - - x - - - - - x + * | | | \ sub / | + * | | | f0\ /f2 | + * x - - x - - x - - x | + * | | f1| / | \ | + * | | elem| / | \ | + * x - - x - - x - - x - - x + * + * How do we identify neighbors in transitioned forests? + * 1) Check whether the current element is a subelement. If yes, then its neighbor might be a sibling subelement (for example sub at f0 or f2). + * If so, then we can quickly compute and return the corresponding neighbor. + * 2) Otherwise (in case of elem at any face or sub at f1), we apply the standard LFN concept of face neighbors or half-face neighbors + * to identify the real neighbor element. Here, it does not matter whether the current element is a subelement or a standard element. + * 3) We are done at 2) if the identified neighbor is no subelement. Otherwise (like in the case of elem at f1), the binary search, based on the Morton-Index + * will randomly pick one subelement of the corresponding transition cell (since, by definition they all share the same Morton Index). + * Hence, in this case we apply some additional - subelement specific - t8_element functions in order to return the right subelement neighbor. */ + + /* In the following, + * - leaf (ts scheme) is the current element of the forest + * - neighbor_leaves[] (neigh_scheme) is an array of neighbors of leaf. At first, it consists of VFNs or VHFNs (virtual (half) face neighbors)of leaf at f + * Later, the real neighbor elements are copied into this array and returned by the LFN function + * - ancestor (neigh_scheme) is a "first guess". It is a leaf element of the forest that corresponds to the VFNs or VHFNs in neighbor_leaves[] */ + + t8_eclass_t neigh_class, eclass; + t8_gloidx_t gneigh_treeid; + t8_locidx_t lneigh_treeid = -1; + t8_locidx_t lghost_treeid = -1, *element_indices, element_index, subelement_neighbor_index, ghost_element_index = -1; + t8_eclass_scheme_c *ts, *neigh_scheme; + + const t8_element_t *ancestor; + t8_element_t **neighbor_leaves; + t8_linearidx_t neigh_id; + int num_children_at_face, at_maxlevel; + int ineigh, *owners, different_owners, have_ghosts, neighbor_is_ghost = 0; + + T8_ASSERT (t8_forest_is_committed (forest)); + + /* This function should only be called for transitioned forests */ + T8_ASSERT (forest->is_transitioned); + + if (forest_is_balanced) { + /* In a balanced forest, the leaf neighbor of a leaf is either the neighbor element itself, + * its parent or its children at the face. If the forest is transitioned, + * then the neighbor might also be a subelement whose level will also differ at most by +-1 to the leaf element. + * The leaf might as well be a subelement. */ + eclass = t8_forest_get_tree_class (forest, ltreeid); + ts = t8_forest_get_eclass_scheme (forest, eclass); + + /* LFN_transitioned does not yet support nonconformal transitioned meshes. + * Hybrid meshes can be used if both schemes support conformal transitioning. */ + SC_CHECK_ABORT ( + (ts->t8_element_scheme_supports_transitioning () && ts->t8_element_transition_scheme_is_conformal ()), + "LFN_transitioned not implemented for nonconformal transition schemes"); + + /* In the following, we compute the children of the face neighbor elements of leaf. For this, we need the + * neighbor tree's eclass, scheme, and tree id */ + neigh_class = t8_forest_element_neighbor_eclass (forest, ltreeid, leaf, face); + neigh_scheme = *pneigh_scheme = t8_forest_get_eclass_scheme (forest, neigh_class); + + /* 1) Check whether the current element is a subelement and whether its neighbor is a sibling subelement */ + + /* If the current element is a subelement, then we can determine whether its neighbor + * at face is a sibling. If so, then we can easily compute its neighbor. + * Note, that a transition cell (a family of subelements will always be within the same process) */ + if (ts->t8_element_is_subelement (leaf)) { + if (ts->t8_element_neighbor_is_sibling (leaf, face) > 0) { + /* subelement siblings will be in the same local tree */ + T8_ASSERT (ts == neigh_scheme); + *num_neighbors = ts->t8_element_get_num_sibling_neighbors_at_face (leaf, face); + neighbor_leaves = *pneighbor_leaves = T8_ALLOC (t8_element_t *, *num_neighbors); + *dual_faces = T8_ALLOC (int, *num_neighbors); + ts->t8_element_new (*num_neighbors, neighbor_leaves); + if (eclass == 4) { + ts->t8_element_get_sibling_neighbor_in_transition_cell_hex (leaf, face, *num_neighbors, neighbor_leaves, + *dual_faces); + } + if (eclass == 2) { + ts->t8_element_get_sibling_neighbor_in_transition_cell (leaf, face, *num_neighbors, neighbor_leaves, + dual_faces); + } + + *pelement_indices = T8_ALLOC (t8_locidx_t, *num_neighbors); + element_indices = *pelement_indices; + for (int neighbor_count = 0; neighbor_count < *num_neighbors; neighbor_count++) { + t8_debugf ("num neighbors %i\n", *num_neighbors); + neigh_id = ts->t8_element_get_linear_id (neighbor_leaves[neighbor_count], forest->maxlevel); + const t8_element_array_t *element_array = t8_forest_get_tree_element_array (forest, ltreeid); + subelement_neighbor_index = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); + element_indices[neighbor_count] = subelement_neighbor_index + - ts->t8_element_get_subelement_id (t8_forest_get_tree_element ( + t8_forest_get_tree (forest, ltreeid), subelement_neighbor_index)) + + ts->t8_element_get_subelement_id (neighbor_leaves[neighbor_count]); + T8_ASSERT (element_indices[neighbor_count] >= 0); + } + return; + } + } + + /* 2) The neighbor is no sibling subelement. Move on with the standard (half)-face-neighbor procedure */ + + /* If we are at the maximum refinement level, + * then we compute the single neighbor instead. */ + at_maxlevel = ts->t8_element_level (leaf) == t8_forest_get_maxlevel (forest); + if (at_maxlevel || ts->t8_element_transition_scheme_is_conformal ()) { + /* If leaf has maxlevel or the current eclass scheme is transitioned and conformal, + * we know that the leaf element has at most one neighbor at each face. + * Note that in hybrid meshes, the mesh must not be conformal since not each tree might be conformal */ + num_children_at_face = 1; + neighbor_leaves = *pneighbor_leaves = T8_ALLOC (t8_element_t *, num_children_at_face); + *dual_faces = T8_ALLOC (int, num_children_at_face); + neigh_scheme->t8_element_new (num_children_at_face, neighbor_leaves); + /* Compute virtual neighbor element and global treeid of the neighbor */ + gneigh_treeid + = t8_forest_element_face_neighbor (forest, ltreeid, leaf, neighbor_leaves[0], neigh_scheme, face, *dual_faces); + } + else { + /* At this point, we expect an arbitrary number of neighbors. Our transition implementation is + * build for this case and does not require a conformal mesh. */ + + /* Note the following: LFN is only implemented for balanced meshes and therefore uses the concept of + * virtual half face neighbors. This concept WILL NOT WORK for a nonconformal transition scheme whose transition cells enable more + * neighbors than the maximum number of neighbors per element in the forests pure balanced version. + * This is because in such cases the concept of half face neighbors would not be sufficient to depict all + * neighbor subelements in a neighboring transition cell. + * TODO: think of a solution for this issue to enable more complex transition cells, consider for example the following situation: + * + * quad transition cell + * x - - - - - x - - - x + * | x - - - x -> transition cell with three subelements + * | x - - - x (too many neighbors for only two half face neighbors of leaf) + * | leaf f x - - - x + * | | | -> normal quad + * | | | + * x - - - - - x - - - x + * + * Note that the above example counts as "balanced", but half face neighbors would not be sufficient to find all neighbors of leaf at f. + * Currently, ancestor would identify as the lower normal quad neighbor and afterwards, we would just use the second HFN of leaf as the + * second neighbor. But in such a transitioned case, there is a whole transition cell of neighbors corresponding to the second HFN. + * Furthermore, even if there would only be one subelement neighbor, this neighbor would be a subelement, NOT EQUALING the second HFN. */ + + /* Allocate neighbor element */ + num_children_at_face = ts->t8_element_num_face_children (leaf, face); + neighbor_leaves = *pneighbor_leaves = T8_ALLOC (t8_element_t *, num_children_at_face); + *dual_faces = T8_ALLOC (int, num_children_at_face); + neigh_scheme->t8_element_new (num_children_at_face, neighbor_leaves); + /* Compute virtual neighbor elements and global treeid of the neighbor */ + gneigh_treeid = t8_forest_element_half_face_neighbors (forest, ltreeid, leaf, neighbor_leaves, neigh_scheme, face, + num_children_at_face, *dual_faces); + } + + /* neighbor_leaves[i] do now equal the FN or HFN of leaf at f. In the following we check whether there is only one neighbor + * and whether neighbor_leaves[i] have to be adjusted further. Finally, the neighbor_leaves element array is returned by the LFN function. */ + + if (gneigh_treeid < 0) { + /* There exists no face neighbor across this face, we return with this info */ + neigh_scheme->t8_element_destroy (1, neighbor_leaves); + T8_FREE (neighbor_leaves); + T8_FREE (*dual_faces); + *dual_faces = NULL; + *num_neighbors = 0; + *pelement_indices = NULL; + *pneighbor_leaves = NULL; + return; + } + T8_ASSERT (gneigh_treeid >= 0 && gneigh_treeid < forest->global_num_trees); + /* We have computed the half face neighbor elements, we now compute their owners, + * if they differ, we know that the half face neighbors are the neighbor leaves. + * If the owners do not differ, we have to check if the neighbor leaf is their + * parent or grandparent. */ + owners = T8_ALLOC (int, num_children_at_face); + different_owners = 0; + have_ghosts = 0; + for (ineigh = 0; ineigh < num_children_at_face; ineigh++) { + /* At first, we check whether the current rank owns the neighbor, since + * this is a constant time check and it is the most common case */ + if (t8_forest_element_check_owner (forest, neighbor_leaves[ineigh], gneigh_treeid, neigh_class, forest->mpirank, + at_maxlevel)) { + owners[ineigh] = forest->mpirank; + /* The neighbor tree is also a local tree. we store its local treeid */ + lneigh_treeid = t8_forest_get_local_id (forest, gneigh_treeid); + } + else { + owners[ineigh] = t8_forest_element_find_owner (forest, gneigh_treeid, neighbor_leaves[ineigh], neigh_class); + /* Store that at least one neighbor is a ghost */ + have_ghosts = 1; + } + if (ineigh > 0) { + /* Check if all owners are the same for all neighbors or not */ + different_owners = different_owners || (owners[ineigh] != owners[ineigh - 1]); + } + } + if (have_ghosts) { + /* At least one neighbor is a ghost, we compute the ghost treeid of the neighbor + * tree. */ + lghost_treeid = t8_forest_ghost_get_ghost_treeid (forest, gneigh_treeid); + T8_ASSERT (lghost_treeid >= 0); + } + + if (!different_owners) { + /* The face neighbors belong to the same process, we thus need to determine + * if they are leaves or their parent or grandparent. */ + neigh_id = neigh_scheme->t8_element_get_linear_id (neighbor_leaves[0], forest->maxlevel); + if (owners[0] != forest->mpirank) { + /* The elements are ghost elements of the same owner */ + neighbor_is_ghost = 1; + const t8_element_array_t *element_array = t8_forest_ghost_get_tree_elements (forest, lghost_treeid); + /* Find the index in element_array of the leaf ancestor of the first neighbor. + * This is either the neighbor itself or its parent, or its grandparent */ + element_index = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); + /* Get the element */ + ancestor = t8_forest_ghost_get_element (forest, lghost_treeid, element_index); + /* Add the number of ghost elements on previous ghost trees and the number + * of local elements. */ + ghost_element_index = element_index; + element_index += t8_forest_ghost_get_tree_element_offset (forest, lghost_treeid); + element_index += t8_forest_get_local_num_elements (forest); + T8_ASSERT (forest->local_num_elements <= element_index + && element_index < forest->local_num_elements + t8_forest_get_num_ghosts (forest)); + } + else { + /* the elements are local elements */ + const t8_element_array_t *element_array = t8_forest_get_tree_element_array (forest, lneigh_treeid); + /* Find the index in element_array of the leaf ancestor of the first neighbor. + * This is either the neighbor itself or its parent, or its grandparent */ + element_index = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); + /* Get the element */ + ancestor = t8_forest_get_tree_element (t8_forest_get_tree (forest, lneigh_treeid), element_index); + /* Add the element offset of this tree to the index */ + element_index += t8_forest_get_tree_element_offset (forest, lneigh_treeid); + } + + if ((neigh_scheme->t8_element_compare (ancestor, neighbor_leaves[0]) < 0) + || ts->t8_element_is_subelement (leaf)) { + + /* we expect only one neighbor (or a neighboring transition cell with possibly multiple + * subelement neighbors if the transition scheme is not conformal) in this case */ + + /* We need to determine the dual face */ + if (neigh_scheme->t8_element_level (ancestor) == ts->t8_element_level (leaf)) { + /* The ancestor is the same-level neighbor of leaf */ + if (!at_maxlevel) { + /* its dual face is the face of the parent of the first neighbor leaf */ + *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); + } + } + else if (neigh_scheme->t8_element_level (ancestor) == ts->t8_element_level (leaf) + 1) { + *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); + } + else { + /* The ancestor is the parent of the parent */ + + T8_ASSERT (neigh_scheme->t8_element_level (ancestor) == ts->t8_element_level (leaf) - 1); + + *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); + if (!at_maxlevel) { + /* We need to compute the dual face of the grandparent. */ + /* Construct the parent of the grand child */ + neigh_scheme->t8_element_parent (neighbor_leaves[0], neighbor_leaves[0]); + /* Compute the face id of the parent's face */ + *dual_faces[0] = neigh_scheme->t8_element_face_parent_face (neighbor_leaves[0], *dual_faces[0]); + } + } + + /* 3) A leaf neighbor (ancestor) is found. First, check whether leaf is a subelement + * in a transition cell. If yes, then apply subelement-specific functions. + * Otherwise, continue with the standard LFN procedure. */ + + /* TODO: nonconformal meshes will need a loop over all ancestors, corresponding to all neighbor_leaves[i]. + * And for each ancestor we would need to check whether it is a subelement and then determine all subelement neighbors + * which might be more than one. */ + + if (neigh_scheme->t8_element_is_subelement (ancestor)) { + /* At this point, "ancestor" is a random subelement of the neighboring transition cell of leaf. */ + /* We need to identify the real subelement neighbor within this transition cell. */ + + /* TODO: in nonconformal transitioned forests there might be more than one subelement neighbor + * in a neighboring transition cell - not implemented yet */ + T8_ASSERT (neigh_scheme->t8_element_transition_scheme_is_conformal ()); + t8_forest_get_transition_cell_face_neighbor ( + forest, ancestor, leaf, element_index, face, lneigh_treeid, neighbor_leaves, pelement_indices, + neighbor_is_ghost, owners, lghost_treeid, ghost_element_index, ts, neigh_scheme, num_neighbors); + return; + } + + /* free memory */ + neigh_scheme->t8_element_destroy (num_children_at_face - 1, neighbor_leaves + 1); + /* copy the ancestor */ + neigh_scheme->t8_element_copy (ancestor, neighbor_leaves[0]); + /* set return values */ + *num_neighbors = 1; + *pelement_indices = T8_ALLOC (t8_locidx_t, 1); + (*pelement_indices)[0] = element_index; + + T8_FREE (owners); + return; + } /* end of (t8_element_compare < 0) || leaf is subelement */ + } /* end of if !different owner */ + + /* neighbor_leaves[i] are not adjusted below this comment. + * Hence, we assume that the FNs or HFNs equal the real leaf neighbor elements. */ + + /* The leaves are the face neighbors that we are looking for. */ + /* The face neighbors either belong to different processes and thus must be leaves + * in the forest, or the ancestor leaf of the first half neighbor is the half + * neighbor itself and thus all half neighbors must be leaves. + * Since the forest is balanced, we found all neighbor leaves. + * It remains to compute their local ids */ + *num_neighbors = num_children_at_face; + *pelement_indices = T8_ALLOC (t8_locidx_t, num_children_at_face); + element_indices = *pelement_indices; + for (ineigh = 0; ineigh < num_children_at_face; ineigh++) { + /* Compute the linear id at maxlevel of the neighbor leaf */ + neigh_id = neigh_scheme->t8_element_get_linear_id (neighbor_leaves[ineigh], forest->maxlevel); + /* Get a pointer to the element array in which the neighbor lies and search + * for the element's index in this array. + * This is either the local leaf array of the local tree or the corresponding leaf array + * in the ghost structure */ + if (owners[ineigh] == forest->mpirank) { + /* The neighbor is a local leaf */ + const t8_element_array_t *element_array = t8_forest_get_tree_element_array (forest, lneigh_treeid); + /* Find the index of the neighbor in the array */ + element_indices[ineigh] = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); + T8_ASSERT (element_indices[ineigh] >= 0); + /* We have to add the tree's element offset to the index found to get + * the actual local element id */ + element_indices[ineigh] += t8_forest_get_tree_element_offset (forest, lneigh_treeid); +#if T8_ENABLE_DEBUG + /* We check whether the element is really the element at this local id */ + { + t8_locidx_t check_ltreeid; + t8_element_t *check_element; + check_element = t8_forest_get_element (forest, element_indices[ineigh], &check_ltreeid); + T8_ASSERT (check_ltreeid == lneigh_treeid); + T8_ASSERT (!neigh_scheme->t8_element_compare (check_element, neighbor_leaves[ineigh])); + } +#endif + } + else { + /* The neighbor is a ghost */ + const t8_element_array_t *element_array = t8_forest_ghost_get_tree_elements (forest, lghost_treeid); + /* Find the index of the neighbor in the array */ + element_indices[ineigh] = t8_forest_bin_search_lower (element_array, neigh_id, forest->maxlevel); + +#if T8_ENABLE_DEBUG + /* We check whether the element is really the element at this local id */ + { + t8_element_t *check_element; + check_element = t8_forest_ghost_get_element (forest, lghost_treeid, element_indices[ineigh]); + T8_ASSERT (!neigh_scheme->t8_element_compare (check_element, neighbor_leaves[ineigh])); + t8_debugf ("LFN_transitioned: the neighbor is a ghost.\n"); + } +#endif + /* Add the element offset of previous ghosts to this index */ + element_indices[ineigh] += t8_forest_ghost_get_tree_element_offset (forest, lghost_treeid); + /* Add the number of all local elements to this index */ + element_indices[ineigh] += t8_forest_get_local_num_elements (forest); + } + } /* End for loop over neighbor leaves */ + T8_FREE (owners); + } /* end of if (forest_is_balanced || forest->is_transitioned) (main part of the LFN) */ + else { + /* TODO: implement unbalanced version */ + SC_ABORT_NOT_REACHED (); + } +} + int t8_forest_leaf_face_orientation (t8_forest_t forest, const t8_locidx_t ltreeid, const t8_eclass_scheme_c *ts, const t8_element_t *leaf, int face) @@ -1742,11 +2255,18 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons T8_ASSERT (t8_forest_is_committed (forest)); T8_ASSERT (t8_forest_element_is_leaf (forest, leaf, ltreeid)); T8_ASSERT (!forest_is_balanced || t8_forest_is_balanced (forest)); + SC_CHECK_ABORT (forest_is_balanced, "leaf face neighbors is not implemented " "for unbalanced forests.\n"); /* TODO: write version for unbalanced forests */ SC_CHECK_ABORT (forest->mpisize == 1 || forest->ghosts != NULL, "Ghost structure is needed for t8_forest_leaf_face_neighbors " "but was not found in forest.\n"); + /* if the given forest is transitioned, then call the transitioned LFN function. */ + if (forest->is_transitioned) { + t8_forest_leaf_face_neighbors_transitioned (forest, ltreeid, leaf, pneighbor_leaves, face, dual_faces, + num_neighbors, pelement_indices, pneigh_scheme, forest_is_balanced); + return; + } if (forest_is_balanced) { /* In a balanced forest, the leaf neighbor of a leaf is either the neighbor element itself, @@ -1968,6 +2488,7 @@ t8_forest_leaf_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, const t8 t8_locidx_t **pelement_indices, t8_eclass_scheme_c **pneigh_scheme, int forest_is_balanced) { + t8_forest_leaf_face_neighbors_ext (forest, ltreeid, leaf, pneighbor_leaves, face, dual_faces, num_neighbors, pelement_indices, pneigh_scheme, forest_is_balanced, NULL, NULL); } @@ -2069,9 +2590,45 @@ 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. - * 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); + + /* An element was found but it may not be the candidate element. + * If our original query element is not a subelement, then we compare with the found element. + * + * Otherwise, if our query element is a subelement, then all subelements from the same element match + * the search and we need to query whether our element is contained in that set. + * If the query element is a leaf then the search_result may point to any other subelement resulting from the + * same element. + */ + t8_locidx_t check_index = search_result; + + if (scheme->t8_element_is_subelement (element)) { + const t8_element_t *check_subelement = t8_element_array_index_locidx (elements, search_result); + T8_ASSERT (check_subelement != NULL); + /* Our query element is a subelement. + * It has a subelement id A. + * If the check element at index "search_result" is not a subelement, + * then the element is not a leaf and we can return. + * If it is a subelement, then it has id B. + * If our query element is contained in the array, then + * it must be at index "search_result - B + A". + */ + const int query_subelement_id = scheme->t8_element_get_subelement_id (element); + if (!scheme->t8_element_is_subelement (check_subelement)) { + // The found element is not a subelement, thus our query element is not a leaf. + return 0; + } + const int check_subelement_id = scheme->t8_element_get_subelement_id (check_subelement); + // Compute the new index where our query element should be found. + check_index = search_result - check_subelement_id + query_subelement_id; + + if (t8_element_array_get_count (elements) <= (size_t) check_index) { + /* The element cannot be contained in the array. */ + return 0; + } + } + + /* Get the respective element from the array and compare with our query element. */ + const t8_element_t *check_element = t8_element_array_index_locidx (elements, check_index); T8_ASSERT (check_element != NULL); return (scheme->t8_element_equal (element, check_element)); } @@ -2800,6 +3357,8 @@ t8_forest_init (t8_forest_t *pforest) forest->maxlevel_existing = -1; forest->stats_computed = 0; forest->incomplete_trees = -1; + forest->set_subelements = 0; + forest->is_transitioned = 0; } int @@ -2814,6 +3373,37 @@ t8_forest_is_committed (const t8_forest_t forest) return forest != NULL && t8_refcount_is_active (&forest->rc) && forest->committed; } +/** Check whether at least one eclass scheme of forest supports transitioning + * \param [in] forest A forest + */ +int +t8_forest_supports_transitioning (t8_forest_t forest) +{ + int supports_transition = 1; + int supports_transition_all_procs = 0; /* Result over all procs */ + int int_eclass; + int mpiret; + t8_eclass_scheme_c *tscheme; + + /* Iterate over all eclasses */ + for (int_eclass = (int) T8_ECLASS_ZERO; int_eclass < (int) T8_ECLASS_COUNT; int_eclass++) { + /* If the forest has trees of the current eclass, check if elements of this + * eclass supports transitioning. */ + if (forest->cmesh->num_local_trees_per_eclass[int_eclass] > 0) { + tscheme = forest->scheme_cxx->eclass_schemes[int_eclass]; + supports_transition = supports_transition && t8_element_scheme_supports_transitioning (tscheme); + } + } + /* Combine the process-local results via a logic or and distribute the + * result over all procs (in the communicator).*/ + mpiret = sc_MPI_Allreduce (&supports_transition, &supports_transition_all_procs, 1, sc_MPI_INT, sc_MPI_LAND, + forest->mpicomm); + SC_CHECK_MPI (mpiret); + + return supports_transition_all_procs; +} + + static void t8_forest_set_mpicomm (t8_forest_t forest, sc_MPI_Comm mpicomm, int do_dup) { @@ -2958,6 +3548,44 @@ t8_forest_set_balance (t8_forest_t forest, const t8_forest_t set_from, int no_re } } +void +t8_forest_set_transition (t8_forest_t forest, const t8_forest_t set_from, int set_transition_with_balance) +{ + T8_ASSERT (t8_forest_is_initialized (forest)); + + if (set_transition_with_balance) { + t8_debugf ("-------------set transition with balance -------------\n"); + /* balance with repartition */ + t8_forest_set_balance (forest, set_from, 0); + } + + if (set_from != NULL) { + /* Note that it is possible to apply transitioning to a forest without transition implementation. + * In this case, the transition refine routine will return 0, keeping the forest unchanged. + * Nevertheless, we assert here in this case. */ + T8_ASSERT (t8_forest_supports_transitioning (set_from)); + /* If set_from = NULL, we assume a previous forest_from was set */ + forest->set_from = set_from; + } + else { + T8_ASSERT (forest->set_from != NULL); + T8_ASSERT (t8_forest_supports_transitioning (forest->set_from)); + } + + /* Add SUBELEMENTS to the from_method. + * This overwrites T8_FOREST_FROM_COPY */ + if (forest->from_method == T8_FOREST_FROM_LAST) { + forest->from_method = T8_FOREST_FROM_TRANSITION; + } + else { + forest->from_method |= T8_FOREST_FROM_TRANSITION; + } + + /* set the forests subelement flag, which is for example used by the LFN routine */ + forest->set_subelements = 1; + // forest->is_transitioned = 1; +} + void t8_forest_set_ghost_ext (t8_forest_t forest, int do_ghost, t8_ghost_type_t ghost_type, int ghost_version) { @@ -3110,7 +3738,6 @@ t8_forest_refines_irregular (t8_forest_t forest) /* Combine the process-local results via a logic or and distribute the result over all procs (in the communicator).*/ mpiret = sc_MPI_Allreduce (&irregular, &irregular_all_procs, 1, sc_MPI_INT, sc_MPI_LOR, forest->mpicomm); SC_CHECK_MPI (mpiret); - return irregular_all_procs; } @@ -3134,7 +3761,6 @@ t8_forest_populate_irregular (t8_forest_t forest) t8_forest_set_cmesh (forest_zero, forest->cmesh, forest->mpicomm); t8_forest_set_scheme (forest_zero, forest->scheme_cxx); t8_forest_commit (forest_zero); - /* Up to the specified level we refine every element. */ for (int i = 1; i <= forest->set_level; i++) { t8_forest_init (&forest_tmp); @@ -3145,10 +3771,13 @@ t8_forest_populate_irregular (t8_forest_t forest) 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; } + /* Copy all elements over to the original forest. */ t8_forest_copy_trees (forest, forest_zero, 1); + t8_forest_unref (&forest_tmp_partition); } @@ -3175,6 +3804,8 @@ t8_forest_commit (t8_forest_t forest) T8_ASSERT (forest->scheme_cxx != NULL); T8_ASSERT (forest->from_method == T8_FOREST_FROM_LAST); T8_ASSERT (forest->incomplete_trees == -1); + T8_ASSERT (forest->set_subelements == 0); + T8_ASSERT (forest->is_transitioned == 0); /* dup communicator if requested */ if (forest->do_dup) { @@ -3202,8 +3833,10 @@ t8_forest_commit (t8_forest_t forest) } forest->global_num_trees = t8_cmesh_get_num_trees (forest->cmesh); forest->incomplete_trees = 0; + forest->is_transitioned = 0; } - else { /* set_from != NULL */ + else { + /* set_from != NULL */ t8_forest_t forest_from = forest->set_from; /* temporarily store set_from, since we may overwrite it */ T8_ASSERT (forest->mpicomm == sc_MPI_COMM_NULL); @@ -3251,15 +3884,23 @@ t8_forest_commit (t8_forest_t forest) /* T8_ASSERT (forest->from_method == T8_FOREST_FROM_COPY); */ if (forest->from_method & T8_FOREST_FROM_ADAPT) { SC_CHECK_ABORT (forest->set_adapt_fn != NULL, "No adapt function specified"); + forest->from_method -= T8_FOREST_FROM_ADAPT; if (forest->from_method > 0) { - /* The forest should also be partitioned/balanced. - * We first adapt the forest, then balance and then partition */ + /* The forest should also be partitioned/balanced/transitioned. + * We first untransition the forest, then adapt the forest, then balance and then partition and then possibly transition the forest again*/ + if (forest->set_from->is_transitioned) { + T8_ASSERT (forest->is_transitioned == 0); + t8_forest_untransition (forest); + } + t8_forest_t forest_adapt; t8_forest_init (&forest_adapt); /* forest_adapt should not change ownership of forest->set_from */ - t8_forest_ref (forest->set_from); + if (forest_from == forest->set_from) { + t8_forest_ref (forest->set_from); + } /* set user data of forest to forest_adapt */ t8_forest_set_user_data (forest_adapt, t8_forest_get_user_data (forest)); /* Construct an intermediate, adapted forest */ @@ -3288,7 +3929,7 @@ t8_forest_commit (t8_forest_t forest) forest->from_method -= T8_FOREST_FROM_PARTITION; if (forest->from_method > 0) { - /* The forest should also be balanced after partition */ + /* The forest should also be balanced/transitioned after partition */ t8_forest_t forest_partition; t8_forest_init (&forest_partition); @@ -3323,31 +3964,64 @@ t8_forest_commit (t8_forest_t forest) if (forest->from_method & T8_FOREST_FROM_BALANCE) { /* balance the forest */ forest->from_method -= T8_FOREST_FROM_BALANCE; - /* This is the last from method that we execute, - * nothing should be left todo */ - T8_ASSERT (forest->from_method == 0); - - /* This forest should only be balanced */ - if (forest->set_balance == T8_FOREST_BALANCE_NO_REPART) { - /* balance without repartition */ - t8_forest_balance (forest, 0); + if (forest->from_method > 0) { + int flag_rep; + if (forest->set_balance == T8_FOREST_BALANCE_NO_REPART) { + /* balance without repartition */ + flag_rep = 1; + } + else { + /* balance with repartition */ + flag_rep = 0; + } + /* in this case, we will transition after balancing */ + t8_forest_t forest_balance; + t8_forest_init (&forest_balance); + /* forest_balance should not change ownership of forest->set_from */ + if (forest_from == forest->set_from) { + t8_forest_ref (forest->set_from); + } + t8_forest_set_balance (forest_balance, forest->set_from, flag_rep); + /* Set profiling if enabled */ + t8_forest_set_profiling (forest_balance, forest->profile != NULL); + t8_forest_commit (forest_balance); + /* The new forest will be partitioned/transitioned from forest_balance */ + forest->set_from = forest_balance; } else { - /* balance with repartition */ - t8_forest_balance (forest, 1); + /* Only execute t8_balance.*/ + T8_ASSERT (forest->from_method == 0); + + /* This forest should only be balanced */ + if (forest->set_balance == T8_FOREST_BALANCE_NO_REPART) { + /* balance without repartition */ + t8_forest_balance (forest, 0); + } + else { + /* balance with repartition */ + t8_forest_balance (forest, 1); + } } } - + if (forest->from_method & T8_FOREST_FROM_TRANSITION) { + forest->from_method -= T8_FOREST_FROM_TRANSITION; + /* this is the last from method that we execute, + * nothing should be left todo */ + T8_ASSERT (forest->from_method == 0); + /* use subelements */ + t8_forest_transition (forest); + forest->is_transitioned = 1; + } if (forest_from != forest->set_from) { /* decrease reference count of intermediate input forest, possibly destroying it */ t8_forest_unref (&forest->set_from); } /* reset forest->set_from */ forest->set_from = forest_from; + /* decrease reference count of input forest, possibly destroying it */ t8_forest_unref (&forest->set_from); } /* end set_from != NULL */ - /* Compute the element offset of the trees */ t8_forest_compute_elements_offset (forest); @@ -3359,6 +4033,7 @@ t8_forest_commit (t8_forest_t forest) forest->set_for_coarsening = 0; forest->set_from = NULL; forest->committed = 1; + t8_debugf ("Forest is transitioned %i \n", forest->is_transitioned); t8_debugf ("Committed forest with %li local elements and %lli " "global elements.\n\tTree range is from %lli to %lli.\n", (long) forest->local_num_elements, (long long) forest->global_num_elements, @@ -3430,6 +4105,36 @@ t8_forest_get_global_num_elements (const t8_forest_t forest) return forest->global_num_elements; } +t8_gloidx_t +t8_forest_get_global_num_subelements (t8_forest_t forest) +{ + T8_ASSERT (forest->global_num_subelements <= forest->global_num_elements); + T8_ASSERT (t8_forest_is_committed (forest)); + + return forest->global_num_subelements; +} + +void +t8_forest_comm_global_num_subelements (t8_forest_t forest) +{ + int mpiret; + t8_gloidx_t local_num_subel; + t8_gloidx_t global_num_subel; + + local_num_subel = (t8_gloidx_t) forest->local_num_subelements; + mpiret = sc_MPI_Allreduce (&local_num_subel, &global_num_subel, 1, T8_MPI_GLOIDX, sc_MPI_SUM, forest->mpicomm); + SC_CHECK_MPI (mpiret); + forest->global_num_subelements = global_num_subel; +} +t8_locidx_t +t8_forest_get_local_num_subelements (t8_forest_t forest) +{ + T8_ASSERT (forest->local_num_subelements <= forest->local_num_elements); + T8_ASSERT (t8_forest_is_committed (forest)); + + return forest->local_num_subelements; +} + t8_locidx_t t8_forest_get_num_ghosts (const t8_forest_t forest) { @@ -4158,6 +4863,7 @@ t8_forest_new_uniform (t8_cmesh_t cmesh, t8_scheme_cxx_t *scheme, const int leve /* Initialize the forest */ t8_forest_init (&forest); + T8_ASSERT (t8_forest_is_initialized (forest)); /* Set the cmesh, scheme and level */ t8_forest_set_cmesh (forest, cmesh, comm); t8_forest_set_scheme (forest, scheme); @@ -4204,6 +4910,7 @@ t8_forest_free_trees (t8_forest_t forest) number_of_trees = forest->trees->elem_count; for (jt = 0; jt < number_of_trees; jt++) { tree = (t8_tree_t) t8_sc_array_index_locidx (forest->trees, jt); + if (t8_forest_get_tree_element_count (tree) >= 1) { /* destroy first and last descendant */ const t8_eclass_t eclass = t8_forest_get_tree_class (forest, jt); diff --git a/src/t8_forest/t8_forest_adapt.cxx b/src/t8_forest/t8_forest_adapt.cxx index 5b8e2caf9f..f11f6c9dfa 100644 --- a/src/t8_forest/t8_forest_adapt.cxx +++ b/src/t8_forest/t8_forest_adapt.cxx @@ -362,6 +362,31 @@ t8_forest_adapt_refine_recursive (t8_forest_t forest, t8_locidx_t ltreeid, t8_lo } /* End while loop */ } +/* There are several rules, the refine value needs to fulfill for transitioned forests, + * independent of the specific refine function that has been applied before. This function + * checks if these rules are fulfilled and may change the refine value. */ +void +t8_forest_adjust_refine_for_transitioned_forests (const t8_forest_t forest, t8_eclass_scheme_c *tscheme, + const t8_element_t *current_element, const t8_locidx_t ltree_id, + int *prefine) +{ + /* refine values >1 are only allowed for eclass schemes that support transitioning */ + T8_ASSERT (!(tscheme->t8_element_get_transition_refine_identifier () == 0 && *prefine > 1)); + + /* Existing transition cells must be removed during adaptation. + * We establish the rule to coarsen a transition cell back to its parent in case of refine = 0. */ + if (tscheme->t8_element_is_subelement (current_element) && *prefine == 0) { + /* current_element is the first subelement in the transition cell (subelement_id = 0). We establish the rule to + * coarsen it back to its parent quad/ hex and skip all of its following sibling subelements. */ + T8_ASSERT (forest->set_from->is_transitioned == 1); + T8_ASSERT (*prefine >= -1 && *prefine <= 1); + T8_ASSERT (tscheme->t8_element_get_subelement_id (current_element) == 0); + *prefine = -1; + } + + return; +} + /* TODO: optimize this when we own forest_from */ void t8_forest_adapt (t8_forest_t forest) @@ -389,6 +414,8 @@ t8_forest_adapt (t8_forest_t forest) int num_elements_to_adapt_callback; int zz; int ci; + unsigned int num_subelements; /* This is only needed when transitioning is enabled. */ + t8_locidx_t subel_inserted; /* This is only needed when transitioning is enabled. */ int refine; int is_family; int element_removed = 0; @@ -421,6 +448,7 @@ t8_forest_adapt (t8_forest_t forest) refine_list = sc_list_new (NULL); } forest->local_num_elements = 0; + forest->local_num_subelements = 0; el_offset = 0; num_trees = t8_forest_get_num_local_trees (forest); /* Iterate over the trees and build the new element arrays for each one. */ @@ -443,6 +471,8 @@ t8_forest_adapt (t8_forest_t forest) el_considered = 0; /* Index into the newly inserted elements */ el_inserted = 0; + /* Index into the newly inserted subelements */ + subel_inserted = 0; /* el_coarsen is the index of the first element in the new element * array which could be coarsened recursively. */ el_coarsen = 0; @@ -521,13 +551,13 @@ t8_forest_adapt (t8_forest_t forest) * 0 if the element should remain as is * -1 if we passed a family and it should get coarsened * -2 if the element should be removed. + * >1 if the element should be refined into a transition cell */ refine = forest->set_adapt_fn (forest, forest->set_from, ltree_id, el_considered, tscheme, is_family, num_elements_to_adapt_callback, elements_from); - T8_ASSERT (is_family || refine != -1); - if (refine > 0 && tscheme->t8_element_level (elements_from[0]) >= forest->maxlevel) { - /* Only refine an element if it does not exceed the maximum level */ + if (refine == 1 && tscheme->t8_element_level (elements_from[0]) >= forest->maxlevel) { + /* Only refine an element regularly if it does not exceed the maximum level */ refine = 0; } if (refine == 1) { @@ -563,6 +593,27 @@ t8_forest_adapt (t8_forest_t forest) } el_considered++; } + else if (refine > 1) { + /* refinement into transition cell */ + + /* determine the number of subelements of the given type for memory allocation */ + num_subelements = tscheme->t8_element_get_number_of_subelements (refine - 1); + + /* We need to reallocate memory for the transition cell */ + elements = T8_REALLOC (elements, t8_element_t *, num_subelements); + + (void) t8_element_array_push_count (telements, num_subelements); + for (zz = 0; zz < num_subelements; zz++) { + elements[zz] = t8_element_array_index_locidx_mutable (telements, el_inserted + zz); + } + tscheme->t8_element_to_transition_cell (elements_from[0], refine - 1, elements); + el_inserted += num_subelements; + el_considered++; + + /* Each time we entry this case, a parent element is refined into subelements. + * We will count the global number of constructed subelements and give this number as additional output. */ + subel_inserted += num_subelements; + } else if (refine == -1) { /* The elements form a family and are to be coarsened. */ /* Make room for one more new element. */ @@ -627,6 +678,8 @@ t8_forest_adapt (t8_forest_t forest) el_offset += el_inserted; /* Add to the new number of local elements. */ forest->local_num_elements += el_inserted; + /* Add to the new number of local subelements */ + forest->local_num_subelements += subel_inserted; /* Possibly shrink the telements array to the correct size */ t8_element_array_resize (telements, el_inserted); @@ -649,6 +702,7 @@ t8_forest_adapt (t8_forest_t forest) /* We now adapted all local trees */ /* Compute the new global number of elements */ + /* Compute the new global number of elements and subelements */ t8_forest_comm_global_num_elements (forest); /* Updating other processes about local (in)complete trees. @@ -666,8 +720,15 @@ t8_forest_adapt (t8_forest_t forest) T8_ASSERT (forest_from->incomplete_trees == 1); forest->incomplete_trees = 1; } - - t8_global_productionf ("Done t8_forest_adapt with %lld total elements\n", (long long) forest->global_num_elements); + t8_forest_comm_global_num_subelements (forest); + /* If any subelement is constructed, give output this number as an additional information. */ + if (forest->global_num_subelements > 0) { + t8_global_productionf ("Done t8_forest_adapt with %lld total elements, %li of which are subelements.\n", + (long long) forest->global_num_elements, forest->global_num_subelements); + } + else { + t8_global_productionf ("Done t8_forest_adapt with %lld total elements\n", (long long) forest->global_num_elements); + } /* if profiling is enabled, measure runtime */ if (forest->profile != NULL) { @@ -676,7 +737,7 @@ t8_forest_adapt (t8_forest_t forest) * even if you do not want this output. It fixes a bug that occurred on JUQUEEN, where the * runtimes were computed to 0. * Only delete the line, if you know what you are doing. */ - t8_global_productionf ("End adadpt %f %f\n", sc_MPI_Wtime (), forest->profile->adapt_runtime); + t8_global_productionf ("End adapt %f %f\n", sc_MPI_Wtime (), forest->profile->adapt_runtime); } } diff --git a/src/t8_forest/t8_forest_balance.cxx b/src/t8_forest/t8_forest_balance.cxx index 53f6061bd5..c47dfc9fac 100644 --- a/src/t8_forest/t8_forest_balance.cxx +++ b/src/t8_forest/t8_forest_balance.cxx @@ -193,6 +193,7 @@ t8_forest_balance (t8_forest_t forest, int repartition) t8_forest_set_profiling (forest_temp, 1); } t8_global_productionf ("Profiling: %i\n", forest->profile != NULL); + /* Adapt the forest */ t8_forest_commit (forest_temp); /* Store the runtimes of adapt and ghost */ diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index 650b520ace..d8df57f444 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -379,6 +379,39 @@ t8_forest_set_load (t8_forest_t forest, const char *filename); void t8_forest_comm_global_num_elements (t8_forest_t forest); +/** Compute the global number of subelements in a forest as the sum + * of the local subelement counts. + * \param [in] forest The forest. + */ +void +t8_forest_comm_global_num_subelements (t8_forest_t forest); + +/** Return the number of global subelements in the forest. + * \param [in] forest A forest. + * \return The number of subelements (summed over all processes) in \a forest. + * \a forest must be committed before calling this function. + */ +t8_gloidx_t +t8_forest_get_global_num_subelements (t8_forest_t forest); + +/** Return the number of process local subelements in the forest. + * \param [in] forest A forest. + * \return The number of subelements on this process in \a forest. + * \a forest must be committed before calling this function. + */ +t8_locidx_t +t8_forest_get_local_num_subelements (t8_forest_t forest); + +/** Set a source forest to use subelements during commit, that will remove hanging faces from the adapted mesh. + * \param [in, out] forest The forest. + * \param [in] set_from A second forest that should be transitioned. + * \param [in] set_transition_with_balance If 1, then set_balance will be applied. If 0 and set_balance + * has been used before, then balance will still be set (0 does not unset balance). + * \note This feature is currently only available for the 2D quad scheme. + */ +void +t8_forest_set_transition (t8_forest_t forest, const t8_forest_t set_from, int set_transition_with_balance); + /** After allocating and adding properties to a forest, commit the changes. * This call sets up the internal state of the forest. * \param [in,out] forest Must be created with \ref t8_forest_init and diff --git a/src/t8_forest/t8_forest_transition.cxx b/src/t8_forest/t8_forest_transition.cxx new file mode 100644 index 0000000000..cd2080024e --- /dev/null +++ b/src/t8_forest/t8_forest_transition.cxx @@ -0,0 +1,336 @@ +/* + 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. +*/ + +/* Description: + * In this file, we define the call-back function that is used to construct transition cells. + */ + +#include "t8_eclass.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* We want to export the whole implementation to be callable from "C" */ +T8_EXTERN_C_BEGIN (); + +/* This is the conformal transition refine function for the 2D quad scheme. + * It will return a values p>1 in order to exchange the current element with a transition cell of type p, + * which is defined in the conformal_quad scheme. */ +int +t8_forest_transition_conformal_quad (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t ltree_id, + t8_locidx_t lelement_id, t8_eclass_scheme_c *ts, const int is_family, + int num_elements, t8_element_t *elements[]) +{ + int iface, num_faces, neigh_face, transition_type = 0; + t8_gloidx_t neighbor_tree; + t8_eclass_t neigh_class; + t8_eclass_scheme_c *neigh_scheme; + t8_element_t *element = elements[0], **face_neighbor; + + /* hanging faces can only exist at non-maxlevel elements */ + if (forest_from->maxlevel_existing <= 0 || ts->t8_element_level (element) < forest_from->maxlevel) { + + num_faces = ts->t8_element_num_faces (element); + + /* We use a binary encoding (depending on the face enumeration), to determine which subelement type to use. + * Every face has a flag parameter, which is set to 1, if there is a neighbor with a higher level + * and to 0, if the level of the neighbor is at most the level of the element. + * + * f0 1 + * x - - x - - x x - - x - - x + * | | | \ | / | + * | | | \ | / | | f3 | f2 | f1 | f0 | + * f3 x | f2 --> 1 x - - x | 0 --> binary code (according to the face enumeration): | 1 | 0 | 0 | 1 | = 9 in base 10 + * | | | / \ | + * | elem | | / \ | + * x - - - - - x x - - - - - x + * f1 0 + * + * Note, that this procedure is independent of the eclass (we only show an example for the quad scheme). + * Each neighbor-structure will lead to a unique binary code. + * Within the element scheme of the given eclass, this binary code is used to construct the right subelement type, + * in order to remove hanging nodes from the mesh. */ + + for (iface = 0; iface < num_faces; iface++) { + /* Get the element class and scheme of the face neighbor */ + neigh_class = t8_forest_element_neighbor_eclass (forest_from, ltree_id, element, iface); + + neigh_scheme = t8_forest_get_eclass_scheme (forest_from, neigh_class); + + /* Allocate memory for the virtual face neighbor */ + face_neighbor = T8_ALLOC (t8_element_t *, 1); + + neigh_scheme->t8_element_new (1, face_neighbor); + + /* Compute the virtual face neighbor of element at this face */ + neighbor_tree = t8_forest_element_face_neighbor (forest_from, ltree_id, element, face_neighbor[0], neigh_scheme, + iface, &neigh_face); + + if (neighbor_tree >= 0) { + if (t8_forest_element_has_leaf_desc (forest_from, neighbor_tree, face_neighbor[0], neigh_scheme)) { + /* Compute transition type as the decimal representation of the binary concatenation */ + transition_type += 1 << ((num_faces - 1) - iface); + } + } + /* clean-up */ + neigh_scheme->t8_element_destroy (1, face_neighbor); + T8_FREE (face_neighbor); + } + + /* returning the right subelement types */ + if (transition_type == 0) { /* no hanging faces in this case */ + return 0; + } + else if (transition_type == 15) { /* four hanging faces in this case */ + return 1; + } + else { /* use a transition cell of subelements and add 1 to every type, to avoid refine = 1 */ + return transition_type + 1; + } + } + return 0; /* if elem has maxlevel then keep it unchanged since there will never be hanging faces */ +} /* end of t8_forest_transition_conformal_quad */ + +int +t8_forest_transition_conformal_hex (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t ltree_id, + t8_locidx_t lelement_id, t8_eclass_scheme_c *ts, const int is_family, + int num_elements, t8_element_t *elements[]) +{ + int iface, num_faces, neigh_face, transition_type = 0; + t8_gloidx_t neighbor_tree; + t8_eclass_t neigh_class; + t8_eclass_scheme_c *neigh_scheme; + t8_element_t *element = elements[0], **face_neighbor; + + /* Hanging faces can only exist at non-maxlevel elements */ + if (forest_from->maxlevel_existing <= 0 || ts->t8_element_level (element) < forest_from->maxlevel) { + + num_faces = ts->t8_element_num_faces (element); + + /* TODO: Update this comment to HEX. */ + /* We use a binary encoding (depending on the face enumeration), to determine which subelement type to use. + * Every face has a flag parameter, which is set to 1, if there is a neighbor with a higher level + * and to 0, if the level of the neighbor is at most the level of the element. + * + * f0 1 + * x - - x - - x x - - x - - x + * | | | \ | / | + * | | | \ | / | | f3 | f2 | f1 | f0 | + * f3 x | f2 --> 1 x - - x | 0 --> binary code (according to the face enumeration): | 1 | 0 | 0 | 1 | = 9 in base 10 + * | | | / \ | + * | elem | | / \ | + * x - - - - - x x - - - - - x + * f1 0 + * + * Note, that this procedure is independent of the eclass (we only show an example for the quad scheme). + * Each neighbor-structure will lead to a unique binary code. + * Within the element scheme of the given eclass, this binary code is used to construct the right subelement type, + * in order to remove hanging nodes from the mesh. */ + + for (iface = 0; iface < num_faces; iface++) { + /* Get the element class and scheme of the face neighbor */ + neigh_class = t8_forest_element_neighbor_eclass (forest_from, ltree_id, element, iface); + + neigh_scheme = t8_forest_get_eclass_scheme (forest_from, neigh_class); + + /* Allocate memory for the virtual face neighbor */ + // t8_element_t Array mit einem Element + face_neighbor = T8_ALLOC (t8_element_t *, 1); + + neigh_scheme->t8_element_new (1, face_neighbor); + + /* Compute the virtual face neighbor of element at this face */ + neighbor_tree = t8_forest_element_face_neighbor (forest_from, ltree_id, element, face_neighbor[0], neigh_scheme, + iface, &neigh_face); + + if (neighbor_tree >= 0) { + if (t8_forest_element_has_leaf_desc (forest_from, neighbor_tree, face_neighbor[0], neigh_scheme)) { + /* Compute transition type as the decimal representation of the binary concatenation */ + transition_type += 1 << ((num_faces - 1) - iface); + } + } + /* clean-up */ + neigh_scheme->t8_element_destroy (1, face_neighbor); + T8_FREE (face_neighbor); + } + + /* returning the right subelement types */ + if (transition_type == 0) { /* no hanging faces in this case */ + return 0; + } + else if (transition_type == 63) { /* Six hanging faces in this case */ + return 1; + } + else { /* use a transition cell of subelements and add 1 to every type, to avoid refine = 1 */ + return transition_type + 1; + } + } + return 0; /* if elem has maxlevel then keep it unchanged since there will never be hanging faces */ +} + +/* This is the entry function for all transition schemes, called bei forest_adapt. + * The eclass of the current element hands off to the specific refine implementation above. + * Other schemes, for other eclasses can easily be added. */ +int +t8_forest_transition_entry (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t ltree_id, t8_locidx_t lelement_id, + t8_eclass_scheme_c *ts, const int is_family, int num_elements, t8_element_t *elements[]) +{ + T8_ASSERT (forest->set_subelements == 1); + T8_ASSERT (forest->is_transitioned == 0 && forest->set_from->is_transitioned == 0); + + /* TODO: there may be a better way for this than using switch statements and int functions */ + + /* the current element decides over the refine function. Normally, this function returns one fixed value per + * element scheme, but note that it would also possible to switch the refine function within one tree. */ + + switch (ts->t8_element_get_transition_refine_identifier ()) { + case T8_TRANSITION_CONFORMAL_ZERO_REFINE_FUNCTION: + /* if no transition scheme is implemented for the given element/tree, + * then return zero and keep the current element unchanged. + * The default_common implementation of the above function returns 0. */ + return 0; + case T8_TRANSITION_CONFORMAL_QUAD_REFINE_FUNCTION: + return t8_forest_transition_conformal_quad (forest, forest_from, ltree_id, lelement_id, ts, is_family, num_elements, + elements); + case T8_TRANSITION_CONFORMAL_HEX_REFINE_FUNCTION: + return t8_forest_transition_conformal_hex (forest, forest_from, ltree_id, lelement_id, ts, is_family, num_elements, + elements); + default: + SC_ABORT ("The given eclass scheme must specify a valid transition refine function."); + } +} /* end of t8_forest_transition_entry */ + +void +t8_forest_transition (t8_forest_t forest) +{ + T8_ASSERT (forest->set_subelements == 1); + T8_ASSERT (forest->is_transitioned == 0 && forest->set_from->is_transitioned == 0); + /* in the following, we will call forest_adapt to with the transition + * refinement function in order to transition the forest. The refinement is then + * based on forest->set_from, which must be balanced. */ + + t8_global_productionf ("Into t8_forest_transition.\n"); + + /* Set ghost layers of all processes */ + if (forest->set_from->ghosts == NULL) { + forest->set_from->ghost_type = T8_GHOST_FACES; + t8_forest_ghost_create (forest->set_from); + } + + forest->set_adapt_fn = t8_forest_transition_entry; + forest->set_adapt_recursive = 0; + t8_forest_copy_trees (forest, forest->set_from, 0); + t8_forest_adapt (forest); + + t8_global_productionf ("Done t8_forest_transition.\n"); +} /* end of t8_forest_transition */ + +/* This is the entry function for all untransition a forest, called bei forest_adapt + * in t8_forest_untransition. + */ +int +t8_forest_untransition_entry (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t ltree_id, + t8_locidx_t lelement_id, t8_eclass_scheme_c *ts, const int is_family, int num_elements, + t8_element_t *elements[]) +{ + T8_ASSERT (forest->set_from->is_transitioned == 1); + + //Iterate through elements array and if an element is a subelement, return -1 for + //coarsening this element in forest_adapt. All other elements remain unchanged. + t8_element_t *element = elements[0]; + + if (ts->t8_element_is_subelement (element)) { + return -1; + } + else { + return 0; + } +} + +void +t8_forest_untransition (t8_forest_t forest) +{ + t8_global_productionf ("Into t8_forest_untransition.\n"); + + T8_ASSERT (forest->set_from->is_transitioned); + + t8_forest_t forest_untransition; + + t8_forest_init (&forest_untransition); + /* forest_untransition should not change ownership of forest->set_from */ + t8_forest_ref (forest->set_from); + /* Construct an intermediate, untransitioned forest */ + t8_forest_set_adapt (forest_untransition, forest->set_from, t8_forest_untransition_entry, 0); + + t8_forest_commit (forest_untransition); + forest_untransition->is_transitioned = 0; + /* The new forest will be partitioned/balanced from forest_adapt */ + forest->set_from = forest_untransition; + + t8_global_productionf ("Done t8_forest_untransition.\n"); + + /* In the following, we will call forest_adapt to coarsen all transition cells + * back to their regular parent element. So, forest->set_from is a transitioned forest and + * forest will be non-transitioned. */ +} + +/* Test whether the forest is transitioned. + * Note 1) We allow non-committed forests in this implementation since this check is used in forest_commit() + * Note 2) This test does only check whether there is at least one subelement in the forest. + * It is not tested whether the forest is conformal or has any other property. */ +int +t8_forest_is_transitioned (t8_forest_t forest) +{ + t8_eclass_scheme_c *tscheme; + t8_locidx_t tree_count, num_trees; + t8_tree_t current_tree; + t8_element_array_t *telements; + t8_locidx_t elem_count, num_elems; + + /* iterate through the forest and check for subelements */ + num_trees = t8_forest_get_num_local_trees (forest); + for (tree_count = 0; tree_count < num_trees; tree_count++) { + current_tree = t8_forest_get_tree (forest, tree_count); + telements = ¤t_tree->elements; + num_elems = (t8_locidx_t) t8_element_array_get_count (telements); + for (elem_count = 0; elem_count < num_elems; elem_count++) { + const t8_element_t *current_element = t8_element_array_index_locidx (telements, elem_count); + tscheme = forest->scheme_cxx->eclass_schemes[current_tree->eclass]; + if (tscheme->t8_element_is_subelement (current_element)) { + /* subelement found -> return true */ + return 1; + } + } + } + + /* only return false if there is no subelement in the forest */ + return 0; +} /* end of t8_forest_is_transitioned */ + +T8_EXTERN_C_END (); diff --git a/src/t8_forest/t8_forest_transition.h b/src/t8_forest/t8_forest_transition.h new file mode 100644 index 0000000000..e2ca490e89 --- /dev/null +++ b/src/t8_forest/t8_forest_transition.h @@ -0,0 +1,103 @@ +/* + 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. +*/ + +/** \file t8_forest_transition.h + * We define the eliminate_hanging_nodes routine to transform a 2:1 balanced, nonconformal forest + * into a conformal forest. The routine relies on a 2D quad-scheme that has been balanced, such that + * there is a 2:1 balance between all elements. + */ + +/* TODO: begin documenting this file: make doxygen 2>&1 | grep t8_forest_balance */ + +#ifndef T8_FOREST_TRANSITION_H +#define T8_FOREST_TRANSITION_H + +#include +#include + +T8_EXTERN_C_BEGIN (); + +/** In this function, use a binary encoding (depending on the face enumeration), to determine which subelement type to use. + * Every face has a flag parameter, which is set to 1, if there is a neighbour with a higher level + * and to 0, if the level of the neighbour is at most the level of the element. + * + * f0 1 + * x - - x - - x x - - x - - x + * | | | \ | / | + * | | | \ | / | | f3 | f2 | f1 | f0 | + * f3 x | f2 --> 1 x - - x | 0 --> binary code (according to the face enumeration): | 1 | 0 | 0 | 1 | = 9 in base 10 + * | | | / \ | + * | elem | | / \ | + * x - - - - - x x - - - - - x + * f1 0 + * + * Note, that this procedure is independent of the eclass (we only show an example for the quad scheme). + * Each neighbour-structure will lead to a unique binary code. + * Within the element scheme of the given eclass, this binary code is used to construct the right subelement type, + * in order to remove hanging nodes from the mesh. + + * \param [in] forest the forest to which the new elements belong + * \param [in] forest_from the forest that is adapted. + * \param [in] which_tree the local tree containing \a elements + * \param [in] lelement_id the local element id in \a forest_old in the tree of the current element + * \param [in] ts the eclass scheme of the tree + * \param [in] is_family if 1, the first \a num_elements entries in \a elements form a family. If 0, they do not. + * \param [in] num_elements the number of entries in \a elements that are defined + * \param [in] elements Pointers to a family or, if \a is_family is zero, + * pointer to one element. + * \return transition type + 1 (to avoid return value 1) + */ + +int +t8_forest_transition_adapt (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t ltree_id, t8_locidx_t lelement_id, + t8_eclass_scheme_c *ts, int num_elements, t8_element_t *elements[]); + +/** This function is the point of entry for the hanging-faces-removing subelements. + * In this function, the corresponding callback function for the refine value in forest_adapt is set + * and the forest is adapted. + * \param [in,out] forest The input forest consists of hanging nodes on faces- + * The output forest contains transition cells and no hanging nodes on faces. + */ +void +t8_forest_transition (t8_forest_t forest); + +/** This function is the point of entry in order to untransition a forest before adapting it. + * Every transition cell gets coarsened back to its parent element. + * \param [in,out] forest The input forest consists of subelements- + * The output forest does not. + */ + +void +t8_forest_untransition (t8_forest_t forest); + +/** Check whether the forest is transitioned, meaning that subelements exist. + * \param [in] forest The input forest that may or may not contains subelements + * \return true if forest contains subelements or false if not. + * + */ + +int +t8_forest_is_transitioned (t8_forest_t forest); + +T8_EXTERN_C_END (); + +#endif /* !T8_FOREST_TRANSITION_H! */ diff --git a/src/t8_forest/t8_forest_types.h b/src/t8_forest/t8_forest_types.h index 10f4c84b72..6a9d5be253 100644 --- a/src/t8_forest/t8_forest_types.h +++ b/src/t8_forest/t8_forest_types.h @@ -55,7 +55,8 @@ typedef int8_t t8_forest_from_t; #define T8_FOREST_FROM_ADAPT 0x1 #define T8_FOREST_FROM_PARTITION 0x2 #define T8_FOREST_FROM_BALANCE 0x4 -#define T8_FOREST_FROM_NONE 0x8 /* A value that is not reached by adding up the other values. No from method used */ +#define T8_FOREST_FROM_TRANSITION 0x8 +#define T8_FOREST_FROM_NONE 0x16 /* A value that is not reached by adding up the other values. No from method used */ #define T8_FOREST_FROM_LAST T8_FOREST_FROM_NONE #define T8_FOREST_BALANCE_REPART 1 /**< Value of forest->set_balance if balancing with repartitioning */ @@ -86,7 +87,7 @@ typedef struct t8_forest 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. */ - + int is_transitioned; /* Flag parameter that states whether the forest is transitioned or not */ t8_forest_t set_from; /**< Temporarily store source forest. */ t8_forest_from_t from_method; /**< Method to derive from \b set_from. */ t8_forest_adapt_t set_adapt_fn; /**< refinement and coarsen function. Called when \b from_method @@ -97,6 +98,7 @@ typedef struct t8_forest See \ref t8_forest_set_balance. If 0, no balance. If 1 balance with repartitioning, if 2 balance without repartitioning, \see t8_forest_balance */ + int set_subelements; /**< Flag to decide whether subelements should be used in the forest */ int do_ghost; /**< If True, a ghost layer will be created when the forest is committed. */ t8_ghost_type_t ghost_type; /**< If a ghost layer will be created, the type of neighbors that count as ghost. */ int ghost_algorithm; /**< Controls the algorithm used for ghost. 1 = balanced only. 2 = also unbalanced @@ -133,7 +135,10 @@ typedef struct t8_forest t8_locidx_t local_num_elements; /**< Number of elements on this processor. */ t8_gloidx_t global_num_elements; /**< Number of elements on all processors. */ + t8_locidx_t local_num_subelements; /**< Number of subelements on this processor. */ + t8_gloidx_t global_num_subelements; /**< Number of subelements on all processors. */ t8_profile_t *profile; /**< If not NULL, runtimes and statistics about forest_commit are stored here. */ + sc_statinfo_t stats[T8_PROFILE_NUM_STATS]; int stats_computed; } t8_forest_struct_t; diff --git a/src/t8_schemes/t8_default/t8_default_common/t8_default_common.cxx b/src/t8_schemes/t8_default/t8_default_common/t8_default_common.cxx index b83349f5ff..0fbd738b2f 100644 --- a/src/t8_schemes/t8_default/t8_default_common/t8_default_common.cxx +++ b/src/t8_schemes/t8_default/t8_default_common/t8_default_common.cxx @@ -168,4 +168,101 @@ t8_default_scheme_common_c::t8_element_deinit (int length, t8_element_t *elem) c { } +/*----------------------------Functions for transition schemes--------------------------------------------*/ +int +t8_default_scheme_common_c::t8_element_find_neighbor_in_transition_cell (const t8_element_t *elem, + const t8_element_t *neigh, int elem_face) +{ + SC_ABORT ("This function is not implemented for the given scheme.\n"); +} + +int +t8_default_scheme_common_c::t8_element_get_transition_type (const t8_element *elem) +{ + SC_ABORT ("This function is not implemented for the given scheme.\n"); +} + +int +t8_default_scheme_common_c::t8_element_neighbor_is_sibling (const t8_element *elem, const int face) const +{ + /* No subelements are implemented and therefore we return false, + * meaning "the neighbor at face is not a sibling of elem". */ + t8_debugf ("This is the default_common implementation of t8_element_neighbor_is_sibling.\n"); + return 0; +} + +int +t8_default_scheme_common_c::t8_element_get_num_sibling_neighbors_at_face (const t8_element *elem, const int face) const + +{ + /* No subelements are implemented and therefore we return false meaning "the neighbor at face is not a sibling of elem". */ + t8_debugf ("This is the default_common implementation of t8_element_get_num_sibling_neighbors_at_face.\n"); + return 0; +} +void +t8_default_scheme_common_c::t8_element_get_sibling_neighbor_in_transition_cell_hex ( + const t8_element_t *elem, const int face, const int num_neighbors, t8_element_t *neighbor_at_face[], int *neigh_face) +{ + SC_ABORT ("This function is not implemented for the given scheme.\n"); +} +void +t8_default_scheme_common_c::t8_element_get_sibling_neighbor_in_transition_cell (const t8_element_t *elem, + const int face, const int num_neighbors, + t8_element_t *neighbor_at_face[], + int *neigh_face[]) +{ + SC_ABORT ("This function is not implemented for the given scheme.\n"); +} +int +t8_default_scheme_common_c::t8_element_transition_scheme_is_conformal (void) +{ + /* Default implementation - current scheme is no transition scheme */ + return 0; +} + +int +t8_default_scheme_common_c::t8_element_get_transition_refine_identifier () const +{ + /* This function will be called by the transition_entry function. It defaults to zero such that + * the adapt routine will keep elem unchanged during the transition step. */ + return 0; +} + +int +t8_default_scheme_common_c::t8_element_is_subelement (const t8_element *elem) const +{ + /* We implement this function since it is a "check" function and + * should not abort the code even if no subelements are implemented in the given eclass. + * Schemes that support subelements must provide their own implementation of this function. */ + + /* No subelements are implemented and therefore we return false meaning "is no subelement". */ + t8_debugf ("This is the default_common implementation of the t8_element_is_subelement check.\n"); + return 0; +} + +int +t8_default_scheme_common_c::t8_element_get_subelement_id (const t8_element *elem) const +{ + SC_ABORT ("This function is not implemented for the given scheme.\n"); +} + +int +t8_default_scheme_common_c::t8_element_get_number_of_subelements (int transition_type) const +{ + SC_ABORT ("This function is not implemented for the given scheme.\n"); +} + +void +t8_default_scheme_common_c::t8_element_to_transition_cell (const t8_element_t *elem, int type, t8_element_t *c[]) +{ + SC_ABORT ("This function is not implemented for the given scheme.\n"); +} + +int +t8_default_scheme_common_c::t8_element_scheme_supports_transitioning (void) +{ + /* Default implementation - current scheme is no transition scheme */ + return 0; +} + T8_EXTERN_C_END (); diff --git a/src/t8_schemes/t8_default/t8_default_common/t8_default_common.hxx b/src/t8_schemes/t8_default/t8_default_common/t8_default_common.hxx index 528840d8d0..418c593595 100644 --- a/src/t8_schemes/t8_default/t8_default_common/t8_default_common.hxx +++ b/src/t8_schemes/t8_default/t8_default_common/t8_default_common.hxx @@ -126,6 +126,97 @@ class t8_default_scheme_common_c: public t8_eclass_scheme_c { virtual void t8_element_anchor (const t8_element_t *elem, int anchor[3]) const = 0; + /*-----------------------------Functions for transition schemes---------------------------------*/ + /** Return the subelement id of the neighbor subelement of elem at face elem_face + * that is a sibling of the subelement neigh. + * \param [in] elem a given element (possibly subelement) + * \param [in] neigh a random subelement (pseudoneighbor) in a transition cell from which we assume that it owns the real neighbor of elem + * \param [in] elem_face a given face number of element elem + * \return the subelement id of the real subelement neighbor of element elem, which is a sibling of neigh. + */ + virtual int + t8_element_find_neighbor_in_transition_cell (const t8_element_t *elem, const t8_element_t *neigh, int elem_face); + + /** Return the transition type of an element + * \param [in] elem A valid element + * \return the transition type of elem (0 if elem is no subelement) + */ + virtual int + t8_element_get_transition_type (const t8_element *elem); + /** Check whether the neighbors of an element at a specific face are siblings + * \param [in] elem A valid element + * \param [in] elem_face A valid face + * \return true if the neighbor of elem at face elem_face is a sibling. + */ + virtual int + t8_element_neighbor_is_sibling (const t8_element *elem, const int elem_face) const; + + /** Check whether the neighbors of an element at a specific face are siblings + * \param [in] elem A valid element + * \param [in] elem_face A valid face + * \return return the number of sibling neighbors at a given face. + */ + virtual int + t8_element_get_num_sibling_neighbors_at_face (const t8_element *elem, const int elem_face) const; + /** Construct all sibling neighbors of elem at face in hex scheme. */ + virtual void + + t8_element_get_sibling_neighbor_in_transition_cell_hex (const t8_element_t *elem, const int face, + const int num_neighbors, t8_element_t *neighbor_at_face[], + int *neigh_face); + + /** Construct all sibling neighbors of elem at face in quad scheme */ + virtual void + + t8_element_get_sibling_neighbor_in_transition_cell (const t8_element_t *elem, const int face, const int num_neighbors, + t8_element_t *neighbor_at_face[], int *neigh_face[]); + + /** Return 1 if the eclass scheme has an implementation for subelements, which is conformal. */ + virtual int + t8_element_transition_scheme_is_conformal (void); + + /** Return zero refine value for schemes that do not have a transition implementation. + * \param [in] elem A valid element + * \return Integer, used as the refine value during transition adaptation. + */ + virtual int + t8_element_get_transition_refine_identifier (void) const; + + /** Check whether a given element is a subelement + * \param [in] elem A valid element + * \return true if elem is a subelement + */ + virtual int + t8_element_is_subelement (const t8_element *elem) const; + + /** Return the number of subelements in a transition cell of type transition_type + * \param [in] transition_type The subelement type as an integer + * \return the number of subelements, this transition cell consists of + */ + virtual int + t8_element_get_number_of_subelements (int transition_type) const; + + /** Return the subelement id of a given element. + * \param [in] elem A valid element + * \return the subelement id of elem (0 if elem is no subelement) + */ + virtual int + t8_element_get_subelement_id (const t8_element *elem) const; + + /** This function refines a parent element into subelements. + * Depending on the subelement type, the number of subelements + * to fill the parent element, can differ. + * \param [in] elem A valid element + * \param [in] type The subelement type + * \param [out] subelements An array of all subelements of the parent quad element elem + */ + virtual void + t8_element_to_transition_cell (const t8_element_t *elem, int type, t8_element_t *c[]); + + /** Return 1 if the eclass scheme has an implementation for subelements. Return 0 otherwise. */ + virtual int + t8_element_scheme_supports_transitioning (void); + #if T8_ENABLE_DEBUG virtual void t8_element_debug_print (const t8_element_t *elem) const; diff --git a/src/t8_schemes/t8_transition/Makefile.am b/src/t8_schemes/t8_transition/Makefile.am new file mode 100644 index 0000000000..5fb585d0f1 --- /dev/null +++ b/src/t8_schemes/t8_transition/Makefile.am @@ -0,0 +1,15 @@ +# This file is part of t8code +# Non-recursive Makefile.am in src/t8_schemes/t8_transition +# Included from toplevel directory + +libt8_installed_headers += \ + src/t8_schemes/t8_transition/t8_transition_cxx.hxx \ + src/t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad_cxx.hxx \ + src/t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex_cxx.hxx + +libt8_compiled_sources += \ + src/t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad.cxx \ + src/t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad_cxx.cxx \ + src/t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex.cxx \ + src/t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex_cxx.cxx + diff --git a/src/t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex.cxx b/src/t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex.cxx new file mode 100644 index 0000000000..f3005111d8 --- /dev/null +++ b/src/t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex.cxx @@ -0,0 +1,58 @@ +/* + 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 +#include +#include + +/* We want to export the whole implementation to be callable from "C" */ +T8_EXTERN_C_BEGIN (); + +t8_scheme_cxx_t * +t8_scheme_new_transition_hex_cxx (void) +{ + t8_scheme_cxx_t *s; + + s = T8_ALLOC_ZERO (t8_scheme_cxx_t, 1); + t8_refcount_init (&s->rc); + + s->eclass_schemes[T8_ECLASS_VERTEX] = NULL; + s->eclass_schemes[T8_ECLASS_LINE] = new t8_default_scheme_line_c (); + s->eclass_schemes[T8_ECLASS_QUAD] = new t8_default_scheme_quad_c (); + s->eclass_schemes[T8_ECLASS_HEX] = new t8_subelement_scheme_hex_c (); + s->eclass_schemes[T8_ECLASS_TRIANGLE] = new t8_default_scheme_tri_c (); + s->eclass_schemes[T8_ECLASS_TET] = NULL; + s->eclass_schemes[T8_ECLASS_PRISM] = NULL; + s->eclass_schemes[T8_ECLASS_PYRAMID] + = new t8_default_scheme_pyramid_c (); /* can be used for hybrid meshes - not conformal as long as no conformal transition pyr class exists */ + + return s; +} + +T8_EXTERN_C_END (); diff --git a/src/t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex_cxx.cxx b/src/t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex_cxx.cxx new file mode 100644 index 0000000000..45d302d08e --- /dev/null +++ b/src/t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex_cxx.cxx @@ -0,0 +1,2837 @@ +/* + 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. +*/ + +/* Description: + * This is the low-level structure of 3D hexahedral elements with transition + * cells of pyramidal subelements. */ + +#include +#include +#include +#include +#include +#include +#include "t8_transition_conformal_hex_cxx.hxx" +#include + +/* *INDENT-OFF* */ +/* Connectivity of subelement faces depends on which type of subelement is considered (6 pyramids = 6 types): + * | + * (Z) | / (Y) + * |/_____ (X) + * + * Type 0 = quadliteral face at face 0 from the hexahdron: Type 3: + * f_0 <-> f_0 f_0 <-> f_1 + * f_1 <-> f_0 f_1 <-> f_3 + * f_2 <-> f_0 f_2 <-> f_3 + * f_3 <-> f_0 f_3 <-> f_3 + * f_4 <-> f_4 (assuming a neighboring transition cell) f_4 <-> f_4 + * Type 1: Type 4: + * f_0 <-> f_1 f_0 <-> f_2 + * f_1 <-> f_1 f_1 <-> f_2 + * f_2 <-> f_1 f_2 <-> f_2 + * f_3 <-> f_1 f_3 <-> f_2 + * f_4 <-> f_4 (assuming a neighboring transition cell) f_4 <-> f_4 + * Type 0: Type 5: + * f_0 <-> f_0 f_0 <-> f_3 + * f_1 <-> f_2 f_1 <-> f_3 + * f_2 <-> f_0 f_2 <-> f_3 + * f_3 <-> f_2 f_3 <-> f_3 + * f_4 <-> f_4 (assuming a neighboring transition cell) f_4 <-> f_4 + */ + +const int subelement_face_dual[6][5] = { { 0, 0, 0, 0, 4 }, { 1, 1, 1, 1, 4 }, { 0, 2, 0, 2, 4 }, + { 1, 3, 3, 3, 4 }, { 2, 2, 2, 2, 4 }, { 3, 3, 3, 3, 4 } }; + +/* Connectivity of a subelements location within a transition cell + * and the parent hexs faces: + * location[0] = 0 -> parents dual face = 1 + * location[0] = 1 -> parents dual face = 0 + * location[0] = 2 -> parents dual face = 3 + * location[0] = 3 -> parents dual face = 2 + * location[0] = 4 -> parents dual face = 5 + * location[0] = 5 -> parents dual face = 4 */ +const int subelement_location_to_parent_dual_face[6] = { 1, 0, 3, 2, 5, 4 }; + +/* Connectivity of a subelements location within a transition cell (only if the subelements are not split) + * --> gets subelement_duals + */ +const int subelement_face_to_dual_subelement[6][5] = { + { 2, 3, 4, 5, -1 }, { 2, 3, 4, 5, -1 }, { 0, 1, 4, 5, -1 }, + { 0, 1, 4, 5, -1 }, { 0, 1, 2, 3, -1 }, { 0, 1, 2, 3, -1 }, +}; +/* Connectivity of a subelements location within a transition cell + * and the parent hexs faces: Wie in mit Koordinatensystem in Davids Masterarbeit + * starte links, dann rechts, vorne, hinten, unten oben. + * location[0] = 0 (left) -> parents face = 0 + * location[0] = 1 (right) -> parents face = 1 + * location[0] = 2 (front) -> parents face = 2 + * location[0] = 3 (back) -> parents face = 3 + * location[0] = 4 (bottom) -> parents face = 4 + * location[0] = 5 (up) -> parents face = 5 + */ +const int subelement_location_to_parent_face[6] = { 0, 1, 2, 3, 4, 5 }; +/* *INDENT-ON* */ + +/* We want to export the whole implementation to be callable from "C" */ +T8_EXTERN_C_BEGIN (); + +/* This function is used by other element functions and we thus need to + * declare it up here */ +t8_linearidx_t +t8_element_get_linear_id (const t8_element_t *elem, int level); + +int +t8_subelement_scheme_hex_c::t8_element_maxlevel (void) const +{ + return P8EST_OLD_QMAXLEVEL; +} + +/* *INDENT-OFF* */ +t8_eclass_t +t8_subelement_scheme_hex_c::t8_element_child_eclass (int childid) const +{ + T8_ASSERT (0 <= childid && childid < P8EST_CHILDREN); + + return T8_ECLASS_HEX; +} + +int +t8_subelement_scheme_hex_c::t8_element_level (const t8_element_t *elem) const +{ + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + T8_ASSERT (t8_element_is_valid (elem)); + return (int) ((const t8_hex_with_subelements *) phex_w_sub)->p8q.level; +} + +void +t8_subelement_scheme_hex_c::t8_element_copy (const t8_element_t *source, t8_element_t *dest) const +{ + const t8_hex_with_subelements *phex_w_sub_source = (const t8_hex_with_subelements *) source; + t8_hex_with_subelements *phex_w_sub_dest = (t8_hex_with_subelements *) dest; + + const p8est_quadrant_t *q = &phex_w_sub_source->p8q; + p8est_quadrant_t *r = &phex_w_sub_dest->p8q; + + T8_ASSERT (t8_element_is_valid (source)); + T8_ASSERT (t8_element_is_valid (dest)); + if (q == r && phex_w_sub_source->transition_type == phex_w_sub_dest->transition_type + && phex_w_sub_source->subelement_id == phex_w_sub_dest->subelement_id) { + /* Do nothing if they are already the same hexahedra. */ + return; + } + *r = *q; + + t8_element_copy_subelement_values (source, dest); +} + +int +t8_subelement_scheme_hex_c::t8_element_compare (const t8_element_t *elem1, const t8_element_t *elem2) const +{ + const t8_hex_with_subelements *phex_w_sub_elem1 = (const t8_hex_with_subelements *) elem1; + const t8_hex_with_subelements *phex_w_sub_elem2 = (const t8_hex_with_subelements *) elem2; + + const p8est_quadrant_t *q = &phex_w_sub_elem1->p8q; + const p8est_quadrant_t *r = &phex_w_sub_elem2->p8q; + + T8_ASSERT (t8_element_is_valid (elem1)); + T8_ASSERT (t8_element_is_valid (elem2)); + + int compare = p8est_quadrant_compare (q, r); + + if (compare == 0 && (t8_element_is_subelement (elem1) || t8_element_is_subelement (elem2))) { + if (t8_element_is_subelement (elem1) && t8_element_is_subelement (elem2)) { + /* Caution: The compare function is used for two subelements. */ + + if (phex_w_sub_elem1->transition_type == phex_w_sub_elem2->transition_type + && phex_w_sub_elem1->subelement_id == phex_w_sub_elem2->subelement_id) { + /* both subelements are identical */ + return 0; + } + /* return != 0 to avoid debug abortion in t8_ghost_add_remote */ + return 1; + } + else if (t8_element_is_subelement (elem1)) { + return -1; /* elem1 is subelement and therefore smaller */ + } + else if (t8_element_is_subelement (elem2)) { + return 1; /* elem2 is subelement and therefore smaller */ + } + } + + /* Note that for subelements, their parent quadrant is compared at this point */ + return compare; + + // SC_ABORT_NOT_REACHED(); +} + +void +t8_subelement_scheme_hex_c::t8_element_parent (const t8_element_t *elem, t8_element_t *parent) const +{ + const t8_hex_with_subelements *phex_w_sub_elem = (const t8_hex_with_subelements *) elem; + t8_hex_with_subelements *phex_w_sub_parent = (t8_hex_with_subelements *) parent; + + const p8est_quadrant_t *q = &phex_w_sub_elem->p8q; + p8est_quadrant_t *r = &phex_w_sub_parent->p8q; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (parent)); + + if (t8_element_is_subelement (elem)) { + phex_w_sub_parent->p8q = phex_w_sub_elem->p8q; + } + else { + p8est_quadrant_parent (q, r); + } + + /* the parent of any element will never be a subelement */ + t8_element_reset_subelement_values (parent); +} + +void +t8_subelement_scheme_hex_c::t8_element_sibling (const t8_element_t *elem, int sibid, t8_element_t *sibling) const +{ + const t8_hex_with_subelements *phex_w_sub_elem = (const t8_hex_with_subelements *) elem; + t8_hex_with_subelements *phex_w_sub_sibling = (t8_hex_with_subelements *) sibling; + + const p8est_quadrant_t *q = &phex_w_sub_elem->p8q; + p8est_quadrant_t *r = &phex_w_sub_sibling->p8q; + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (sibling)); + + p8est_quadrant_sibling (q, r, sibid); + // t8_element_copy_surround (q, r); + + // SC_ABORT_NOT_REACHED(); +} + +int +t8_subelement_scheme_hex_c::t8_element_num_faces (const t8_element_t *elem) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + return (t8_element_is_subelement (elem) ? T8_HEX_SUBELEMENT_FACES : P8EST_FACES); +} + +int +t8_subelement_scheme_hex_c::t8_element_max_num_faces (const t8_element_t *elem) const +{ + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + + return P8EST_FACES; +} + +int +t8_subelement_scheme_hex_c::t8_element_num_children (const t8_element_t *elem) const +{ + /* Note that children of subelements equal the children of the parent hexahedra. + * Therefore, the number of children of a subelement equals P8EST_CHILDREN */ + T8_ASSERT (t8_element_is_valid (elem)); + return P8EST_CHILDREN; +} + +/* *INDENT-OFF* */ +/* indent bug, indent adds a second "const" modifier */ + +int +t8_subelement_scheme_hex_c::t8_element_num_siblings (const t8_element_t *elem) const + +{ + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + + //no hanging nodes + if (phex_w_sub->transition_type == 0) { + return P8EST_CHILDREN; + } + + int num_hanging_faces = 0; + int iface; + for (iface = 0; iface < P8EST_FACES; iface++) { + /* Count the number of ones of the binary transition type. + * This number equals the number of hanging faces. */ + + // binary shift << 1 Left-shift, d.h. *2¹ + // Right shift >> 1 Right-shift, d.h. *2⁻¹ + num_hanging_faces += (phex_w_sub->transition_type & (1 << iface)) >> iface; + } + return P8EST_FACES + 3 * num_hanging_faces; +} + +int +t8_subelement_scheme_hex_c::t8_element_num_face_children (const t8_element_t *elem, int face) const +{ + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + + T8_ASSERT (t8_element_is_valid (elem)); + /* if we use hex scheme without set_transition, then we are only balanced + * and four neighbors are possible */ + return 4; +} + +int +t8_subelement_scheme_hex_c::t8_element_neighbor_is_sibling (const t8_element_t *elem, const int face) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_subelement (elem)); + + if (face == 0 || face == 1 || face == 2 || face == 3) { + return 1; + } + + return 0; +} + +int +t8_subelement_scheme_hex_c::t8_element_get_num_sibling_neighbors_at_face (const t8_element_t *elem, + const int face) const +{ + const t8_hex_with_subelements *hex_w_sub = (const t8_hex_with_subelements *) elem; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_subelement (elem)); + T8_ASSERT (face == 0 || face == 1 || face == 2 || face == 3); + + int location[3] = {}; + //( location[0] = face_number of transition cell, location[1] = if split or not ( 1 = split ), location[2] = sub_id + t8_element_get_location_of_subelement (elem, location); + int split = location[1]; + int hex_face = location[0]; + int neigh_hex_face; + int transition_type; + if (split == 1) { + return 1; + } + else { + neigh_hex_face = subelement_face_to_dual_subelement[hex_face][face]; + transition_type = hex_w_sub->transition_type; + + if ((transition_type & (int) pow (2, 5 - neigh_hex_face)) != 0) { + return 2; + } + else { + return 1; + } + } +} + +int +t8_subelement_scheme_hex_c::t8_element_get_transition_refine_identifier () const +{ + + return T8_TRANSITION_CONFORMAL_HEX_REFINE_FUNCTION; +} +/* *INDENT-ON* */ + +int +t8_subelement_scheme_hex_c::t8_element_get_face_corner (const t8_element_t *elem, int face, int corner) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + if (!t8_element_is_subelement (elem)) { + + //face has to be between 0 and 4 + //Corner has to be between 0 and 4 + T8_ASSERT (0 <= face && face < P8EST_FACES); + T8_ASSERT (0 <= corner && corner < 4); + + return p8est_face_corners[face][corner]; + } + else { + int t8_face_corners_subelement[5][4] = { + { 0, 2, 4, -1 }, //f0 + { 1, 3, 4, -1 }, //f1 + { 0, 1, 4, -1 }, //f2 + { 2, 3, 4, -1 }, //f3 + { 0, 1, 2, 3 } //f4 + }; + T8_ASSERT (0 <= face && face < T8_HEX_SUBELEMENT_FACES); + T8_ASSERT (0 <= corner && corner < 5); + + return t8_face_corners_subelement[face][corner]; + } +} + +int +t8_subelement_scheme_hex_c::t8_element_get_corner_face (const t8_element_t *elem, int corner, int face) const +{ + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + SC_ABORT ("This function is not implemented yet.\n"); + return 0; +} + +void +t8_subelement_scheme_hex_c::t8_element_child (const t8_element_t *elem, int childid, t8_element_t *child) const +{ + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + const p8est_quadrant_t *q = (const p8est_quadrant_t *) elem; + const p4est_qcoord_t shift = P8EST_QUADRANT_LEN (q->level + 1); + p8est_quadrant_t *r = (p8est_quadrant_t *) child; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (child)); + T8_ASSERT (p8est_quadrant_is_extended (q)); + T8_ASSERT (q->level < P8EST_OLD_QMAXLEVEL); + T8_ASSERT (0 <= childid && childid < P8EST_CHILDREN); + + r->x = childid & 0x01 ? (q->x | shift) : q->x; + r->y = childid & 0x02 ? (q->y | shift) : q->y; + r->z = childid & 0x04 ? (q->z | shift) : q->z; + r->level = q->level + 1; + if (q != r) { + T8_ASSERT (p8est_quadrant_is_parent (q, r)); + } + t8_element_reset_subelement_values (child); +} + +void +t8_subelement_scheme_hex_c::t8_element_get_sibling_neighbor_in_transition_cell_hex ( + const t8_element_t *elem, const int face, const int num_neighbors, t8_element_t *neighbor_at_face[], int *neigh_face) +{ + T8_ASSERT (t8_element_is_subelement (elem)); + T8_ASSERT (t8_element_neighbor_is_sibling (elem, face)); + T8_ASSERT ((num_neighbors == 1) || (num_neighbors == 2)); + T8_ASSERT (t8_element_is_valid (neighbor_at_face[0])); + + /* source = elem, destination = neighbor at face. --> They have the same anchor node + Morton index + level.*/ + t8_element_copy (elem, neighbor_at_face[0]); + /* Expand neighbor_at_face to a subelement (subelement_id + transititon_type) + copy it into phex_w_sub_neighbor_at_face*/ + t8_hex_with_subelements *phex_w_sub_neighbor_at_face = (t8_hex_with_subelements *) neighbor_at_face[0]; + + //iterator variable + int iter; + //Get information about the location of the subelement. + int location[3] = {}; + // location[0] = face_number of transition cell, location[1] = if split or not ( 1 = split ), location[2] = sub_id + t8_element_get_location_of_subelement (elem, location); + + //Create a temporary variable to store the possible subelement_id of the neighbor + int subelement_id_tmp = 0; + int transition_type_tmp = 0; + int amount_subelements = 0; + int transition_type = t8_element_get_transition_type (elem); + int neigh_hex_face; + int hlp; + + /* There are 4 cases that can happen: + * 1. The subelement itself is not split, and its face neighbor is also not split. + * 2. The subelement itself is not split, but its face neighbor is split. (Two face neighbors) + * 3. The subelement itself is split, but its face neighbor is not split. + * 4. The subelement itself is split, and its face neighbor is also split. + */ + + neigh_face[0] = subelement_face_dual[location[0]][face]; + + //First check if (the own) face is split. + if (location[1] == 0) { //Not split + + //get hex_face_number of the face_neighbored subelement + // neigh_hex_face is only correct, if the neighbor lies not at the same hex face. + neigh_hex_face = subelement_face_to_dual_subelement[location[0]][face]; + /*Check if the dual subelement is split. If it's split,the element has two neighbors + * as siblings (case 2). Then we always take the left, front or down (in this order) subelement (the subelement with the lower sub + * element ID). If the transition type is = 1 at the hex_face, it's split.*/ + + //-----------------------------CASE 1-------------------------------------------------------- + t8_debugf ("CASE 1\n"); + + //make rightshift until only the bits for the faces before our neighbors face are left. + transition_type_tmp = transition_type >> (5 - neigh_hex_face); + + amount_subelements = 0; + + int i = (int) pow (2, 5 - neigh_hex_face); + + if ((transition_type & i) == 0) { //neighbor not split + + for (iter = 0; iter <= neigh_hex_face; iter++) { + + //Count the elements until our neighbored hex_face. + if (((transition_type_tmp >> iter) & 1) != 0) { + amount_subelements += 4; + } + else { + amount_subelements += 1; + } + } + + //The subelement_id of the neighbor is then the amount of subelements till then - 1 + subelement_id_tmp = amount_subelements - 1; + phex_w_sub_neighbor_at_face->subelement_id = subelement_id_tmp; + } + //-------------------------CASE 2-------------------------------------- + else { //neighbor is split. We have to return 2 subelements. + t8_debugf ("CASE 2\n"); + + amount_subelements = 0; + int subelement_id_tmp2 = 0; // In here we store the second subelement ID in case 2 if elem has two neighbors + /* For case 2 we need a second face neighbor */ + + t8_hex_with_subelements *phex_w_sub_neighbor_at_face2 = (t8_hex_with_subelements *) neighbor_at_face[1]; + + t8_element_copy (elem, neighbor_at_face[1]); + + //make rightshift until only the bits for the faces before our neighbors face are left. + transition_type_tmp = transition_type >> (5 - neigh_hex_face); + + for (iter = 0; iter <= neigh_hex_face; iter++) { + + //Count the elements until our neighbored hex_face. + if (((transition_type_tmp >> iter) & 1) != 0) { + amount_subelements += 4; + } + else { + amount_subelements += 1; + } + } + + //If we know the hex_face_number of the neighbored element, we know which subelement_IDs to take. + if (location[0] == 0) { //For hex_face 0 its always the "front" two sub-ids + subelement_id_tmp = amount_subelements - 4; + subelement_id_tmp2 = amount_subelements - 2; + } + if (location[0] == 1) { //For hex_face 1 its always the right to sub-ids + subelement_id_tmp = amount_subelements - 3; + subelement_id_tmp2 = amount_subelements - 1; + } + if (location[0] == 2) { //for hex_face 2 its the "front" sub-ids + if ((face == 0) || (face == 1)) { + subelement_id_tmp = amount_subelements - 4; + subelement_id_tmp2 = amount_subelements - 2; + } + if ((face == 2) || (face == 3)) { + subelement_id_tmp = amount_subelements - 4; + subelement_id_tmp2 = amount_subelements - 3; + } + } + if (location[0] == 3) { //for hex_face 3 its the "back" sub-ids + if ((face == 0) || (face == 1)) { + subelement_id_tmp = amount_subelements - 3; + subelement_id_tmp2 = amount_subelements - 1; + } + else if ((face == 2) || (face == 3)) { + subelement_id_tmp = amount_subelements - 2; + subelement_id_tmp2 = amount_subelements - 1; + } + } //for hex_face 4 its the "down" sub-ids + if (location[0] == 4) { + subelement_id_tmp = amount_subelements - 4; + subelement_id_tmp2 = amount_subelements - 3; + } + if (location[0] == 5) { //for hex_face 5 its the "up" sub-ids + subelement_id_tmp = amount_subelements - 2; + subelement_id_tmp2 = amount_subelements - 1; + } + + phex_w_sub_neighbor_at_face->subelement_id = subelement_id_tmp; + phex_w_sub_neighbor_at_face2->subelement_id = subelement_id_tmp2; + + if (neigh_face != NULL) { + neigh_face[1] = neigh_face[0]; + } + } + } + + //-------------------------CASE 3-------------------------------------- + else { + t8_debugf ("CASE 3\n"); + t8_debugf ("location[0] = %i\n", location[0]); + + //The own face is split + //get hex_face_number of the face_neighbored subelement + //Condition that the neighbor lies not on the same hex face: + hlp = 0; + //hex face 0 and 1 + if (location[0] == 0 || location[0] == 1) { + if (((location[2] & 2) == 0) && (face == 1)) { //front + hlp += 1; + t8_debugf ("hlp front %i\n", hlp); + } + if (((location[2] & 2) != 0) && (face == 0)) { //back + hlp += 1; + t8_debugf ("hlp back %i\n", hlp); + } + + if (((location[2] & 1) == 0) && (face == 3)) { //bottom + hlp += 1; + t8_debugf ("hlp bottom %i\n", hlp); + } + if (((location[2] & 1) != 0) && (face == 2)) { //up + hlp += 1; + t8_debugf ("hlp up %i\n", hlp); + } + } + //hex face 2 and 3 + if (location[0] == 2 || location[0] == 3) { + if (((location[2] & 4) == 0) && (face == 1)) { //left + hlp += 1; + } + if (((location[2] & 4) != 0) && (face == 0)) { //right + hlp += 1; + } + + if (((location[2] & 1) == 0) && (face == 3)) { //bottom + hlp += 1; + } + if (((location[2] & 1) != 0) && (face == 2)) { //up + hlp += 1; + } + } + + //hex face 4 and 5 + if (location[0] == 4 || location[0] == 5) { + if (((location[2] & 4) == 0) && (face == 1)) { //left + hlp += 1; + t8_debugf ("hlp left %i\n", hlp); + } + if (((location[2] & 4) != 0) && (face == 0)) { //right + hlp += 1; + t8_debugf ("hlp right %i\n", hlp); + } + + if (((location[2] & 2) == 0) && (face == 3)) { //front + hlp += 1; + t8_debugf ("hlp front %i\n", hlp); + } + if (((location[2] & 2) != 0) && (face == 2)) { //back + hlp += 1; + t8_debugf ("hlp back %i\n", hlp); + } + } + //if none of these conditions is true, we are in case 3 + neigh_hex_face = subelement_face_to_dual_subelement[location[0]][face]; + if ((hlp == 0) && ((transition_type & (int) pow (2, 5 - neigh_hex_face)) == 0)) { + + if ((transition_type & (int) pow (2, 5 - neigh_hex_face)) == 0) { //neighbor not split + + //it's not possible, that the neighbor lies on the same hex face here, because the own face is split here and if the neighbor would lie on the same face + //its face would obviously be split too. + neigh_hex_face = subelement_face_to_dual_subelement[location[0]][face]; + //make rightshift until only the bits for the faces before our neighbors face are left. + transition_type_tmp = transition_type >> (5 - neigh_hex_face); + + for (iter = 0; iter <= neigh_hex_face; iter++) { + + //Count the elements until our neighbored hex_face. + if (((transition_type_tmp >> iter) & 1) != 0) { + amount_subelements += 4; + } + else { + amount_subelements += 1; + } + } + + subelement_id_tmp = amount_subelements - 1; + } + + phex_w_sub_neighbor_at_face->subelement_id = subelement_id_tmp; + } + + //---------------------------CASE 4-------------------------------------- + else { + t8_debugf ("CASE 4\n"); + //It's possible, that the neighbored subelement has the same face_hex number as the element itself + // + amount_subelements = 0; + subelement_id_tmp = 0; + hlp = 0; + + //Now we need the subelement_id_type (location[2]) to determine the exact location of the + //subelement in the transition cell + + /* We have to go through all hex_faces + */ + if (location[0] == 0 || location[0] == 1) { // hex_face = 0/1 + if ((location[2] & 2) != 0) { //back + if (face == 0) { //then it's the element before. + subelement_id_tmp = phex_w_sub_neighbor_at_face->subelement_id - 1; + hlp += 1; + } + } + else { + if (face == 1) { //then it's the element after. + subelement_id_tmp = phex_w_sub_neighbor_at_face->subelement_id + 1; + hlp += 1; + } + } + if ((location[2] & 1) != 0) { //up + if (face == 2) { //then it's the element below + subelement_id_tmp = phex_w_sub_neighbor_at_face->subelement_id - 2; + hlp += 1; + } + } + else { //down + if (face == 3) { // down + subelement_id_tmp = phex_w_sub_neighbor_at_face->subelement_id + 2; + hlp += 1; + } + } + } + + if (location[0] == 2 || location[0] == 3) { // hex_face = 2/3 + if ((location[2] & 4) == 0) { //left + if (face == 1) { // then it's the next element + subelement_id_tmp = phex_w_sub_neighbor_at_face->subelement_id + 1; + hlp += 1; + } + else { //right + if (face == 0) { //then it's the element before. + subelement_id_tmp = phex_w_sub_neighbor_at_face->subelement_id - 1; + hlp += 1; + } + } + } + + if ((location[2] & 1) != 0) { //up + if (face == 2) { //then it's the element below + subelement_id_tmp = phex_w_sub_neighbor_at_face->subelement_id - 2; + hlp += 1; + } + } + else { //down + if (face == 3) { //then its the element above + subelement_id_tmp = phex_w_sub_neighbor_at_face->subelement_id + 2; + hlp += 1; + } + } + } + + if (location[0] == 4 || location[0] == 5) { // hex_face = 4/5 + + if ((location[2] & 4) != 0) { //right + if (face == 0) { //then it's the element before. + subelement_id_tmp = phex_w_sub_neighbor_at_face->subelement_id - 1; + hlp += 1; + } + } + else { + if (face == 1) { + subelement_id_tmp = phex_w_sub_neighbor_at_face->subelement_id + 1; + hlp += 1; + } + } + if ((location[2] & 2) != 0) { //back + if (face == 2) { //then it's the element in front + subelement_id_tmp = phex_w_sub_neighbor_at_face->subelement_id - 2; + hlp += 1; + } + } + else { //front + if (face == 3) { + subelement_id_tmp = phex_w_sub_neighbor_at_face->subelement_id + 2; + hlp += 1; + } + } + } + neigh_face[0] = subelement_location_to_parent_dual_face[face]; + phex_w_sub_neighbor_at_face->subelement_id = subelement_id_tmp; + + if (hlp == 0) { //if the neighbor lies not on the same hex face as elem + //get hex_face_number of the face_neighbored subelement + neigh_face[0] = subelement_face_dual[location[0]][face]; + + neigh_hex_face = subelement_face_to_dual_subelement[location[0]][face]; + + //The neighbored element is not at the same hex face + //First count the amount of subelements until the hex_face of the neighbored subelement is reached. + //make rightshift until only the bits for the faces before our neighbors face are left. + transition_type_tmp = transition_type >> (5 - neigh_hex_face); + + for (iter = 0; iter <= neigh_hex_face; iter++) { + + //Count the elements until our neighbored hex_face. + if (((transition_type_tmp >> iter) & 1) != 0) { + amount_subelements += 4; + } + else { + amount_subelements += 1; + } + } + + //For the hex_faces 0 or 1 all possible neighbors will have a subelement_id that is greater than the own subelement_id + if ((location[0] == 0) || (location[0] == 1)) { + if (face == 0) { + subelement_id_tmp = amount_subelements - 4 + phex_w_sub_neighbor_at_face->subelement_id; + } + if (face == 1) { + subelement_id_tmp = amount_subelements - 5 + phex_w_sub_neighbor_at_face->subelement_id; + } + if (face == 2 || face == 3) { + //We have to distinguish if the element is in the front or back. + if ((location[2] & 2) == 0) { //front + subelement_id_tmp = amount_subelements - 4 + location[0]; + } + else { + subelement_id_tmp = amount_subelements - 2 + location[0]; + } + } + } + /* Because all faces = 0 of subelements at hex face 2,3,4 and 5 touch the hex_face 0, + * all faces = 1 touch hex_face 1 and all faces = 2 touch the hex_face 4, + * and all faces = 3 touch hex_face 5, we can hardcode these cases */ + else { + if (face == 0) { + //We have to distinguish if the element is in the front or back. + if (location[0] >= 4) { + if ((location[2] & 2) == 0) { //front + if (location[0] == 4) { // we are at hex_face 4 + subelement_id_tmp = 0; + } + else { //hex face 5 + subelement_id_tmp = 2; + } + } + + else { //back + if (location[0] == 4) { // we are at hex_face 4 + subelement_id_tmp = 1; + } + if (location[0] == 5) { + subelement_id_tmp = 4; + } + } + } + if (location[0] <= 3) { //only for hex faces 2 and the the subelements can be up or down + + if ((location[2] & 1) == 0) { //down + if (location[0] == 2) { // we are at hex_face 2 + subelement_id_tmp = 0; + } + if (location[0] == 3) { + subelement_id_tmp = 1; + } + else { //up + if (location[0] == 2) { // we are at hex_face 2 + subelement_id_tmp = 2; + } + if (location[0] == 3) { + subelement_id_tmp = 3; + } + } + } + } + } + //same for face = 1 + if (face == 1) { + //We have to distinguish if the element is in the front or back. + if ((location[2] & 2) == 0) { //front + if (location[0] == 4) { // we are at hex_face 4 + subelement_id_tmp = amount_subelements - 4; + } + if (location[0] == 5) { + subelement_id_tmp = amount_subelements - 2; + } + } + else { //back + if (location[0] == 4) { // we are at hex_face 4 + subelement_id_tmp = amount_subelements - 3; + } + if (location[0] == 5) { + subelement_id_tmp = amount_subelements - 1; + } + } + if ((location[2] & 1) == 0) { //down + if (location[0] == 2) { // we are at hex_face 2 + subelement_id_tmp = amount_subelements - 4; + } + if (location[0] == 3) { + subelement_id_tmp = amount_subelements - 3; + } + else { //up + if (location[0] == 2) { // we are at hex_face 2 + subelement_id_tmp = amount_subelements - 2; + } + if (location[0] == 3) { + subelement_id_tmp = amount_subelements - 1; + } + } + } + } + //same for face = 2 + if (face == 3 || face == 2) { + //We have to distinguish if the element is in the front or back. + if ((location[2] & 4) == 0) { //right + if (location[0] == 2) { // we are at hex_face 2 + subelement_id_tmp = amount_subelements - 3; + } + if (location[0] == 3) { + subelement_id_tmp = amount_subelements - 1; + } + } + else { //left + if (location[0] == 2) { // we are at hex_face 2 + subelement_id_tmp = amount_subelements - 4; + } + if (location[0] == 3) { + subelement_id_tmp = amount_subelements - 2; + } + } + } + } + phex_w_sub_neighbor_at_face->subelement_id = subelement_id_tmp; + } + } + } + phex_w_sub_neighbor_at_face->subelement_id = subelement_id_tmp; + t8_debugf ("subelement ID nach neighbor function %i\n", subelement_id_tmp); +} + +void +t8_subelement_scheme_hex_c::t8_element_children (const t8_element_t *elem, int length, t8_element_t *c[]) const +{ + /* if elem is a subelement, then this function will construct the children of its parent p8est quadrant */ + const t8_hex_with_subelements *phex_w_sub_elem = (const t8_hex_with_subelements *) elem; + t8_hex_with_subelements **phex_w_sub_children = (t8_hex_with_subelements **) c; + + const p8est_quadrant_t *q = &phex_w_sub_elem->p8q; + + int ichild; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (length == P8EST_CHILDREN); + +#ifdef T8_ENABLE_DEBUG + { + int i; + for (i = 0; i < P8EST_CHILDREN; i++) { + T8_ASSERT (t8_element_is_valid (c[i])); + } + } +#endif + + /* set coordinates and levels of the children */ + p8est_quadrant_children (q, &phex_w_sub_children[0]->p8q, &phex_w_sub_children[1]->p8q, &phex_w_sub_children[2]->p8q, + &phex_w_sub_children[3]->p8q, &phex_w_sub_children[4]->p8q, &phex_w_sub_children[5]->p8q, + &phex_w_sub_children[6]->p8q, &phex_w_sub_children[7]->p8q); + + for (ichild = 0; ichild < P8EST_CHILDREN; ++ichild) { + t8_element_reset_subelement_values (c[ichild]); + } +} + +int +t8_subelement_scheme_hex_c::t8_element_child_id (const t8_element_t *elem) const +{ + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + const p8est_quadrant_t *q = &phex_w_sub->p8q; + + T8_ASSERT (t8_element_is_valid (elem)); + + return (t8_element_is_subelement (elem) ? phex_w_sub->subelement_id : p8est_quadrant_child_id (q)); +} + +int +t8_subelement_scheme_hex_c::t8_element_ancestor_id (const t8_element_t *elem, int level) const +{ + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + const p8est_quadrant_t *q = &phex_w_sub->p8q; + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + + return p8est_quadrant_ancestor_id (q, level); +} + +int +t8_subelement_scheme_hex_c::t8_element_is_family (t8_element_t *const *fam) const +{ + /* Note that this test is very rudimentary, especially when there subelements are in fam */ + t8_hex_with_subelements **phex_w_sub_family = (t8_hex_with_subelements **) fam; + +#ifdef T8_ENABLE_DEBUG + { + int i; + int num_siblings = t8_element_num_siblings (fam[0]); + for (i = 0; i < num_siblings; i++) { + T8_ASSERT (t8_element_is_valid (fam[i])); + } + } +#endif + + /* Subelements can not be refined into other elements of a higher level. + * So if the first element of fam is a subelement, we assume that the following num_siblings + * many elements are its siblings and therefore form a family. */ + if (phex_w_sub_family[0]->transition_type != 0) { + return 1; + } + /* If the first element of fam is no subelement we check the following elements of fam */ + else { + /* If any of the following elements is a subelement, then they can not form a family */ + if ((phex_w_sub_family[1]->transition_type != 0) || (phex_w_sub_family[2]->transition_type != 0) + || (phex_w_sub_family[3]->transition_type != 0) || (phex_w_sub_family[4]->transition_type != 0) + || (phex_w_sub_family[5]->transition_type != 0) || (phex_w_sub_family[6]->transition_type != 0) + || (phex_w_sub_family[7]->transition_type != 0)) { + return 0; + } + /* If all elements of fam are no subelements, then we can use the p8est check is_family */ + else { + return p8est_quadrant_is_family ( + &phex_w_sub_family[0]->p8q, &phex_w_sub_family[1]->p8q, &phex_w_sub_family[2]->p8q, &phex_w_sub_family[3]->p8q, + &phex_w_sub_family[4]->p8q, &phex_w_sub_family[5]->p8q, &phex_w_sub_family[6]->p8q, &phex_w_sub_family[7]->p8q); + } + } +} + +void +t8_subelement_scheme_hex_c::t8_element_set_linear_id (t8_element_t *elem, int level, t8_linearidx_t id) const +{ + t8_hex_with_subelements *phex_w_sub = (t8_hex_with_subelements *) elem; + p8est_quadrant_t *q = &phex_w_sub->p8q; + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (0 <= level && level <= P8EST_OLD_QMAXLEVEL); + T8_ASSERT (0 <= id && id < ((t8_linearidx_t) 1) << P8EST_DIM * level); + + p8est_quadrant_set_morton (q, level, id); +} + +t8_linearidx_t +t8_subelement_scheme_hex_c::t8_element_get_linear_id (const t8_element_t *elem, int level) const +{ + t8_hex_with_subelements *phex_w_sub = (t8_hex_with_subelements *) elem; + p8est_quadrant_t *q = &phex_w_sub->p8q; + + /* Note that the id of a subelement equals the id of its parent quadrant. + * Therefore, the binary search (for example used in the leaf_face_neighbor function) + * will find a random subelement of the transition cell which might not be the desired neighbor of a given element. */ + T8_ASSERT (t8_element_subelement_values_are_valid (elem)); + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (0 <= level && level <= P8EST_OLD_QMAXLEVEL); + + return p8est_quadrant_linear_id ((p8est_quadrant_t *) q, level); +} + +void +t8_subelement_scheme_hex_c::t8_element_first_descendant (const t8_element_t *elem, t8_element_t *desc, int level) const +{ + const t8_hex_with_subelements *phex_w_sub_elem = (const t8_hex_with_subelements *) elem; + t8_hex_with_subelements *phex_w_sub_desc = (t8_hex_with_subelements *) desc; + + const p8est_quadrant_t *q = &phex_w_sub_elem->p8q; + p8est_quadrant_t *r = &phex_w_sub_desc->p8q; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (desc)); + T8_ASSERT (0 <= level && level <= P8EST_OLD_QMAXLEVEL); + + p8est_quadrant_first_descendant (q, r, level); + + /* We allow constructing a last descendant from a subelement. + * Keep in mind, that transforming a hex element to a subelement does not change the + * p8est quadrant. Therefore, we are constructing the last descendant of the parent + * hex element of the given subelement. Since the last descendant is not meant to be + * a subelement, we reset the corresponding subelement values. */ + t8_element_reset_subelement_values (desc); +} + +void +t8_subelement_scheme_hex_c::t8_element_last_descendant (const t8_element_t *elem, t8_element_t *desc, int level) const +{ + const t8_hex_with_subelements *phex_w_sub_elem = (const t8_hex_with_subelements *) elem; + t8_hex_with_subelements *phex_w_sub_desc = (t8_hex_with_subelements *) desc; + + const p8est_quadrant_t *q = &phex_w_sub_elem->p8q; + p8est_quadrant_t *r = &phex_w_sub_desc->p8q; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (desc)); + T8_ASSERT (0 <= level && level <= P8EST_OLD_QMAXLEVEL); + p8est_quadrant_last_descendant (q, r, level); + + /* We allow constructing a last descendant from a subelement. + * Keep in mind, that transforming a hex element to a subelement does not change the + * p8est quadrant. Therefore, we are constructing the last descendant of the parent + * hex element of the given subelement. Since the last descendant is not meant to be + * a subelement, we reset the corresponding subelement values. */ + t8_element_reset_subelement_values (desc); +} + +void +t8_subelement_scheme_hex_c::t8_element_successor (const t8_element_t *elem1, t8_element_t *elem2) const +{ + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem1)); + + T8_ASSERT (t8_element_is_valid (elem1)); + T8_ASSERT (t8_element_is_valid (elem2)); + T8_ASSERT (0 <= t8_element_level (elem1) && t8_element_level (elem1) <= P8EST_OLD_QMAXLEVEL); + p8est_quadrant_successor ((p8est_quadrant_t *) elem1, (p8est_quadrant_t *) elem2); +} + +void +t8_subelement_scheme_hex_c::t8_element_nca (const t8_element_t *elem1, const t8_element_t *elem2, + t8_element_t *nca) const +{ + const t8_hex_with_subelements *phex_w_sub_elem1 = (const t8_hex_with_subelements *) elem1; + const t8_hex_with_subelements *phex_w_sub_elem2 = (const t8_hex_with_subelements *) elem2; + t8_hex_with_subelements *phex_w_sub_nca = (t8_hex_with_subelements *) nca; + + const p8est_quadrant_t *q1 = &phex_w_sub_elem1->p8q; + const p8est_quadrant_t *q2 = &phex_w_sub_elem2->p8q; + p8est_quadrant_t *r = &phex_w_sub_nca->p8q; + + T8_ASSERT (t8_element_is_valid (elem1)); + T8_ASSERT (t8_element_is_valid (elem2)); + + /* In case of subelements, we use the parent quadrant and construct nca of the parent quadrant */ + t8_element_reset_subelement_values (nca); + p8est_nearest_common_ancestor (q1, q2, r); +} + +t8_element_shape_t +t8_subelement_scheme_hex_c::t8_element_face_shape (const t8_element_t *elem, int face) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + if (t8_element_is_subelement (elem)) { + if (face == 4) { + return T8_ECLASS_QUAD; + } + else { + return T8_ECLASS_TRIANGLE; + } + } + else { + return T8_ECLASS_QUAD; + } +} + +void +t8_subelement_scheme_hex_c::t8_element_children_at_face (const t8_element_t *elem, int face, t8_element_t *children[], + int num_children, int *child_indices) const +{ + int child_ids_local[4], i, *child_ids; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (!t8_element_is_subelement (elem)); + T8_ASSERT (num_children == 4); + +#ifdef T8_ENABLE_DEBUG + { + int j; + for (j = 0; j < P4EST_CHILDREN; j++) { + T8_ASSERT (t8_element_is_valid (children[j])); + } + } +#endif + T8_ASSERT (0 <= face && face < P8EST_FACES); + T8_ASSERT (num_children == t8_element_num_face_children (elem, face)); + + if (child_indices != NULL) { + child_ids = child_indices; + } + else { + child_ids = child_ids_local; + } + /* + * Compute the child id of the first and second child at the face. + * + * The faces of the quadrant are enumerated like this: + * + * f_3 + * x ---- x + * / f_5 /| z y + * x ---- x | |/ + * f_0 | | x f_1 -- x + * | f_2 |/ + * x ---- x + * f_4 + */ + for (i = 0; i < P8EST_HALF; ++i) { + child_ids[i] = p8est_face_corners[face][i]; + } + + /* Create the four face children */ + /* We have to revert the order and compute the zeroth child last, since + * the usage allows for elem == children[0]. + */ + for (i = 3; i >= 0; i--) { + t8_element_child (elem, child_ids[i], children[i]); + } +} + +int +t8_subelement_scheme_hex_c::t8_element_face_child_face (const t8_element_t *elem, int face, int face_child) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + /* For octants the face enumeration of children is the same as for the parent. */ + if (t8_element_is_subelement (elem)) { + T8_ASSERT (face == 4); + return t8_element_face_parent_face (elem, face); + } + else { + /* For quadrants the face enumeration of children is the same as for the parent. */ + return face; + } +} + +int +t8_subelement_scheme_hex_c::t8_element_face_parent_face (const t8_element_t *elem, int face) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (face >= -1 && face < P8EST_FACES); + + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + const p8est_quadrant_t *q = &phex_w_sub->p8q; + + int child_id; + if (face == -1) { + return -1; + } + + /* For subelements we need to adjust the output of this function. + * A subelements face is a subface of the parent quadrant (the transition cell) if and only if the face number is 4. */ + if (t8_element_is_subelement (elem)) { + if (face == 4) { + /* In this case the face is a subface of the parent. We use the location function in order + * to determine which of the parents faces intersects the subelements face. */ + int location[3] = {}; + t8_element_get_location_of_subelement (elem, location); + + return subelement_location_to_parent_face[location[0]]; + } + else { + return -1; + } + } + + if (q->level == 0) { + return face; + } + /* Determine whether face is a subface of the parent. + * This is the case if the child_id matches one of the faces corners */ + child_id = p8est_quadrant_child_id (q); + if (child_id == p8est_face_corners[face][0] || child_id == p8est_face_corners[face][1] + || child_id == p8est_face_corners[face][2] || child_id == p8est_face_corners[face][3]) { + return face; + } + return -1; +} + +void +t8_subelement_scheme_hex_c::t8_element_transform_face (const t8_element_t *elem1, t8_element_t *elem2, int orientation, + int sign, int is_smaller_face) const +{ + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem1)); + SC_ABORT ("This function is not implemented yet.\n"); + return; +} + +int +t8_subelement_scheme_hex_c::t8_element_extrude_face (const t8_element_t *face, const t8_eclass_scheme_c *face_scheme, + t8_element_t *elem, int root_face) const +{ + /* build (extrude) elem from a given face element */ + t8_hex_with_subelements *phex_w_sub = (t8_hex_with_subelements *) elem; + p8est_quadrant_t *q = &phex_w_sub->p8q; + + const p4est_quadrant_t *b = (const p4est_quadrant_t *) face; + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (face_scheme->eclass == T8_ECLASS_QUAD); + T8_ASSERT (face_scheme->t8_element_is_valid (face)); + T8_ASSERT (0 <= root_face && root_face < P8EST_FACES); + q->level = b->level; + /* + * The faces of the root quadrant are enumerated like this: + * + * x ---- x + * / f_5 /| + * x ---- x | + * f_0 | | x f_1 + * | f_2 |/ + * x ---- x + * f_4 + * + * We need to rescale the coordinates since a quadrant may have a different + * root length than an octant. + */ + switch (root_face) { + case 0: + q->x = 0; + q->y = ((int64_t) b->x * P8EST_ROOT_LEN) / P4EST_ROOT_LEN; + q->z = ((int64_t) b->y * P8EST_ROOT_LEN) / P4EST_ROOT_LEN; + break; + case 1: + q->x = P8EST_LAST_OFFSET (q->level); + q->y = ((int64_t) b->x * P8EST_ROOT_LEN) / P4EST_ROOT_LEN; + q->z = ((int64_t) b->y * P8EST_ROOT_LEN) / P4EST_ROOT_LEN; + break; + case 2: + q->x = ((int64_t) b->x * P8EST_ROOT_LEN) / P4EST_ROOT_LEN; + q->y = 0; + q->z = ((int64_t) b->y * P8EST_ROOT_LEN) / P4EST_ROOT_LEN; + break; + case 3: + q->x = ((int64_t) b->x * P8EST_ROOT_LEN) / P4EST_ROOT_LEN; + q->y = P8EST_LAST_OFFSET (q->level); + q->z = ((int64_t) b->y * P8EST_ROOT_LEN) / P4EST_ROOT_LEN; + break; + case 4: + q->x = ((int64_t) b->x * P8EST_ROOT_LEN) / P4EST_ROOT_LEN; + q->y = ((int64_t) b->y * P8EST_ROOT_LEN) / P4EST_ROOT_LEN; + q->z = 0; + break; + case 5: + q->x = ((int64_t) b->x * P8EST_ROOT_LEN) / P4EST_ROOT_LEN; + q->y = ((int64_t) b->y * P8EST_ROOT_LEN) / P4EST_ROOT_LEN; + q->z = P8EST_LAST_OFFSET (q->level); + break; + } + /* We return the face of q at which we extruded. This is the same number + * as root_face. */ + return root_face; +} + +int +t8_subelement_scheme_hex_c::t8_element_tree_face (const t8_element_t *elem, int face) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + /* If elem is a subelement, then this function should only be called together with + * face = 4 since other faces will never intersect a tree face. */ + if (t8_element_is_subelement (elem)) { + T8_ASSERT (face == 4); + + return t8_element_face_parent_face (elem, face); + } + else { + T8_ASSERT (0 <= face && face < P8EST_FACES); + /* For hex the face and the tree face number are the same. */ + return face; + } +} + +/** Construct the first descendant of an element that touches a given face. */ +void +t8_subelement_scheme_hex_c::t8_element_first_descendant_face (const t8_element_t *elem, int face, + t8_element_t *first_desc, int level) const +{ + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + + const p8est_quadrant_t *q = (const p8est_quadrant_t *) elem; + p8est_quadrant_t *desc = (p8est_quadrant_t *) first_desc; + int first_face_corner; + + T8_ASSERT (0 <= face && face < P8EST_FACES); + T8_ASSERT (0 <= level && level <= P8EST_OLD_QMAXLEVEL); + + /* Get the first corner of q that belongs to face */ + first_face_corner = p8est_face_corners[face][0]; + /* Construct the descendant of q in this corner */ + p8est_quadrant_corner_descendant (q, desc, first_face_corner, level); +} + +/** Construct the last descendant of an element that touches a given face. */ +void +t8_subelement_scheme_hex_c::t8_element_last_descendant_face (const t8_element_t *elem, int face, + t8_element_t *last_desc, int level) const +{ + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + T8_ASSERT (!t8_element_is_subelement (last_desc)); + + const p8est_quadrant_t *q = (const p8est_quadrant_t *) elem; + p8est_quadrant_t *desc = (p8est_quadrant_t *) last_desc; + int last_face_corner; + + T8_ASSERT (0 <= face && face < P8EST_FACES); + T8_ASSERT (0 <= level && level <= P8EST_OLD_QMAXLEVEL); + + /* Get the last corner of q that belongs to face */ + last_face_corner = p8est_face_corners[face][3]; + /* Construct the descendant of q in this corner */ + p8est_quadrant_corner_descendant (q, desc, last_face_corner, level); +} + +void +t8_subelement_scheme_hex_c::t8_element_boundary_face (const t8_element_t *elem, int face, t8_element_t *boundary, + const t8_eclass_scheme_c *boundary_scheme) const +{ + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + const p8est_quadrant_t *q = &phex_w_sub->p8q; + + p4est_quadrant_t *b = (p4est_quadrant_t *) boundary; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (boundary_scheme->eclass == T8_ECLASS_QUAD); + T8_ASSERT (boundary_scheme->t8_element_is_valid (boundary)); + T8_ASSERT (0 <= face && face < P8EST_FACES); + + if (!t8_element_is_subelement (elem)) { + T8_ASSERT (0 <= face && face < P8EST_FACES); + /* The level of the boundary element is the same as the quadrant's level */ + b->level = q->level; + /* + * The faces of the quadrant are enumerated like this: + * + * x ---- x + * / f_5 /| + * x ---- x | + * f_0 | | x f_1 + * | f_2 |/ + * x ---- x + * f_4 + * + * If face = 0 or face = 1 then b->x = q->y, b->y = q->z + * if face = 2 or face = 3 then b->x = q->x, b->y = q->z + * if face = 4 or face = 5 then b->x = q->x, b->y = q->y + * + * We have to scale the coordinates since a root quadrant may have + * different length than a root hex. + */ + b->x = (face >> 1 ? q->x : q->y) * ((t8_linearidx_t) P4EST_ROOT_LEN / P8EST_ROOT_LEN); /* true if face >= 2 */ + b->y = (face >> 2 ? q->y : q->z) * ((t8_linearidx_t) P4EST_ROOT_LEN / P8EST_ROOT_LEN); /* true if face >= 4 */ + T8_ASSERT (!p8est_quadrant_is_extended (q) || p4est_quadrant_is_extended (b)); + } + else { + /* face number 4 is the only face of a subelement that points outward of the transition cell */ + T8_ASSERT (face == 4); + /* + * for a split subelement, the boundary face has a higher level + * for a non split element, the boundary face has the same level. + */ + /* location = {location of subelement (face number of transition cell), split, subelement type (left/right, front/back, bottom/up)} */ + int location[3] = {}; + t8_element_get_location_of_subelement (elem, location); + int split = location[1]; + int subelement_type = location[2]; + + if (split) { /* if the subelement lies at a split face */ + b->level = q->level + 1; + int len = P8EST_QUADRANT_LEN (phex_w_sub->p8q.level + 1); + if ((location[0] == 0) || (location[0] == 1)) { /* left or right face */ + if ((subelement_type & 2) != 0) { //back + b->x = q->y + len; + } + else if ((subelement_type & 1) != 0) { //up + b->y = q->z + len; + } + } + else if ((location[0] == 2) || (location[0] == 3)) { /* front or back face */ + if ((subelement_type & 4) != 0) { //right + b->x = q->x + len; + } + else if ((subelement_type & 1) != 0) { // up + b->y = q->z + len; + } + } + else if ((location[0] == 2) || (location[0] == 3)) { /* bottom or up face */ + if ((subelement_type & 4) != 0) { //right + b->x = q->x + len; + } + else if ((subelement_type & 2) != 0) { // back + b->y = q->y + len; + } + } + } + } +} + +void +t8_subelement_scheme_hex_c::t8_element_boundary (const t8_element_t *elem, int min_dim, int length, + t8_element_t **boundary) const +{ + SC_ABORT ("Not implemented\n"); +#if 0 +#ifdef T8_ENABLE_DEBUG + int per_eclass[T8_ECLASS_COUNT]; +#endif + int iface; + + T8_ASSERT (length == + t8_eclass_count_boundary (T8_ECLASS_HEX, min_dim, per_eclass)); + + T8_ASSERT (length == P8EST_FACES); + for (iface = 0; iface < P8EST_FACES; iface++) { + t8_element_boundary_face (elem, iface, boundary[iface]); + } +#endif +} + +int +t8_subelement_scheme_hex_c::t8_element_is_root_boundary (const t8_element_t *elem, int face) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + const p8est_quadrant_t *q = &phex_w_sub->p8q; + + p4est_qcoord_t coord; + + /* In case of a subelement, we need to change its face number to the face number of the parent hex */ + if (t8_element_is_subelement (elem)) { + if (face == 4) { + /* adjust face of subelement to face of parent */ + face = t8_element_face_parent_face (elem, face); + } + else { /* in case of a subelement and face 0 or 2 the face is no subface of the root boundary */ + return 0; + } + } + + T8_ASSERT (0 <= face && face < P8EST_FACES); + + /* if face is 0 or 1 q->x + * 2 or 3 q->y + */ + coord = face >> 2 ? q->z : face >> 1 ? q->y : q->x; + /* If face is 0,2 or 4 check against 0. + * If face is 1,3 or 5 check against LAST_OFFSET */ + return coord == (face & 1 ? P8EST_LAST_OFFSET (q->level) : 0); +} + +int +t8_subelement_scheme_hex_c::t8_element_face_neighbor_inside (const t8_element_t *elem, t8_element_t *neigh, int face, + int *neigh_face) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (neigh)); + T8_ASSERT (0 <= face && face < P8EST_FACES); + + const t8_hex_with_subelements *phex_w_sub_elem = (const t8_hex_with_subelements *) elem; + t8_hex_with_subelements *phex_w_sub_neigh = (t8_hex_with_subelements *) neigh; + + const p8est_quadrant_t *q = &phex_w_sub_elem->p8q; + p8est_quadrant_t *n = &phex_w_sub_neigh->p8q; + + // /* In case of a subelement one should construct the face neighbor of the face-corresponding child quadrant + // * of the subelements parent quadrant. Therefore we might want to adjust the level and adapt the + // * anchor node. */ + if (t8_element_is_subelement (elem)) { /* if elem is a subelement */ + T8_ASSERT (0 <= face && face < T8_HEX_SUBELEMENT_FACES); + int location[3] = {}; + t8_element_get_location_of_subelement (elem, location); + if (face < 4) { /* in this case the face neighbor of the subelement is a sibling */ + /* level and anchor stay the same */ + n->x = q->x; + n->y = q->y; + n->z = q->z; + n->level = q->level; + + T8_ASSERT (face != 4); + SC_ABORT_NOT_REACHED (); + /* return dual face with resprect to neighboring sibling subelement (note that the constructed neigh is NOT a subelement but the parent hex) */ + /* Compute the face number as seen from q. + * 0 -> 2 2 -> 0 + */ + *neigh_face = subelement_face_dual[location[0]][face]; + } + else { /* in this case the face neighbor is no sibling */ + + /* setting the anchor node of the neighbor element */ + n->x = q->x; + n->y = q->y; + n->z = q->z; + + /* half the side length of the transition cell of the subelement */ + const p4est_qcoord_t shift = P8EST_QUADRANT_LEN (q->level + 1); + + int split = location[1]; + int subelement_type = location[2]; + + /* we need to take into account whether the subelement is split or not */ + if (split) { /* split */ + /* increase the level by one */ + n->level = q->level + 1; + + /* adjust the anchor node of the neighbor of the subelement depending on its location */ + if (location[0] == 0) { /* left face */ + n->x = q->x - shift; + if ((subelement_type & 2) != 0) { //back + n->y = q->y + shift; + } + if ((subelement_type & 1) != 0) { //up + n->z = q->z + shift; + } + } + else if (location[0] == 1) { /* right face */ + n->x = q->x + 2 * shift; + if ((subelement_type & 2) != 0) { //back + n->y = q->y + shift; + } + if ((subelement_type & 1) != 0) { //up + n->z = q->z + shift; + } + } + else if (location[0] == 2) { /* front face */ + n->y = q->y - shift; + if ((subelement_type & 4) != 0) { //right + n->x = q->x + shift; + } + if ((subelement_type & 1) != 0) { //up + n->z = q->z + shift; + } + } + else if (location[0] == 3) { //back face + n->y = q->y + 2 * shift; + if ((subelement_type & 4) != 0) { //right + n->x = q->x + shift; + } + if ((subelement_type & 1) != 0) { //up + n->z = q->z + shift; + } + } + else if (location[0] == 4) { //bottom face + if ((subelement_type & 4) != 0) { //right + n->x = q->x + shift; + } + if ((subelement_type & 2) != 0) { //back + n->z = q->z + shift; + } + } + else if (location[0] == 5) { //upper face + n->z = q->z + 2 * shift; + if ((subelement_type & 4) != 0) { //right + n->x = q->x + shift; + } + if ((subelement_type & 2) != 0) { //back + n->z = q->z + shift; + } + } + } + + else { /* not split */ + /* level stays the same */ + n->level = q->level; + + /* adjust the anchor node of the neighbor of the subelement depending on its location */ + if (location[0] == 0) { /* left face */ + n->x = q->x - 2 * shift; + } + else if (location[0] == 1) { /* right face */ + n->x = q->x + 2 * shift; + } + else if (location[0] == 2) { /* front face */ + n->y = q->y - 2 * shift; + } + else if (location[0] == 3) { /* back face */ + n->y = q->y + 2 * shift; + } + else if (location[0] == 4) { /* bottom face */ + n->z = q->z - 2 * shift; + } + else if (location[0] == 5) { /* upper face */ + n->z = q->z + 2 * shift; + } + } + + *neigh_face = subelement_location_to_parent_dual_face[location[0]]; + } + } + else { /* if elem is no subelement */ + + /* Compute the face neighbor */ + p8est_quadrant_face_neighbor (q, face, n); + /* Compute the face of q that coincides with face. + * face neigh_face face neigh_face + * 0 1 4 5 + * 1 0 5 4 + * 2 3 + * 3 2 + */ + + T8_ASSERT (neigh_face != NULL); + *neigh_face = p8est_face_dual[face]; + } + t8_element_reset_subelement_values (neigh); + + if (p8est_quadrant_is_inside_root (n) == 0) { + } + + /* return true if neigh is inside the root */ + return p8est_quadrant_is_inside_root (n); +} + +void +t8_subelement_scheme_hex_c::t8_element_anchor (const t8_element_t *elem, int coord[3]) const +{ + t8_hex_with_subelements *phex_w_sub = (t8_hex_with_subelements *) elem; + p8est_quadrant_t *q = &phex_w_sub->p8q; + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + + T8_ASSERT (t8_element_is_valid (elem)); + + coord[0] = q->x; + coord[1] = q->y; + coord[2] = q->z; +} + +int +t8_subelement_scheme_hex_c::t8_element_root_len (const t8_element_t *elem) const +{ + return P8EST_ROOT_LEN; +} + +int +t8_subelement_scheme_hex_c::t8_element_refines_irregular () const +{ + /* In general, subelements do not refine regularly */ + return 1; +} + +void +t8_subelement_scheme_hex_c::t8_element_vertex_integer_coords (const t8_element_t *elem, int vertex, int coords[]) const +{ + SC_ABORT ("This function is not implemented for the given scheme.\n"); +} + +void +t8_subelement_scheme_hex_c::t8_element_reference_coords (const t8_element_t *elem, const double *ref_coords, + const size_t num_coords, double *out_coords) const +{ + T8_ASSERT (num_coords > 0); + T8_ASSERT (ref_coords != NULL); + T8_ASSERT (out_coords != NULL); + T8_ASSERT (t8_element_is_valid (elem)); + + const t8_hex_with_subelements *element = (t8_hex_with_subelements *) elem; + if (!t8_element_is_subelement (elem)) { + const t8_element_t *hex = (const t8_element_t *) &element->p8q; + // We call the default hex reference coord function for the hex coordinates. + default_hex_scheme.t8_element_reference_coords (hex, ref_coords, num_coords, out_coords); + } + else { + /* This element is a subelement and hence a pyramid. */ + T8_ASSERT (t8_element_shape (elem) == T8_ECLASS_PYRAMID); + + /* + * We use t8_geom_triangular_interpolation to compute the coordinates + * given the coordinates of the vertices. + * For this we consider the pyramid as a cube with 4 corners collapsed into one. + */ + constexpr int num_vertices = 5; // Should use t8_eclass_num_vertices[T8_ECLASS_TRIANGLE]; but is not constexpr + constexpr int num_vertex_coords = 8 * 3; // We need 8 corners since we use trilinear interpolation. + double vertex_coords[num_vertex_coords]; // Stores the pyramids vertices + for (int ivertex = 0; ivertex < num_vertices; ++ivertex) { + // Compute the coordinates of the 5 pyramid vertices. + t8_element_vertex_reference_coords (elem, ivertex, vertex_coords + 3 * ivertex); + } + /* Copy the values of the pyramids last vertex over to vertex_coords 5,6,7 + * in order to build a virtual cube where all top corners collapse onto one. + * We can then us the cubical linear interpolation function. */ + for (int ivertex = num_vertices; ivertex < 8; ++ivertex) { + for (int idim = 0; idim < 3; ++idim) { + vertex_coords[3 * ivertex + idim] = vertex_coords[3 * (num_vertices - 1) + idim]; + } + } + + // For each incoming coordinate do the interpolation + for (size_t icoord = 0; icoord < num_coords; ++icoord) { + // The ref_coords are always 3 dimensional - even though we do not use the 3rd entry. + // The out_coords are 2 dimensional. + const int offset_ref = icoord * 3; // offset for 3 dim ref_coords when iterating over points + const int offset_out = icoord * 3; // offset for 3 dim out_coords when iterating over points + t8_geom_linear_interpolation (ref_coords + offset_ref, vertex_coords, 3, 3, out_coords + offset_out); + } + } +} + +void +t8_subelement_scheme_hex_c::t8_element_vertex_reference_coords (const t8_element_t *t, int vertex, + double coords[]) const +{ + int coords_int[3] = {}; + t8_element_vertex_coords (t, vertex, coords_int); + + /* We divide the integer coordinates by the root length of the hex + * to obtain the reference coordinates. */ + coords[0] = (double) coords_int[0] / (double) P8EST_ROOT_LEN; + coords[1] = (double) coords_int[1] / (double) P8EST_ROOT_LEN; + coords[2] = (double) coords_int[2] / (double) P8EST_ROOT_LEN; +} + +void +t8_subelement_scheme_hex_c::t8_element_vertex_coords (const t8_element_t *elem, int vertex, int coords[]) const +{ + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + const p8est_quadrant_t *q1 = &phex_w_sub->p8q; + + T8_ASSERT (t8_element_is_valid (elem)); + + if (!t8_element_is_subelement (elem)) { + int len; + /* Get the length of the quadrant */ + len = P8EST_QUADRANT_LEN (q1->level); + /* Compute the x, y and z coordinates of the vertex depending on the + * vertex number */ + coords[0] = q1->x + (vertex & 1 ? 1 : 0) * len; + coords[1] = q1->y + (vertex & 2 ? 1 : 0) * len; + coords[2] = q1->z + (vertex & 4 ? 1 : 0) * len; + } + else { + t8_element_vertex_coords_of_subelement (elem, vertex, coords); + } +} + +void +t8_subelement_scheme_hex_c::t8_element_vertex_coords_of_subelement (const t8_element_t *elem, int vertex, + int coords[]) const +{ + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + const p8est_quadrant_t *q1 = &phex_w_sub->p8q; + + int len; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_subelement (elem)); + T8_ASSERT (vertex >= 0 + && vertex < T8_HEX_SUBELEMENT_FACES); /* all subelements are pyramids so T8_HEX_SUBELEMENT_FACES = 5 */ + /* get the length of the current quadrant */ + len = P8EST_QUADRANT_LEN (q1->level); + + /* get location information of the given subelement */ + int location[3] = {}; + t8_element_get_location_of_subelement (elem, location); + + /* the face number, the subelement is adjacent to */ + int face_number = location[0]; + /* = 1, if the adjacent face is split and = 0, if not */ + int split = location[1]; + /* subelement_id type. First bit (left) = 1 if right, = 0 if left, second bit( middle) = 1 if back , = 0 if front, third bit (right) = 0 if up and is = 1 if down + + * second bit front = 0, back = 1, third bit 0 = bottom 1 = up. For example: 110 stands for right and back (so only hex face f_4 and f_5 are possible.) + */ + int sub_face_id = location[2]; + + /* Check, whether the get_location function provides meaningful location data */ + T8_ASSERT ((face_number >= 0) && face_number <= 5); + + coords[0] = q1->x; + coords[1] = q1->y; + coords[2] = q1->z; + + switch (vertex) { + + case 4: //vertex 4 always equals the center of the hexahedron + coords[0] += (len / 2); + coords[1] += (len / 2); + coords[2] += (len / 2); + break; + + case 0: + if (split == 0) { //not split + //for face numbers 0,2 and 4 nothing happens + if (face_number == 1) { + coords[0] += len; + } + if (face_number == 3) { + coords[1] += len; + } + if (face_number == 5) { + coords[2] += len; + } + } + /* ----------- face 0 + 1 (split) --------------------*/ + else { + if ((face_number == 0) || (face_number == 1)) { + if ((sub_face_id & 1) != 0) { // up + coords[2] += (len / 2); + } + if ((sub_face_id & 2) != 0) { // back + coords[1] += (len / 2); + } + + if (face_number == 1) { + coords[0] += len; + } + } + /* ----------- face 2 + 3 (split) --------------------*/ + if ((face_number == 2) || (face_number == 3)) { + if ((sub_face_id & 1) != 0) { // up + coords[2] += (len / 2); + } + if ((sub_face_id & 4) != 0) { // right + coords[0] += (len / 2); + } + if (face_number == 3) { + coords[1] += len; + } + } + /* ----------- face 4 + 5 (split) --------------------*/ + if ((face_number == 4) || (face_number == 5)) { + if ((sub_face_id & 2) != 0) { // back + coords[1] += len / 2; + } + if ((sub_face_id & 4) != 0) { // right + coords[0] += len / 2; + } + if (face_number == 5) { + coords[2] += len; + } + } + } + break; + + case 1: + if (split == 0) { + if ((face_number == 0) || (face_number == 1 || (face_number == 3))) { + coords[1] += len; + } + if (face_number > 0) { + coords[0] += len; + } + if (face_number == 5) { + coords[2] += len; + } + } + /* ----------- face 0 + 1 (split) --------------------*/ + else { + if ((face_number == 0) || (face_number == 1)) { + if ((sub_face_id & 1) != 0) { // up + coords[2] += len / 2; + } + if ((sub_face_id & 2) != 0) { // back + coords[1] += len; + } + else if ((sub_face_id & 2) == 0) { //front + coords[1] += len / 2; + } + + if (face_number == 1) { + coords[0] += len; + } + } + /* ----------- face 2 + 3 (split) --------------------*/ + if ((face_number == 2) || (face_number == 3)) { + if ((sub_face_id & 1) != 0) { // up + coords[2] += len / 2; + } + if ((sub_face_id & 4) != 0) { // right + coords[0] += len; + } + else if ((sub_face_id & 4) == 0) { //left + coords[0] += len / 2; + } + if (face_number == 3) { + coords[1] += len; + } + } + + /* ----------- face 4 + 5 (split) --------------------*/ + if ((face_number == 4) || (face_number == 5)) { + if ((sub_face_id & 2) != 0) { // back + coords[1] += (len / 2); + } + if ((sub_face_id & 4) != 0) { // right + coords[0] += len; + } + else if ((sub_face_id & 4) == 0) { //left + coords[0] += (len / 2); + } + if (face_number == 5) { + coords[2] += len; + } + } + } + break; + case 2: + if (split == 0) { + if (face_number != 4) { + coords[2] += len; + } + if (face_number == 1) { + coords[0] += len; + } + if (face_number > 2) { + coords[1] += len; + } + } + /* ----------- face 0 + 1 (split) --------------------*/ + else { + if ((face_number == 0) || (face_number == 1)) { + if ((sub_face_id & 2) != 0) { // back + coords[1] += len / 2; + } + if ((sub_face_id & 1) != 0) { // up + coords[2] += len; + } + else if ((sub_face_id & 1) == 0) { //bottom + coords[2] += len / 2; + } + if (face_number == 1) { + coords[0] += len; + } + } + + /* ----------- face 2 + 3 (split) --------------------*/ + if ((face_number == 2) || (face_number == 3)) { + if ((sub_face_id & 1) != 0) { // up + coords[2] += len; + } + else if ((sub_face_id & 1) == 0) { + coords[2] += len / 2; + } + if ((sub_face_id & 4) != 0) { // right + coords[0] += len / 2; + } + if (face_number == 3) { + coords[1] += len; + } + } + /* ----------- face 4 + 5 (split) --------------------*/ + + if ((face_number == 4) || (face_number == 5)) { + if ((sub_face_id & 2) != 0) { // back + coords[1] += len; + } + else if ((sub_face_id & 2) == 0) { //front + coords[1] += len / 2; + } + if ((sub_face_id & 4) != 0) { // right + coords[0] += len / 2; + } + if (face_number == 5) { + coords[2] += len; + } + } + } + + break; + case 3: + if (split == 0) { + if (face_number != 2) { + coords[1] += len; + } + if (face_number != 4) { + coords[2] += len; + } + if (face_number > 0) { + coords[0] += len; + } + } + /* ----------- face 0 + 1 (split) --------------------*/ + else { + if ((face_number == 0) || (face_number == 1)) { + if ((sub_face_id & 2) != 0) { // back + coords[1] += len; + } + else if ((sub_face_id & 2) == 0) { //front + coords[1] += len / 2; + } + if ((sub_face_id & 1) != 0) { // up + coords[2] += len; + } + else if ((sub_face_id & 1) == 0) { // bottom + coords[2] += len / 2; + } + if (face_number == 1) { + coords[0] += len; + } + } + /* ----------- face 2 + 3 (split) --------------------*/ + if ((face_number == 2) || (face_number == 3)) { + if ((sub_face_id & 1) != 0) { // up + coords[2] += len; + } + else if ((sub_face_id & 1) == 0) { + coords[2] += len / 2; + } + if ((sub_face_id & 4) != 0) { // right + coords[0] += len; + } + else if ((sub_face_id & 4) == 0) { // left + coords[0] += len / 2; + } + if (face_number == 3) { + coords[1] += len; + } + } + + /* ----------- face 4 + 5 (split) --------------------*/ + if ((face_number == 4) || (face_number == 5)) { + if ((sub_face_id & 2) != 0) { // back + + coords[1] += len; + } + else if ((sub_face_id & 2) == 0) { //front + coords[1] += len / 2; + } + if ((sub_face_id & 4) != 0) { // right + coords[0] += len; + } + else if ((sub_face_id & 4) == 0) { // left + coords[0] += len / 2; + } + if (face_number == 5) { + coords[2] += len; + } + } + } + + break; + } +} + +void +t8_subelement_scheme_hex_c::t8_element_to_transition_cell (const t8_element_t *elem, int transition_type, + t8_element_t *c[]) +{ + const t8_hex_with_subelements *phex_w_sub_elem = (const t8_hex_with_subelements *) elem; + t8_hex_with_subelements **phex_w_sub_subelement = (t8_hex_with_subelements **) c; + + const p8est_quadrant_t *q = &phex_w_sub_elem->p8q; + + /* this function should not be callable by subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (transition_type >= 0 && transition_type <= T8_SUB_HEX_MAX_TRANSITION_TYPE); + + int num_subelements = t8_element_get_number_of_subelements (transition_type); + +#ifdef T8_ENABLE_DEBUG + { + int j; + for (j = 0; j < num_subelements; j++) { + T8_ASSERT (t8_element_is_valid (c[j])); + } + } +#endif + + /* get the length of a children-quadrant */ + const int8_t level = (int8_t) (q->level); + + T8_ASSERT (p8est_quadrant_is_extended (q)); + T8_ASSERT (q->level < P8EST_OLD_QMAXLEVEL); + + int sub_id_counter = 0; + for (sub_id_counter = 0; sub_id_counter < num_subelements; sub_id_counter++) { + phex_w_sub_subelement[sub_id_counter]->p8q.x = q->x; + phex_w_sub_subelement[sub_id_counter]->p8q.y = q->y; + phex_w_sub_subelement[sub_id_counter]->p8q.z = q->z; + phex_w_sub_subelement[sub_id_counter]->p8q.level = level; + + phex_w_sub_subelement[sub_id_counter]->transition_type = transition_type; + + phex_w_sub_subelement[sub_id_counter]->subelement_id = sub_id_counter; + T8_ASSERT (t8_element_is_valid (c[sub_id_counter])); + } +} + +int +t8_subelement_scheme_hex_c::t8_element_get_number_of_subelements (int transition_type) const +{ + /* we could return 0 for transition type 0 but we will assert this case for safety reasons */ + T8_ASSERT (transition_type != 0); + + /* consider transition_type 16 = 010000 in base two -> there are 6 + (1)*3 = 9 subelements */ + int num_hanging_faces = 0; + int ichild; + for ( + ichild = 0; ichild < P8EST_FACES; + ichild++) { /* Count the number of ones of the binary transition type. This number equals the number of hanging faces. */ + num_hanging_faces += (transition_type & (1 << ichild)) >> ichild; + } + + return P8EST_FACES + num_hanging_faces * 3; +} + +void +t8_subelement_scheme_hex_c::t8_element_get_location_of_subelement (const t8_element_t *elem, int location[]) const +{ + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + + /* this function only works for subelements */ + T8_ASSERT (t8_element_is_subelement (elem)); + + T8_ASSERT (t8_element_is_valid (elem)); + + /* Consider the following transition cell of type 13: + * + * f0 1 + * x - - x - - x x - - x - - x + * | | | \ 2 | 3 / | faces: f3 f2 f1 f0 + * | | | 1 \ | / 4 | binary code: 1 1 0 1 (=13) + * f3 x x f2 --> 1 x - - x - - x 1 --> rearrange binaries s.t. the faces are enumerated clockwise: 1 1 1 0 + * | | | 0 / \ 5 | number subelements at face: 2 2 1 2 + * | elem | | / 6 \ | consider sub_id 3: x -> second subelement on the upper face + * + - - - - - x x - - - - - x + * f1 0 + * + * We will use the binary representation to determine the location of the given subelement. + * + * We need to know: + * i) the face number of the first vertex (values: {0,1,2,3,4,5}). + * ii) whether this face is split in half (values: {0,1}). + * iii) the subelement_id_type. + * + * These information are then saved in the location array which will be used by the element_vertex function, + * to automatically determine the vertex coordinates of the given subelement. + */ + + /* 1) convert the transition type from a decimal to a binary representation */ + int type = phex_w_sub->transition_type; + int binary_array[P8EST_FACES] = {}; + + int iface; + + /* We need an array with 6 elements to store all subelement types of the hex scheme from 1 to 63 ({0, 0, 0, 0, 0, 1} to {1, 1, 1, 1, 1, 1}) */ + for (iface = 0; iface < P8EST_FACES; iface++) { + binary_array[(P8EST_FACES - 1) - iface] = (type & (1 << iface)) >> iface; + } /* we now got a binary representation of the transition type, bitwise stored in an array */ + + /* 3) use the rearranged binary representation, and the sub_id to determine the location of the subelement and store these information in an array */ + /* 3.1) location[0] -> the face_number, the subelement is adjacent to */ + /* 3.2) location[1] -> if the face is split or not */ + /* 3.3) location[2] -> if the subelement is the left/right, front/back or bottom/up Same idea as with the transition type: first bit 0 = left, 1 = right, + * second bit front = 0, back = 1, third bit 0 = bottom 1 = up. For example: 110 stands for right and back (so only hex face f_4 and f_5 are possible.) */ + T8_ASSERT (phex_w_sub->subelement_id < t8_element_get_number_of_subelements (phex_w_sub->transition_type)); + + int sub_id = phex_w_sub->subelement_id; + + int sub_face_id_array[3] = { 0, 0, 0 }; + int sub_face_id = 0; + int face_number = -1; + int split; + + int cum_neigh_array[P8EST_FACES] = {}; + + /* construct a cumulative array of the number of neighbors from face 0 to face 5 */ + cum_neigh_array[0] = binary_array[0] * 3 + 1; + cum_neigh_array[1] = cum_neigh_array[0] + binary_array[1] * 3 + 1; + cum_neigh_array[2] = cum_neigh_array[1] + binary_array[2] * 3 + 1; + cum_neigh_array[3] = cum_neigh_array[2] + binary_array[3] * 3 + 1; + cum_neigh_array[4] = cum_neigh_array[3] + binary_array[4] * 3 + 1; + cum_neigh_array[5] = cum_neigh_array[4] + binary_array[5] * 3 + 1; + + /* 3.1) we can use the cumulative array to determine the face number of the given subelement */ + if (sub_id < cum_neigh_array[0]) { + face_number = 0; + } + else { + for (iface = 0; iface < P8EST_FACES - 1; ++iface) { + if (sub_id >= cum_neigh_array[iface] && sub_id < cum_neigh_array[iface + 1]) { + face_number = iface + 1; + break; + } + } + } + + /* make sure that a face_number has been found */ + T8_ASSERT (face_number >= 0); + + /* 3.2) determine, whether the face is split or not */ + if (binary_array[face_number] == 0) { + split = 0; /* the face is not split */ + } + else { + split = 1; /* the face is split */ + } + if (split == 1) { + + /* 3.3) determine, whether the subelement is the left/right, front/back or bottom/up subelement at the face + First left/ right (only for face number 2, 3, 4, 5)*/ + if (face_number > 1) { + if (((sub_id + 1) == cum_neigh_array[face_number]) || ((sub_id + 3) == cum_neigh_array[face_number])) { + sub_face_id_array[0] = 1; /* right*/ + } + else if (((sub_id + 2) == cum_neigh_array[face_number]) || ((sub_id + 4) == cum_neigh_array[face_number])) { + sub_face_id_array[0] = 0; /* left */ + } + } + //Second: Check front or back (only for face numbers 0, 1, 4, 5) + if (face_number <= 1) { + if (((sub_id + 1) == cum_neigh_array[face_number]) || ((sub_id + 3) == cum_neigh_array[face_number])) { + sub_face_id_array[1] = 1; /* back subelement */ + } + else if (((sub_id + 2) == cum_neigh_array[face_number]) || ((sub_id + 4) == cum_neigh_array[face_number])) { + sub_face_id_array[1] = 0; /* front subelement */ + } + } + else if (face_number >= 4) { + if (((sub_id + 1) == cum_neigh_array[face_number]) || ((sub_id + 2) == cum_neigh_array[face_number])) { + sub_face_id_array[1] = 1; /* back subelement */ + } + else if (((sub_id + 3) == cum_neigh_array[face_number]) || ((sub_id + 4) == cum_neigh_array[face_number])) { + sub_face_id_array[1] = 0; /* front subelement */ + } + } + /*Third check up or down (only for face numbers 0, 1, 2, 3)*/ + if (face_number < 4) { + if (((sub_id + 2) == cum_neigh_array[face_number]) || ((sub_id + 1) == cum_neigh_array[face_number])) { + sub_face_id_array[2] = 1; /* up subelement */ + } + else if (((sub_id + 3) == cum_neigh_array[face_number]) || ((sub_id + 4) == cum_neigh_array[face_number])) { + sub_face_id_array[2] = 0; /* bottom subelement */ + } + } + /*Calculate the sub_face_id out of the sub_face_id_array*/ + for (int i = 0; i < 3; i++) { + if (sub_face_id_array[i] == 1) { + sub_face_id += std::pow (2, 2 - i); + } + } + } + + location[0] = face_number; + location[1] = split; + location[2] = sub_face_id; +} + +void +t8_subelement_scheme_hex_c::t8_element_reset_subelement_values (t8_element *elem) const +{ + t8_hex_with_subelements *phex_w_sub = (t8_hex_with_subelements *) elem; + + phex_w_sub->transition_type = 0; + phex_w_sub->subelement_id = 0; +} + +void +t8_subelement_scheme_hex_c::t8_element_copy_subelement_values (const t8_element *source, t8_element *dest) const +{ + const t8_hex_with_subelements *phex_w_sub_source = (const t8_hex_with_subelements *) source; + t8_hex_with_subelements *phex_w_sub_dest = (t8_hex_with_subelements *) dest; + phex_w_sub_dest->transition_type = phex_w_sub_source->transition_type; + phex_w_sub_dest->subelement_id = phex_w_sub_source->subelement_id; +} + +int +t8_subelement_scheme_hex_c::t8_element_is_subelement (const t8_element *elem) const +{ + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + + T8_ASSERT (phex_w_sub->transition_type >= 0); + + /* transition_type == 0 => elem is no subelement. + * transition_type != 0 => elem is subelement + */ + return (phex_w_sub->transition_type == 0 ? false : true); +} + +int +t8_subelement_scheme_hex_c::t8_element_get_transition_type (const t8_element *elem) +{ + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + return phex_w_sub->transition_type; +} + +int +t8_subelement_scheme_hex_c::t8_element_get_subelement_id (const t8_element *elem) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + + return phex_w_sub->subelement_id; +} + +t8_element_shape_t +t8_subelement_scheme_hex_c::t8_element_shape (const t8_element_t *elem) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + return (t8_element_is_subelement (elem) ? T8_ECLASS_PYRAMID : T8_ECLASS_HEX); +} + +int +t8_subelement_scheme_hex_c::t8_element_num_corners (const t8_element_t *elem) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + return (t8_element_is_subelement (elem) ? T8_HEX_SUBELEMENT_FACES : 8); +} + +int +t8_subelement_scheme_hex_c::t8_element_find_neighbor_in_transition_cell (const t8_element_t *elem, + const t8_element_t *pseudo_neigh, + int elem_face) +{ + /* In this function, we assume pseudo_neigh to be a random subelement of a transition cell that includes + * the real neighbor of elem at face elem_face. This function will output the subelement_id of the real neighbor of elem. */ + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (pseudo_neigh)); + /* we expect neigh to be a element in a transition cell, thus to be a subelement */ + T8_ASSERT (t8_element_is_subelement (pseudo_neigh)); + /* Case 1: the neighbor is ab sibling of elem --> function t8_element_get_sibling_neighbor_in_transition_cell + * Thus, we expect elem_face = 4*/ + // T8_ASSERT (elem_face == 4); + + const t8_hex_with_subelements *phex_w_sub_elem = (const t8_hex_with_subelements *) elem; + const t8_hex_with_subelements *phex_w_sub_pseudo_neigh = (const t8_hex_with_subelements *) pseudo_neigh; + // t8_debugf("\n~~~~~~~~~~~Into find neighbor in transition cell ~~~~~~~~~~~~~~~\n"); + /* In the following, all possible neighbor configurations are defined, such that subelement neighbors can be + * identified in LFN_transitioned. */ + + /* Below are the cases in which the neighbor is no sibling. + * The idea is to fill a location array with the desired properties of the real neighbor. + * Together with the type of the transition cell of pseudo_neigh, we can then identify the sub_id of the right neighbor. */ + /* get the location of elem */ + + //Case 2: The element is a subelement and we are looking for a face neighbor at face 4. + if (t8_element_is_subelement (elem)) { + int location_elem[3] = {}; /* {face, is_split, number of subelement at face} */ + t8_element_get_location_of_subelement (elem, location_elem); + /* In this case, we have the following examplary situation in the 2D quad case: + * + * x - - - - - - - x - - - - - - - x + * | \ / | \ / | + * | \ / | \ / | + * | \ / | \ / | + * x - - - x neigh | elem x | + * | / \ | / | \ | + * | /pseudo \ | / | \ | + * | / neigh \ | / | \ | + * x - - - - - - - x - - - x - - - x + * + * A subelement elem is given as well as a random subelement pseudo_neigh from a neighboring transition cell. + * We are searching for the subelement id of the real neighbor neigh. + * Note that both transition cells can have different levels. */ + + /* Initialize the location array of the real neighbor. */ + int location_neigh[3] = { -1, -1, 0 }; + + //Check if the neighbor has a lower level than the element + if (phex_w_sub_pseudo_neigh->p8q.level < phex_w_sub_elem->p8q.level) { + + location_neigh[0] = subelement_location_to_parent_dual_face[location_elem[0]]; + /* the pseudo_neigh transition cell has a lower level than the elem transition cell, so the second entry + of the location array has to be 1 (= split) */ + location_neigh[1] = 1; /* split */ + /* First, check left/right face of transition cell */ + if ((location_elem[0] == 0) || (location_elem[0] == 1)) { + //If the y-coordinates differ, we can deviate that the neighbor lies in the back. + //Thus, we need to increment the subelement_type by 2^1 + if (phex_w_sub_pseudo_neigh->p8q.y != phex_w_sub_elem->p8q.y) { + location_neigh[2] = location_neigh[2] + (int) pow (2, 1); /* back*/ + } + //Analogously: if the z-coordinates differ, we can deviate that the neighbor lies up. + //Thus, we need to increment the subelement_type by 2^0 + if (phex_w_sub_pseudo_neigh->p8q.z != phex_w_sub_elem->p8q.z) { + location_neigh[2] = location_neigh[2] + (int) pow (2, 0); /* up */ + } + } + /* Second, check front/back face of transition cell */ + if ((location_elem[0] == 2) || (location_elem[0] == 3)) { + if (phex_w_sub_pseudo_neigh->p8q.x != phex_w_sub_elem->p8q.x) { + location_neigh[2] = location_neigh[2] + (int) pow (2, 2); /* right */ + } + if (phex_w_sub_pseudo_neigh->p8q.z != phex_w_sub_elem->p8q.z) { + location_neigh[2] = location_neigh[2] + (int) pow (2, 0); /* up */ + } + } + /*Third, check lower/up face of transition cell */ + if ((location_elem[0] == 4) || (location_elem[0] == 5)) { + if (phex_w_sub_pseudo_neigh->p8q.x != phex_w_sub_elem->p8q.x) { + location_neigh[2] = location_neigh[2] + (int) pow (2, 2); /* right */ + } + if (phex_w_sub_pseudo_neigh->p8q.y != phex_w_sub_elem->p8q.y) { + location_neigh[2] = location_neigh[2] + (int) pow (2, 1); /* back */ + } + } + } + /* the pseudo_neigh transition cell has not a lower level than the elem transition cell, so it's face is not split */ + else { + location_neigh[0] = subelement_location_to_parent_dual_face[location_elem[0]]; + location_neigh[1] = 0; /* not split */ + } + + /* check, that a neighbor is found and the location array is adjusted */ + T8_ASSERT (location_neigh[0] >= 0 && location_neigh[1] >= 0 && location_neigh[2] >= 0); + + /* Depending on the location of elem, we have filled location_neigh with the data of the real neighbor. + * This data will be used to determine the sub_id of the neighbor within the transition cell of pseudo_neigh. */ + return t8_element_get_id_from_location (t8_element_get_transition_type (pseudo_neigh), location_neigh); + } + // elem is no subelement. + else { + /* In this case, we have the following examplary situation for the 2D quad case: + * + * x - - - - - - - x - - - - - - - x + * | \ / | | + * | \ / | | + * | \ / | | + * x - - - x neigh | elem | + * | / \ | | + * | /pseudo \ | | + * | / neigh \ | | + * x - - - - - - - x - - - - - - - x + * + * Subelement elem is given as well as a random subelement neigh from a neighboring transition cell. + * We are searching for the subelement id of the real neighbor neigh. + * Note that the transition cell of pseudo_neigh and elem can have different levels. */ + + /* Initialize the location array of the real neighbor. */ + int location_neigh[3] = { 0, 0, 0 }; + + /* the pseudo_neigh transition cell has a lower level than elem */ + if (phex_w_sub_pseudo_neigh->p8q.level + < phex_w_sub_elem->p8q.level) { //actually same case as case 2 just without location array of elem + // location_neigh[0] = subelement_location_to_parent_dual_face[location_elem[0]]; + location_neigh[0] = subelement_location_to_parent_dual_face[elem_face]; + + location_neigh[1] = 1; /* split */ + if ((elem_face == 0) || (elem_face == 1)) { /* left/right face of transition cell */ + if (phex_w_sub_pseudo_neigh->p8q.y != phex_w_sub_elem->p8q.y) { + location_neigh[2] = location_neigh[2] + (int) pow (2, 1); /* back*/ + } + if (phex_w_sub_pseudo_neigh->p8q.z != phex_w_sub_elem->p8q.z) { + location_neigh[2] = location_neigh[2] + (int) pow (2, 0); /* up */ + } + } + if ((elem_face == 2) || (elem_face == 3)) { /* front/back face of transition cell */ + if (phex_w_sub_pseudo_neigh->p8q.x != phex_w_sub_elem->p8q.x) { + + location_neigh[2] = location_neigh[2] + (int) pow (2, 2); /* right */ + } + if (phex_w_sub_pseudo_neigh->p8q.z != phex_w_sub_elem->p8q.z) { + location_neigh[2] = location_neigh[2] + (int) pow (2, 0); /* up */ + } + } + if ((elem_face == 4) || (elem_face == 5)) { /* lower/up face of transition cell */ + if (phex_w_sub_pseudo_neigh->p8q.x != phex_w_sub_elem->p8q.x) { + location_neigh[2] = location_neigh[2] + (int) pow (2, 2); /* right */ + } + if (phex_w_sub_pseudo_neigh->p8q.y != phex_w_sub_elem->p8q.y) { + location_neigh[2] = location_neigh[2] + (int) pow (2, 1); /* back */ + } + } + } + /* the pseudo_neigh transition cell has the same level as elem + * Note that the level of the transition cell can not be higher as the level of elem in this case, + * since elem would then be a subelement in a transition cell. */ + if (phex_w_sub_pseudo_neigh->p8q.level == phex_w_sub_elem->p8q.level) { + location_neigh[1] = 0; /* not split */ + location_neigh[2] = 0; /* default value */ + location_neigh[0] = subelement_location_to_parent_dual_face[elem_face]; + } + + /* check, that a neighbor is found and the location array is adjusted */ + T8_ASSERT (location_neigh[0] >= 0 && location_neigh[1] >= 0 && location_neigh[2] >= 0); + /* Depending on the location of elem, we have filled location_neigh with the data of the real neighbor. + * This data will be used to determine the sub_id of the neighbor within the transition cell of pseudo_neigh. */ + return t8_element_get_id_from_location (t8_element_get_transition_type (pseudo_neigh), location_neigh); + } + + return -1; /* return negative if no neighbor element could be found */ +} + +int +t8_subelement_scheme_hex_c::t8_element_get_id_from_location (int type, int location[]) +{ + T8_ASSERT (type >= 0 && type <= T8_SUB_HEX_MAX_TRANSITION_TYPE); + + int sub_id, subelements_count = 0; + double type_temp = double (type); // would work for ints but we use libc pow(double, double) + int binary_type[P8EST_FACES] = {}; + + /* get the type as a binary array */ + int iface; + for (iface = 0; iface < P8EST_FACES; iface++) { + if (type_temp >= pow (2.0, 6 - (iface + 1))) { + binary_type[iface] = 1; + type_temp -= pow (2.0, 6 - (iface + 1)); + } + else { + binary_type[iface] = 0; + } + } + + /* count the number of elements up to the given location */ + int element_count; + for (element_count = 0; element_count <= location[0]; element_count++) { + if (element_count == location[0]) { + if (location[1] == 0) { + subelements_count += 1; + } + else { + subelements_count += 4; + if (location[0] == 0 || location[0] == 1) { + + if ((location[2] & 2) == 0) { // front + subelements_count -= 1; + } + if ((location[2] & 1) == 0) { //bottom + subelements_count -= 2; + } + } + if (location[0] == 2 || location[0] == 3) { + + if ((location[2] & 4) == 0) { // left + subelements_count -= 1; + } + if ((location[2] & 1) == 0) { //bottom + subelements_count -= 2; + } + } + if (location[0] == 4 || location[0] == 5) { + if ((location[2] & 4) == 0) { // left + subelements_count -= 1; + } + if ((location[2] & 2) == 0) { //front + subelements_count -= 2; + } + } + } + } + + else { + if (binary_type[element_count] == 1) { + subelements_count += 4; + } + else { + subelements_count += 1; + } + } + } + /* get the sub_id */ + sub_id = subelements_count - 1; + + return sub_id; +} + +void +t8_subelement_scheme_hex_c::t8_element_new (int length, t8_element_t **elem) const +{ + /* allocate memory */ + t8_default_scheme_common_c::t8_element_new (length, elem); + + for (int i = 0; i < length; i++) { + t8_hex_with_subelements *phex_w_sub = (t8_hex_with_subelements *) elem[i]; + t8_element_init (1, elem[i]); + T8_QUAD_SET_TDIM ((p8est_quadrant_t *) &phex_w_sub->p8q, 3); + } +} + +void +t8_subelement_scheme_hex_c::t8_element_init (int length, t8_element_t *elem) const +{ + t8_hex_with_subelements *phex_w_sub = (t8_hex_with_subelements *) elem; + + for (int i = 0; i < length; i++) { + /* initialize subelement parameters */ + phex_w_sub[i].transition_type = 0; + phex_w_sub[i].subelement_id = 0; +#ifdef T8_ENABLE_DEBUG + /* In debugging mode we iterate over all length many elements and + * set their hex to the level 0 hex with ID 0. */ + p8est_quadrant_t *hex = &phex_w_sub[i].p8q; + p8est_quadrant_set_morton (hex, 0, 0); + T8_QUAD_SET_TDIM (hex, 3); + T8_ASSERT (p8est_quadrant_is_extended (hex)); + +#endif + } +} + +int +t8_subelement_scheme_hex_c::t8_element_scheme_supports_transitioning (void) +{ + return T8_HEX_TRANSITION_IS_IMPLEMENTED; +} + +int +t8_subelement_scheme_hex_c::t8_element_equal (const t8_element_t *elem1, const t8_element_t *elem2) const +{ + + return (p8est_quadrant_is_equal ((const p8est_quadrant_t *) elem1, (const p8est_quadrant_t *) elem2)) + && (t8_element_get_subelement_id ((const t8_element *) elem1) + == t8_element_get_subelement_id ((const t8_element *) elem2)); +} + +void +t8_subelement_scheme_hex_c::t8_element_root (t8_element_t *elem) const +{ + p8est_quadrant_t *hex = (p8est_quadrant_t *) elem; + p8est_quadrant_set_morton (hex, 0, 0); + T8_ASSERT (p8est_quadrant_is_extended (hex)); +} +int +t8_subelement_scheme_hex_c::t8_element_transition_scheme_is_conformal (void) +{ + return T8_HEX_TRANSITION_SCHEME_IS_CONFORMAL; +} + +#ifdef T8_ENABLE_DEBUG + +/* *INDENT-OFF* */ +/* indent bug, indent adds a second "const" modifier */ +int +t8_subelement_scheme_hex_c::t8_element_is_valid (const t8_element_t *elem) const +/* *INDENT-ON* */ + +{ + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + const p8est_quadrant_t *q = &phex_w_sub->p8q; + + /* the p8est quadrant AND the subelement values must be valid such that the whole element is valid */ + return (p8est_quadrant_is_extended (q) && t8_element_subelement_values_are_valid (elem)); +} + +/* *INDENT-OFF* */ +/* indent bug, indent adds a second "const" modifier */ +int +t8_subelement_scheme_hex_c::t8_element_subelement_values_are_valid (const t8_element_t *elem) const +/* *INDENT-ON* */ + +{ + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + // t8_debugf("transition type %i, sub id %i \n", phex_w_sub->transition_type, phex_w_sub->subelement_id); + return ((phex_w_sub->transition_type >= 0 && phex_w_sub->transition_type <= T8_SUB_HEX_MAX_TRANSITION_TYPE) + && (phex_w_sub->subelement_id >= 0 && phex_w_sub->subelement_id <= T8_SUB_HEX_MAX_SUBELEMENT_ID)); +} + +void +t8_subelement_scheme_hex_c::t8_element_to_string (const t8_element_t *elem, char *debug_string, + const int string_size) const +{ + T8_ASSERT (!t8_element_is_subelement (elem)); + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (debug_string != NULL); + t8_hex_with_subelements *hex_with_sub = (t8_hex_with_subelements *) elem; + const p8est_quadrant_t *hex = &hex_with_sub->p8q; + + snprintf (debug_string, string_size, "x: %i, y: %i, z: %i, level: %i", hex->x, hex->y, hex->z, hex->level); +} + +void +t8_subelement_scheme_hex_c::t8_element_debug_print (const t8_element_t *elem) const +{ + const t8_hex_with_subelements *phex_w_sub = (const t8_hex_with_subelements *) elem; + + t8_productionf ("\n|------------ t8_element_debug_print: ------------|" + "\n| Transition Type: %i" + "\n| Subelement ID: %i" + "\n| Anchor (Morton): (%i,%i,%i)" + "\n| Anchor (ref coords): (%lf,%lf,%lf)" + "\n| Level: %i" + "\n|-------------------------------------------------|\n", + phex_w_sub->transition_type, phex_w_sub->subelement_id, phex_w_sub->p8q.x, phex_w_sub->p8q.y, + phex_w_sub->p8q.z, (double) phex_w_sub->p8q.x / (double) P8EST_ROOT_LEN, + (double) phex_w_sub->p8q.y / (double) P8EST_ROOT_LEN, + (double) phex_w_sub->p8q.z / (double) P8EST_ROOT_LEN, phex_w_sub->p8q.level); + + /* if the element is not valid, abort, but after printing */ + T8_ASSERT (t8_element_is_valid (elem)); +} + +#endif + +/* each hex is packed as x,y,z coordinates, the subelement ID, transition type and the level */ +void +t8_subelement_scheme_hex_c::t8_element_MPI_Pack (t8_element_t **const elements, const unsigned int count, + void *send_buffer, const int buffer_size, int *position, + sc_MPI_Comm comm) const +{ + int mpiret; + p8est_quadrant_t **quads = (p8est_quadrant_t **) elements; + t8_hex_with_subelements **quads_with_sub = (t8_hex_with_subelements **) elements; + for (unsigned int ielem = 0; ielem < count; ielem++) { + mpiret = sc_MPI_Pack (&(quads[ielem]->x), 1, sc_MPI_INT, send_buffer, buffer_size, position, comm); + SC_CHECK_MPI (mpiret); + mpiret = sc_MPI_Pack (&(quads[ielem]->y), 1, sc_MPI_INT, send_buffer, buffer_size, position, comm); + SC_CHECK_MPI (mpiret); + mpiret = sc_MPI_Pack (&(quads[ielem]->z), 1, sc_MPI_INT, send_buffer, buffer_size, position, comm); + SC_CHECK_MPI (mpiret); + mpiret + = sc_MPI_Pack (&quads_with_sub[ielem]->subelement_id, 1, sc_MPI_INT, send_buffer, buffer_size, position, comm); + SC_CHECK_MPI (mpiret); + mpiret + = sc_MPI_Pack (&quads_with_sub[ielem]->transition_type, 1, sc_MPI_INT, send_buffer, buffer_size, position, comm); + SC_CHECK_MPI (mpiret); + mpiret = sc_MPI_Pack (&(quads[ielem]->level), 1, sc_MPI_INT8_T, send_buffer, buffer_size, position, comm); + SC_CHECK_MPI (mpiret); + } +} + +/* each hex is packed as x,y,z coordinates, the subelement ID, transition type and the level */ +void +t8_subelement_scheme_hex_c::t8_element_MPI_Pack_size (const unsigned int count, sc_MPI_Comm comm, int *pack_size) const +{ + int singlesize = 0; + int datasize = 0; + int mpiret; + + /* x,y,z, subelement ID and transition type */ + mpiret = sc_MPI_Pack_size (1, sc_MPI_INT, comm, &datasize); + SC_CHECK_MPI (mpiret); + singlesize += 5 * datasize; + + /* level */ + mpiret = sc_MPI_Pack_size (1, sc_MPI_INT8_T, comm, &datasize); + SC_CHECK_MPI (mpiret); + singlesize += datasize; + + *pack_size = count * singlesize; +} + +/* each hex is packed as x,y,z coordinates and the level */ +void +t8_subelement_scheme_hex_c::t8_element_MPI_Unpack (void *recvbuf, const int buffer_size, int *position, + t8_element_t **elements, const unsigned int count, + sc_MPI_Comm comm) const +{ + int mpiret; + p8est_quadrant_t **quads = (p8est_quadrant_t **) elements; + t8_hex_with_subelements **quads_with_sub = (t8_hex_with_subelements **) elements; + + for (unsigned int ielem = 0; ielem < count; ielem++) { + mpiret = sc_MPI_Unpack (recvbuf, buffer_size, position, &(quads[ielem]->x), 1, sc_MPI_INT, comm); + SC_CHECK_MPI (mpiret); + mpiret = sc_MPI_Unpack (recvbuf, buffer_size, position, &(quads[ielem]->y), 1, sc_MPI_INT, comm); + SC_CHECK_MPI (mpiret); + mpiret = sc_MPI_Unpack (recvbuf, buffer_size, position, &(quads[ielem]->z), 1, sc_MPI_INT, comm); + SC_CHECK_MPI (mpiret); + mpiret + = sc_MPI_Unpack (recvbuf, buffer_size, position, &(quads_with_sub[ielem]->subelement_id), 1, sc_MPI_INT, comm); + SC_CHECK_MPI (mpiret); + mpiret + = sc_MPI_Unpack (recvbuf, buffer_size, position, &(quads_with_sub[ielem]->transition_type), 1, sc_MPI_INT, comm); + SC_CHECK_MPI (mpiret); + mpiret = sc_MPI_Unpack (recvbuf, buffer_size, position, &(quads[ielem]->level), 1, sc_MPI_INT8_T, comm); + SC_CHECK_MPI (mpiret); + } +} + +/* Constructor */ +t8_subelement_scheme_hex_c::t8_subelement_scheme_hex_c (void) +{ + eclass = T8_ECLASS_HEX; + element_size = sizeof (t8_phex_sub_t); + ts_context = sc_mempool_new (element_size); +} + +t8_subelement_scheme_hex_c::~t8_subelement_scheme_hex_c () +{ + /* This destructor is empty since the destructor of the + * default_common scheme is called automatically and it + * suffices to destroy the hex_scheme. + * However we need to provide an implementation of the destructor + * and hence this empty function. */ +} + +T8_EXTERN_C_END (); diff --git a/src/t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex_cxx.hxx b/src/t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex_cxx.hxx new file mode 100644 index 0000000000..952d684cbe --- /dev/null +++ b/src/t8_schemes/t8_transition/t8_transition_conformal_hex/t8_transition_conformal_hex_cxx.hxx @@ -0,0 +1,647 @@ +/* + 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. +*/ + +/** \file t8_transition_conformal_HEX_cxx.hxx + * We use a p4est_HEXrant_t object as storage for the T8 HEXrant. + * Additionally, we store information for transition cells of triangular subelements: + * + * (i) transition_type - type of the transition cell of the current element + * (ii) subelement_id - subelement id of the current element + * + * In order to refine a HEX element into a transition cell, it is important to know these additional parameter. + */ + +#ifndef T8_TRANSITION_CONFORMAL_HEX_CXX_HXX +#define T8_TRANSITION_CONFORMAL_HEX_CXX_HXX + +#include +#include + +#include +#include +#include + +#include + +/** The structure holding a HEXrilateral element in the default scheme. + * We make this definition public for interoperability of element classes. + * We might want to put this into a private, scheme-specific header file. + */ + +/* Define the struct, that stores all information needed for the HEX scheme and subelements. + * + * p4est HEXrant recursive HEX refinement, using a transition + * refinement cell with subelements + * x - - - - - x x - - x - - x x - - - - - x + * | | | | | | \ 2 / | + * | | | | | | 1 \ / | + * | | --> x - - x - - x or x - - x 3 | + * | | | | | | 0 / | \ | + * | | | | | | / 5 | 4 \ | + * x - - - - - x x - - x - - x x - - x - - x + * + * A p4est HEXrant can be refined, using either the standard HEX scheme, or a transition cell, consisting of different subelements. + * The HEX refinement scheme is recursive, whereas a transition cell can only be used once, for example to remove hanging nodes, after the mesh has been adapted and balanced. + * There are different types of transition cells possible, which we will refer to as transition_type. + * Each transition cell consists of different subelements. The given example consists of 6 different subelements, whose ids range from 0 to 5. + * A dummy variable will store the information, whether a given element is a subelement or a standard HEX element. */ + +typedef struct +{ + /* p8est quadrant */ + p8est_quadrant_t p8q; + /* stores transition cell information (default for non-subelements is 0 and for subelements it is != 0 - is therefore used as a is_subelement check) */ + int transition_type; + /* stores subelement information (default for non-subelements is 0) */ + int subelement_id; + +} t8_hex_with_subelements; + +typedef t8_hex_with_subelements t8_phex_sub_t; + +/** define some subelement related constants */ +#define T8_SUB_HEX_MAX_TRANSITION_TYPE 63 + +//A hexahedron has six faces that results in 6*4 = 24 possible subelement id's +#define T8_SUB_HEX_MAX_SUBELEMENT_ID 24 +// A pyramid has 5 faces +#define T8_HEX_SUBELEMENT_FACES 5 + +#define T8_HEX_TRANSITION_IS_IMPLEMENTED 1 +#define T8_HEX_TRANSITION_SCHEME_IS_CONFORMAL 1 + +#if 0 +/** Provide an implementation for the hexahedral element class with subelements. */ +t8_eclass_scheme_t *t8_subelement_scheme_new_hex (void); +#endif + +struct t8_subelement_scheme_hex_c: public t8_default_scheme_common_c +{ + public: + /** The virtual table for a particular implementation of an element class. */ + + /** Constructor. */ + t8_subelement_scheme_hex_c (); + + ~t8_subelement_scheme_hex_c (); + + /** Allocate memory for an array of subelements and initialize them. + * \param [in] length The number of hex to be allocated. + * \param [in,out] elems On input an array of \b length many unallocated + * element pointers. + * On output all these pointers will point to an allocated + * and initialized element. + * \note Not every element that is created in t8code will be created by a call + * to this function. However, if an element is not created using \ref t8_element_new, + * then it is guaranteed that \ref t8_element_init is called on it. + * \note In debugging mode, an element that was created with \ref t8_element_new + * must pass \ref t8_element_is_valid. + * \note If an element was created by \ref t8_element_new then \ref t8_element_init + * may not be called for it. Thus, \ref t8_element_new should initialize an element + * in the same way as a call to \ref t8_element_init would. + * \see t8_element_init + * \see t8_element_is_valid + */ + virtual void + t8_element_new (int length, t8_element_t **elem) const; + + /** Return the maximum allowed level for this element class. + * \return The maximum allowed level for elements of this class (in this case HEX). + */ + virtual int + t8_element_maxlevel (void) const; + + /** Return the type of each child in the ordering of the implementation. */ + virtual t8_eclass_t + t8_element_child_eclass (int childid) const; + + /** Return the refinement level of an element. */ + virtual int + t8_element_level (const t8_element_t *elem) const; + + /** Copy one element to another */ + virtual void + t8_element_copy (const t8_element_t *source, t8_element_t *dest) const; + + /** Compare to elements. returns negative if elem1 < elem2, zero if elem1 equals elem2 + * and positive if elem1 > elem2. + * If elem2 is a copy of elem1 then the elements are equal. + * If both elements are sibling subelements, return 0 if they are identical (same sub_id) and 1 otherwise. + */ + virtual int + t8_element_compare (const t8_element_t *elem1, const t8_element_t *elem2) const; + + /** Construct the parent of a given element. */ + virtual void + t8_element_parent (const t8_element_t *elem, t8_element_t *parent) const; + + /** Construct a same-size sibling of a given element. */ + virtual void + t8_element_sibling (const t8_element_t *elem, int sibid, t8_element_t *sibling) const; + + /** Compute the number of face of a given element. */ + virtual int + t8_element_num_faces (const t8_element_t *elem) const; + + /** Compute the maximum number of faces of a given element and all of its + * descendants. + * \param [in] elem The element. + * \return The maximum number of faces of \a elem and its descendants. + */ + virtual int + t8_element_max_num_faces (const t8_element_t *elem) const; + + /** Return the number of children of an element when it is refined. */ + virtual int + t8_element_num_children (const t8_element_t *elem) const; + + /** Return the number of siblings of an element (or the number of elements in the family of elem) */ + virtual int + t8_element_num_siblings (const t8_element_t *elem) const; + + /** Return the number of children of an element's face when the element is refined. */ + virtual int + t8_element_num_face_children (const t8_element_t *elem, const int face) const; + + /** + * + * \param [in] elem The element. + * \param [in] face A face of elem + * \return True if the neighbor of elems' face is a sibling + * + */ + virtual int + t8_element_neighbor_is_sibling (const t8_element_t *elem, int face) const; + + /** + * + * \param [in] elem The element. + * \param [in] face A face of elem + * \return Number of sibling neighbors at face + * + */ + virtual int + t8_element_get_num_sibling_neighbors_at_face (const t8_element_t *elem, int face) const; + + /** \return zero refine value for schemes that do not have a transition implementation. */ + virtual int + t8_element_get_transition_refine_identifier (void) const; + + /** Return the corner number of an element's face corner. + * \param [in] element The element. + * \param [in] face A face index for \a element. + * \param [in] corner A corner index for the face 0 <= \a corner < num_face_corners. + * \return The corner number of the \a corner-th vertex of \a face. + */ + virtual int + t8_element_get_face_corner (const t8_element_t *element, int face, int corner) const; + + /** Return the face numbers of the faces sharing an element's corner. + * \param [in] element The element. + * \param [in] corner A corner index for the face. + * \param [in] face A face index for \a corner. + * \return The face number of the \a face-th face at \a corner. + */ + virtual int + t8_element_get_corner_face (const t8_element_t *element, int corner, int face) const; + + /** Construct the child element of a given number. + * \param [in] elem This must be a valid element, bigger than maxlevel. + * \param [in] childid The number of the child to construct. + * \param [in,out] child The storage for this element must exist + * and match the element class of the child. + * On output, a valid element. + * It is valid to call this function with elem = child. + */ + virtual void + t8_element_child (const t8_element_t *elem, int childid, t8_element_t *child) const; + + /** Construct all sibling neighbors of elem at face - it is required that sibling neighbors of elem at face exist */ + virtual void + t8_element_get_sibling_neighbor_in_transition_cell_hex (const t8_element_t *elem, const int face, + const int num_neighbors, t8_element_t *neighbor_at_face[], + int *neigh_face); + + /** Construct all children of a given element. */ + virtual void + t8_element_children (const t8_element_t *elem, int length, t8_element_t *c[]) const; + + /** Return the child id of an element */ + virtual int + t8_element_child_id (const t8_element_t *elem) const; + + /** Compute the ancestor id of an element */ + virtual int + t8_element_ancestor_id (const t8_element_t *elem, int level) const; + + /** Return nonzero if collection of elements is a family */ + virtual int + t8_element_is_family (t8_element_t *const *fam) const; + + /** Construct the nearest common ancestor of two elements in the same tree. */ + virtual void + t8_element_nca (const t8_element_t *elem1, const t8_element_t *elem2, t8_element_t *nca) const; + + /** Compute the element shape of the face of an element. */ + virtual t8_element_shape_t + t8_element_face_shape (const t8_element_t *elem, int face) const; + + /** Given an element and a face of the element, compute all children of + * the element that touch the face. */ + /** Given an element and a face of the element, compute all children of + * the element that touch the face. */ + virtual void + t8_element_children_at_face (const t8_element_t *elem, int face, t8_element_t *children[], int num_children, + int *child_indices) const; + + /** Given a face of an element and a child number of a child of that face, return the face number + * of the child of the element that matches the child face. */ + virtual int + t8_element_face_child_face (const t8_element_t *elem, int face, int face_child) const; + + /** Given a face of an element return the face number + * of the parent of the element that matches the element's face. Or return -1 if + * no face of the parent matches the face. */ + virtual int + t8_element_face_parent_face (const t8_element_t *elem, int face) const; + + /** Transform the coordinates of a hexaliteral considered as boundary element + * in a tree-tree connection. */ + virtual void + t8_element_transform_face (const t8_element_t *elem1, t8_element_t *elem2, int orientation, int sign, + int is_smaller_face) const; + + /** Given a boundary face inside a root tree's face construct + * the element inside the root tree that has the given face as a + * face. */ + virtual int + t8_element_extrude_face (const t8_element_t *face, const t8_eclass_scheme_c *face_scheme, t8_element_t *elem, + int root_face) const; + + /** Return the tree face id given a boundary face. */ + virtual int + t8_element_tree_face (const t8_element_t *elem, int face) const; + + /** Construct the first descendant of an element that touches a given face. */ + virtual void + t8_element_first_descendant_face (const t8_element_t *elem, int face, t8_element_t *first_desc, int level) const; + + /** Construct the last descendant of an element that touches a given face. */ + virtual void + t8_element_last_descendant_face (const t8_element_t *elem, int face, t8_element_t *last_desc, int level) const; + + /** Return 1 if the eclass scheme has an implementation for subelements. Return 0 otherwise. */ + virtual int + t8_element_transition_scheme_is_conformal (void); + + /** Construct the boundary element at a specific face. */ + virtual void + t8_element_boundary_face (const t8_element_t *elem, int face, t8_element_t *boundary, + const t8_eclass_scheme_c *boundary_scheme) const; + + /** Construct all codimension-one boundary elements of a given element. */ + virtual void + t8_element_boundary (const t8_element_t *elem, int min_dim, int length, t8_element_t **boundary) const; + + /** Compute whether a given element shares a given face with its root tree. + * \param [in] elem The input element. + * \param [in] face A face of \a elem. + * \return True if \a face is a subface of the element's root element. + */ + virtual int + t8_element_is_root_boundary (const t8_element_t *elem, int face) const; + + /** Construct the face neighbor of a given element if this face neighbor + * is inside the root tree. Return 0 otherwise. */ + virtual int + t8_element_face_neighbor_inside (const t8_element_t *elem, t8_element_t *neigh, int face, int *neigh_face) const; + + /** Initialize an element according to a given linear id */ + virtual void + t8_element_set_linear_id (t8_element_t *elem, int level, t8_linearidx_t id) const; + + /** Calculate the linear id of an element */ + virtual t8_linearidx_t + t8_element_get_linear_id (const t8_element_t *elem, int level) const; + + /** Calculate the first descendant of a given element e. That is, the + * first element in a uniform refinement of e of the maximal possible level. + */ + virtual void + t8_element_first_descendant (const t8_element_t *elem, t8_element_t *desc, int level) const; + + /** Calculate the last descendant of a given element e. That is, the + * last element in a uniform refinement of e of the maximal possible level. + */ + virtual void + t8_element_last_descendant (const t8_element_t *elem, t8_element_t *desc, int level) const; + + /** Compute s as a successor of t*/ + virtual void + t8_element_successor (const t8_element_t *t, t8_element_t *s) const; + + /** Get the integer coordinates of the anchor node of an element */ + virtual void + t8_element_anchor (const t8_element_t *elem, int anchor[3]) const; + + /** Get the integer root length of an element, that is the length of + * the level 0 ancestor. + */ + virtual int + t8_element_root_len (const t8_element_t *elem) const; + + /** Compute the integer coordinates of a given element vertex. */ + virtual void + t8_element_vertex_coords (const t8_element_t *t, int vertex, int coords[]) const; + + /** Compute the integer coordinates of a given element vertex. + * The default scheme implements the Morton type SFCs. In these SFCs the + * elements are positioned in a cube [0,1]^(dL) with dimension d (=0,1,2,3) and + * L the maximum refinement level. + * All element vertices have integer coordinates in this cube. + * \param [in] elem The element. + * \param [in] vertex The id of the vertex whose coordinates shall be computed. + * \param [out] coords An array of at least as many integers as the element's dimension + * whose entries will be filled with the coordinates of \a vertex. + */ + virtual void + t8_element_vertex_integer_coords (const t8_element_t *elem, int vertex, int coords[]) const; + + /** Convert a point in the reference space of an element to a point in the + * reference space of the tree. + * + * \param [in] elem The element. + * \param [in] coords_input The coordinates of the point in the reference space of the element. + * \param [in] user_data User data. + * \param [out] out_coords The coordinates of the point in the reference space of the tree. + */ + virtual void + t8_element_reference_coords (const t8_element_t *elem, const double *ref_coords, const size_t num_coords, + double *out_coords) const; + + /** Construct a transition cell of type type + * + * \param [in] elem The element. + * \param [in] type The transition type + * \param [out] out_coords The coordinates of the point in the reference space of the tree. + */ + virtual void + t8_element_to_transition_cell (const t8_element_t *elem, int type, t8_element_t *c[]); + + /** Determine the number of sibling subelements, of a transition cell of a specific type + * + * \param [in] type The transition type. + * \return The number of sibling subelements, of a transition cell with the given transition type. + */ + virtual int + t8_element_get_number_of_subelements (int transition_type) const; + + /** Test whether a given element is a subelement or not + * + * \param [in] elem The element. + * \return true if elem is a subelement, otherwise false. + */ + virtual int + t8_element_is_subelement (const t8_element *elem) const; + + /** Get the subelement type of an element + * + * \param [in] elem The element. + * \return Transition type of elem. + */ + virtual int + t8_element_get_transition_type (const t8_element *elem); + + /** Get the subelement ID of an element + * + * \param [in] elem The element. + * \return Subelement ID of \a elem. + */ + virtual int + t8_element_get_subelement_id (const t8_element *elem) const; + + /** + */ + /** Get the subelement ID of the neighbor subelement of elem at face elem_face + * that is a sibling of the subelement neigh (That means: lives in the same transition cell) + * + * \param [in] elem The element. + * \param [in] neigh The neighbor. + * \param [in] elem_face The face number of elem + * \return Subelement ID of \a neigh which is a neighbor of \a elem at face \a elem_face. + */ + virtual int + t8_element_find_neighbor_in_transition_cell (const t8_element_t *elem, const t8_element_t *neigh, int elem_face); + + /** Check if the eclass scheme has an implementation for subelements. + * \return True if the scheme has an implementation for subelements, otherwise return false. + */ + virtual int + t8_element_scheme_supports_transitioning (void); + + /** Checks if there is one element in the tree, that does not refine into 2^dim children. + * + * \return True, if there is one element in the tree that does not refine into 2^dim children, return false otherwise. + */ + virtual int + t8_element_refines_irregular (void) const; + + /** Get the shape of a given element. Subelements of the hexahedral transition scheme are pyramids. + * + * \param [in] elem The element. + * \return Shape of \a elem. + */ + virtual t8_element_shape_t + t8_element_shape (const t8_element_t *elem) const; + + /** Return the number of vertices of an element. A subelement in the hexahedral transition scheme has five vertices. + * + * \param [in] elem The element. + * \return Number of vertices of \a elem. + */ + virtual int + t8_element_num_corners (const t8_element_t *elem) const; + + /** Compute the coordinates of a given element vertex inside a reference tree + * that is embedded into [0,1]^d (d = dimension). + * \param [in] t The element to be considered. + * \param [in] vertex The id of the vertex whose coordinates shall be computed. + * \param [out] coords An array of at least as many doubles as the element's dimension + * whose entries will be filled with the coordinates of \a vertex. + */ + virtual void + t8_element_vertex_reference_coords (const t8_element_t *t, int vertex, double coords[]) const; + +#ifdef T8_ENABLE_DEBUG + /** TODO: this should be the new element_print_element function */ + virtual void + t8_element_debug_print (const t8_element_t *elem) const; + + /** Query whether an element is valid */ + virtual int + t8_element_is_valid (const t8_element_t *t) const; +#endif + + protected: + /** This function determines the vertex coordinates of subelements. + * \param [in] elem A valid subelement + * \param [in] vertex the number of the vertex whose coordinates should be determined + * \param [out] coords An array whose entries will be filled with the coordinates of the + * subelement. + * Note that subelements can have another number of vertices compared to the used + * eclass scheme. For example, subelements that remove hanging nodes from the HEX scheme + * are triangles with 3 instead of 4 vertices. + */ + void + t8_element_vertex_coords_of_subelement (const t8_element_t *t, int vertex, int coords[]) const; + + /** This function will determine the location of a specific subelement in the parent element. + * Since different subelement types are possible, it is a priori not known where for example the + * subelement with id 3 is located. + * \param [in] elem A valid subelement + * \param [out] location An array, whose entries are face_number, split and sub_id_type + * face_number: the face number the given subelement is adjacent to (value between 0 and 5) + * It can be translated to the HEX enumeration via subelement_location_to_parent_dual_face[location[0]] + * split: whether there is a hanging node at the face, the subelement is adjacent to + * (value 0 if there is not hanging node and 1 if there is one) + * sub_id_type: if there is a hanging node at the face, it is important to know where exactly the subelement is located. + * The information in the location can be used to automatically determine the vertices of any subelement. + * Since this function is only used to determine the vertices of subelements, it can be declared as a private/protected function. + */ + void + t8_element_get_location_of_subelement (const t8_element_t *elem, int location[]) const; + + /** This help function returns the subelement ID of an element whose location and transition type is known. + * \param [in] type Transition type + * \param [in] location The location array of a subelement. + * \param [out] subelement_ID The subelement ID of the subelement with transition type \a type and location array \a location[] + */ + int + t8_element_get_id_from_location (int type, int location[]); + + /** This function copies the subelement values from source to dest. + * \param [in] source A valid element + * \param [in,out] dest A valid element, whose subelement values are equal to those of source + */ + void + t8_element_copy_subelement_values (const t8_element_t *source, t8_element_t *dest) const; + + /** This function resets the subelement values of an element to the default value 0. + * \param [in,out] elem A valid element, whose subelement values have been reset. + */ + void + t8_element_reset_subelement_values (t8_element_t *elem) const; + + /** Check if two elements are equal. + * \param [in] ts Implementation of a class scheme. + * \param [in] elem1 The first element. + * \param [in] elem2 The second element. + * \return 1 (true) if the elements are equal, 0 (false) if they are not equal + */ + virtual int + t8_element_equal (const t8_element_t *elem1, const t8_element_t *elem2) const; + + /** Fills an element with the root element. + * \param [in,out] elem The element to be filled with root. + */ + void + t8_element_root (t8_element_t *elem) const; + + /** Initialize an array of allocated hexahedra/subelements. + * \param [in] length The number of hex to be initialized. + * \param [in,out] elems On input an array of \b length many allocated + * elements. + * \param [in] called_new True if the elements in \a elem were created by a call + * to \ref t8_element_new. False if no element in \a elem + * was created in this way. The case that only some elements + * were created by \ref t8_element_new should never occur. + * \note In debugging mode, an element that was passed to \ref t8_element_init + * must pass \ref t8_element_is_valid. + * \note If an element was created by \ref t8_element_new then \ref t8_element_init + * may not be called for it. Thus, \ref t8_element_new should initialize an element + * in the same way as a call to \ref t8_element_init would. + * Thus, if \a called_new is true this function should usually do nothing. + * \see t8_element_new + * \see t8_element_is_valid + */ + virtual void + t8_element_init (int length, t8_element_t *elem) const; + + /** Pack multiple elements (also subelements) into contiguous memory, so they can be sent via MPI. + * \param [in] elements Array of elements that are to be packed + * \param [in] count Number of elements to pack + * \param [in,out] send_buffer Buffer in which to pack the elements + * \param [in] buffer_size size of the buffer (in order to check that we don't access out of range) + * \param [in, out] position the position of the first byte that is not already packed + * \param [in] comm MPI Communicator + */ + virtual void + t8_element_MPI_Pack (t8_element_t **const elements, const unsigned int count, void *send_buffer, int buffer_size, + int *position, sc_MPI_Comm comm) const; + + /** Determine an upper bound for the size of the packed message of \b count elements + * \param [in] count Number of elements to pack + * \param [in] comm MPI Communicator + * \param [out] pack_size upper bound on the message size + */ + virtual void + t8_element_MPI_Pack_size (const unsigned int count, sc_MPI_Comm comm, int *pack_size) const; + + /** Unpack multiple elements from contiguous memory that was received via MPI. + * \param [in] recvbuf Buffer from which to unpack the elements + * \param [in] buffer_size size of the buffer (in order to check that we don't access out of range) + * \param [in, out] position the position of the first byte that is not already packed + * \param [in] elements Array of initialised elements that is to be filled from the message + * \param [in] count Number of elements to unpack + * \param [in] comm MPI Communicator + */ + virtual void + t8_element_MPI_Unpack (void *recvbuf, const int buffer_size, int *position, t8_element_t **elements, + const unsigned int count, sc_MPI_Comm comm) const; + +#ifdef T8_ENABLE_DEBUG + /** Query whether an elements subelement values are valid + * \param [in] source A element + * \return true, if the subelement values are valid + */ + int + t8_element_subelement_values_are_valid (const t8_element_t *elem) const; + + /** + * Print a given element. For a example for a triangle print the coordinates + * and the level of the triangle. This function is only available in the + * debugging configuration. + * + * \param [in] elem The element to print + */ + virtual void + t8_element_to_string (const t8_element_t *elem, char *debug_string, const int string_size) const; + +#endif + + /** Default hex scheme which is used to query hex specific computations, + * for example in \ref t8_element_reference_coords + */ + struct t8_default_scheme_hex_c default_hex_scheme; +}; + +#endif /* !T8_TRANSITION_HEX_CXX_HXX */ diff --git a/src/t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad.cxx b/src/t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad.cxx new file mode 100644 index 0000000000..bfd482c488 --- /dev/null +++ b/src/t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad.cxx @@ -0,0 +1,57 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 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 + +/* We want to export the whole implementation to be callable from "C" */ +T8_EXTERN_C_BEGIN (); + +t8_scheme_cxx_t * +t8_scheme_new_transition_quad_cxx (void) +{ + t8_scheme_cxx_t *s; + + s = T8_ALLOC_ZERO (t8_scheme_cxx_t, 1); + t8_refcount_init (&s->rc); + + s->eclass_schemes[T8_ECLASS_VERTEX] = NULL; + s->eclass_schemes[T8_ECLASS_LINE] + = new t8_default_scheme_line_c (); /* Standard T8_ECLASS_LINE is used by the quad scheme */ + s->eclass_schemes[T8_ECLASS_QUAD] = new t8_subelement_scheme_quad_c (); + s->eclass_schemes[T8_ECLASS_HEX] = NULL; + s->eclass_schemes[T8_ECLASS_TRIANGLE] + = new t8_default_scheme_tri_c (); /* can be used for hybrid meshes - not conformal as long as no conformal transition tri class exists */ + s->eclass_schemes[T8_ECLASS_TET] = NULL; + s->eclass_schemes[T8_ECLASS_PRISM] = NULL; + s->eclass_schemes[T8_ECLASS_PYRAMID] = NULL; + + return s; +} + +T8_EXTERN_C_END (); diff --git a/src/t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad_cxx.cxx b/src/t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad_cxx.cxx new file mode 100644 index 0000000000..6264d45d4e --- /dev/null +++ b/src/t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad_cxx.cxx @@ -0,0 +1,2362 @@ +/* + 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. +*/ + +/* Description: + * This is the low-level structure of 2D quadrilateral elements with transition cells of triangular subelements. */ + +#include +#include +#include +#include +#include "t8.h" +#include "t8_transition_conformal_quad_cxx.hxx" + +/* *INDENT-OFF* */ +/* Connectivity of subelement faces: + * f_0 <-> f_2 + * f_1 <-> f_1 (assuming a neighboring transition cell) + * f_2 <-> f_0 */ +const int subelement_face_dual[3] = { 2, 1, 0 }; + +/* Connectivity of a subelements location within a transition cell + * and the parent quads faces: + * location[0] = 0 -> parents face = 1 + * location[0] = 1 -> parents face = 2 + * location[0] = 2 -> parents face = 0 + * location[0] = 3 -> parents face = 3 */ +const int subelement_location_to_parent_dual_face[4] = { 1, 2, 0, 3 }; + +/* Connectivity of a subelements location within a transition cell + * and the parent quads faces: + * location[0] = 0 (clockwise) -> parents face = 0 + * location[0] = 1 (clockwise) -> parents face = 3 + * location[0] = 2 (clockwise) -> parents face = 1 + * location[0] = 3 (clockwise) -> parents face = 2 */ +const int subelement_location_to_parent_face[4] = { 0, 3, 1, 2 }; +/* *INDENT-ON* */ + +/* We want to export the whole implementation to be callable from "C" */ +T8_EXTERN_C_BEGIN (); + +/* This function is used by other element functions and we thus need to + * declare it up here */ +t8_linearidx_t +t8_element_get_linear_id (const t8_element_t *elem, int level); + +int +t8_subelement_scheme_quad_c::t8_element_maxlevel (void) const +{ + return P4EST_QMAXLEVEL; +} + +/* *INDENT-OFF* */ +t8_eclass_t +t8_subelement_scheme_quad_c::t8_element_child_eclass (int childid) const +/* *INDENT-ON* */ + +{ + T8_ASSERT (0 <= childid && childid < P4EST_CHILDREN); + + return T8_ECLASS_QUAD; +} + +int +t8_subelement_scheme_quad_c::t8_element_level (const t8_element_t *elem) const +{ + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + T8_ASSERT (t8_element_is_valid (elem)); + return (int) ((const t8_quad_with_subelements *) pquad_w_sub)->p4q.level; +} + +static void +t8_element_copy_surround (const p4est_quadrant_t *q, p4est_quadrant_t *r) +{ + T8_QUAD_SET_TDIM (r, T8_QUAD_GET_TDIM (q)); + if (T8_QUAD_GET_TDIM (q) == 3) { + T8_QUAD_SET_TNORMAL (r, T8_QUAD_GET_TNORMAL (q)); + T8_QUAD_SET_TCOORD (r, T8_QUAD_GET_TCOORD (q)); + } +} + +void +t8_subelement_scheme_quad_c::t8_element_copy (const t8_element_t *source, t8_element_t *dest) const +{ + const t8_quad_with_subelements *pquad_w_sub_source = (const t8_quad_with_subelements *) source; + t8_quad_with_subelements *pquad_w_sub_dest = (t8_quad_with_subelements *) dest; + + const p4est_quadrant_t *q = &pquad_w_sub_source->p4q; + p4est_quadrant_t *r = &pquad_w_sub_dest->p4q; + + T8_ASSERT (t8_element_is_valid (source)); + T8_ASSERT (t8_element_is_valid (dest)); + if (q == r && pquad_w_sub_source->transition_type == pquad_w_sub_dest->transition_type + && pquad_w_sub_source->subelement_id == pquad_w_sub_dest->subelement_id) { + /* Do nothing if they are already the same quadrant. */ + return; + } + *r = *q; + + t8_element_copy_subelement_values (source, dest); + t8_element_copy_surround (q, r); +} + +int +t8_subelement_scheme_quad_c::t8_element_compare (const t8_element_t *elem1, const t8_element_t *elem2) const +{ + const t8_quad_with_subelements *pquad_w_sub_elem1 = (const t8_quad_with_subelements *) elem1; + const t8_quad_with_subelements *pquad_w_sub_elem2 = (const t8_quad_with_subelements *) elem2; + + const p4est_quadrant_t *q = &pquad_w_sub_elem1->p4q; + const p4est_quadrant_t *r = &pquad_w_sub_elem2->p4q; + + T8_ASSERT (t8_element_is_valid (elem1)); + T8_ASSERT (t8_element_is_valid (elem2)); + + int compare = p4est_quadrant_compare (q, r); + + if (compare == 0 && (t8_element_is_subelement (elem1) || t8_element_is_subelement (elem2))) { + t8_debugf ("Caution, t8_element_compare is used with subelements.\n"); + if (t8_element_is_subelement (elem1) && t8_element_is_subelement (elem2)) { + /* Caution: The compare function is used for two subelements. */ + + if (pquad_w_sub_elem1->transition_type == pquad_w_sub_elem2->transition_type + && pquad_w_sub_elem1->subelement_id == pquad_w_sub_elem2->subelement_id) { + /* both subelements are identical */ + return 0; + } + /* return != 0 to avoid debug abortion in t8_ghost_add_remote */ + return 1; + } + else if (t8_element_is_subelement (elem1)) { + return -1; /* elem1 is subelement and therefore smaller */ + } + else if (t8_element_is_subelement (elem2)) { + return 1; /* elem2 is subelement and therefore smaller */ + } + } + + /* Note that for subelements, their parent quadrant is compared at this point */ + return compare; +} + +void +t8_subelement_scheme_quad_c::t8_element_parent (const t8_element_t *elem, t8_element_t *parent) const +{ + const t8_quad_with_subelements *pquad_w_sub_elem = (const t8_quad_with_subelements *) elem; + t8_quad_with_subelements *pquad_w_sub_parent = (t8_quad_with_subelements *) parent; + + const p4est_quadrant_t *q = &pquad_w_sub_elem->p4q; + p4est_quadrant_t *r = &pquad_w_sub_parent->p4q; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (parent)); + + if (t8_element_is_subelement (elem)) { + pquad_w_sub_parent->p4q = pquad_w_sub_elem->p4q; + } + else { + p4est_quadrant_parent (q, r); + } + + /* the parent of any element will never be a subelement */ + t8_element_reset_subelement_values (parent); + + t8_element_copy_surround (q, r); +} + +void +t8_subelement_scheme_quad_c::t8_element_sibling (const t8_element_t *elem, int sibid, t8_element_t *sibling) const +{ + const t8_quad_with_subelements *pquad_w_sub_elem = (const t8_quad_with_subelements *) elem; + t8_quad_with_subelements *pquad_w_sub_sibling = (t8_quad_with_subelements *) sibling; + + const p4est_quadrant_t *q = &pquad_w_sub_elem->p4q; + p4est_quadrant_t *r = &pquad_w_sub_sibling->p4q; + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (sibling)); + + p4est_quadrant_sibling (q, r, sibid); + t8_element_copy_surround (q, r); +} + +int +t8_subelement_scheme_quad_c::t8_element_num_faces (const t8_element_t *elem) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + return (t8_element_is_subelement (elem) ? T8_QUAD_SUBELEMENT_FACES : P4EST_FACES); +} + +int +t8_subelement_scheme_quad_c::t8_element_max_num_faces (const t8_element_t *elem) const +{ + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + + return P4EST_FACES; +} + +int +t8_subelement_scheme_quad_c::t8_element_num_children (const t8_element_t *elem) const +{ + /* Note that children of subelements equal the children of the parent quadrant. + * Therefore, the number of children of a subelement equals P4EST_CHILDREN */ + T8_ASSERT (t8_element_is_valid (elem)); + return P4EST_CHILDREN; +} + +/* *INDENT-OFF* */ +/* indent bug, indent adds a second "const" modifier */ +int +t8_subelement_scheme_quad_c::t8_element_num_siblings (const t8_element_t *elem) const +/* *INDENT-ON* */ + +{ + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + + if (pquad_w_sub->transition_type == 0) + return P4EST_FACES; + + int num_hanging_faces = 0; + int iface; + for ( + iface = 0; iface < P4EST_FACES; + iface++) { /* Count the number of ones of the binary transition type. This number equals the number of hanging faces. */ + num_hanging_faces += (pquad_w_sub->transition_type & (1 << iface)) >> iface; + } + + return P4EST_CHILDREN + num_hanging_faces; +} + +int +t8_subelement_scheme_quad_c::t8_element_num_face_children (const t8_element_t *elem, int face) const +{ + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + T8_ASSERT (t8_element_is_valid (elem)); + + /* if we use this scheme without set_transition, then we are only balanced and two neighbors are possible */ + return 2; +} + +int +t8_subelement_scheme_quad_c::t8_element_neighbor_is_sibling (const t8_element_t *elem, const int face) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_subelement (elem)); + + if (face == 0 || face == 2) { + return 1; + } + + return 0; +} + +/* *INDENT-OFF* */ +int +t8_subelement_scheme_quad_c::t8_element_get_num_sibling_neighbors_at_face (const t8_element_t *elem, + const int face) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_subelement (elem)); + T8_ASSERT (face == 0 || face == 2); + + return 1; +} + +int +t8_subelement_scheme_quad_c::t8_element_get_transition_refine_identifier () const +{ + return T8_TRANSITION_CONFORMAL_QUAD_REFINE_FUNCTION; +} +/* *INDENT-ON* */ + +int +t8_subelement_scheme_quad_c::t8_element_get_face_corner (const t8_element_t *elem, int face, int corner) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + if (!t8_element_is_subelement (elem)) { + /* + * 2 f_2 3 + * x -->-- x + * | | + * ^ ^ + * f_0 | | f_1 + * x -->-- x + * 0 f_3 1 + */ + + T8_ASSERT (0 <= face && face < P4EST_FACES); + T8_ASSERT (0 <= corner && corner < 4); + + return p4est_face_corners[face][corner]; + } + else { + int t8_face_corners_subelement[3][2] = { { 0, 1 }, { 1, 2 }, { 2, 0 } }; + /* + * + * x - - - - - x 1 + * | \ f0 / | + * | \ 0 / | + * x - - x el | f1 + * | / \ | + * | / f2 \ | + * x - - x - - x 2 + * + * The vertecies of a subelement are enumerated clockwise, starting with the center vertex of the transition cell + */ + + T8_ASSERT (0 <= face && face < T8_QUAD_SUBELEMENT_FACES); + T8_ASSERT (0 <= corner && corner < 3); + + return t8_face_corners_subelement[face][corner]; + } +} + +int +t8_subelement_scheme_quad_c::t8_element_get_corner_face (const t8_element_t *elem, int corner, int face) const +{ + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (0 <= corner && corner < P4EST_CHILDREN); + T8_ASSERT (0 <= face && face < 2); + + return p4est_corner_faces[corner][face]; +} + +void +t8_subelement_scheme_quad_c::t8_element_child (const t8_element_t *elem, int childid, t8_element_t *child) const +{ + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + /* + * + * x - - - - - x x - - x - - x + * | | | | | + * | | | 2 | 3 | + * | elem | => x - - x - - x + * | | | | | + * | | | 0 | 1 | + * x - - - - - x x - - x - - x + * + */ + const t8_quad_with_subelements *pquad_w_sub_elem = (const t8_quad_with_subelements *) elem; + t8_quad_with_subelements *pquad_w_sub_child = (t8_quad_with_subelements *) child; + + const p4est_quadrant_t *q = &pquad_w_sub_elem->p4q; + p4est_quadrant_t *r = &pquad_w_sub_child->p4q; + + const p4est_qcoord_t shift = P4EST_QUADRANT_LEN (q->level + 1); + + /* it should not be possible to construct a child of a subelement */ + // T8_ASSERT (!t8_element_is_subelement (elem)); + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (child)); + T8_ASSERT (p4est_quadrant_is_extended (q)); + T8_ASSERT (q->level < P4EST_QMAXLEVEL); + + T8_ASSERT (childid >= 0 && childid < P4EST_CHILDREN); + + r->x = childid & 0x01 ? (q->x | shift) : q->x; + r->y = childid & 0x02 ? (q->y | shift) : q->y; + r->level = q->level + 1; + + T8_ASSERT (p4est_quadrant_is_parent (q, r)); + + t8_element_reset_subelement_values (child); + + t8_element_copy_surround (q, r); +} + +/* *INDENT-OFF* */ +void +t8_subelement_scheme_quad_c::t8_element_get_sibling_neighbor_in_transition_cell (const t8_element_t *elem, + const int face, + const int num_neighbors, + t8_element_t *neighbor_at_face[], + int *neigh_face[]) +{ + T8_ASSERT (t8_element_is_subelement (elem)); + T8_ASSERT (t8_element_neighbor_is_sibling (elem, face)); + T8_ASSERT (num_neighbors == 1); + T8_ASSERT (t8_element_is_valid (neighbor_at_face[0])); + + /* If face = 0, then the sibling subelement neighbor is the next subelement in counter clockwise enumeration, + * if face = 2, then it is the sibling subelement neighbor in clockwise enumeration. */ + t8_element_copy (elem, neighbor_at_face[0]); + + t8_quad_with_subelements *pquad_w_sub_neighbor_at_face = (t8_quad_with_subelements *) neighbor_at_face[0]; + + int num_siblings = t8_element_num_siblings (elem); + + if (face == 0) { + /* adjust subelement id counter clockwise */ + if (pquad_w_sub_neighbor_at_face->subelement_id == 0) { + pquad_w_sub_neighbor_at_face->subelement_id += num_siblings - 1; + } + else { + pquad_w_sub_neighbor_at_face->subelement_id -= 1; + } + } + else { + /* adjust subelement id clockwise */ + if (pquad_w_sub_neighbor_at_face->subelement_id == num_siblings - 1) { + pquad_w_sub_neighbor_at_face->subelement_id = 0; + } + else { + pquad_w_sub_neighbor_at_face->subelement_id += 1; + } + } + + /* return dual face with resprect to neighboring sibling subelement */ + /* Compute the face number as seen from elem. + * 0 -> 2 2 -> 0 + */ + *neigh_face[0] = subelement_face_dual[face]; +} +/* *INDENT-ON* */ + +void +t8_subelement_scheme_quad_c::t8_element_children (const t8_element_t *elem, int length, t8_element_t *c[]) const +{ + /* if elem is a subelement, then this function will construct the children of its parent p4est quadrant */ + const t8_quad_with_subelements *pquad_w_sub_elem = (const t8_quad_with_subelements *) elem; + t8_quad_with_subelements **pquad_w_sub_children = (t8_quad_with_subelements **) c; + + const p4est_quadrant_t *q = &pquad_w_sub_elem->p4q; + + int ichild; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (length == P4EST_CHILDREN); + +#ifdef T8_ENABLE_DEBUG + { + int i; + for (i = 0; i < P4EST_CHILDREN; i++) { + T8_ASSERT (t8_element_is_valid (c[i])); + } + } +#endif + + /* set coordinates and levels of the children */ + p4est_quadrant_children (q, &pquad_w_sub_children[0]->p4q, &pquad_w_sub_children[1]->p4q, + &pquad_w_sub_children[2]->p4q, &pquad_w_sub_children[3]->p4q); + + for (ichild = 0; ichild < P4EST_CHILDREN; ++ichild) { + t8_element_reset_subelement_values (c[ichild]); + t8_element_copy_surround (q, &pquad_w_sub_children[ichild]->p4q); + } +} + +int +t8_subelement_scheme_quad_c::t8_element_child_id (const t8_element_t *elem) const +{ + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + const p4est_quadrant_t *q = &pquad_w_sub->p4q; + + T8_ASSERT (t8_element_is_valid (elem)); + + return (t8_element_is_subelement (elem) ? pquad_w_sub->subelement_id : p4est_quadrant_child_id (q)); +} + +int +t8_subelement_scheme_quad_c::t8_element_ancestor_id (const t8_element_t *elem, int level) const +{ + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + const p4est_quadrant_t *q = &pquad_w_sub->p4q; + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + + return p4est_quadrant_ancestor_id (q, level); +} + +int +t8_subelement_scheme_quad_c::t8_element_is_family (t8_element_t *const *fam) const +{ + /* Note that this test is very rudimentary, especially when there subelements are in fam */ + t8_quad_with_subelements **pquad_w_sub_family = (t8_quad_with_subelements **) fam; + +#ifdef T8_ENABLE_DEBUG + { + int i; + int num_siblings = t8_element_num_siblings (fam[0]); + for (i = 0; i < num_siblings; i++) { + T8_ASSERT (t8_element_is_valid (fam[i])); + } + } +#endif + + /* Subelements can not be refined into other elements of a higher level. + * So if the first element of fam is a subelement, we assume that the following num_siblings + * many elements are its siblings and therefore form a family. */ + if (pquad_w_sub_family[0]->transition_type != 0) { + return 1; + } + /* If the first element of fam is no subelement we check the following elements of fam */ + else { + /* If any of the following elements is a subelement, then they can not form a family */ + if (pquad_w_sub_family[1]->transition_type != 0 || pquad_w_sub_family[2]->transition_type != 0 + || pquad_w_sub_family[3]->transition_type != 0) { + return 0; + } + /* If all elements of fam are no subelements, then we can use the p4est check is_family */ + else { + return p4est_quadrant_is_family (&pquad_w_sub_family[0]->p4q, &pquad_w_sub_family[1]->p4q, + &pquad_w_sub_family[2]->p4q, &pquad_w_sub_family[3]->p4q); + } + } +} + +void +t8_subelement_scheme_quad_c::t8_element_set_linear_id (t8_element_t *elem, int level, t8_linearidx_t id) const +{ + t8_quad_with_subelements *pquad_w_sub = (t8_quad_with_subelements *) elem; + p4est_quadrant_t *q = &pquad_w_sub->p4q; + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (0 <= level && level <= P4EST_QMAXLEVEL); + T8_ASSERT (0 <= id && id < ((t8_linearidx_t) 1) << P4EST_DIM * level); + + p4est_quadrant_set_morton (q, level, id); + T8_QUAD_SET_TDIM (q, 2); +} + +t8_linearidx_t +t8_subelement_scheme_quad_c::t8_element_get_linear_id (const t8_element_t *elem, int level) const +{ + t8_quad_with_subelements *pquad_w_sub = (t8_quad_with_subelements *) elem; + p4est_quadrant_t *q = &pquad_w_sub->p4q; + + /* Note that the id of a subelement equals the id of its parent quadrant. + * Therefore, the binary search (for example used in the leaf_face_neighbor function) + * will find a random subelement of the transition cell which might not be the desired neighbor of a given element. */ + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (0 <= level && level <= P4EST_QMAXLEVEL); + + return p4est_quadrant_linear_id (q, level); +} + +void +t8_subelement_scheme_quad_c::t8_element_first_descendant (const t8_element_t *elem, t8_element_t *desc, int level) const +{ + const t8_quad_with_subelements *pquad_w_sub_elem = (const t8_quad_with_subelements *) elem; + t8_quad_with_subelements *pquad_w_sub_desc = (t8_quad_with_subelements *) desc; + + const p4est_quadrant_t *q = &pquad_w_sub_elem->p4q; + p4est_quadrant_t *r = &pquad_w_sub_desc->p4q; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (desc)); + T8_ASSERT (0 <= level && level <= P4EST_QMAXLEVEL); + + p4est_quadrant_first_descendant (q, r, level); + T8_QUAD_SET_TDIM (r, 2); + + /* We allow constructing a last descendant from a subelement. + * Keep in mind, that transforming a quad element to a subelement does not change the + * p4est quadrant. Therefore, we are constructing the last descendant of the parent + * quad element of the given subelement. Since the last descendant is not meant to be + * a subelement, we reset the corresponding subelement values. */ + t8_element_reset_subelement_values (desc); +} + +void +t8_subelement_scheme_quad_c::t8_element_last_descendant (const t8_element_t *elem, t8_element_t *desc, int level) const +{ + const t8_quad_with_subelements *pquad_w_sub_elem = (const t8_quad_with_subelements *) elem; + t8_quad_with_subelements *pquad_w_sub_desc = (t8_quad_with_subelements *) desc; + + const p4est_quadrant_t *q = &pquad_w_sub_elem->p4q; + p4est_quadrant_t *r = &pquad_w_sub_desc->p4q; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (desc)); + T8_ASSERT (0 <= level && level <= P4EST_QMAXLEVEL); + + p4est_quadrant_last_descendant (q, r, level); + T8_QUAD_SET_TDIM (r, 2); + + /* We allow constructing a last descendant from a subelement. + * Keep in mind, that transforming a quad element to a subelement does not change the + * p4est quadrant. Therefore, we are constructing the last descendant of the parent + * quad element of the given subelement. Since the last descendant is not meant to be + * a subelement, we reset the corresponding subelement values. */ + t8_element_reset_subelement_values (desc); +} + +void +t8_subelement_scheme_quad_c::t8_element_successor (const t8_element_t *elem1, t8_element_t *elem2) const +{ + const t8_quad_with_subelements *pquad_w_sub_elem1 = (const t8_quad_with_subelements *) elem1; + t8_quad_with_subelements *pquad_w_sub_elem2 = (t8_quad_with_subelements *) elem2; + + const p4est_quadrant_t *q = &pquad_w_sub_elem1->p4q; + p4est_quadrant_t *r = &pquad_w_sub_elem2->p4q; + + t8_linearidx_t id; + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem1)); + + T8_ASSERT (t8_element_is_valid (elem1)); + T8_ASSERT (t8_element_is_valid (elem2)); + T8_ASSERT (0 <= t8_element_level (elem1) && t8_element_level (elem1) <= P4EST_QMAXLEVEL); + + id = p4est_quadrant_linear_id (q, t8_element_level (elem1)); + T8_ASSERT (id + 1 < ((t8_linearidx_t) 1) << P4EST_DIM * t8_element_level (elem1)); + t8_element_reset_subelement_values (elem2); + p4est_quadrant_set_morton (r, t8_element_level (elem1), id + 1); + t8_element_copy_surround (q, r); +} + +void +t8_subelement_scheme_quad_c::t8_element_nca (const t8_element_t *elem1, const t8_element_t *elem2, + t8_element_t *nca) const +{ + const t8_quad_with_subelements *pquad_w_sub_elem1 = (const t8_quad_with_subelements *) elem1; + const t8_quad_with_subelements *pquad_w_sub_elem2 = (const t8_quad_with_subelements *) elem2; + t8_quad_with_subelements *pquad_w_sub_nca = (t8_quad_with_subelements *) nca; + + const p4est_quadrant_t *q1 = &pquad_w_sub_elem1->p4q; + const p4est_quadrant_t *q2 = &pquad_w_sub_elem2->p4q; + p4est_quadrant_t *r = &pquad_w_sub_nca->p4q; + + T8_ASSERT (t8_element_is_valid (elem1)); + T8_ASSERT (t8_element_is_valid (elem2)); +#if 0 + /* TODO: This assertions throws an error since it expects a 3D hex. + * this does not make sense. investigate. */ + T8_ASSERT (t8_element_surround_matches (q1, q2)); +#endif + + /* In case of subelements, we use the parent quadrant and construct nca of the parent quadrant */ + t8_element_reset_subelement_values (nca); + p4est_nearest_common_ancestor (q1, q2, r); + t8_element_copy_surround (q1, r); +} + +t8_element_shape_t +t8_subelement_scheme_quad_c::t8_element_face_shape (const t8_element_t *elem, int face) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + return T8_ECLASS_LINE; +} + +void +t8_subelement_scheme_quad_c::t8_element_children_at_face (const t8_element_t *elem, int face, t8_element_t *children[], + int num_children, int *child_indices) const +{ +#ifdef T8_ENABLE_DEBUG + { + int i; + for (i = 0; i < num_children; i++) { + T8_ASSERT (t8_element_is_valid (children[i])); + } + } +#endif + /* This function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (0 <= face && face < P4EST_FACES); + T8_ASSERT (num_children == t8_element_num_face_children (elem, face)); + + /* + * Compute the child id of the first and second child at the face. + * + * 3 + * + * x - - x - - x This picture shows a refined quadrant + * | | | with child_ids and the label for the faces. + * | 2 | 3 | For example for face 2 (bottom face) we see + * 0 x - - x - - x 1 first_child = 0 and second_child = 1. + * | | | + * | 0 | 1 | + * x - - x - - x + * + * 2 + */ + + T8_ASSERT (num_children == 2); + int first_child; + int second_child; + /* TODO: Think about a short and easy bitwise formula. */ + switch (face) { + case 0: + first_child = 0; + second_child = 2; + break; + case 1: + first_child = 1; + second_child = 3; + break; + case 2: + first_child = 0; + second_child = 1; + break; + case 3: + first_child = 2; + second_child = 3; + break; + default: + SC_ABORT_NOT_REACHED (); + } + + /* From the child ids we now construct the children at the faces. */ + /* We have to revert the order and compute second child first, since + * the usage allows for elem == children[0]. + */ + this->t8_element_child (elem, second_child, children[1]); + this->t8_element_child (elem, first_child, children[0]); + if (child_indices != NULL) { + child_indices[0] = first_child; + child_indices[1] = second_child; + } +} + +int +t8_subelement_scheme_quad_c::t8_element_face_child_face (const t8_element_t *elem, int face, int face_child) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + if (t8_element_is_subelement (elem)) { + T8_ASSERT (face == 1); + return t8_element_face_parent_face (elem, face); + } + else { + /* For quadrants the face enumeration of children is the same as for the parent. */ + return face; + } +} + +int +t8_subelement_scheme_quad_c::t8_element_face_parent_face (const t8_element_t *elem, int face) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (face >= -1 && face <= P4EST_FACES); + + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + const p4est_quadrant_t *q = &pquad_w_sub->p4q; + + int child_id; + + if (face == -1) { + return -1; + } + + /* For subelements we need to adjust the output of this function. + * A subelements face is a subface of the parent quadrant (the transition cell) if and only if the face number is 1. */ + if (t8_element_is_subelement (elem)) { + if (face == 1) { + /* In this case the face is a subface of the parent. We use the location function in order + * to determine which of the parents faces intersects the subelements face. */ + int location[3] = {}; + t8_element_get_location_of_subelement (elem, location); + + /* subelements in location are enumerated clockwise (not as quadrant faces) */ + return subelement_location_to_parent_face[location[0]]; + } + else { + return -1; + } + } + + if (q->level == 0) { + return face; + } + /* Determine whether face is a subface of the parent. + * This is the case if the child_id matches one of the faces corners */ + child_id = p4est_quadrant_child_id (q); + if (child_id == p4est_face_corners[face][0] || child_id == p4est_face_corners[face][1]) { + return face; + } + return -1; +} + +void +t8_subelement_scheme_quad_c::t8_element_transform_face (const t8_element_t *elem1, t8_element_t *elem2, int orientation, + int sign, int is_smaller_face) const +{ + const t8_quad_with_subelements *pquad_w_sub_elem1 = (const t8_quad_with_subelements *) elem1; + t8_quad_with_subelements *pquad_w_sub_elem2 = (t8_quad_with_subelements *) elem2; + + const p4est_quadrant_t *qin = &pquad_w_sub_elem1->p4q; + p4est_quadrant_t *p = &pquad_w_sub_elem2->p4q; + + const p4est_quadrant_t *q; + p4est_qcoord_t h = P4EST_QUADRANT_LEN (qin->level); + p4est_qcoord_t x = qin->x; /* temp storage for x coordinate in case elem1 = elem 2 */ + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem1)); + T8_ASSERT (t8_element_is_valid (elem1)); + T8_ASSERT (t8_element_is_valid (elem2)); + T8_ASSERT (0 <= orientation && orientation < P4EST_FACES); + + if (sign) { + /* The tree faces have the same topological orientation, and + * thus we have to perform a coordinate switch. */ + /* We use p as storage, since elem1 and elem2 are allowed to + * point to the same quad */ + q = (const p4est_quadrant_t *) p; + t8_element_copy_surround (qin, (p4est_quadrant_t *) q); + ((p4est_quadrant_t *) q)->x = qin->y; + ((p4est_quadrant_t *) q)->y = x; + x = q->x; /* temp storage in case elem1 = elem 2 */ + } + else { + q = qin; + } + + p->level = q->level; + /* + * The faces of the root quadrant are enumerated like this: + * + * v_2 v_3 + * x -->-- x + * | | + * ^ ^ + * | | + * x -->-- x + * v_0 v_1 + * + * Orientation is the corner number of the bigger face that coincides + * with the corner v_0 of the smaller face. + */ + /* If this face is not smaller, switch the orientation: + * sign = 0 sign = 1 + * 0 -> 0 0 -> 0 + * 1 -> 2 1 -> 1 + * 2 -> 1 2 -> 2 + * 3 -> 3 3 -> 3 + */ + if (!is_smaller_face && (orientation == 1 || orientation == 2) && !sign) { + orientation = 3 - orientation; + } + + switch (orientation) { + case 0: /* Nothing to do */ + p->x = q->x; + p->y = q->y; + break; + case 1: + p->x = P4EST_ROOT_LEN - q->y - h; + p->y = x; + break; + case 2: + p->x = q->y; + p->y = P4EST_ROOT_LEN - x - h; + break; + case 3: + p->x = P4EST_ROOT_LEN - q->x - h; + p->y = P4EST_ROOT_LEN - q->y - h; + break; + default: + SC_ABORT_NOT_REACHED (); + } + T8_QUAD_SET_TDIM (p, 2); + + t8_element_reset_subelement_values (elem2); +} + +int +t8_subelement_scheme_quad_c::t8_element_extrude_face (const t8_element_t *face, const t8_eclass_scheme_c *face_scheme, + t8_element_t *elem, int root_face) const +{ + /* build (extrude) elem from a given face element */ + t8_quad_with_subelements *pquad_w_sub = (t8_quad_with_subelements *) elem; + p4est_quadrant_t *q = &pquad_w_sub->p4q; + + const t8_dline_t *l = (const t8_dline_t *) face; + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (T8_COMMON_IS_TYPE (face_scheme, const t8_default_scheme_line_c *)); + T8_ASSERT (face_scheme->eclass == T8_ECLASS_LINE); + T8_ASSERT (face_scheme->t8_element_is_valid (elem)); + T8_ASSERT (0 <= root_face && root_face < P4EST_FACES); + + /* + * The faces of the root quadrant are enumerated like this: + * + * f_2 + * x -->-- x + * | | + * ^ ^ + * f_0 | | f_1 + * x -->-- x + * f_3 + * + * The arrows >,^ denote the orientation of the faces. + * We need to scale the coordinates since a root line may have a different + * length than a root quad. + */ + q->level = l->level; + switch (root_face) { + case 0: + q->x = 0; + q->y = ((int64_t) l->x * P4EST_ROOT_LEN) / T8_DLINE_ROOT_LEN; + break; + case 1: + q->x = P4EST_LAST_OFFSET (q->level); + q->y = ((int64_t) l->x * P4EST_ROOT_LEN) / T8_DLINE_ROOT_LEN; + break; + case 2: + q->x = ((int64_t) l->x * P4EST_ROOT_LEN) / T8_DLINE_ROOT_LEN; + q->y = 0; + break; + case 3: + q->x = ((int64_t) l->x * P4EST_ROOT_LEN) / T8_DLINE_ROOT_LEN; + q->y = P4EST_LAST_OFFSET (q->level); + break; + default: + SC_ABORT_NOT_REACHED (); + } + t8_element_reset_subelement_values (elem); + /* We return the face of q at which we extruded. This is the same number + * as root_face. */ + return root_face; +} + +int +t8_subelement_scheme_quad_c::t8_element_tree_face (const t8_element_t *elem, int face) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + /* If elem is a subelement, then this function should only be called together with + * face = 1 since other faces will never intersect a tree face. */ + if (t8_element_is_subelement (elem)) { + T8_ASSERT (face == 1); + + return t8_element_face_parent_face (elem, face); + } + else { + T8_ASSERT (0 <= face && face < P4EST_FACES); + /* For quadrants the face and the tree face number are the same. */ + return face; + } +} + +/** Construct the first descendant of an element that touches a given face. */ +void +t8_subelement_scheme_quad_c::t8_element_first_descendant_face (const t8_element_t *elem, int face, + t8_element_t *first_desc, int level) const +{ + const t8_quad_with_subelements *pquad_w_sub_elem = (const t8_quad_with_subelements *) elem; + t8_quad_with_subelements *pquad_w_sub_first_desc = (t8_quad_with_subelements *) first_desc; + + const p4est_quadrant_t *q = &pquad_w_sub_elem->p4q; + p4est_quadrant_t *desc = &pquad_w_sub_first_desc->p4q; + + int first_face_corner; + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + T8_ASSERT (0 <= face && face < P4EST_FACES); + T8_ASSERT (0 <= level && level <= P4EST_QMAXLEVEL); + + /* Get the first corner of q that belongs to face */ + first_face_corner = p4est_face_corners[face][0]; + /* Construce the descendant in that corner */ + p4est_quadrant_corner_descendant (q, desc, first_face_corner, level); + t8_element_reset_subelement_values (first_desc); +} + +/** Construct the last descendant of an element that touches a given face. */ +void +t8_subelement_scheme_quad_c::t8_element_last_descendant_face (const t8_element_t *elem, int face, + t8_element_t *last_desc, int level) const +{ + const t8_quad_with_subelements *pquad_w_sub_elem = (const t8_quad_with_subelements *) elem; + t8_quad_with_subelements *pquad_w_sub_last_desc = (t8_quad_with_subelements *) last_desc; + + const p4est_quadrant_t *q = &pquad_w_sub_elem->p4q; + p4est_quadrant_t *desc = &pquad_w_sub_last_desc->p4q; + + int last_face_corner; + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + T8_ASSERT (!t8_element_is_subelement (last_desc)); + T8_ASSERT (0 <= face && face < P4EST_FACES); + T8_ASSERT (0 <= level && level <= P4EST_QMAXLEVEL); + + /* Get the last corner of q that belongs to face */ + last_face_corner = p4est_face_corners[face][1]; + /* Construce the descendant in that corner */ + p4est_quadrant_corner_descendant (q, desc, last_face_corner, level); + t8_element_reset_subelement_values (last_desc); +} + +void +t8_subelement_scheme_quad_c::t8_element_boundary_face (const t8_element_t *elem, int face, t8_element_t *boundary, + const t8_eclass_scheme_c *boundary_scheme) const +{ + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + const p4est_quadrant_t *q = &pquad_w_sub->p4q; + + t8_dline_t *l = (t8_dline_t *) boundary; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (T8_COMMON_IS_TYPE (boundary_scheme, const t8_default_scheme_line_c *)); + T8_ASSERT (boundary_scheme->eclass == T8_ECLASS_LINE); + T8_ASSERT (boundary_scheme->t8_element_is_valid (boundary)); + + if (!t8_element_is_subelement (elem)) { + T8_ASSERT (0 <= face && face < P4EST_FACES); + /* The level of the boundary element is the same as the quadrant's level */ + l->level = q->level; + /* + * The faces of the quadrant are enumerated like this: + * f_2 + * x ---- x + * | | + * f_0 | | f_1 + * x ---- x + * f_3 + * + * If face = 0 or face = 1 then l->x = q->y + * if face = 2 or face = 3 then l->x = q->x + */ + l->x = ((face >> 1 ? q->x : q->y) * ((int64_t) T8_DLINE_ROOT_LEN) / P4EST_ROOT_LEN); + } + else { + /* face number 1 is the only face of a subelement that points outward of the transition cell */ + T8_ASSERT (face == 1); + /* boundary faces of subelements: + * + * x - - - - - x + * | \ / | + * | \ / | + * x - - x e2 | f1 + * |e1 / \ | + * f1 | / \ | + * x - - x - - x + * + * for a split subelement (e1), the boundary face has a higher level + * for a non split element (e2), the boundary face has the same level. + */ + + int location[3] + = {}; /* location = {location of subelement (face number of transition cell), split, first or second element if split} */ + t8_element_get_location_of_subelement (elem, location); + int split = location[1]; + int second = location[2]; + + if (split) { /* if the subelement lies at a split face */ + l->level = q->level + 1; + int len = P4EST_QUADRANT_LEN (pquad_w_sub->p4q.level + 1); + if (second) { /* second subelement */ + if (location[0] == 0) { /* left face */ + l->x = q->y + len; + } + else if (location[0] == 1) { /* upper face */ + l->x = q->x + len; + } + else if (location[0] == 2) { /* right face */ + l->x = q->y; + } + else { /* lower face */ + l->x = q->x; + } + } + else { /* first subelement */ + if (location[0] == 0) { /* left face */ + l->x = q->y; + } + else if (location[0] == 1) { /* upper face */ + l->x = q->x; + } + else if (location[0] == 2) { /* right face */ + l->x = q->y + len; + } + else { /* lower face */ + l->x = q->x + len; + } + } + } + else { /* if the subelement is not split */ + l->level = q->level; + if (location[0] == 0) { /* left face */ + l->x = q->y; + } + else if (location[0] == 1) { /* upper face */ + l->x = q->x; + } + else if (location[0] == 2) { /* right face */ + l->x = q->y; + } + else { /* lower face */ + l->x = q->x; + } + } + } +} + +void +t8_subelement_scheme_quad_c::t8_element_boundary (const t8_element_t *elem, int min_dim, int length, + t8_element_t **boundary) const +{ + SC_ABORT ("Not implemented\n"); +#if 0 +#ifdef T8_ENABLE_DEBUG + int per_eclass[T8_ECLASS_COUNT]; +#endif + int iface; + + T8_ASSERT (length == + t8_eclass_count_boundary (T8_ECLASS_QUAD, min_dim, per_eclass)); + + T8_ASSERT (length == P4EST_FACES); + for (iface = 0; iface < P4EST_FACES; iface++) { + t8_element_boundary_face (elem, iface, boundary[iface]); + } +#endif +} + +int +t8_subelement_scheme_quad_c::t8_element_is_root_boundary (const t8_element_t *elem, int face) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + const p4est_quadrant_t *q = &pquad_w_sub->p4q; + + p4est_qcoord_t coord; + + /* In case of a subelement, we need to change its face number to the face number of the parent quad */ + if (t8_element_is_subelement (elem)) { + if (face == 1) { + /* adjust face of subelement to face of parent */ + face = t8_element_face_parent_face (elem, face); + } + else { /* in case of a subelement and face 0 or 2 the face is no subface of the root boundary */ + return 0; + } + } + + T8_ASSERT (0 <= face && face < P4EST_FACES); + + /* if face is 0 or 1 q->x + * 2 or 3 q->y + */ + coord = face >> 1 ? q->y : q->x; + /* If face is 0 or 2 check against 0. + * If face is 1 or 3 check against LAST_OFFSET */ + return coord == (face & 1 ? P4EST_LAST_OFFSET (q->level) : 0); +} + +int +t8_subelement_scheme_quad_c::t8_element_face_neighbor_inside (const t8_element_t *elem, t8_element_t *neigh, int face, + int *neigh_face) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (neigh)); + T8_ASSERT (0 <= face && face < P4EST_FACES); + + const t8_quad_with_subelements *pquad_w_sub_elem = (const t8_quad_with_subelements *) elem; + t8_quad_with_subelements *pquad_w_sub_neigh = (t8_quad_with_subelements *) neigh; + + const p4est_quadrant_t *q = &pquad_w_sub_elem->p4q; + p4est_quadrant_t *n = &pquad_w_sub_neigh->p4q; + + /* In case of a subelement one should construct the face neighbor of the face-corresponding child quadrant + * of the subelements parent quadrant. Therefore we might want to adjust the level and adapt the + * anchor node. */ + if (t8_element_is_subelement (elem)) { /* if elem is a subelement */ + + T8_ASSERT (0 <= face && face < T8_QUAD_SUBELEMENT_FACES); + + if (face == 0) { /* in this case the face neighbor of the subelement is a sibling */ + /* level and anchor stay the same */ + n->x = q->x; + n->y = q->y; + n->level = q->level; + } + if (face == 2) { /* in this case the face neighbor of the subelement is a sibling */ + /* level and anchor stay the same */ + n->x = q->x; + n->y = q->y; + n->level = q->level; + } + if (face == 1) { /* in this case the face neighbor is no sibling */ + int location[3] = {}; + t8_element_get_location_of_subelement (elem, location); + + /* setting the anchor node of the neighbor element */ + n->x = q->x; + n->y = q->y; + + /* half the side length of the transition cell of the subelement */ + const p4est_qcoord_t shift = P4EST_QUADRANT_LEN (q->level + 1); + + int split = location[1]; + int second = location[2]; + + /* we need to take into account whether the subelement is split or not */ + if (split) { /* split */ + + /* increase the level by one */ + n->level = q->level + 1; + + /* adjust the anchor node of the neighbor of the subelement depending on its location */ + if (location[0] == 0) { /* left face */ + if (!second) { + n->x = q->x - shift; + } + else { + n->x = q->x - shift; + n->y = q->y + shift; + } + } + else if (location[0] == 2) { /* right face */ + if (!second) { + n->x = q->x + 2 * shift; + n->y = q->y + shift; + } + else { + n->x = q->x + 2 * shift; + } + } + else if (location[0] == 3) { /* lower face */ + if (!second) { + n->x = q->x + shift; + n->y = q->y - shift; + } + else { + n->y = q->y - shift; + } + } + else { /* upper face */ + if (!second) { + n->y = q->y + 2 * shift; + } + else { + n->x = q->x + shift; + n->y = q->y + 2 * shift; + } + } + } + + else { /* not split */ + /* level stays the same */ + n->level = q->level; + + /* adjust the anchor node of the neighbor of the subelement depending on its location */ + if (location[0] == 0) { /* left face */ + n->x = q->x - 2 * shift; + } + else if (location[0] == 2) { /* right face */ + n->x = q->x + 2 * shift; + } + else if (location[0] == 3) { /* lower face */ + n->y = q->y - 2 * shift; + } + else { /* upper face */ + n->y = q->y + 2 * shift; + } + } + } + } + else { /* if elem is no subelement */ + /* Directly construct the face neighbor */ + p4est_quadrant_face_neighbor (q, face, n); + } + + t8_element_reset_subelement_values (neigh); + + T8_QUAD_SET_TDIM (n, 2); + + /* In the following we set the dual faces of our element at the given face. */ + if (t8_element_is_subelement (elem)) { + if (face == 1) { + /* return dual face with respect to neighboring quad element */ + int location[3] = {}; + t8_element_get_location_of_subelement (elem, location); + /* if the face is pointing outwards, then we set the face equal to the transition cell face and determine its dual face. + * Compute the face number as seen from q. + * 0 -> 1 1 -> 2 2 -> 0 3 -> 3 + */ + *neigh_face = subelement_location_to_parent_dual_face[location[0]]; + } + else { + T8_ASSERT (face == 0 || face == 2); + /* return dual face with resprect to neighboring sibling subelement (note that the constructed neigh is NOT a subelement but the parent quad) */ + /* Compute the face number as seen from q. + * 0 -> 2 2 -> 0 + */ + *neigh_face = subelement_face_dual[face]; + } + } + else { + /* Compute the face number as seen from q. + * 0 -> 1 1 -> 0 2 -> 3 3 -> 2 + */ + T8_ASSERT (neigh_face != NULL); + *neigh_face = p4est_face_dual[face]; + } + + /* return true if neigh is inside the root */ + return p4est_quadrant_is_inside_root (n); +} + +void +t8_subelement_scheme_quad_c::t8_element_anchor (const t8_element_t *elem, int coord[3]) const +{ + t8_quad_with_subelements *pquad_w_sub = (t8_quad_with_subelements *) elem; + p4est_quadrant_t *q = &pquad_w_sub->p4q; + + /* this function is not implemented for subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + + T8_ASSERT (t8_element_is_valid (elem)); + + coord[0] = q->x; + coord[1] = q->y; + coord[2] = 0; + T8_QUAD_SET_TDIM (q, 2); +} + +int +t8_subelement_scheme_quad_c::t8_element_root_len (const t8_element_t *elem) const +{ + return P4EST_ROOT_LEN; +} + +int +t8_subelement_scheme_quad_c::t8_element_refines_irregular () const +{ + /* In general, subelements do not refine regularly */ + return 1; +} + +void +t8_subelement_scheme_quad_c::t8_element_vertex_integer_coords (const t8_element_t *elem, int vertex, int coords[]) const +{ + SC_ABORT ("This function is not implemented for the given scheme.\n"); +} + +void +t8_subelement_scheme_quad_c::t8_element_reference_coords (const t8_element_t *elem, const double *ref_coords, + const size_t num_coords, double *out_coords) const +{ + T8_ASSERT (num_coords > 0); + T8_ASSERT (ref_coords != NULL); + T8_ASSERT (out_coords != NULL); + T8_ASSERT (t8_element_is_valid (elem)); + + const t8_quad_with_subelements *element = (t8_quad_with_subelements *) elem; + if (!t8_element_is_subelement (elem)) { + const t8_element_t *quad = (const t8_element_t *) &element->p4q; + // We call the default quad reference coord function for the quad coordinates. + default_quad_scheme.t8_element_reference_coords (quad, ref_coords, num_coords, out_coords); + } + else { + /* This element is a subelement and hence a triangle. */ + T8_ASSERT (t8_element_shape (elem) == T8_ECLASS_TRIANGLE); + + /* + * We use t8_geom_triangular_interpolation to compute the coordinates + * given the coordinates of the triangle's vertices. + */ + constexpr int num_vertices = 3; // Should use t8_eclass_num_vertices[T8_ECLASS_TRIANGLE]; but is not constexpr + constexpr int num_vertex_coords = num_vertices * 3; + double vertex_coords[num_vertex_coords]; // Stores the triangle's vertices + for (int ivertex = 0; ivertex < num_vertices; ++ivertex) { + t8_element_vertex_reference_coords (elem, ivertex, vertex_coords + 3 * ivertex); + } + + // For each incoming coordinate we do the interpolation + for (size_t icoord = 0; icoord < num_coords; ++icoord) { + // The ref_coords are always 3 dimensional - even though we do not use the 3rd entry. + // The out_coords are 2 dimensional. + const int offset_ref = icoord * 3; // offset for 3 dim ref_coords when iterating over points + const int offset_out = icoord * 2; // offset for 2 dim out_coords when iterating over points + t8_geom_triangular_interpolation (ref_coords + offset_ref, vertex_coords, 2, 2, out_coords + offset_out); + } + } +} + +void +t8_subelement_scheme_quad_c::t8_element_vertex_reference_coords (const t8_element_t *t, int vertex, + double coords[]) const +{ + int coords_int[2] = {}; + t8_element_vertex_coords (t, vertex, coords_int); + + /* We divide the integer coordinates by the root length of the quad + * to obtain the reference coordinates. */ + coords[0] = (double) coords_int[0] / (double) P4EST_ROOT_LEN; + coords[1] = (double) coords_int[1] / (double) P4EST_ROOT_LEN; +} + +void +t8_subelement_scheme_quad_c::t8_element_vertex_coords (const t8_element_t *elem, int vertex, + t8_dquad_subelement_coord_t coords[]) const +{ + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + const p4est_quadrant_t *q1 = &pquad_w_sub->p4q; + + T8_ASSERT (t8_element_is_valid (elem)); + + if (!t8_element_is_subelement (elem)) { + int len; + + T8_ASSERT (0 <= vertex && vertex < 4); + /* Get the length of the quadrant */ + len = P4EST_QUADRANT_LEN (q1->level); + + /* Compute the x and y coordinates of the vertex depending on the + * vertex number */ + coords[0] = q1->x + (vertex & 1 ? 1 : 0) * len; + coords[1] = q1->y + (vertex & 2 ? 1 : 0) * len; + } + else { + t8_element_vertex_coords_of_subelement (elem, vertex, coords); + } +} + +void +t8_subelement_scheme_quad_c::t8_element_vertex_coords_of_subelement (const t8_element_t *elem, int vertex, + t8_dquad_subelement_coord_t coords[]) const +{ + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + const p4est_quadrant_t *q1 = &pquad_w_sub->p4q; + + int len; + + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_subelement (elem)); + T8_ASSERT (vertex >= 0 && vertex < T8_QUAD_SUBELEMENT_FACES); /* all subelements are triangles */ + + /* get the length of the current quadrant */ + len = P4EST_QUADRANT_LEN (q1->level); + + /* Compute the x and y coordinates of subelement vertices, depending on the transition type, id and vertex number + * (faces enumerated clockwise, starting at the center of the transition cell): + * + * f1 V1 + * x - - - - - x x + * | \ 2 / | / | + * | 1 \ / 3 | / 3 | + * f0 x - - + - - x f2 --> + - - x + * | 0 / | \ 4 | V0 V2 + * | / 6 | 5 \ | + * x - - x - - x + * f3 + * + * In this example, the below location array would contain the values [2, 1, 1] + * (second face, split, first subelement at this face) */ + + /* get location information of the given subelement */ + int location[3] = {}; + t8_element_get_location_of_subelement (elem, location); + + /* the face number, the subelement is adjacent to */ + int face_number = location[0]; + /* = 1, if the adjacent face is split and = 0, if not */ + int split = location[1]; + /* = 0, if the subelement is the first (of two) subelements, at the adjacent face and = 1 if it is the second */ + int sub_face_id = location[2]; + + /* Check, whether the get_location function provides meaningful location data */ + T8_ASSERT (face_number == 0 || face_number == 1 || face_number == 2 || face_number == 3); + T8_ASSERT ((split == 0 && sub_face_id == 0) || (split == 1 && (sub_face_id == 0 || sub_face_id == 1))); + + coords[0] = q1->x; + coords[1] = q1->y; + + /* using the location data to determine vertex coordinates */ + if (vertex == 0) { /* vertex 0 (the first vertex always equals the center of the element) */ + coords[0] += len / 2; + coords[1] += len / 2; + } /* end of vertex == 0 */ + else if (vertex == 1) { /* vertex 1 */ + if (face_number == 0) { + if (split && sub_face_id) { + coords[1] += len / 2; + } + } + else if (face_number == 1) { + coords[1] += len; + if (split && sub_face_id) { + coords[0] += len / 2; + } + } + else if (face_number == 2) { + coords[0] += len; + coords[1] += len; + if (split && sub_face_id) { + coords[1] -= len / 2; + } + } + else { + coords[0] += len; + if (split && sub_face_id) { + coords[0] -= len / 2; + } + } + } /* end of vertex == 1 */ + else { /* vertex 2 */ + if (face_number == 0) { + coords[1] += len; + if (split && !sub_face_id) { + coords[1] -= len / 2; + } + } + else if (face_number == 1) { + coords[0] += len; + coords[1] += len; + if (split && !sub_face_id) { + coords[0] -= len / 2; + } + } + else if (face_number == 2) { + coords[0] += len; + if (split && !sub_face_id) { + coords[1] += len / 2; + } + } + else { + if (split && !sub_face_id) { + coords[0] += len / 2; + } + } + } /* end of vertex == 2 */ +} + +void +t8_subelement_scheme_quad_c::t8_element_to_transition_cell (const t8_element_t *elem, int type, t8_element_t *c[]) +{ + const t8_quad_with_subelements *pquad_w_sub_elem = (const t8_quad_with_subelements *) elem; + t8_quad_with_subelements **pquad_w_sub_subelement = (t8_quad_with_subelements **) c; + + const p4est_quadrant_t *q = &pquad_w_sub_elem->p4q; + + /* this function should not be callable by subelements */ + T8_ASSERT (!t8_element_is_subelement (elem)); + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (type >= 0 && type <= T8_SUB_QUAD_MAX_TRANSITION_TYPE); + + int num_subelements = t8_element_get_number_of_subelements (type); + +#ifdef T8_ENABLE_DEBUG + { + int j; + for (j = 0; j < num_subelements; j++) { + T8_ASSERT (t8_element_is_valid (c[j])); + } + } +#endif + + /* get the length of a children-quadrant */ + const int8_t level = (int8_t) (q->level); + + T8_ASSERT (p4est_quadrant_is_extended (q)); + T8_ASSERT (q->level < P4EST_QMAXLEVEL); + + /* Setting the parameter values for different subelements. + * The different subelement types (up to rotation) are: + * + * x - - - - - - x x - - - - - x x - - - - - x x - - - - - x x - - x - - x x - - x - - x + * | | | \ 2 / | | \ / | | \ / | | \ | / | | \ | / | + * | | | 1 \ / | | \ / | | \ / | | \ | / | | \ | / | + * | | --> x - - X 3 | or x - - x | or x - - x - - x or x - - x - - x or x - - x - - x + * | | | 0 / \ | | / | \ | | / \ | | / \ | | / | \ | + * | elem | | / 4 \ | | / | \ | | / \ | | / \ | | / | \ | + * + - - - - - - x x - - - - - x x - - x - - x x - - - - - x x - - - - - x x - - x - - x + * + * Sub_ids are counted clockwise, starting with the (lower) left subelement with id 0. + * Note, that we do not change the p4est quadrant. */ + + int sub_id_counter = 0; + for (sub_id_counter = 0; sub_id_counter < num_subelements; sub_id_counter++) { + pquad_w_sub_subelement[sub_id_counter]->p4q.x = q->x; + pquad_w_sub_subelement[sub_id_counter]->p4q.y = q->y; + pquad_w_sub_subelement[sub_id_counter]->p4q.level = level; + + pquad_w_sub_subelement[sub_id_counter]->transition_type = type; + pquad_w_sub_subelement[sub_id_counter]->subelement_id = sub_id_counter; + + T8_ASSERT (t8_element_is_valid (c[sub_id_counter])); + t8_element_copy_surround (q, &pquad_w_sub_subelement[sub_id_counter]->p4q); + } +} + +int +t8_subelement_scheme_quad_c::t8_element_get_number_of_subelements (int transition_type) const +{ + /* we could return 0 for transition type 0 but we will assert this case for safety reasons */ + T8_ASSERT (transition_type != 0); + + /* consider transition_type 13 = 1101 in base two -> there are 4 + (1+1+0+1) = 7 subelements */ + int num_hanging_faces = 0; + int ichild; + for ( + ichild = 0; ichild < P4EST_FACES; + ichild++) { /* Count the number of ones of the binary transition type. This number equals the number of hanging faces. */ + num_hanging_faces += (transition_type & (1 << ichild)) >> ichild; + } + + /* The number of subelements equals the number of neighbours: */ + return P4EST_FACES + num_hanging_faces; +} + +void +t8_subelement_scheme_quad_c::t8_element_get_location_of_subelement (const t8_element_t *elem, int location[]) const +{ + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + + /* this function only works for subelements */ + T8_ASSERT (t8_element_is_subelement (elem)); + + T8_ASSERT (t8_element_is_valid (elem)); + + /* Consider the following transition cell of type 13: + * + * f0 1 + * x - - x - - x x - - x - - x + * | | | \ 2 | 3 / | faces: f3 f2 f1 f0 + * | | | 1 \ | / 4 | binary code: 1 1 0 1 (=13) + * f3 x x f2 --> 1 x - - x - - x 1 --> rearrange binaries s.t. the faces are enumerated clockwise: 1 1 1 0 + * | | | 0 / \ 5 | number subelements at face: 2 2 2 1 + * | elem | | / 6 \ | consider sub_id 3: x -> second subelement on the upper face + * + - - - - - x x - - - - - x + * f1 0 + * + * We will use the binary representation to determine the location of the given subelement. + * + * We need to know: + * i) the face number of the first vertex (values: {0,1,2,3}). + * ii) whether this face is split in half (values: {0,1}). + * iii) if the subelement is the first or second subelement at the face (values: {0,1}). + * + * These information are then saved in the location array which will be used by the element_vertex function, + * to automatically determine the vertex coordinates of the given subelement. + * + * The location array for the above example would be {1,1,1} (upper face, split = true, second subelement at the upper face). */ + + /* 1) convert the transition type from a decimal to a binary representation */ + int type = pquad_w_sub->transition_type; + int binary_array[P4EST_FACES] = {}; + + int iface; + + for ( + iface = 0; iface < P4EST_FACES; + iface++) { /* need an array with 4 elements to store all subelement types of the quad scheme from 1 to 15 ({0,0,0,1} to {1,1,1,1}) */ + binary_array[(P4EST_FACES - 1) - iface] = (type & (1 << iface)) >> iface; + } /* we now got a binary representation of the transition type, bitwise stored in an array */ + + /* 2) rearrange the binary representation to be in clockwise order */ + int binary_array_temp[P4EST_FACES] = {}; + + for (iface = 0; iface < P4EST_FACES; iface++) { /* copying the binary array */ + binary_array_temp[iface] = binary_array[iface]; + } + + for (iface = 0; iface < P4EST_FACES; iface++) { /* bringing the entries of binary array into clockwise order */ + binary_array[iface] = binary_array_temp[subelement_location_to_parent_face[iface]]; + } + + /* 3) use the rearranged binary representation, and the sub_id to determine the location of the subelement and store these information in an array */ + /* 3.1) location[0] -> the face_number, the subelement is adjacent to */ + /* 3.2) location[1] -> if the face is split or not */ + /* 3.3) location[2] -> if the subelement is the first or second subelement of the face (always the first, if the face is not split) */ + T8_ASSERT (pquad_w_sub->subelement_id < t8_element_get_number_of_subelements (pquad_w_sub->transition_type)); + + int sub_id = pquad_w_sub->subelement_id; + int sub_face_id; + int face_number = -1; + int split; + + int cum_neigh_array[P4EST_FACES] = {}; + + /* construct a cumulative array of the number of neighbors from face 0 to face 3 */ + cum_neigh_array[0] = binary_array[0] + 1; + cum_neigh_array[1] = cum_neigh_array[0] + binary_array[1] + 1; + cum_neigh_array[2] = cum_neigh_array[1] + binary_array[2] + 1; + cum_neigh_array[3] = cum_neigh_array[2] + binary_array[3] + 1; + + /* 3.1) we can use the cumulative array to determine the face number of the given subelement */ + if (sub_id < cum_neigh_array[0]) { + face_number = 0; + } + else { + for (iface = 0; iface < P4EST_FACES - 1; ++iface) { + if (sub_id >= cum_neigh_array[iface] && sub_id < cum_neigh_array[iface + 1]) { + face_number = iface + 1; + break; + } + } + } + + /* make sure that a face_number has been found */ + T8_ASSERT (face_number >= 0); + + /* 3.2) determine, whether the face is split or not */ + if (binary_array[face_number] == 0) { + split = 0; /* the face is not split */ + } + else { + split = 1; /* the face is split */ + } + + /* 3.3) determine, whether the subelement is the first or the second subelement at the face */ + if (sub_id + 1 == cum_neigh_array[face_number] && split == 1) { + sub_face_id = 1; /* second subelement */ + } + else { + sub_face_id = 0; /* first subelement */ + } + + location[0] = face_number; + location[1] = split; + location[2] = sub_face_id; +} + +void +t8_subelement_scheme_quad_c::t8_element_reset_subelement_values (t8_element *elem) const +{ + t8_quad_with_subelements *pquad_w_sub = (t8_quad_with_subelements *) elem; + + pquad_w_sub->transition_type = 0; + pquad_w_sub->subelement_id = 0; +} + +void +t8_subelement_scheme_quad_c::t8_element_copy_subelement_values (const t8_element *source, t8_element *dest) const +{ + const t8_quad_with_subelements *pquad_w_sub_source = (const t8_quad_with_subelements *) source; + t8_quad_with_subelements *pquad_w_sub_dest = (t8_quad_with_subelements *) dest; + + pquad_w_sub_dest->transition_type = pquad_w_sub_source->transition_type; + pquad_w_sub_dest->transition_type = pquad_w_sub_source->transition_type; + pquad_w_sub_dest->subelement_id = pquad_w_sub_source->subelement_id; +} + +int +t8_subelement_scheme_quad_c::t8_element_is_subelement (const t8_element *elem) const +{ + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + + T8_ASSERT (pquad_w_sub->transition_type >= 0); + + /* transition_type == 0 => elem is no subelement. + * transition_type != 0 => elem is subelement + */ + return (pquad_w_sub->transition_type == 0 ? false : true); +} + +int +t8_subelement_scheme_quad_c::t8_element_get_transition_type (const t8_element *elem) +{ + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + + return pquad_w_sub->transition_type; +} + +int +t8_subelement_scheme_quad_c::t8_element_get_subelement_id (const t8_element *elem) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + + return pquad_w_sub->subelement_id; +} + +t8_element_shape_t +t8_subelement_scheme_quad_c::t8_element_shape (const t8_element_t *elem) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + return (t8_element_is_subelement (elem) ? T8_ECLASS_TRIANGLE : T8_ECLASS_QUAD); +} + +int +t8_subelement_scheme_quad_c::t8_element_num_corners (const t8_element_t *elem) const +{ + T8_ASSERT (t8_element_is_valid (elem)); + + return (t8_element_is_subelement (elem) ? T8_QUAD_SUBELEMENT_FACES : P4EST_FACES); +} + +int +t8_subelement_scheme_quad_c::t8_element_find_neighbor_in_transition_cell (const t8_element_t *elem, + const t8_element_t *pseudo_neigh, + int elem_face) +{ + /* In this function, we assume pseudo_neigh to be a random subelement of a transition cell that includes + * the real neighbor of elem at face elem_face. This function will output the subelement_id of the real neighbor of elem. */ + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_valid (pseudo_neigh)); + + /* we expect neigh to be a element in a transition cell, thus to be a subelement */ + T8_ASSERT (t8_element_is_subelement (pseudo_neigh)); + + const t8_quad_with_subelements *pquad_w_sub_elem = (const t8_quad_with_subelements *) elem; + const t8_quad_with_subelements *pquad_w_sub_pseudo_neigh = (const t8_quad_with_subelements *) pseudo_neigh; + + /* In the following, all possible neighbor configurations are defined, such that subelement neighbors can be + * identified in LFN_transitioned. */ + if (pquad_w_sub_elem->transition_type != 0 && (elem_face == 0 || elem_face == 2)) { + /* In this case, we have the following situation: + * + * x - - - - - - - x + * | \ elem / | + * | \ / | + * |N_f0 \ / N_f2| + * x - - - + - - - x + * | / \ | + * | /pseudo \ | + * | / neigh \ | + * x - - - - - - - x + * + * Elem and face 0 or face 2 is given and a random sibling subelement neigh is given, too. + * We are searching for the subelement id of the real neighbor N_f0 or N_f2, depending on the face number. */ + int shift; + if (elem_face == 0) { + shift = -1; + } + if (elem_face == 2) { + shift = 1; + } + int num_subelements = t8_element_get_number_of_subelements (pquad_w_sub_elem->transition_type); + return ((pquad_w_sub_elem->subelement_id + shift) + num_subelements) + % num_subelements; /* the neighbor is directly before or after elem modulo the number of subelements in the transition cell */ + } + /* Below are the cases in which the neighbor can not be identified as simple as above. + * The idea is to fill a location array with the desired properties of the real neighbor. + * Together with the type of the transition cell of pseudo_neigh, we can then identify the sub_id of the right neighbor. */ + + if (pquad_w_sub_elem->transition_type != 0 && elem_face == 1) { + /* In this case, we have the following situation: + * + * x - - - - - - - x - - - - - - - x + * | \ / | \ / | + * | \ / | \ / | + * | \ / | \ / | + * x - - - x neigh | elem x | + * | / \ | / | \ | + * | /pseudo \ | / | \ | + * | / neigh \ | / | \ | + * x - - - - - - - x - - - x - - - x + * + * A subelement elem is given as well as a random subelement pseudo_neigh from a neighboring transition cell. + * We are searching for the subelement id of the real neighbor neigh. + * Note that both transition cells can have different levels. */ + + /* get the location of elem */ + int location_elem[3] = {}; /* {face, is_split, number of subelement at face} */ + t8_element_get_location_of_subelement (elem, location_elem); + + /* Initialize the location array of the real neighbor. */ + int location_neigh[3] = { -1, -1, -1 }; + + /* the pseudo_neigh tranaition cell has a lower level than the elem transition cell */ + if (pquad_w_sub_pseudo_neigh->p4q.level < pquad_w_sub_elem->p4q.level) { + if (location_elem[0] == 0) { /* left face of transition cell */ + if (pquad_w_sub_pseudo_neigh->p4q.y == pquad_w_sub_elem->p4q.y) { + location_neigh[0] = 2; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 1; /* second subelement at face */ + } + if (pquad_w_sub_pseudo_neigh->p4q.y != pquad_w_sub_elem->p4q.y) { + location_neigh[0] = 2; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 0; /* first subelement at face */ + } + } + if (location_elem[0] == 1) { /* upper face of transition cell */ + if (pquad_w_sub_pseudo_neigh->p4q.x == pquad_w_sub_elem->p4q.x) { + location_neigh[0] = 3; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 1; /* first or second subelement at face */ + } + if (pquad_w_sub_pseudo_neigh->p4q.x != pquad_w_sub_elem->p4q.x) { + location_neigh[0] = 3; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 0; /* first subelement at face */ + } + } + if (location_elem[0] == 2) { /* right face of transition cell */ + if (pquad_w_sub_pseudo_neigh->p4q.y == pquad_w_sub_elem->p4q.y) { + location_neigh[0] = 0; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 0; /* first subelement at face */ + } + if (pquad_w_sub_pseudo_neigh->p4q.y != pquad_w_sub_elem->p4q.y) { + location_neigh[0] = 0; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 1; /* first or second subelement at face */ + } + } + if (location_elem[0] == 3) { /* lower face of transition cell */ + if (pquad_w_sub_pseudo_neigh->p4q.x == pquad_w_sub_elem->p4q.x) { + location_neigh[0] = 1; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 0; /* first subelement at face */ + } + if (pquad_w_sub_pseudo_neigh->p4q.x != pquad_w_sub_elem->p4q.x) { + location_neigh[0] = 1; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 1; /* second subelement at face */ + } + } + } + /* the pseudo_neigh tranaition cell has not a lower level than the elem transition cell */ + if (pquad_w_sub_pseudo_neigh->p4q.level >= pquad_w_sub_elem->p4q.level) { + if (location_elem[0] == 0) { /* left face of transition cell */ + location_neigh[0] = 2; /* face */ + location_neigh[1] = 0; /* not split */ + location_neigh[2] = 0; /* first (only) subelement at face */ + } + if (location_elem[0] == 1) { /* upper face of transition cell */ + location_neigh[0] = 3; /* face */ + location_neigh[1] = 0; /* not split */ + location_neigh[2] = 0; /* first (only) subelement at face */ + } + if (location_elem[0] == 2) { /* right face of transition cell */ + location_neigh[0] = 0; /* face */ + location_neigh[1] = 0; /* not split */ + location_neigh[2] = 0; /* first (only) subelement at face */ + } + if (location_elem[0] == 3) { /* lower face of transition cell */ + location_neigh[0] = 1; /* face */ + location_neigh[1] = 0; /* not split */ + location_neigh[2] = 0; /* first (only) subelement at face */ + } + } + + /* check, that a neighbor is found and the location array is adjusted */ + T8_ASSERT (location_neigh[0] >= 0 && location_neigh[1] >= 0 && location_neigh[2] >= 0); + + /* Depending on the location of elem, we have filled location_neigh with the data of the real neighbor. + * This data will be used to determine the sub_id of the neighbor within the transition cell of pseudo_neigh. */ + return t8_element_get_id_from_location (t8_element_get_transition_type (pseudo_neigh), location_neigh); + } + if (!t8_element_is_subelement (elem)) { + /* In this case, we have the following situation: + * + * x - - - - - - - x - - - - - - - x + * | \ / | | + * | \ / | | + * | \ / | | + * x - - - x neigh | elem | + * | / \ | | + * | /pseudo \ | | + * | / neigh \ | | + * x - - - - - - - x - - - - - - - x + * + * Subelement elem is given as well as a random subelement neigh from a neighboring transition cell. + * We are searching for the subelement id of the real neighbor neigh. + * Note that the transition cell of pseudo_neigh and elem can have different levels. */ + + /* Initialize the location array of the real neighbor. */ + int location_neigh[3] = { -1, -1, -1 }; + + /* the pseudo_neigh tranaition cell has a lower level than elem */ + if (pquad_w_sub_pseudo_neigh->p4q.level < pquad_w_sub_elem->p4q.level) { + if (elem_face == 0) { /* left face */ + if (pquad_w_sub_pseudo_neigh->p4q.y == pquad_w_sub_elem->p4q.y) { + location_neigh[0] = 2; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 1; /* second subelement at face */ + } + if (pquad_w_sub_pseudo_neigh->p4q.y != pquad_w_sub_elem->p4q.y) { + location_neigh[0] = 2; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 0; /* first subelement at face */ + } + } + if (elem_face == 1) { /* right face */ + if (pquad_w_sub_pseudo_neigh->p4q.y == pquad_w_sub_elem->p4q.y) { + location_neigh[0] = 0; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 0; /* first subelement at face */ + } + if (pquad_w_sub_pseudo_neigh->p4q.y != pquad_w_sub_elem->p4q.y) { + location_neigh[0] = 0; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 1; /* first or second subelement at face */ + } + } + if (elem_face == 2) { /* lower face */ + if (pquad_w_sub_pseudo_neigh->p4q.x == pquad_w_sub_elem->p4q.x) { + location_neigh[0] = 1; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 0; /* first subelement at face */ + } + if (pquad_w_sub_pseudo_neigh->p4q.x != pquad_w_sub_elem->p4q.x) { + location_neigh[0] = 1; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 1; /* second subelement at face */ + } + } + if (elem_face == 3) { /* upper face */ + if (pquad_w_sub_pseudo_neigh->p4q.x == pquad_w_sub_elem->p4q.x) { + location_neigh[0] = 3; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 1; /* first or second subelement at face */ + } + if (pquad_w_sub_pseudo_neigh->p4q.x != pquad_w_sub_elem->p4q.x) { + location_neigh[0] = 3; /* face */ + location_neigh[1] = 1; /* split */ + location_neigh[2] = 0; /* first subelement at face */ + } + } + } + /* the pseudo_neigh tranaition cell has the same level as elem + * Note that the level of the trnasition cell can not be higher as the level of elem in this case, + * since elem would then be a subelement in a transition cell. */ + if (pquad_w_sub_pseudo_neigh->p4q.level == pquad_w_sub_elem->p4q.level) { + if (elem_face == 0) { /* left face */ + location_neigh[0] = 2; /* face */ + location_neigh[1] = 0; /* not split */ + location_neigh[2] = 0; /* first (only) subelement at face */ + } + if (elem_face == 1) { /* right face */ + location_neigh[0] = 0; /* face */ + location_neigh[1] = 0; /* not split */ + location_neigh[2] = 0; /* first (only) subelement at face */ + } + if (elem_face == 2) { /* lower face */ + location_neigh[0] = 1; /* face */ + location_neigh[1] = 0; /* not split */ + location_neigh[2] = 0; /* first (only) subelement at face */ + } + if (elem_face == 3) { /* upper face */ + location_neigh[0] = 3; /* face */ + location_neigh[1] = 0; /* not split */ + location_neigh[2] = 0; /* first (only) subelement at face */ + } + } + + /* check, that a neighbor is found and the location array is adjusted */ + T8_ASSERT (location_neigh[0] >= 0 && location_neigh[1] >= 0 && location_neigh[2] >= 0); + + /* Depending on the location of elem, we have filled location_neigh with the data of the real neighbor. + * This data will be used to determine the sub_id of the neighbor within the transition cell of pseudo_neigh. */ + return t8_element_get_id_from_location (t8_element_get_transition_type (pseudo_neigh), location_neigh); + } + return -1; /* return negative if no neighbor element could be found */ +} + +int +t8_subelement_scheme_quad_c::t8_element_get_id_from_location (int type, int location[]) +{ + T8_ASSERT (type >= 0 && type <= T8_SUB_QUAD_MAX_TRANSITION_TYPE); + + int sub_id, subelements_count = 0; + double type_temp = double (type); // would work for ints but we use libc pow(double, double) + int binary_type[4] = {}; + int binary_type_clockwise[4] = {}; + + /* get the type as a binary array */ + int iface; + for (iface = 0; iface < P4EST_FACES; iface++) { + if (type_temp >= pow (2.0, 4 - (iface + 1))) { + binary_type[iface] = 1; + type_temp -= pow (2.0, 4 - (iface + 1)); + } + else { + binary_type[iface] = 0; + } + } + + for (iface = 0; iface < P4EST_FACES; + iface++) { /* rearrange the binary type to be in clockwise order of the faces, starting with the left face */ + binary_type_clockwise[iface] = binary_type[subelement_location_to_parent_face[iface]]; + } + + /* count the number of elements up to the given location */ + int element_count; + for (element_count = 0; element_count <= location[0]; element_count++) { + if (element_count == location[0]) { + if (location[1] == 0) { + subelements_count += 1; + } + else { + if (location[2] == 0) { + subelements_count += 1; + } + else { + subelements_count += 2; + } + } + } + else { + subelements_count += binary_type_clockwise[element_count] + 1; + } + } + + /* get the sub_id */ + sub_id = subelements_count - 1; + + return sub_id; +} + +int +t8_subelement_scheme_quad_c::t8_element_get_face_number_of_hypotenuse (const t8_element_t *elem) +{ + T8_ASSERT (t8_element_is_valid (elem)); + T8_ASSERT (t8_element_is_subelement (elem)); + + int location[3] = {}; + t8_element_get_location_of_subelement (elem, location); + + int split = location[1]; + int second = location[2]; + + if (!split) { /* if the face is not split, then the hypotenuse is always face number one */ + return 1; + } + else { + if (!second) { /* otherwise, the subelment is mirrored, depending on the value of 'second' */ + return 0; + } + else { + return 2; + } + } +} + +void +t8_subelement_scheme_quad_c::t8_element_new (int length, t8_element_t **elem) const +{ + /* allocate memory */ + t8_default_scheme_common_c::t8_element_new (length, elem); + + int elem_count; + for (elem_count = 0; elem_count < length; elem_count++) { + t8_quad_with_subelements *pquad_w_sub = (t8_quad_with_subelements *) elem[elem_count]; + t8_element_init (1, elem[elem_count]); + /* set dimension of quad to 2 */ + T8_QUAD_SET_TDIM ((p4est_quadrant_t *) &pquad_w_sub->p4q, 2); + } +} + +void +t8_subelement_scheme_quad_c::t8_element_init (int length, t8_element_t *elem) const +{ + t8_quad_with_subelements *pquad_w_sub = (t8_quad_with_subelements *) elem; + + int elem_count; + + for (elem_count = 0; elem_count < length; elem_count++) { + /* initialize subelement parameters */ + pquad_w_sub[elem_count].transition_type = 0; + pquad_w_sub[elem_count].subelement_id = 0; + +#ifdef T8_ENABLE_DEBUG + /* In debugging mode we iterate over all length many elements and + * set their quad to the level 0 quad with ID 0. */ + p4est_quadrant_t *quad = &pquad_w_sub[elem_count].p4q; + /* Set all values to 0 */ + p4est_quadrant_set_morton (quad, 0, 0); + T8_QUAD_SET_TDIM (quad, 2); + T8_ASSERT (p4est_quadrant_is_extended (quad)); +#endif + } +} + +int +t8_subelement_scheme_quad_c::t8_element_scheme_supports_transitioning (void) +{ + return T8_QUAD_TRANSITION_IS_IMPLEMENTED; +} + +int +t8_subelement_scheme_quad_c::t8_element_transition_scheme_is_conformal (void) +{ + return T8_QUAD_TRANSITION_SCHEME_IS_CONFORMAL; +} + +int +t8_subelement_scheme_quad_c::t8_element_equal (const t8_element_t *elem1, const t8_element_t *elem2) const +{ + + if (t8_element_get_subelement_id ((const t8_element_t *) elem1) + == t8_element_get_subelement_id ((const t8_element_t *) elem2)) { + return 1; + } + else { + return 0; + } +} + +void +t8_subelement_scheme_quad_c::t8_element_root (t8_element_t *elem) const +{ + SC_ABORT_NOT_REACHED (); +} + +void +t8_subelement_scheme_quad_c::t8_element_MPI_Pack (t8_element_t **const elements, const unsigned int count, + void *send_buffer, const int buffer_size, int *position, + sc_MPI_Comm comm) const +{ + SC_ABORT_NOT_REACHED (); + + // int mpiret; + // p4est_quadrant_t **quads = (p4est_quadrant_t **) elements; + // for (unsigned int ielem = 0; ielem < count; ielem++) { + // mpiret = sc_MPI_Pack (&(quads[ielem]->x), 1, sc_MPI_INT, send_buffer, buffer_size, position, comm); + // SC_CHECK_MPI (mpiret); + // mpiret = sc_MPI_Pack (&quads[ielem]->y, 1, sc_MPI_INT, send_buffer, buffer_size, position, comm); + // SC_CHECK_MPI (mpiret); + // mpiret = sc_MPI_Pack (&quads[ielem]->level, 1, sc_MPI_INT8_T, send_buffer, buffer_size, position, comm); + // SC_CHECK_MPI (mpiret); + // } +} + +/* each quad is packed as x,y coordinates and the level */ +void +t8_subelement_scheme_quad_c::t8_element_MPI_Pack_size (const unsigned int count, sc_MPI_Comm comm, int *pack_size) const +{ + SC_ABORT_NOT_REACHED (); + // int singlesize = 0; + // int datasize = 0; + // int mpiret; + + // /* x,y */ + // mpiret = sc_MPI_Pack_size (1, sc_MPI_INT, comm, &datasize); + // SC_CHECK_MPI (mpiret); + // singlesize += 2 * datasize; + + // /* level */ + // mpiret = sc_MPI_Pack_size (1, sc_MPI_INT8_T, comm, &datasize); + // SC_CHECK_MPI (mpiret); + // singlesize += datasize; + + // *pack_size = count * singlesize; +} + +/* each quad is packed as x,y coordinates and the level */ +void +t8_subelement_scheme_quad_c::t8_element_MPI_Unpack (void *recvbuf, const int buffer_size, int *position, + t8_element_t **elements, const unsigned int count, + sc_MPI_Comm comm) const +{ + SC_ABORT_NOT_REACHED (); + // int mpiret; + // p4est_quadrant_t **quads = (p4est_quadrant_t **) elements; + // for (unsigned int ielem = 0; ielem < count; ielem++) { + // mpiret = sc_MPI_Unpack (recvbuf, buffer_size, position, &(quads[ielem]->x), 1, sc_MPI_INT, comm); + // SC_CHECK_MPI (mpiret); + // mpiret = sc_MPI_Unpack (recvbuf, buffer_size, position, &(quads[ielem]->y), 1, sc_MPI_INT, comm); + // SC_CHECK_MPI (mpiret); + // mpiret = sc_MPI_Unpack (recvbuf, buffer_size, position, &(quads[ielem]->level), 1, sc_MPI_INT8_T, comm); + // SC_CHECK_MPI (mpiret); + // } +} + +#ifdef T8_ENABLE_DEBUG +void +t8_subelement_scheme_quad_c::t8_element_debug_print (const t8_element_t *elem) const +{ + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + + t8_productionf ("\n|------------ t8_element_debug_print: ------------|" + "\n| Transition Type: %i" + "\n| Subelement ID: %i" + "\n| Anchor (Morton): (%i,%i)" + "\n| Anchor (ref coords): (%lf,%lf)" + "\n| Level: %i" + "\n|-------------------------------------------------|\n", + pquad_w_sub->transition_type, pquad_w_sub->subelement_id, pquad_w_sub->p4q.x, pquad_w_sub->p4q.y, + (double) pquad_w_sub->p4q.x / (double) P4EST_ROOT_LEN, + (double) pquad_w_sub->p4q.y / (double) P4EST_ROOT_LEN, pquad_w_sub->p4q.level); + + /* if the element is not valid, abort, but after printing */ + T8_ASSERT (t8_element_is_valid (elem)); +} + +/* *INDENT-OFF* */ +/* indent bug, indent adds a second "const" modifier */ +int +t8_subelement_scheme_quad_c::t8_element_is_valid (const t8_element_t *elem) const +/* *INDENT-ON* */ + +{ + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + const p4est_quadrant_t *q = &pquad_w_sub->p4q; + + /* the 4pest quadrant AND the subelement values must be valid such that the whole element is valid */ + return (p4est_quadrant_is_extended (q) && t8_element_subelement_values_are_valid (elem)); +} + +/* *INDENT-OFF* */ +/* indent bug, indent adds a second "const" modifier */ +int +t8_subelement_scheme_quad_c::t8_element_subelement_values_are_valid (const t8_element_t *elem) const +/* *INDENT-ON* */ + +{ + const t8_quad_with_subelements *pquad_w_sub = (const t8_quad_with_subelements *) elem; + + return ((pquad_w_sub->transition_type >= 0 && pquad_w_sub->transition_type <= T8_SUB_QUAD_MAX_TRANSITION_TYPE) + || pquad_w_sub->transition_type == 0) + && ((pquad_w_sub->subelement_id >= 0 && pquad_w_sub->subelement_id <= T8_SUB_QUAD_MAX_SUBELEMENT_ID) + || pquad_w_sub->subelement_id == 0); +} + +void +t8_subelement_scheme_quad_c::t8_element_to_string (const t8_element_t *elem, char *debug_string, + const int string_size) const +{ + SC_ABORT_NOT_REACHED (); +} +#endif + +/* Constructor */ +t8_subelement_scheme_quad_c::t8_subelement_scheme_quad_c (void) +{ + eclass = T8_ECLASS_QUAD; + element_size = sizeof (t8_quad_with_subelements); + ts_context = sc_mempool_new (element_size); +} + +t8_subelement_scheme_quad_c::~t8_subelement_scheme_quad_c () +{ + /* This destructor is empty since the destructor of the + * default_common scheme is called automatically and it + * suffices to destroy the quad_scheme. + * However we need to provide an implementation of the destructor + * and hence this empty function. */ +} + +T8_EXTERN_C_END (); diff --git a/src/t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad_cxx.hxx b/src/t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad_cxx.hxx new file mode 100644 index 0000000000..578b4ca637 --- /dev/null +++ b/src/t8_schemes/t8_transition/t8_transition_conformal_quad/t8_transition_conformal_quad_cxx.hxx @@ -0,0 +1,556 @@ +/* + 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. +*/ + +/** \file t8_transition_conformal_quad_cxx.hxx + * We use a p4est_quadrant_t object as storage for the T8 quadrant. + * Additionally, we store information for transition cells of triangular subelements: + * + * (i) transition_type - type of the transition cell of the current element + * (ii) subelement_id - subelement id of the current element + * + * In order to refine a quad element into a transition cell, it is important to know these additional parameter. + */ + +#ifndef T8_TRANSITION_CONFORMAL_QUAD_CXX_HXX +#define T8_TRANSITION_CONFORMAL_QUAD_CXX_HXX + +#include +#include + +#include + +#include +#include +#include +#include + +/** The structure holding a quadrilateral element in the default scheme. + * We make this definition public for interoperability of element classes. + * We might want to put this into a private, scheme-specific header file. + */ + +/* Define the struct, that stores all information needed for the quad scheme and subelements. + * + * p4est quadrant recursive quad refinement, using a transition + * refinement cell with subelements + * x - - - - - x x - - x - - x x - - - - - x + * | | | | | | \ 2 / | + * | | | | | | 1 \ / | + * | | --> x - - x - - x or x - - x 3 | + * | | | | | | 0 / | \ | + * | | | | | | / 5 | 4 \ | + * x - - - - - x x - - x - - x x - - x - - x + * + * A p4est quadrant can be refined, using either the standard quad scheme, or a transition cell, consisting of different subelements. + * The quad refinement scheme is recursive, whereas a transition cell can only be used once, for example to remove hanging nodes, after the mesh has been adapted and balanced. + * There are different types of transition cells possible, which we will refer to as transition_type. + * Each transition cell consists of different subelements. The given example consists of 6 different subelements, whose ids range from 0 to 5. + * A dummy variable will store the information, whether a given element is a subelement or a standard quad element. */ + +/** Variable type for an integer valued coordinate of a quad (sub-)element. */ +typedef p4est_qcoord_t t8_dquad_subelement_coord_t; + +typedef struct +{ + /* p4est quadrant */ + p4est_quadrant_t p4q; + /* stores transition cell information (default for non-subelements is 0 and for subelements it is != 0 - is therefore used as a is_subelement check) */ + int transition_type; + /* stores subelement information (default for non-subelements is 0) */ + int subelement_id; +} t8_quad_with_subelements; + +/** define some subelement related constants */ +#define T8_SUB_QUAD_MAX_TRANSITION_TYPE 15 +#define T8_SUB_QUAD_MAX_SUBELEMENT_ID 7 +#define T8_QUAD_SUBELEMENT_FACES 3 + +#define T8_QUAD_TRANSITION_IS_IMPLEMENTED 1 +#define T8_QUAD_TRANSITION_SCHEME_IS_CONFORMAL 1 + +#if 0 +/** Provide an implementation for the quadrilateral element class with subelements. */ +t8_eclass_scheme_t *t8_subelement_scheme_new_quad (void); +#endif + +struct t8_subelement_scheme_quad_c: public t8_default_scheme_common_c +{ + public: + /** The virtual table for a particular implementation of an element class. */ + + /** Constructor. */ + t8_subelement_scheme_quad_c (); + + ~t8_subelement_scheme_quad_c (); + + /** Allocate memory for a given number of elements. + * In debugging mode, ensure that all elements are valid \ref t8_element_is_valid. + */ + virtual void + t8_element_new (int length, t8_element_t **elem) const; + + /** Return the maximum level allowed for this element class. */ + virtual int + t8_element_maxlevel (void) const; + + /** Return the type of each child in the ordering of the implementation. */ + virtual t8_eclass_t + t8_element_child_eclass (int childid) const; + + /** Return the refinement level of an element. */ + virtual int + t8_element_level (const t8_element_t *elem) const; + + /** Copy one element to another */ + virtual void + t8_element_copy (const t8_element_t *source, t8_element_t *dest) const; + + /** Compare to elements. returns negative eif elem1 < elem2, zero if elem1 equals elem2 + * and positive if elem1 > elem2. + * If elem2 is a copy of elem1 then the elements are equal. + * If both elements are sibling subelements, return 0 if they are identical (same sub_id) and 1 otherwise. + */ + virtual int + t8_element_compare (const t8_element_t *elem1, const t8_element_t *elem2) const; + + /** Construct the parent of a given element. */ + virtual void + t8_element_parent (const t8_element_t *elem, t8_element_t *parent) const; + + /** Construct a same-size sibling of a given element. */ + virtual void + t8_element_sibling (const t8_element_t *elem, int sibid, t8_element_t *sibling) const; + + /** Compute the number of face of a given element. */ + virtual int + t8_element_num_faces (const t8_element_t *elem) const; + + /** Compute the maximum number of faces of a given element and all of its + * descendants. + * \param [in] elem The element. + * \return The maximum number of faces of \a elem and its descendants. + */ + virtual int + t8_element_max_num_faces (const t8_element_t *elem) const; + + /** Return the number of children of an element when it is refined. */ + virtual int + t8_element_num_children (const t8_element_t *elem) const; + + /** Return the number of siblings of an element (or the number of elements in the family of elem) */ + virtual int + t8_element_num_siblings (const t8_element_t *elem) const; + + /** Return the number of children of an element's face when the element is refined. */ + virtual int + t8_element_num_face_children (const t8_element_t *elem, const int face) const; + + /** Return the number of children of an element's face when the element is refined. */ + virtual int + t8_element_neighbor_is_sibling (const t8_element_t *elem, int face) const; + + /** Return the number of sibling neighbors at a given face. */ + virtual int + t8_element_get_num_sibling_neighbors_at_face (const t8_element_t *elem, int face) const; + + /** Return zero refine value for schemes that do not have a transition implementation. */ + virtual int + t8_element_get_transition_refine_identifier (void) const; + + /** Return the corner number of an element's face corner. */ + virtual int + t8_element_get_face_corner (const t8_element_t *element, int face, int corner) const; + + /** Return the face numbers of the faces sharing an element's corner. */ + virtual int + t8_element_get_corner_face (const t8_element_t *element, int corner, int face) const; + + /** Construct the child element of a given number. */ + virtual void + t8_element_child (const t8_element_t *elem, int childid, t8_element_t *child) const; + + /** Construct all sibling neighbors of elem at face - it is required that sibling neighbors of elem at face exist */ + virtual void + + t8_element_get_sibling_neighbor_in_transition_cell (const t8_element_t *elem, const int face, const int num_neighbors, + t8_element_t *neighbor_at_face[], int *neigh_face[]); + + /** Construct all children of a given element. */ + virtual void + t8_element_children (const t8_element_t *elem, int length, t8_element_t *c[]) const; + + /** Return the child id of an element */ + virtual int + t8_element_child_id (const t8_element_t *elem) const; + + /** Compute the ancestor id of an element */ + virtual int + t8_element_ancestor_id (const t8_element_t *elem, int level) const; + + /** Return nonzero if collection of elements is a family */ + virtual int + t8_element_is_family (t8_element_t *const *fam) const; + + /** Construct the nearest common ancestor of two elements in the same tree. */ + virtual void + t8_element_nca (const t8_element_t *elem1, const t8_element_t *elem2, t8_element_t *nca) const; + + /** Compute the element shape of the face of an element. */ + virtual t8_element_shape_t + t8_element_face_shape (const t8_element_t *elem, int face) const; + + /** Given an element and a face of the element, compute all children of + * the element that touch the face. */ + /** Given an element and a face of the element, compute all children of + * the element that touch the face. */ + virtual void + t8_element_children_at_face (const t8_element_t *elem, int face, t8_element_t *children[], int num_children, + int *child_indices) const; + + /** Given a face of an element and a child number of a child of that face, return the face number + * of the child of the element that matches the child face. */ + virtual int + t8_element_face_child_face (const t8_element_t *elem, int face, int face_child) const; + + /** Given a face of an element return the face number + * of the parent of the element that matches the element's face. Or return -1 if + * no face of the parent matches the face. */ + virtual int + t8_element_face_parent_face (const t8_element_t *elem, int face) const; + + /** Transform the coordinates of a quadrilateral considered as boundary element + * in a tree-tree connection. */ + virtual void + t8_element_transform_face (const t8_element_t *elem1, t8_element_t *elem2, int orientation, int sign, + int is_smaller_face) const; + + /** Given a boundary face inside a root tree's face construct + * the element inside the root tree that has the given face as a + * face. */ + virtual int + t8_element_extrude_face (const t8_element_t *face, const t8_eclass_scheme_c *face_scheme, t8_element_t *elem, + int root_face) const; + + /** Return the tree face id given a boundary face. */ + virtual int + t8_element_tree_face (const t8_element_t *elem, int face) const; + + /** Construct the first descendant of an element that touches a given face. */ + virtual void + t8_element_first_descendant_face (const t8_element_t *elem, int face, t8_element_t *first_desc, int level) const; + + /** Construct the last descendant of an element that touches a given face. */ + virtual void + t8_element_last_descendant_face (const t8_element_t *elem, int face, t8_element_t *last_desc, int level) const; + + /** Construct the boundary element at a specific face. */ + virtual void + t8_element_boundary_face (const t8_element_t *elem, int face, t8_element_t *boundary, + const t8_eclass_scheme_c *boundary_scheme) const; + + /** Construct all codimension-one boundary elements of a given element. */ + virtual void + t8_element_boundary (const t8_element_t *elem, int min_dim, int length, t8_element_t **boundary) const; + + /** Compute whether a given element shares a given face with its root tree. + * \param [in] elem The input element. + * \param [in] face A face of \a elem. + * \return True if \a face is a subface of the element's root element. + */ + virtual int + t8_element_is_root_boundary (const t8_element_t *elem, int face) const; + + /** Construct the face neighbor of a given element if this face neighbor + * is inside the root tree. Return 0 otherwise. */ + virtual int + t8_element_face_neighbor_inside (const t8_element_t *elem, t8_element_t *neigh, int face, int *neigh_face) const; + + /** Initialize an element according to a given linear id */ + virtual void + t8_element_set_linear_id (t8_element_t *elem, int level, t8_linearidx_t id) const; + + /** Calculate the linear id of an element */ + virtual t8_linearidx_t + t8_element_get_linear_id (const t8_element_t *elem, int level) const; + + /** Calculate the first descendant of a given element e. That is, the + * first element in a uniform refinement of e of the maximal possible level. + */ + virtual void + t8_element_first_descendant (const t8_element_t *elem, t8_element_t *desc, int level) const; + + /** Calculate the last descendant of a given element e. That is, the + * last element in a uniform refinement of e of the maximal possible level. + */ + virtual void + t8_element_last_descendant (const t8_element_t *elem, t8_element_t *desc, int level) const; + + /** Compute s as a successor of t*/ + virtual void + t8_element_successor (const t8_element_t *t, t8_element_t *s) const; + + /** Get the integer coordinates of the anchor node of an element */ + virtual void + t8_element_anchor (const t8_element_t *elem, int anchor[3]) const; + + /** Get the integer root length of an element, that is the length of + * the level 0 ancestor. + */ + virtual int + t8_element_root_len (const t8_element_t *elem) const; + + /** Compute the integer coordinates of a given element vertex. */ + virtual void + t8_element_vertex_coords (const t8_element_t *t, int vertex, t8_dquad_subelement_coord_t coords[]) const; + + /** Compute the integer coordinates of a given element vertex. + * The default scheme implements the Morton type SFCs. In these SFCs the + * elements are positioned in a cube [0,1]^(dL) with dimension d (=0,1,2,3) and + * L the maximum refinement level. + * All element vertices have integer coordinates in this cube. + * \param [in] elem The element. + * \param [in] vertex The id of the vertex whose coordinates shall be computed. + * \param [out] coords An array of at least as many integers as the element's dimension + * whose entries will be filled with the coordinates of \a vertex. + */ + virtual void + t8_element_vertex_integer_coords (const t8_element_t *elem, int vertex, t8_dquad_subelement_coord_t coords[]) const; + + /** Convert a point in the reference space of an element to a point in the + * reference space of the tree. + * + * \param [in] elem The element. + * \param [in] coords_input The coordinates of the point in the reference space of the element. + * \param [in] user_data User data. + * \param [out] out_coords The coordinates of the point in the reference space of the tree. + */ + virtual void + t8_element_reference_coords (const t8_element_t *elem, const double *ref_coords, const size_t num_coords, + double *out_coords) const; + + /** Construct a transition cell of type type */ + virtual void + t8_element_to_transition_cell (const t8_element_t *elem, int type, t8_element_t *c[]); + + /** Determine the number of sibling subelements, of a transition cell of a specific type */ + virtual int + t8_element_get_number_of_subelements (int transition_type) const; + + /** Test whether a given element is a subelement or not */ + virtual int + t8_element_is_subelement (const t8_element *elem) const; + + /** Get the subelement type of elem */ + virtual int + t8_element_get_transition_type (const t8_element *elem); + + /** Get the subelement id of elem */ + virtual int + t8_element_get_subelement_id (const t8_element *elem) const; + + /** Get the subelement id of the neighbor subelement of elem at face elem_face + * that is a sibling of the subelement neigh. + */ + virtual int + t8_element_find_neighbor_in_transition_cell (const t8_element_t *elem, const t8_element_t *neigh, int elem_face); + + /** Get the face-number of the hypotenuse of the triangular subelement */ + virtual int + t8_element_get_face_number_of_hypotenuse (const t8_element_t *elem); + + /** Return 1 if the eclass scheme has an implementation for subelements. Return 0 otherwise. */ + virtual int + t8_element_scheme_supports_transitioning (void); + + /** Return 1 if the eclass scheme has an implementation for subelements. Return 0 otherwise. */ + virtual int + t8_element_transition_scheme_is_conformal (void); + + /** Returns true, if there is one element in the tree, that does not refine into 2^dim children. + * Returns false otherwise. + * \return non-zero if there is one element in the tree that does not refine into 2^dim children. + */ + virtual int + t8_element_refines_irregular (void) const; + + /** Get the shape of a given element. Subelements are triangles */ + virtual t8_element_shape_t + t8_element_shape (const t8_element_t *elem) const; + + /** Return the number of vertices of an element */ + virtual int + t8_element_num_corners (const t8_element_t *elem) const; + + /** Compute the coordinates of a given element vertex inside a reference tree + * that is embedded into [0,1]^d (d = dimension). + * \param [in] t The element to be considered. + * \param [in] vertex The id of the vertex whose coordinates shall be computed. + * \param [out] coords An array of at least as many doubles as the element's dimension + * whose entries will be filled with the coordinates of \a vertex. + */ + virtual void + t8_element_vertex_reference_coords (const t8_element_t *t, int vertex, double coords[]) const; + + /** Check if two elements are equal. + * \param [in] ts Implementation of a class scheme. + * \param [in] elem1 The first element. + * \param [in] elem2 The second element. + * \return 1 if the elements are equal, 0 if they are not equal + */ + virtual int + t8_element_equal (const t8_element_t *elem1, const t8_element_t *elem2) const; + + /** Fills an element with the root element. + * \param [in,out] elem The element to be filled with root. + */ + void + t8_element_root (t8_element_t *elem) const; + + /** Initialize an array of allocated quads/subelements. + * \param [in] length The number of hex to be initialized. + * \param [in,out] elems On input an array of \b length many allocated + * elements. + * \param [in] called_new True if the elements in \a elem were created by a call + * to \ref t8_element_new. False if no element in \a elem + * was created in this way. The case that only some elements + * were created by \ref t8_element_new should never occur. + * \note In debugging mode, an element that was passed to \ref t8_element_init + * must pass \ref t8_element_is_valid. + * \note If an element was created by \ref t8_element_new then \ref t8_element_init + * may not be called for it. Thus, \ref t8_element_new should initialize an element + * in the same way as a call to \ref t8_element_init would. + * Thus, if \a called_new is true this function should usually do nothing. + * \see t8_element_new + * \see t8_element_is_valid + */ + virtual void + t8_element_init (int length, t8_element_t *elem) const; + +#ifdef T8_ENABLE_DEBUG + /** TODO: this should be the new element_print_element function */ + virtual void + t8_element_debug_print (const t8_element_t *elem) const; + + /** Query whether an element is valid */ + virtual int + t8_element_is_valid (const t8_element_t *t) const; + + /** + * Print a given element. For a example for a triangle print the coordinates + * and the level of the triangle. This function is only available in the + * debugging configuration. + * + * \param [in] elem The element to print + */ + virtual void + t8_element_to_string (const t8_element_t *elem, char *debug_string, const int string_size) const; + +#endif + + protected: + /** This function determines the vertex coordinates of subelements. + * \param [in] elem A valid subelement + * \param [in] vertex the number of the vertex whose coordinates should be determined + * \param [out] coords An array whose entries will be filled with the coordinates of the + * subelement. + * Note that subelements can have another number of vertices compared to the used + * eclass scheme. For example, subelements that remove hanging nodes from the quad scheme + * are triangles with 3 instead of 4 vertices. + */ + void + t8_element_vertex_coords_of_subelement (const t8_element_t *t, int vertex, int coords[]) const; + + /** This function will determine the location of a specific subelement in the parent element. + * Since different subelement types are possible, it is a priori not known where for example the + * subelement with id 3 is located. + * \param [in] elem A valid subelement + * \param [out] An array, whose entries are face_number, split and sub_face_id + * face_number: the face number (clockwise enumeration), the given subelement is adjacent to (value between 0 and 3) + * It can be translated to the quad enumeration via subelement_location_to_parent_dual_face[location[0]] + * split: whether there is a hanging node at the face, the subelement is adjacent to + * (value 0 if there is not hanging node and 1 if there is one) + * sub_face_id: if there is a hanging node at the face, it is important to know if the given + * subelement is the first or the second subelement at this face + * (value 0 if it is the first and 1 if it is the second) + * The information in the location can be used to automatically determine the vertices of any subelement. + * Since this function is only used to determine the vertices of subelements, it can be declared as a private/protected function. + */ + void + t8_element_get_location_of_subelement (const t8_element_t *elem, int location[]) const; + + /** This help function returns the subelement if of an element whose location and transition type is known. */ + int + t8_element_get_id_from_location (int type, int location[]); + + /** This function copies the subelement values from source to dest. + * \param [in] source A valid element + * \param [in,out] dest A valid element, whose subelement values are equal to those of source + */ + void + t8_element_copy_subelement_values (const t8_element_t *source, t8_element_t *dest) const; + + /** This function resets the subelement values of an element to the default value -1. + * \param [in,out] elem A valid element, whose subelement values have been reset. + */ + void + t8_element_reset_subelement_values (t8_element_t *elem) const; + + virtual void + t8_element_MPI_Pack (t8_element_t **const elements, const unsigned int count, void *send_buffer, int buffer_size, + int *position, sc_MPI_Comm comm) const; + + /** Determine an upper bound for the size of the packed message of \b count elements + * \param [in] count Number of elements to pack + * \param [in] comm MPI Communicator + * \param [out] pack_size upper bound on the message size + */ + virtual void + t8_element_MPI_Pack_size (const unsigned int count, sc_MPI_Comm comm, int *pack_size) const; + + /** Unpack multiple elements from contiguous memory that was received via MPI. + * \param [in] recvbuf Buffer from which to unpack the elements + * \param [in] buffer_size size of the buffer (in order to check that we don't access out of range) + * \param [in, out] position the position of the first byte that is not already packed + * \param [in] elements Array of initialised elements that is to be filled from the message + * \param [in] count Number of elements to unpack + * \param [in] comm MPI Communicator + */ + virtual void + t8_element_MPI_Unpack (void *recvbuf, const int buffer_size, int *position, t8_element_t **elements, + const unsigned int count, sc_MPI_Comm comm) const; + +#ifdef T8_ENABLE_DEBUG + /** Query whether an elements subelement values are valid + * \param [in] source A element + * \return true, if the subelement values are valid + */ + int + t8_element_subelement_values_are_valid (const t8_element_t *elem) const; +#endif + + /** Default quad scheme which is used to query quad specific computations, + * for example in \ref t8_element_reference_coords + */ + struct t8_default_scheme_quad_c default_quad_scheme; +}; + +#endif /* !T8_TRANSITION_QUAD_CXX_HXX */ diff --git a/src/t8_schemes/t8_transition/t8_transition_cxx.hxx b/src/t8_schemes/t8_transition/t8_transition_cxx.hxx new file mode 100644 index 0000000000..34c9d29b85 --- /dev/null +++ b/src/t8_schemes/t8_transition/t8_transition_cxx.hxx @@ -0,0 +1,51 @@ +/* + 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. +*/ + +/** \file t8_transition_cxx.hxx + * This file is the point of entry for our transition implementation. + * This scheme points to a consistent implementation of all element classes. + */ + +#ifndef T8_TRANSITION_CXX_HXX +#define T8_TRANSITION_CXX_HXX + +#include + +/* This is the unique identifier for the transition function. */ + +enum T8_TRANSITION_REFINE_IDENTIFIER { + T8_TRANSITION_CONFORMAL_ZERO_REFINE_FUNCTION = 0, + T8_TRANSITION_CONFORMAL_QUAD_REFINE_FUNCTION, + T8_TRANSITION_CONFORMAL_HEX_REFINE_FUNCTION +}; + +T8_EXTERN_C_BEGIN (); + +/** Return the subelement element implementations of t8code. */ +t8_scheme_cxx_t * +t8_scheme_new_transition_quad_cxx (void); +t8_scheme_cxx_t * +t8_scheme_new_transition_hex_cxx (void); + +T8_EXTERN_C_END (); + +#endif /* !T8_TRANSITION_CXX_HXX */ diff --git a/test/Makefile.am b/test/Makefile.am index 0dc6b221d1..edc8541597 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -11,6 +11,7 @@ t8code_googletest_internal_headers = \ thirdparty/googletest-mpi/gtest/gtest.h \ test/t8_gtest_macros.hxx \ test/t8_schemes/t8_gtest_dfs_base.hxx \ + test/t8_schemes_transition/t8_gtest_dfs_base.hxx \ test/t8_cmesh_generator/t8_cmesh_example_sets.hxx \ test/t8_cmesh_generator/t8_gtest_cmesh_cartestian_product.hxx \ test/t8_cmesh_generator/t8_gtest_cmesh_sum_of_sets.hxx \ @@ -29,8 +30,10 @@ t8code_googletest_programs = \ test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_lagrange \ test/t8_gtest_cmesh_bcast \ test/t8_schemes/t8_gtest_nca \ + test/t8_schemes_transition/t8_gtest_nca \ test/t8_schemes/t8_gtest_pyra_connectivity \ test/t8_schemes/t8_gtest_face_neigh \ + test/t8_schemes_transition/t8_gtest_face_neigh \ test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_linear \ test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_cad \ test/t8_gtest_eclass \ @@ -47,9 +50,13 @@ t8code_googletest_programs = \ test/t8_schemes/t8_gtest_element_ref_coords \ test/t8_geometry/t8_gtest_geometry_handling \ test/t8_schemes/t8_gtest_descendant \ + test/t8_schemes_transition/t8_gtest_descendant \ test/t8_schemes/t8_gtest_find_parent \ + test/t8_schemes_transition/t8_gtest_find_parent \ test/t8_schemes/t8_gtest_equal \ + test/t8_schemes_transition/t8_gtest_equal \ test/t8_schemes/t8_gtest_root \ + test/t8_schemes_transition/t8_gtest_root \ test/t8_cmesh/t8_gtest_cmesh_face_is_boundary \ test/t8_cmesh/t8_gtest_cmesh_partition \ test/t8_cmesh/t8_gtest_cmesh_copy \ @@ -60,6 +67,7 @@ t8code_googletest_programs = \ test/t8_cmesh/t8_gtest_cmesh_add_attributes_when_derive \ test/t8_cmesh/t8_gtest_attribute_gloidx_array \ test/t8_schemes/t8_gtest_successor \ + test/t8_schemes_transition/t8_gtest_successor \ test/t8_schemes/t8_gtest_boundary_extrude \ test/t8_forest/t8_gtest_search \ test/t8_gtest_netcdf_linkage \ @@ -69,6 +77,7 @@ t8code_googletest_programs = \ test/t8_forest/t8_gtest_find_owner \ test/t8_forest/t8_gtest_forest_face_normal \ test/t8_schemes/t8_gtest_face_descendant \ + test/t8_schemes_transition/t8_gtest_face_descendant \ test/t8_geometry/t8_gtest_point_inside \ test/t8_forest/t8_gtest_user_data \ test/t8_forest/t8_gtest_transform \ @@ -76,6 +85,7 @@ t8code_googletest_programs = \ test/t8_forest/t8_gtest_ghost_delete \ test/t8_forest/t8_gtest_ghost_and_owner \ test/t8_forest/t8_gtest_forest_commit \ + test/t8_forest_transition/t8_gtest_forest_commit \ test/t8_forest/t8_gtest_balance \ test/t8_forest/t8_gtest_element_is_leaf \ test/t8_IO/t8_gtest_vtk_reader \ @@ -88,11 +98,11 @@ t8code_googletest_programs = \ test/t8_cmesh/t8_gtest_cmesh_tree_vertices_negative_volume \ test/t8_schemes/t8_gtest_default \ test/t8_schemes/t8_gtest_pack_unpack \ + test/t8_schemes_transition/t8_gtest_pack_unpack \ test/t8_schemes/t8_gtest_child_parent_face \ - test/t8_cmesh_generator/t8_gtest_cmesh_generator_test \ + test/t8_schemes_transition/t8_gtest_child_parent_face \ test/t8_forest/t8_gtest_partition_data - test_t8_IO_t8_gtest_vtk_reader_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_IO/t8_gtest_vtk_reader.cxx @@ -105,6 +115,10 @@ test_t8_schemes_t8_gtest_nca_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_schemes/t8_gtest_nca.cxx +test_t8_schemes_transition_t8_gtest_nca_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_schemes_transition/t8_gtest_nca.cxx + test_t8_schemes_t8_gtest_pyra_connectivity_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_schemes/t8_gtest_pyra_connectivity.cxx @@ -113,6 +127,10 @@ test_t8_schemes_t8_gtest_face_neigh_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_schemes/t8_gtest_face_neigh.cxx +test_t8_schemes_transition_t8_gtest_face_neigh_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_schemes_transition/t8_gtest_face_neigh.cxx + test_t8_geometry_t8_geometry_implementations_t8_gtest_geometry_linear_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_linear.cxx @@ -185,18 +203,34 @@ test_t8_schemes_t8_gtest_descendant_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_schemes/t8_gtest_descendant.cxx +test_t8_schemes_transition_t8_gtest_descendant_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_schemes_transition/t8_gtest_descendant.cxx + test_t8_schemes_t8_gtest_find_parent_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_schemes/t8_gtest_find_parent.cxx +test_t8_schemes_transition_t8_gtest_find_parent_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_schemes_transition/t8_gtest_find_parent.cxx + test_t8_schemes_t8_gtest_equal_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_schemes/t8_gtest_equal.cxx +test_t8_schemes_transition_t8_gtest_equal_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_schemes_transition/t8_gtest_equal.cxx + test_t8_schemes_t8_gtest_root_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_schemes/t8_gtest_root.cxx +test_t8_schemes_transition_t8_gtest_root_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_schemes_transition/t8_gtest_root.cxx + test_t8_cmesh_t8_gtest_cmesh_face_is_boundary_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_cmesh/t8_gtest_cmesh_face_is_boundary.cxx @@ -229,6 +263,10 @@ test_t8_schemes_t8_gtest_successor_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_schemes/t8_gtest_successor.cxx +test_t8_schemes_transition_t8_gtest_successor_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_schemes_transition/t8_gtest_successor.cxx + test_t8_schemes_t8_gtest_boundary_extrude_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_schemes/t8_gtest_boundary_extrude.cxx @@ -265,6 +303,10 @@ test_t8_schemes_t8_gtest_face_descendant_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_schemes/t8_gtest_face_descendant.cxx +test_t8_schemes_transition_t8_gtest_face_descendant_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_schemes_transition/t8_gtest_face_descendant.cxx + test_t8_geometry_t8_gtest_point_inside_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_geometry/t8_gtest_point_inside.cxx @@ -293,6 +335,10 @@ test_t8_forest_t8_gtest_forest_commit_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_forest/t8_gtest_forest_commit.cxx +test_t8_forest_transition_t8_gtest_forest_commit_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_forest_transition/t8_gtest_forest_commit.cxx + test_t8_forest_t8_gtest_balance_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_forest/t8_gtest_balance.cxx @@ -331,12 +377,20 @@ test_t8_schemes_t8_gtest_default_SOURCES = \ test_t8_schemes_t8_gtest_pack_unpack_SOURCES = \ test/t8_gtest_main.cxx \ - test/t8_schemes/t8_gtest_pack_unpack.cxx + test/t8_schemes/t8_gtest_pack_unpack.cxx + +test_t8_schemes_transition_t8_gtest_pack_unpack_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_schemes_transition/t8_gtest_pack_unpack.cxx test_t8_schemes_t8_gtest_child_parent_face_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_schemes/t8_gtest_child_parent_face.cxx +test_t8_schemes_transition_t8_gtest_child_parent_face_SOURCES = \ + test/t8_gtest_main.cxx \ + test/t8_schemes_transition/t8_gtest_child_parent_face.cxx + test_t8_cmesh_generator_t8_gtest_cmesh_generator_test_SOURCES = \ test/t8_gtest_main.cxx \ test/t8_cmesh_generator/t8_gtest_cmesh_generator_test.cxx @@ -369,6 +423,10 @@ test_t8_schemes_t8_gtest_nca_LDADD = $(t8_gtest_target_ld_add) test_t8_schemes_t8_gtest_nca_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_schemes_t8_gtest_nca_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_schemes_transition_t8_gtest_nca_LDADD = $(t8_gtest_target_ld_add) +test_t8_schemes_transition_t8_gtest_nca_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_schemes_transition_t8_gtest_nca_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_schemes_t8_gtest_pyra_connectivity_LDADD = $(t8_gtest_target_ld_add) test_t8_schemes_t8_gtest_pyra_connectivity_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_schemes_t8_gtest_pyra_connectivity_CPPFLAGS = $(t8_gtest_target_cpp_flags) @@ -377,6 +435,10 @@ test_t8_schemes_t8_gtest_face_neigh_LDADD = $(t8_gtest_target_ld_add) test_t8_schemes_t8_gtest_face_neigh_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_schemes_t8_gtest_face_neigh_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_schemes_transition_t8_gtest_face_neigh_LDADD = $(t8_gtest_target_ld_add) +test_t8_schemes_transition_t8_gtest_face_neigh_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_schemes_transition_t8_gtest_face_neigh_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_geometry_t8_geometry_implementations_t8_gtest_geometry_linear_LDADD = $(t8_gtest_target_ld_add) test_t8_geometry_t8_geometry_implementations_t8_gtest_geometry_linear_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_geometry_t8_geometry_implementations_t8_gtest_geometry_linear_CPPFLAGS = $(t8_gtest_target_cpp_flags) @@ -449,18 +511,34 @@ test_t8_schemes_t8_gtest_descendant_LDADD = $(t8_gtest_target_ld_add) test_t8_schemes_t8_gtest_descendant_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_schemes_t8_gtest_descendant_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_schemes_transition_t8_gtest_descendant_LDADD = $(t8_gtest_target_ld_add) +test_t8_schemes_transition_t8_gtest_descendant_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_schemes_transition_t8_gtest_descendant_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_schemes_t8_gtest_find_parent_LDADD = $(t8_gtest_target_ld_add) test_t8_schemes_t8_gtest_find_parent_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_schemes_t8_gtest_find_parent_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_schemes_transition_t8_gtest_find_parent_LDADD = $(t8_gtest_target_ld_add) +test_t8_schemes_transition_t8_gtest_find_parent_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_schemes_transition_t8_gtest_find_parent_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_schemes_t8_gtest_equal_LDADD = $(t8_gtest_target_ld_add) test_t8_schemes_t8_gtest_equal_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_schemes_t8_gtest_equal_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_schemes_transition_t8_gtest_equal_LDADD = $(t8_gtest_target_ld_add) +test_t8_schemes_transition_t8_gtest_equal_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_schemes_transition_t8_gtest_equal_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_schemes_t8_gtest_root_LDADD = $(t8_gtest_target_ld_add) test_t8_schemes_t8_gtest_root_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_schemes_t8_gtest_root_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_schemes_transition_t8_gtest_root_LDADD = $(t8_gtest_target_ld_add) +test_t8_schemes_transition_t8_gtest_root_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_schemes_transition_t8_gtest_root_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_cmesh_t8_gtest_cmesh_face_is_boundary_LDADD = $(t8_gtest_target_ld_add) test_t8_cmesh_t8_gtest_cmesh_face_is_boundary_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_cmesh_t8_gtest_cmesh_face_is_boundary_CPPFLAGS = $(t8_gtest_target_cpp_flags) @@ -493,6 +571,10 @@ test_t8_schemes_t8_gtest_successor_LDADD = $(t8_gtest_target_ld_add) test_t8_schemes_t8_gtest_successor_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_schemes_t8_gtest_successor_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_schemes_transition_t8_gtest_successor_LDADD = $(t8_gtest_target_ld_add) +test_t8_schemes_transition_t8_gtest_successor_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_schemes_transition_t8_gtest_successor_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_schemes_t8_gtest_boundary_extrude_LDADD = $(t8_gtest_target_ld_add) test_t8_schemes_t8_gtest_boundary_extrude_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_schemes_t8_gtest_boundary_extrude_CPPFLAGS = $(t8_gtest_target_cpp_flags) @@ -529,6 +611,10 @@ test_t8_schemes_t8_gtest_face_descendant_LDADD = $(t8_gtest_target_ld_add) test_t8_schemes_t8_gtest_face_descendant_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_schemes_t8_gtest_face_descendant_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_schemes_transition_t8_gtest_face_descendant_LDADD = $(t8_gtest_target_ld_add) +test_t8_schemes_transition_t8_gtest_face_descendant_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_schemes_transition_t8_gtest_face_descendant_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_geometry_t8_gtest_point_inside_LDADD = $(t8_gtest_target_ld_add) test_t8_geometry_t8_gtest_point_inside_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_geometry_t8_gtest_point_inside_CPPFLAGS = $(t8_gtest_target_cpp_flags) @@ -557,6 +643,10 @@ test_t8_forest_t8_gtest_forest_commit_LDADD = $(t8_gtest_target_ld_add) test_t8_forest_t8_gtest_forest_commit_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_forest_t8_gtest_forest_commit_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_forest_transition_t8_gtest_forest_commit_LDADD = $(t8_gtest_target_ld_add) +test_t8_forest_transition_t8_gtest_forest_commit_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_forest_transition_t8_gtest_forest_commit_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_forest_t8_gtest_balance_LDADD = $(t8_gtest_target_ld_add) test_t8_forest_t8_gtest_balance_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_forest_t8_gtest_balance_CPPFLAGS = $(t8_gtest_target_cpp_flags) @@ -601,10 +691,18 @@ test_t8_schemes_t8_gtest_pack_unpack_LDADD = $(t8_gtest_target_ld_add) test_t8_schemes_t8_gtest_pack_unpack_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_schemes_t8_gtest_pack_unpack_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_schemes_transition_t8_gtest_pack_unpack_LDADD = $(t8_gtest_target_ld_add) +test_t8_schemes_transition_t8_gtest_pack_unpack_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_schemes_transition_t8_gtest_pack_unpack_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_schemes_t8_gtest_child_parent_face_LDADD = $(t8_gtest_target_ld_add) test_t8_schemes_t8_gtest_child_parent_face_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_schemes_t8_gtest_child_parent_face_CPPFLAGS = $(t8_gtest_target_cpp_flags) +test_t8_schemes_transition_t8_gtest_child_parent_face_LDADD = $(t8_gtest_target_ld_add) +test_t8_schemes_transition_t8_gtest_child_parent_face_LDFLAGS = $(t8_gtest_target_ld_flags) +test_t8_schemes_transition_t8_gtest_child_parent_face_CPPFLAGS = $(t8_gtest_target_cpp_flags) + test_t8_cmesh_generator_t8_gtest_cmesh_generator_test_LDADD = $(t8_gtest_target_ld_add) test_t8_cmesh_generator_t8_gtest_cmesh_generator_test_LDFLAGS = $(t8_gtest_target_ld_flags) test_t8_cmesh_generator_t8_gtest_cmesh_generator_test_CPPFLAGS = $(t8_gtest_target_cpp_flags) @@ -625,8 +723,10 @@ test_t8_IO_t8_gtest_vtk_writer_CPPFLAGS = $(t8_gtest_target_cpp_flags) if !T8_ENABLE_MPI test_t8_gtest_cmesh_bcast_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_nca_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_schemes_transition_t8_gtest_nca_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_pyra_connectivity_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_face_neigh_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_schemes_transition_t8_gtest_face_neigh_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_geometry_t8_geometry_implementations_t8_gtest_geometry_linear_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_geometry_t8_geometry_implementations_t8_gtest_geometry_cad_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_gtest_eclass_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) @@ -644,9 +744,13 @@ test_t8_schemes_t8_gtest_element_count_leaves_CPPFLAGS += $(t8_gtest_target_mpi_ test_t8_schemes_t8_gtest_element_ref_coords_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_geometry_t8_gtest_geometry_handling_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_descendant_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_schemes_transition_t8_gtest_descendant_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_find_parent_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_schemes_transition_t8_gtest_find_parent_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_equal_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_schemes_transition_t8_gtest_equal_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_root_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_schemes_transition_t8_gtest_root_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_cmesh_t8_gtest_cmesh_face_is_boundary_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_cmesh_t8_gtest_cmesh_partition_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_cmesh_t8_gtest_cmesh_set_partition_offsets_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) @@ -655,6 +759,7 @@ test_t8_cmesh_t8_gtest_multiple_attributes_CPPFLAGS += $(t8_gtest_target_mpi_cpp test_t8_cmesh_t8_gtest_cmesh_add_attributes_when_derive_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_cmesh_t8_gtest_attribute_gloidx_array_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_successor_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_schemes_transition_t8_gtest_successor_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_boundary_extrude_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_search_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_gtest_netcdf_linkage_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) @@ -664,6 +769,7 @@ test_t8_forest_t8_gtest_half_neighbors_CPPFLAGS += $(t8_gtest_target_mpi_cpp_fla test_t8_forest_t8_gtest_find_owner_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_forest_face_normal_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_face_descendant_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_schemes_transition_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_transform_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) @@ -671,6 +777,7 @@ test_t8_forest_t8_gtest_ghost_exchange_CPPFLAGS += $(t8_gtest_target_mpi_cpp_fla test_t8_forest_t8_gtest_ghost_delete_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_ghost_and_owner_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_forest_commit_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_forest_transition_t8_gtest_forest_commit_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_balance_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_forest_t8_gtest_element_is_leaf_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_IO_t8_gtest_vtk_reader_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) @@ -682,7 +789,9 @@ test_t8_forest_incomplete_t8_gtest_empty_global_tree_CPPFLAGS += $(t8_gtest_targ test_t8_cmesh_t8_gtest_cmesh_tree_vertices_negative_volume_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_default_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_pack_unpack_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_schemes_transition_t8_gtest_pack_unpack_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_schemes_t8_gtest_child_parent_face_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) +test_t8_schemes_transition_t8_gtest_child_parent_face_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_cmesh_generator_t8_gtest_cmesh_generator_test_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_cmesh_t8_gtest_cmesh_copy_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) test_t8_IO_t8_gtest_vtk_writer_CPPFLAGS += $(t8_gtest_target_mpi_cpp_flags) diff --git a/test/t8_forest/t8_gtest_element_is_leaf.cxx b/test/t8_forest/t8_gtest_element_is_leaf.cxx index 51d32755ae..393c0c7feb 100644 --- a/test/t8_forest/t8_gtest_element_is_leaf.cxx +++ b/test/t8_forest/t8_gtest_element_is_leaf.cxx @@ -34,6 +34,8 @@ * and that it returns false for the parent and the first child. */ +/* TODO: Implement a test for subelements as soon as subelement schemes are available. */ + /* Maximum uniform level for forest. */ #define T8_IS_LEAF_MAX_LVL 4 diff --git a/test/t8_forest_transition/t8_gtest_forest_commit.cxx b/test/t8_forest_transition/t8_gtest_forest_commit.cxx new file mode 100644 index 0000000000..d995af0182 --- /dev/null +++ b/test/t8_forest_transition/t8_gtest_forest_commit.cxx @@ -0,0 +1,168 @@ +/* + 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 +#include "test/t8_cmesh_generator/t8_cmesh_example_sets.hxx" +#include +#include +#include + +/* In this test, we adapt, balance and partition a uniform forest. + * We do this in two ways: + * 1st All operations are performed in one single call to t8_forest_commit + * 2nd Each intermediate step is performed in a separate commit + * + * After these two forests are created, we check for equality. + */ + +/* Adapt a forest such that always the first child of a + * tree is refined and no other elements. This results in a highly + * imbalanced forest. */ +static int +t8_test_adapt_balance (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, t8_locidx_t lelement_id, + t8_eclass_scheme_c *ts, const int is_family, const int num_elements, t8_element_t *elements[]) +{ + T8_ASSERT (!is_family || (is_family && num_elements == ts->t8_element_num_children (elements[0]))); + + int level = ts->t8_element_level (elements[0]); + + /* we set a maximum refinement level as forest user data */ + int maxlevel = *(int *) t8_forest_get_user_data (forest); + // if (level >= maxlevel) { + // /* Do not refine after the maxlevel */ + // return 0; + // } + // int child_id = ts->t8_element_child_id (elements[0]); + // if (child_id == 1) { + // return 1; + // } + // return 0; + if (lelement_id == 0) { + return 1; + } + return 0; +} + +/* adapt, balance and partition a given forest in one step */ +static t8_forest_t +t8_test_forest_commit_abp (t8_forest_t forest, int maxlevel) +{ + t8_debugf ("---------start t8_test_forest_commit_abp ---------------------\n"); + + t8_forest_t forest_ada_bal_par; + + /* Adapt, balance and partition the uniform forest */ + t8_forest_init (&forest_ada_bal_par); + /* Set user data for adapt */ + t8_forest_set_user_data (forest_ada_bal_par, &maxlevel); + t8_forest_set_adapt (forest_ada_bal_par, forest, t8_test_adapt_balance, 0); + t8_forest_set_balance (forest_ada_bal_par, NULL, 0); + t8_forest_set_transition (forest_ada_bal_par, NULL, 0); + t8_forest_set_partition (forest_ada_bal_par, NULL, 0); + t8_forest_commit (forest_ada_bal_par); + + t8_debugf ("---------finsish t8_test_forest_commit_abp ---------------------\n"); + return forest_ada_bal_par; +} + +/* adapt, balance and partition a given forest in 3 steps */ +static t8_forest_t +t8_test_forest_commit_abp_3step (t8_forest_t forest, int maxlevel) +{ + t8_forest_t forest_adapt; + t8_forest_t forest_balance; + t8_forest_t forest_partition; + t8_forest_t forest_transition; + + t8_forest_init (&forest_adapt); + t8_forest_init (&forest_balance); + t8_forest_init (&forest_partition); + t8_forest_init (&forest_transition); + + /* adapt the forest */ + t8_forest_set_user_data (forest_adapt, &maxlevel); + t8_forest_set_adapt (forest_adapt, forest, t8_test_adapt_balance, 0); + t8_forest_commit (forest_adapt); + + /* balance the forest */ + t8_forest_set_balance (forest_balance, forest_adapt, 0); + t8_forest_commit (forest_balance); + + /* transition the forest */ + t8_forest_set_transition (forest_transition, forest_balance, 0); + t8_forest_commit (forest_transition); + + /* partrition the forest */ + t8_forest_set_partition (forest_partition, forest_transition, 0); + t8_forest_commit (forest_partition); + + return forest_partition; +} + +TEST (forest_commit, test_forest_commit) +{ + t8_cmesh_t cmesh; + t8_forest_t forest; + t8_forest_t forest_ada_bal_tra_part; + t8_forest_t forest_abtp_3part; + + const int level_step = 0; + /* construct a single tree hex cmesh */ + cmesh = t8_cmesh_new_hypercube (T8_ECLASS_HEX, sc_MPI_COMM_WORLD, 0, 0, 0); + + t8_scheme_cxx_t *scheme = t8_scheme_new_transition_hex_cxx (); + + int maxlevel = -1; + for (int level = 0; level <= maxlevel; level++) { + /* ref the cmesh since we reuse it */ + t8_cmesh_ref (cmesh); + t8_debugf ("Testing forest commit level %i\n", level); + + /* Create a uniformly refined forest */ + forest = t8_forest_new_uniform (cmesh, scheme, level, 0, sc_MPI_COMM_WORLD); + + /* We need to use forest twice, so we ref it */ + t8_forest_ref (forest); + /* Adapt, balance, transition and partition the forest */ + forest_ada_bal_tra_part = t8_test_forest_commit_abp (forest, maxlevel); + + /* Adapt, balance, transition and partition the forest using three separate steps */ + forest_abtp_3part = t8_test_forest_commit_abp_3step (forest, maxlevel); + + ASSERT_TRUE (t8_forest_is_equal (forest_abtp_3part, forest_ada_bal_tra_part)) << "The forests are not equal"; + t8_scheme_cxx_ref (scheme); + + t8_forest_unref (&forest_ada_bal_tra_part); + t8_forest_unref (&forest_abtp_3part); + } + t8_scheme_cxx_unref (&scheme); + t8_cmesh_unref (&cmesh); + + t8_debugf ("Done testing forest commit."); +} \ No newline at end of file diff --git a/test/t8_schemes_transition/t8_gtest_child_parent_face.cxx b/test/t8_schemes_transition/t8_gtest_child_parent_face.cxx new file mode 100644 index 0000000000..5ec073f830 --- /dev/null +++ b/test/t8_schemes_transition/t8_gtest_child_parent_face.cxx @@ -0,0 +1,84 @@ +/* + 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) 2023 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 "t8_gtest_dfs_base.hxx" +#include + +class class_child_parent_face: public TestDFS { + virtual void + check_element () + { + const int num_faces = ts->t8_element_num_faces (element); + for (int iface = 0; iface < num_faces; iface++) { + /* Iterate over all faces and determine the facechildren*/ + const int num_face_children = ts->t8_element_num_face_children (element, iface); + t8_element_t **children; + children = T8_ALLOC (t8_element_t *, num_face_children); + ts->t8_element_new (num_face_children, children); + + ts->t8_element_children_at_face (element, iface, children, num_face_children, NULL); + + for (int ifacechild = 0; ifacechild < num_face_children; ifacechild++) { + /* Iterate over those children and determine the childface corresponding to the parentface */ + int childface = ts->t8_element_face_child_face (element, iface, ifacechild); + ASSERT_NE (childface, -1); + /* Determine the parentface corresponding to the childface */ + int parentface = ts->t8_element_face_parent_face (children[ifacechild], childface); + /* Check, that this is equal to the face that we started with */ + EXPECT_EQ (iface, parentface); + } + ts->t8_element_destroy (num_face_children, children); + T8_FREE (children); + } + } + + protected: + void + SetUp () override + { + /* Setup DFS test */ + dfs_test_setup (); + } + void + TearDown () override + { + /* Destroy DFS test */ + dfs_test_teardown (); + } +}; + +TEST_P (class_child_parent_face, t8_recursive_dfs_child_parent_face) +{ +#ifdef T8_ENABLE_LESS_TESTS + const int maxlvl = 4; +#else + const int maxlvl = 6; +#endif + check_recursive_dfs_to_max_lvl (maxlvl); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_child_parent_face, class_child_parent_face, testing::Values (T8_ECLASS_HEX), + print_eclass); diff --git a/test/t8_schemes_transition/t8_gtest_descendant.cxx b/test/t8_schemes_transition/t8_gtest_descendant.cxx new file mode 100644 index 0000000000..fe0a03ae3a --- /dev/null +++ b/test/t8_schemes_transition/t8_gtest_descendant.cxx @@ -0,0 +1,161 @@ +/* + 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 types in parallel. + + Copyright (C) 2010 The University of Texas System + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + 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 + +/* This program tests the descendant function of an element. */ + +class class_schemes_descendant: public testing::TestWithParam { + protected: + void + SetUp () override + { + eclass = GetParam (); + + scheme = t8_scheme_new_transition_hex_cxx (); + ts = scheme->eclass_schemes[eclass]; + ts->t8_element_new (1, &elem); + ts->t8_element_new (1, &desc); + ts->t8_element_new (1, &test); + ts->t8_element_root (elem); + } + void + TearDown () override + { + ts->t8_element_destroy (1, &elem); + ts->t8_element_destroy (1, &desc); + ts->t8_element_destroy (1, &test); + t8_scheme_cxx_unref (&scheme); + } +#ifdef T8_ENABLE_DEBUG + const int maxlvl = 3; +#else + const int maxlvl = 4; +#endif + + t8_scheme_cxx *scheme; + t8_eclass_scheme_c *ts; + t8_eclass_t eclass; + t8_element_t *elem; + t8_element_t *desc; + t8_element_t *test; +}; + +/* Test recursively if the first and last descendant of an element is + * computed correctly. Only the descendant of elem->level + 1 is tested. + */ +static void +t8_recursive_descendant (t8_element_t *elem, t8_element_t *desc, t8_element_t *test, t8_eclass_scheme_c *ts, int maxlvl) +{ + const int num_children = ts->t8_element_num_children (elem); + const int level = ts->t8_element_level (elem); + for (int ichild = 0; ichild < num_children; ichild++) { + ts->t8_element_child (elem, ichild, desc); + /* first child == first descendant. */ + if (ichild == 0) { + ts->t8_element_first_descendant (elem, test, level + 1); + EXPECT_ELEM_EQ (ts, desc, test); + } + /* last child == last descendant. */ + else if (ichild == num_children - 1) { + ts->t8_element_last_descendant (elem, test, level + 1); + EXPECT_ELEM_EQ (ts, desc, test); + } + else if (level > maxlvl) { + t8_recursive_descendant (desc, elem, test, ts, maxlvl); + ts->t8_element_parent (desc, elem); + } + } +} + +/* Test, if the first descendant of an element is computed correctly over a range + * of levels. + */ +static void +t8_deep_first_descendant (t8_element_t *elem, t8_element_t *desc, t8_element_t *test, t8_eclass_scheme_c *ts, int level) +{ + const int elem_level = ts->t8_element_level (elem); + ts->t8_element_copy (elem, test); + + for (int ilevel = elem_level; ilevel < level; ilevel++) { + ts->t8_element_child (test, 0, desc); + ts->t8_element_copy (desc, test); + } + ts->t8_element_first_descendant (elem, test, level); + EXPECT_ELEM_EQ (ts, desc, test); +} + +/* Test, if the last descendant of an element is computed correctly over a range + * of levels. + */ +static void +t8_deep_last_descendant (t8_element_t *elem, t8_element_t *desc, t8_element_t *test, t8_eclass_scheme_c *ts, int level) +{ + ts->t8_element_copy (elem, test); + + /* Compute the correct element. */ + for (int ilevel = ts->t8_element_level (elem); ilevel < level; ilevel++) { + const int num_children = ts->t8_element_num_children (test); + ts->t8_element_child (test, num_children - 1, desc); + ts->t8_element_copy (desc, test); + } + /* Check for equality. */ + ts->t8_element_last_descendant (elem, test, level); + EXPECT_ELEM_EQ (ts, desc, test); +} + +/* Test if the first and last descendant of an element are computed correctly. + * The level between the element and the descendant is larger or equal to one. + */ +static void +t8_large_step_descendant (t8_element_t *elem, t8_element_t *desc, t8_element_t *test, t8_eclass_scheme_c *ts, + int maxlvl) +{ + for (int ilevel = ts->t8_element_level (elem); ilevel < maxlvl; ilevel++) { + + const int num_children = ts->t8_element_num_children (elem); + /* Use these functions to perform the actual test. */ + t8_deep_first_descendant (elem, desc, test, ts, maxlvl); + t8_deep_last_descendant (elem, desc, test, ts, maxlvl); + for (int jchild = 0; jchild < num_children; jchild++) { + ts->t8_element_child (elem, jchild, desc); + t8_large_step_descendant (desc, elem, test, ts, maxlvl); + ts->t8_element_parent (desc, elem); + } + } +} + +TEST_P (class_schemes_descendant, test_recursive_descendant) +{ + t8_recursive_descendant (elem, desc, test, ts, maxlvl); + t8_deep_first_descendant (elem, desc, test, ts, ts->t8_element_maxlevel ()); + t8_deep_last_descendant (elem, desc, test, ts, ts->t8_element_maxlevel ()); + t8_large_step_descendant (elem, desc, test, ts, maxlvl); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_descendant, class_schemes_descendant, testing::Values (T8_ECLASS_HEX), print_eclass); diff --git a/test/t8_schemes_transition/t8_gtest_dfs_base.hxx b/test/t8_schemes_transition/t8_gtest_dfs_base.hxx new file mode 100644 index 0000000000..b136ed3e48 --- /dev/null +++ b/test/t8_schemes_transition/t8_gtest_dfs_base.hxx @@ -0,0 +1,93 @@ +/* + 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) 2023 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. +*/ + +#ifndef T8_GTEST_SCHEME_HELPER_H +#define T8_GTEST_SCHEME_HELPER_H + +#include +#include +#include +#include + +class TestDFS: public testing::TestWithParam { + public: + /** recursive tests check something for all descendants of a starting element (currently only root) upto maxlevel +*/ + virtual void + check_element () {}; + + /** recursive depth first search to iterate over all descendants of elem up to max_dfs_recursion_level */ + void + check_recursive_dfs_to_max_lvl (const int max_dfs_recursion_level) + { + int level = ts->t8_element_level (element); + ASSERT_LE (level, max_dfs_recursion_level); + ASSERT_LT (max_dfs_recursion_level, ts->t8_element_maxlevel ()); + + /** call the implementation of the specific test*/ + check_element (); + + if (ts->t8_element_level (element) < max_dfs_recursion_level) { + /* iterate over all children */ + const int num_children = ts->t8_element_num_children (element); + for (int ichild = 0; ichild < num_children; ichild++) { + ts->t8_element_child (element, ichild, element); + check_recursive_dfs_to_max_lvl (max_dfs_recursion_level); + ts->t8_element_parent (element, element); + } + } + } + + void + dfs_test_setup () + { + scheme = t8_scheme_new_transition_hex_cxx (); + eclass = GetParam (); + ts = scheme->eclass_schemes[eclass]; + ts->t8_element_new (1, &element); + ts->t8_element_root (element); + } + void + dfs_test_teardown () + { + ts->t8_element_destroy (1, &element); + t8_scheme_cxx_unref (&scheme); + } + + void + SetUp () override + { + dfs_test_setup (); + } + void + TearDown () override + { + dfs_test_teardown (); + } + + t8_scheme_cxx *scheme; + t8_eclass_t eclass; + t8_eclass_scheme_c *ts; + t8_element_t *element; +}; + +#endif /*T8_GTEST_SCHEME_HELPER_H*/ diff --git a/test/t8_schemes_transition/t8_gtest_equal.cxx b/test/t8_schemes_transition/t8_gtest_equal.cxx new file mode 100644 index 0000000000..b78b614ce0 --- /dev/null +++ b/test/t8_schemes_transition/t8_gtest_equal.cxx @@ -0,0 +1,88 @@ +/* + 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 "t8_gtest_dfs_base.hxx" +#include + +class class_test_equal: public TestDFS { + virtual void + check_element () + { + const int num_children = ts->t8_element_num_children (element); + for (int ichild1 = 0; ichild1 < num_children; ichild1++) { + ts->t8_element_child (element, ichild1, child1); + /* the child must be different than its parent */ + EXPECT_FALSE (ts->t8_element_equal (element, child1)); + for (int ichild2 = 0; ichild2 < num_children; ichild2++) { + ts->t8_element_child (element, ichild2, child2); + /* the child must be different than its parent */ + EXPECT_FALSE (ts->t8_element_equal (element, child2)); + const int equal = ts->t8_element_equal (child1, child2); + /* The children must be equal if and only if their indices are equal. */ + EXPECT_EQ (equal, ichild1 == ichild2); + /* t8_element_equal should compute the same as t8_element_compare, + * when we only check if compare has 0 as result. */ + const int compare_equal = !ts->t8_element_compare (child1, child2); + EXPECT_EQ (equal, compare_equal); + } + } + } + + protected: + void + SetUp () override + { + dfs_test_setup (); + /* Get element and initialize it */ + ts->t8_element_new (1, &child1); + ts->t8_element_new (1, &child2); + } + void + TearDown () override + { + /* Destroy element */ + ts->t8_element_destroy (1, &child1); + ts->t8_element_destroy (1, &child2); + + /* Destroy DFS test */ + dfs_test_teardown (); + } + t8_element_t *child1; + t8_element_t *child2; +}; + +TEST_P (class_test_equal, test_equal_dfs) +{ +#ifdef T8_ENABLE_LESS_TESTS + const int maxlvl = 3; +#else + const int maxlvl = 5; +#endif + check_recursive_dfs_to_max_lvl (maxlvl); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_test_all_imps, class_test_equal, testing::Values (T8_ECLASS_HEX), print_eclass); diff --git a/test/t8_schemes_transition/t8_gtest_face_descendant.cxx b/test/t8_schemes_transition/t8_gtest_face_descendant.cxx new file mode 100644 index 0000000000..c65f8e3dde --- /dev/null +++ b/test/t8_schemes_transition/t8_gtest_face_descendant.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 types in parallel. + + Copyright (C) 2010 The University of Texas System + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + 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 "t8_gtest_dfs_base.hxx" +#include + +/* compute the first/last descendant by iteratively taking the first/last child at each level*/ +static void +t8_test_manual_first_last_face_descendant (const t8_eclass_scheme_c *ts, const t8_element_t *element, const int iface, + const int desc_level, const int last, t8_element_t *face_desc) +{ + const int num_children_at_face = ts->t8_element_num_face_children (element, iface); + + int *child_indices = T8_ALLOC (int, num_children_at_face); + t8_element_t **children = T8_ALLOC (t8_element_t *, num_children_at_face); + ts->t8_element_new (num_children_at_face, children); + + ts->t8_element_copy (element, face_desc); + const int level = ts->t8_element_level (element); + for (int ilevel = level; ilevel < desc_level; ilevel++) { + EXPECT_EQ (ts->t8_element_num_face_children (element, iface), num_children_at_face); + /* Compute child_id of the test_child_id-th child. */ + ts->t8_element_children_at_face (face_desc, iface, children, num_children_at_face, child_indices); + + /* chose correct face_id dependent on if we want first or last face desc.*/ + const int face_child_id = last ? num_children_at_face - 1 : 0; + + const int child_id = child_indices[face_child_id]; + + ts->t8_element_child (face_desc, child_id, face_desc); + } + ts->t8_element_destroy (num_children_at_face, children); + T8_FREE (children); + T8_FREE (child_indices); +} + +class class_descendant: public TestDFS { + virtual void + check_element () + { + /* Check the linear first and last descendants of an element along all faces. + * For the test the descendants are computed manually by t8_test_manual_first_last_face_descendant and + * by the scheme implementation t8_element_first_descendant for the first descendants over the levels. + */ + + const int level = ts->t8_element_level (element); + const int num_faces = ts->t8_element_num_faces (element); + + /* Testing the linear first descendant. */ + for (int ilevel = level + 1; ilevel < max_test_lvl; ilevel++) { + for (int jface = 0; jface < num_faces; jface++) { + + t8_test_manual_first_last_face_descendant (ts, element, jface, ilevel, 0, manual_face_desc); + ts->t8_element_first_descendant_face (element, jface, scheme_face_desc, ilevel); + /* Compare the manually computed child with the result of t8_element_first_descendant_face. */ + EXPECT_ELEM_EQ (ts, scheme_face_desc, manual_face_desc); + + t8_test_manual_first_last_face_descendant (ts, element, jface, ilevel, 1, manual_face_desc); + ts->t8_element_last_descendant_face (element, jface, scheme_face_desc, ilevel); + /* Compare the manually computed child with the result of t8_element_last_descendant_face. */ + EXPECT_ELEM_EQ (ts, scheme_face_desc, manual_face_desc); + } + } + } + + protected: + void + SetUp () override + { + dfs_test_setup (); + max_test_lvl = ts->t8_element_maxlevel (); + ts->t8_element_new (1, &manual_face_desc); + ts->t8_element_new (1, &scheme_face_desc); + } + void + TearDown () override + { + ts->t8_element_destroy (1, &manual_face_desc); + ts->t8_element_destroy (1, &scheme_face_desc); + dfs_test_teardown (); + } + int max_test_lvl; + t8_element_t *manual_face_desc; + t8_element_t *scheme_face_desc; +}; + +TEST_P (class_descendant, t8_check_face_desc) +{ + +#ifdef T8_ENABLE_LESS_TESTS + const int maxlvl = 3; +#else + const int maxlvl = 5; +#endif + + check_recursive_dfs_to_max_lvl (maxlvl); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_element_face_descendant, class_descendant, testing::Values (T8_ECLASS_HEX), + print_eclass); diff --git a/test/t8_schemes_transition/t8_gtest_face_neigh.cxx b/test/t8_schemes_transition/t8_gtest_face_neigh.cxx new file mode 100644 index 0000000000..3068440ae8 --- /dev/null +++ b/test/t8_schemes_transition/t8_gtest_face_neigh.cxx @@ -0,0 +1,209 @@ +/* + 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 + +/* *INDENT-OFF* */ +class face_neigh: public testing::TestWithParam { + protected: + void + SetUp () override + { + eclass = GetParam (); + scheme = t8_scheme_new_transition_hex_cxx (); + + ts = scheme->eclass_schemes[eclass]; + ts->t8_element_new (1, &element); + ts->t8_element_new (1, &child); + ts->t8_element_new (1, &neigh); + ts->t8_element_root (element); + } + + void + TearDown () override + { + ts->t8_element_destroy (1, &element); + ts->t8_element_destroy (1, &child); + ts->t8_element_destroy (1, &neigh); + t8_scheme_cxx_unref (&scheme); + } + t8_element_t *element; + t8_element_t *child; + t8_element_t *neigh; + t8_scheme_cxx *scheme; + t8_eclass_scheme_c *ts; + t8_eclass_t eclass; + +#ifdef T8_ENABLE_LESS_TESTS + const int maxlvl = 3; +#else + const int maxlvl = 4; +#endif +}; +/* *INDENT-ON* */ + +void +t8_test_face_neighbor_inside (int num_faces, t8_element_t *element, t8_element_t *child, t8_element_t *neigh, + t8_eclass_scheme_c *ts) +{ + int face_num; + int check; + + for (int iface = 0; iface < num_faces; iface++) { + /* Compute the neighbors neighbor along a given face and check, if the result is the + * original element. */ + ts->t8_element_face_neighbor_inside (child, neigh, iface, &face_num); + ts->t8_element_face_neighbor_inside (neigh, element, face_num, &check); + + EXPECT_TRUE (ts->t8_element_equal (child, element)) << "Got a false neighbor."; + EXPECT_ELEM_EQ (ts, child, element); + } +} + +int +t8_test_get_middle_child (t8_eclass_t eclass, int ilevel, t8_element_t *element, t8_element_t *child, + t8_eclass_scheme_c *ts) +{ + /* Get the child number of the child in the middle of the element, depending of the shape of the element. */ + switch (eclass) { + case T8_ECLASS_VERTEX: + return 0; + case T8_ECLASS_LINE: + return 0; + case T8_ECLASS_QUAD: + /* There are no inner children in level one refinement. The test starts with level two, because this is the first level, inner children exists. + The third child of level one child 0 is one of four middle children in level two. */ + ts->t8_element_child (element, 0, child); + ts->t8_element_copy (child, element); + return 3; + case T8_ECLASS_TRIANGLE: + return 3; + case T8_ECLASS_HEX: + /* There are no inner children in level one refinement. The test starts with level two, because this is the first level, inner children existing. + The third child of level one child 4 is one of eight middle children in level two. */ + ts->t8_element_child (element, 4, child); + ts->t8_element_copy (child, element); + return 3; + case T8_ECLASS_TET: + return 3; + case T8_ECLASS_PRISM: + /* There are no inner children in level one refinement. The test starts with level two, because this is the first level, inner children existing. + The last child of level one child 4 is one of eight middle children in level two. */ + ts->t8_element_child (element, 4, child); + ts->t8_element_copy (child, element); + return 7; + case T8_ECLASS_PYRAMID: { + t8_dpyramid_t *pyramid = (t8_dpyramid_t *) element; + /* middle_child_id of Pyramid Type 6. */ + if (pyramid->pyramid.type == T8_DPYRAMID_FIRST_TYPE) { + return 8; + } + /* middle_child_id of Pyramid Type 7. */ + else { + return 3; + } + } + default: + return 0; + } +} + +/* Compute all children along all faces. Compute their neighbors along the face, + * check, if the children have root contact, and if the neighbors are outside of the + * root. */ +TEST_P (face_neigh, check_not_inside_root) +{ + /* Are the neighbors of the element really outside?. */ + + const int num_faces = ts->t8_element_num_faces (element); + + for (int iface = 0; iface < num_faces; iface++) { + + const int num_children = ts->t8_element_num_face_children (element, iface); + int *child_indices = T8_ALLOC (int, num_children); + t8_element_t **children = T8_ALLOC (t8_element_t *, num_children); + ts->t8_element_new (num_children, children); + ts->t8_element_children_at_face (element, iface, children, num_children, child_indices); + + for (int jchild = 0; jchild < num_children; jchild++) { + + const int child_id = child_indices[jchild]; + const int face_contact = ts->t8_element_face_child_face (element, iface, jchild); + + ts->t8_element_child (element, child_id, child); + int face_num; + int inside = ts->t8_element_face_neighbor_inside (child, neigh, face_contact, &face_num); + + ASSERT_EQ (inside, 0) << "Element is not outside."; + + inside = ts->t8_element_tree_face (child, face_contact); + ASSERT_EQ (inside, iface) << "Wrong face."; + } + ts->t8_element_destroy (num_children, children); + T8_FREE (children); + T8_FREE (child_indices); + } +} + +void +t8_recursive_check_diff (t8_element_t *element, t8_element_t *child, t8_element_t *neigh, t8_eclass_scheme_c *ts, + int maxlvl, int level) +{ + + T8_ASSERT (level <= maxlvl && maxlvl <= ts->t8_element_maxlevel () - 1); + if (level == maxlvl) { + return; + } + + /* Compute the neighbors neighbor along a given face and check, if the result is the + * original element. */ + int num_faces = ts->t8_element_num_faces (element); + + t8_test_face_neighbor_inside (num_faces, child, element, neigh, ts); + + int num_children = ts->t8_element_num_children (child); + for (int ichild = 0; ichild < num_children; ichild++) { + ts->t8_element_child (element, ichild, child); + t8_recursive_check_diff (child, element, neigh, ts, maxlvl, level + 1); + ts->t8_element_parent (child, element); + } +} + +/* Recursively check, if all neighbors are computed correct up to a given level. */ +TEST_P (face_neigh, recursive_check_diff) +{ + int level = 1; + int middle_child_id = t8_test_get_middle_child (eclass, level, element, child, ts); + ts->t8_element_child (element, middle_child_id, child); + + t8_recursive_check_diff (child, element, neigh, ts, maxlvl, level); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_face_neigh, face_neigh, testing::Values (T8_ECLASS_HEX), print_eclass); diff --git a/test/t8_schemes_transition/t8_gtest_find_parent.cxx b/test/t8_schemes_transition/t8_gtest_find_parent.cxx new file mode 100644 index 0000000000..8ee9a34a3b --- /dev/null +++ b/test/t8_schemes_transition/t8_gtest_find_parent.cxx @@ -0,0 +1,78 @@ +/* + 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 + +class class_find_parent: public TestDFS { + virtual void + check_element () + { + const int num_children = ts->t8_element_num_children (element); + for (int ichild = 0; ichild < num_children; ichild++) { + ts->t8_element_child (element, ichild, child); + /* Compute parent of child */ + ts->t8_element_parent (child, test_parent); + /* Check that it is equal to the original element */ + EXPECT_ELEM_EQ (ts, element, test_parent); + } + } + + protected: + void + SetUp () override + { + dfs_test_setup (); + /* Get element and initialize it */ + ts->t8_element_new (1, &child); + ts->t8_element_new (1, &test_parent); + } + void + TearDown () override + { + /* Destroy element */ + ts->t8_element_destroy (1, &child); + ts->t8_element_destroy (1, &test_parent); + + /* Destroy DFS test */ + dfs_test_teardown (); + } + t8_element_t *child; + t8_element_t *test_parent; +}; + +TEST_P (class_find_parent, t8_compute_child_find_parent) +{ +#ifdef T8_ENABLE_LESS_TESTS + const int maxlvl = 4; +#else + const int maxlvl = 6; +#endif + check_recursive_dfs_to_max_lvl (maxlvl); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_find_parent, class_find_parent, testing::Values (T8_ECLASS_HEX), print_eclass); diff --git a/test/t8_schemes_transition/t8_gtest_nca.cxx b/test/t8_schemes_transition/t8_gtest_nca.cxx new file mode 100644 index 0000000000..b740af4632 --- /dev/null +++ b/test/t8_schemes_transition/t8_gtest_nca.cxx @@ -0,0 +1,313 @@ +/* +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. +*/ + +/** \file t8_gtest_nca.cxx +* Provide tests to check the functionality of the nearest-common-ancestor function +* for every element. +*/ + +#include +#include +#include +#include +#include +#include + +class nca: public testing::TestWithParam { + protected: + void + SetUp () override + { + eclass = GetParam (); + scheme = t8_scheme_new_transition_hex_cxx (); + ts = scheme->eclass_schemes[eclass]; + ts->t8_element_new (1, &correct_nca); + ts->t8_element_new (1, &desc_a); + ts->t8_element_new (1, &desc_b); + ts->t8_element_new (1, &check); + ts->t8_element_set_linear_id (correct_nca, 0, 0); + } + void + TearDown () override + { + ts->t8_element_destroy (1, &correct_nca); + ts->t8_element_destroy (1, &desc_a); + ts->t8_element_destroy (1, &desc_b); + ts->t8_element_destroy (1, &check); + t8_scheme_cxx_unref (&scheme); + } + /* correct_nca -> the nearest common ancestor that we check for + * desc_a -> a descendant of correct_nca + * desc_b -> another descendant of correct_nca, different from desc_a + * check -> the computed nca of desc_a and desc_b, should be equal to correct_nca + */ + t8_element_t *correct_nca, *desc_a, *desc_b, *check; + t8_scheme_cxx *scheme; + t8_eclass_scheme_c *ts; + t8_eclass_t eclass; +}; + +/** + * Test the nca for the children of the root-element + * + */ +TEST_P (nca, nca_check_shallow) +{ + int i, j; + const int num_children = ts->t8_element_num_children (correct_nca); + /* Iterate over all combinations of two children from correct_nca */ + for (i = 0; i < num_children - 1; i++) { + ts->t8_element_child (correct_nca, i, desc_a); + for (j = i + 1; j < num_children; j++) { + ts->t8_element_child (correct_nca, j, desc_b); + /*Compute the nca */ + ts->t8_element_nca (desc_a, desc_b, check); + /*expect equality */ + EXPECT_ELEM_EQ (ts, check, correct_nca); + } + } +} + +/** + * Check the nca for elements on the maximal level. + * We iteratively compute an element that is the correct nca up to level 10. + * The first and the last descendant of this element at the maximal level are + * computed and used as an input for t8_element_nca. + */ +TEST_P (nca, nca_check_deep) +{ + const int max_lvl = 10; + const int elem_max_level = ts->t8_element_maxlevel (); + /* num_children is not a const here, cause this can change for pyramids */ + int num_children; + /* iterate over levels and children */ + int lvl, check_lvl_a, check_lvl_b, child_id; + t8_element_t *tmp; + + ts->t8_element_new (1, &tmp); + ts->t8_element_copy (correct_nca, tmp); + for (lvl = 1; lvl <= max_lvl; lvl++) { + num_children = ts->t8_element_num_children (tmp); + for (child_id = 0; child_id < num_children; child_id++) { + ts->t8_element_child (tmp, child_id, correct_nca); + /* Compute first and last descendant at every level up to elem_max_lvl. + * They have the correct_nca as the nca */ + for (check_lvl_a = lvl + 1; check_lvl_a < elem_max_level; check_lvl_a++) { + ts->t8_element_first_descendant (correct_nca, desc_a, check_lvl_a); + for (check_lvl_b = lvl + 1; check_lvl_b < elem_max_level; check_lvl_b++) { + ts->t8_element_last_descendant (correct_nca, desc_b, check_lvl_b); + /* Compute the nca of desc_a and desc_b */ + ts->t8_element_nca (desc_a, desc_b, check); + if (eclass == T8_ECLASS_VERTEX) { + /* first- last-descendant logic does not hold for vertices. */ + EXPECT_EQ (ts->t8_element_level (check), + SC_MIN (ts->t8_element_level (desc_a), ts->t8_element_level (desc_b))); + } + else { + /* Expect equality of correct_nca and check for every other class */ + EXPECT_ELEM_EQ (ts, correct_nca, check); + } + } + } + } + /* Determine next element */ + if (num_children != 1) { + ts->t8_element_copy (tmp, correct_nca); + /* Continue in the middle */ + ts->t8_element_child (correct_nca, num_children / 2, tmp); + } + else { + ts->t8_element_copy (correct_nca, tmp); + } + } + ts->t8_element_destroy (1, &tmp); +} + +/** + * Recursively check the computation of the nca of all possible combination of descendants of the + * \a correct_nca that have \a correct_nca as the nca. + * + * \param[in] correct_nca The correct nearest common ancestor + * \param[in] desc_a Storage for the computation of a descendant of \correct_nca + * \param[in] desc_b Storage for the computation of a descendant of \correct_nca + * \param[in] check Storage for the computation of the nca of \a desc_a and \a desc_b + * \param[in] parent_a An initialized element, descendant of \a correct_nca, not a descendant or ancestor of \a parent_b. \a desc_a will be a child of it + * \param[in] parent_b An initialized element, descendant of \a correct_nca, not a descendant or ancestor of \a parent_a. \a desc_b will be a child of it + * \param[in] max_lvl The maximal depth of the recursion + * \param[in] ts the scheme to use + */ +static void +t8_recursive_nca_check (t8_element_t *check_nca, t8_element_t *desc_a, t8_element_t *desc_b, t8_element_t *check, + t8_element_t *parent_a, t8_element_t *parent_b, const int max_lvl, t8_eclass_scheme_c *ts) +{ + T8_ASSERT (max_lvl <= ts->t8_element_maxlevel () - 1); + /* compute the level of the parents */ + int level_a = ts->t8_element_level (parent_a); + int level_b = ts->t8_element_level (parent_b); + int num_children_a, num_children_b; + int i, j; + /* If one parent has reached the maximal level, the test returns */ + if (level_a == max_lvl || level_b == max_lvl) { + return; + } + num_children_a = ts->t8_element_num_children (parent_a); + num_children_b = ts->t8_element_num_children (parent_b); + + /* Iterate over all children of parent_a */ + for (i = 0; i < num_children_a; i++) { + ts->t8_element_child (parent_a, i, desc_a); + /* Iterate over all children of parent_b */ + for (j = 0; j < num_children_b; j++) { + ts->t8_element_child (parent_b, j, desc_b); + ts->t8_element_nca (desc_a, desc_b, check); + + if (!ts->t8_element_equal (check_nca, check)) { + level_a = ts->t8_element_level (desc_a); + level_b = ts->t8_element_level (desc_b); + + int level_c = ts->t8_element_level (check_nca); + int level_nca = ts->t8_element_level (check); + /* Output the linear id of the descendants where the computation fails. + * This makes debugging a lot easier, as one can reconstruct the descendants + * via t8_element_set_linear_id and can directly test them instead of waiting + * until the recursion reaches the faulty computation. */ + t8_debugf ("id of desc_a: %li, level: %i\n", ts->t8_element_get_linear_id (desc_a, level_a), level_a); + t8_debugf ("id of desc_b: %li, level: %i\n", ts->t8_element_get_linear_id (desc_b, level_b), level_b); + + for (int k = SC_MAX (level_a, level_b); k >= 0; k--) { + t8_debugf ("id of desc_a: %li, level: %i\n", ts->t8_element_get_linear_id (desc_a, k), k); + t8_debugf ("id of desc_b: %li, level: %i\n", ts->t8_element_get_linear_id (desc_b, k), k); + } + + t8_debugf ("id of the correct nca: %li, level: %i\n", ts->t8_element_get_linear_id (check_nca, level_c), + level_c); + + t8_debugf ("id of the computed nca: %li, level: %i\n", ts->t8_element_get_linear_id (check, level_nca), + level_nca); + + SC_ABORT ("Computed nca is not the correct nca!\n"); + } + /* parent_a stays fixed, b-part goes one level deeper into the recursion */ + t8_recursive_nca_check (check_nca, desc_a, parent_b, check, parent_a, desc_b, max_lvl, ts); + /* We reused parent_b, hence we have to recompute the correct parent */ + ts->t8_element_parent (desc_b, parent_b); + } + /* We reused parent_a, hence we have to recompute the correct parent */ + ts->t8_element_parent (desc_a, parent_a); + } +} + +/* Recursively check the computation of the nca. recursion_depth defines up to which + * level we compute descendants of correct_nca that should have correct_nca as the + * output of t8_element_nca.*/ +TEST_P (nca, recursive_check) +{ +#ifdef T8_ENABLE_LESS_TESTS + const int recursion_depth = 3; +#else + /* User lower recursion depth for pyramids, it takes to much time otherwise */ + const int recursion_depth = 4; +#endif + t8_element_t *parent_a, *parent_b; + int num_children; + num_children = ts->t8_element_num_children (correct_nca); + int i, j; + if (num_children > 1) { + ts->t8_element_new (1, &parent_a); + ts->t8_element_new (1, &parent_b); + ts->t8_element_child (correct_nca, 0, parent_a); + ts->t8_element_child (correct_nca, 1, parent_b); + for (i = 0; i < num_children - 1; i++) { + ts->t8_element_child (correct_nca, i, parent_a); + for (j = i + 1; j < num_children; j++) { + ts->t8_element_child (correct_nca, j, parent_b); + t8_recursive_nca_check (correct_nca, desc_a, desc_b, check, parent_a, parent_b, recursion_depth, ts); + } + } + } + else { + GTEST_SKIP (); + } + ts->t8_element_destroy (1, &parent_a); + ts->t8_element_destroy (1, &parent_b); +} + +/* Test the nca recursively for elements in the middle of the uniform refinement tree + * up to the maximal level. + * Be careful when increasing the recursion_depth, as it increases the number of test-cases exponentially. */ +TEST_P (nca, recursive_check_higher_level) +{ + + const int recursion_depth = 3; + const int max_lvl = ts->t8_element_maxlevel (); + t8_element_t *parent_a; + t8_element_t *parent_b; + t8_element_t *correct_nca_high_level; + int num_children; + int i, k, l; + t8_gloidx_t leaves_on_level; + EXPECT_TRUE (max_lvl - recursion_depth >= 0); + + ts->t8_element_new (1, &parent_a); + ts->t8_element_new (1, &parent_b); + ts->t8_element_new (1, &correct_nca_high_level); + + /* Test on different levels around the middle of the refinement tree */ + for (i = recursion_depth; i < max_lvl; i++) { + leaves_on_level = ts->t8_element_count_leaves (correct_nca, i - recursion_depth); + /* middle = leaves/2 */ + ts->t8_element_set_linear_id (correct_nca_high_level, i - recursion_depth, leaves_on_level / 2); + + /* Initialization for recursive_nca_check */ + num_children = ts->t8_element_num_children (correct_nca_high_level); + if (num_children > 1) { + /* Compute children on to different branches in the tree an test them. + * This ensures, that the nca of all their descendants has to be correct_nca_high_level*/ + for (k = 0; k < num_children; k++) { + ts->t8_element_child (correct_nca_high_level, k, parent_a); + for (l = 0; l < num_children; l++) { + ts->t8_element_child (correct_nca_high_level, l, parent_b); + if (k != l) { + t8_recursive_nca_check (correct_nca_high_level, desc_a, desc_b, check, parent_a, parent_b, i, ts); + } + else { + ts->t8_element_nca (parent_a, parent_b, check); + EXPECT_ELEM_EQ (ts, parent_a, check); + EXPECT_ELEM_EQ (ts, parent_b, check); + } + } + } + } + else { + ts->t8_element_destroy (1, &parent_a); + ts->t8_element_destroy (1, &parent_b); + ts->t8_element_destroy (1, &correct_nca_high_level); + GTEST_SKIP (); + } + } + /* Clean-up */ + ts->t8_element_destroy (1, &parent_a); + ts->t8_element_destroy (1, &parent_b); + ts->t8_element_destroy (1, &correct_nca_high_level); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_nca, nca, testing::Values (T8_ECLASS_HEX), print_eclass); diff --git a/test/t8_schemes_transition/t8_gtest_pack_unpack.cxx b/test/t8_schemes_transition/t8_gtest_pack_unpack.cxx new file mode 100644 index 0000000000..892fa0f0d0 --- /dev/null +++ b/test/t8_schemes_transition/t8_gtest_pack_unpack.cxx @@ -0,0 +1,139 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2024 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include + +/** Use DFS to check for all elements, if packing them, sending them to ourself and unpacking them results in the same element + * Here, each element is sent individually. + */ +class class_test_pack: public TestDFS { + virtual void + + /* pack the element and its children, send to ourself, unpack and check if it is the same element */ + check_element () + { + size_t count = 1; + int position = 0; + + /* Compute pack size and allocate send buffer */ + int pack_size; + int num_children = ts->t8_element_num_children (element); + ts->t8_element_MPI_Pack_size (count, comm, &pack_size); + pack_size *= (num_children + 1); + char *sendbuf = T8_ALLOC (char, pack_size); + + /* pack data */ + ts->t8_element_MPI_Pack (&element, count, sendbuf, pack_size, &position, comm); + t8_element_t **children = T8_ALLOC (t8_element_t *, num_children); + ts->t8_element_new (num_children, children); + ts->t8_element_children (element, num_children, children); + ts->t8_element_MPI_Pack (children, num_children, sendbuf, pack_size, &position, comm); + + int recvBufferSize = pack_size; + char *recvbuf = T8_ALLOC (char, recvBufferSize); +#if T8_ENABLE_MPI + /* Send data */ + sc_MPI_Request request; + mpiret = sc_MPI_Isend (sendbuf, position, sc_MPI_PACKED, rank, T8_MPI_TEST_ELEMENT_PACK_TAG, comm, &request); + SC_CHECK_MPI (mpiret); + + /* Probe size and allocate */ + sc_MPI_Status status; + sc_MPI_Probe (rank, T8_MPI_TEST_ELEMENT_PACK_TAG, comm, &status); + + /* receive data */ + mpiret + = sc_MPI_Recv (recvbuf, position, sc_MPI_PACKED, rank, T8_MPI_TEST_ELEMENT_PACK_TAG, comm, sc_MPI_STATUS_IGNORE); + SC_CHECK_MPI (mpiret); + + /* Finalize non-blocking send communication */ + mpiret = sc_MPI_Wait (&request, sc_MPI_STATUS_IGNORE); + SC_CHECK_MPI (mpiret); +#else + /* just copy the data, if we did not compile with MPI*/ + mempcpy (recvbuf, sendbuf, pack_size); +#endif + /* Unpack data */ + position = 0; + ts->t8_element_MPI_Unpack (recvbuf, recvBufferSize, &position, &element_compare, count, comm); + t8_element_t **children_compare = T8_ALLOC (t8_element_t *, num_children); + ts->t8_element_new (num_children, children_compare); + ts->t8_element_MPI_Unpack (recvbuf, recvBufferSize, &position, children_compare, num_children, comm); + + /* free buffers */ + T8_FREE (sendbuf); + T8_FREE (recvbuf); + + /* Check that data was sent and received correctly */ + EXPECT_ELEM_EQ (ts, element, element_compare); + for (int ichild = 0; ichild < num_children; ichild++) { + EXPECT_ELEM_EQ (ts, children[ichild], children_compare[ichild]); + } + ts->t8_element_destroy (num_children, children); + ts->t8_element_destroy (num_children, children_compare); + T8_FREE (children); + T8_FREE (children_compare); + } + + protected: + void + SetUp () override + { + dfs_test_setup (); + /* Get element and initialize it */ + ts->t8_element_new (1, &element_compare); + + comm = sc_MPI_COMM_WORLD; + mpiret = sc_MPI_Comm_rank (comm, &rank); + SC_CHECK_MPI (mpiret); + } + void + TearDown () override + { + /* Destroy element */ + ts->t8_element_destroy (1, &element_compare); + + /* Destroy DFS test */ + dfs_test_teardown (); + } + t8_element_t *element_compare; + sc_MPI_Comm comm; + int rank; + int mpiret; +}; + +TEST_P (class_test_pack, test_equal_dfs) +{ +#ifdef T8_ENABLE_LESS_TESTS + const int maxlvl = 4; +#else + const int maxlvl = 6; +#endif + check_recursive_dfs_to_max_lvl (maxlvl); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_test_all_imps, class_test_pack, testing::Values (T8_ECLASS_HEX)); diff --git a/test/t8_schemes_transition/t8_gtest_root.cxx b/test/t8_schemes_transition/t8_gtest_root.cxx new file mode 100644 index 0000000000..7caeeef6be --- /dev/null +++ b/test/t8_schemes_transition/t8_gtest_root.cxx @@ -0,0 +1,72 @@ +/* +This file is part of t8code. +t8code is a C library to manage a collection (a forest) of multiple +connected adaptive space-trees of general element classes in parallel. + +Copyright (C) 2024 the developers + +t8code is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +t8code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with t8code; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_gtest_root.cxx +*/ + +#include +#include +#include +#include +#include +#include + +class root: public testing::TestWithParam { + protected: + void + SetUp () override + { + eclass = GetParam (); + scheme = t8_scheme_new_transition_hex_cxx (); + ts = scheme->eclass_schemes[eclass]; + ts->t8_element_new (1, &element); + ts->t8_element_root (element); + } + void + TearDown () override + { + ts->t8_element_destroy (1, &element); + t8_scheme_cxx_unref (&scheme); + } + t8_element_t *element; + t8_scheme_cxx *scheme; + t8_eclass_scheme_c *ts; + t8_eclass_t eclass; +}; + +/*Test root*/ + +TEST_P (root, has_level_zero) +{ + EXPECT_EQ (ts->t8_element_level (element), 0); +} + +TEST_P (root, equals_linear_id_0_0) +{ + t8_element_t *root_compare; + ts->t8_element_new (1, &root_compare); + ts->t8_element_set_linear_id (root_compare, 0, 0); + EXPECT_ELEM_EQ (ts, element, root_compare); + ts->t8_element_destroy (1, &root_compare); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_root, root, testing::Values (T8_ECLASS_HEX), print_eclass); diff --git a/test/t8_schemes_transition/t8_gtest_successor.cxx b/test/t8_schemes_transition/t8_gtest_successor.cxx new file mode 100644 index 0000000000..188622b0d1 --- /dev/null +++ b/test/t8_schemes_transition/t8_gtest_successor.cxx @@ -0,0 +1,159 @@ +/* + 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 types in parallel. + + Copyright (C) 2010 The University of Texas System + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + 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 + +class class_successor: public testing::TestWithParam { + protected: + void + SetUp () override + { + eclass = GetParam (); + + scheme = t8_scheme_new_transition_hex_cxx (); + ts = scheme->eclass_schemes[eclass]; + + ts->t8_element_new (1, &element); + ts->t8_element_new (1, &successor); + ts->t8_element_new (1, &child); + ts->t8_element_new (1, &last); + + ts->t8_element_root (element); + if (eclass == T8_ECLASS_VERTEX) + GTEST_SKIP (); + } + void + TearDown () override + { + ts->t8_element_destroy (1, &element); + ts->t8_element_destroy (1, &successor); + ts->t8_element_destroy (1, &child); + ts->t8_element_destroy (1, &last); + + t8_scheme_cxx_unref (&scheme); + } + t8_eclass_t eclass; + t8_eclass_scheme_c *ts; + t8_scheme_cxx *scheme; + t8_element_t *element; + t8_element_t *successor; + t8_element_t *child; + t8_element_t *last; +}; + +/* Check the computation of the successor recursively. Iterate through the elements + * via DFS. On the given maxlvl-1 the children are computeted iteratively. For + * each child, the successor is checked. + */ +static void +t8_recursive_successor (t8_element_t *element, t8_element_t *successor, t8_element_t *child, t8_element_t *last, + t8_eclass_scheme_c *ts, const int maxlvl) +{ + const int level = ts->t8_element_level (element); + ASSERT_TRUE (ts->t8_element_level (element) <= maxlvl && maxlvl <= ts->t8_element_maxlevel () - 1); + const int num_children = ts->t8_element_num_children (element); + if (level == maxlvl - 1) { + /* Check, if the successor of the last recursion is the first child of + * of this element. + */ + ts->t8_element_child (element, 0, child); + EXPECT_ELEM_EQ (ts, child, successor); + /*Check if the successor in this element is computed correctly */ + for (int ichild = 1; ichild < num_children; ichild++) { + EXPECT_EQ (ts->t8_element_level (child), maxlvl); + ts->t8_element_successor (child, successor); + ts->t8_element_child (element, ichild, child); + EXPECT_ELEM_EQ (ts, child, successor); + } + /*If the iterator is the last element, the test can finish */ + if (ts->t8_element_equal (last, child)) { + return; + } + /*Compute the next successor / "jump" out of the current element */ + else { + EXPECT_EQ (ts->t8_element_level (child), maxlvl); + ts->t8_element_successor (child, successor); + } + } + else { + /*DFS run through the elements */ + for (int ichild = 0; ichild < num_children; ichild++) { + ts->t8_element_child (element, ichild, child); + t8_recursive_successor (child, successor, element, last, ts, maxlvl); + ts->t8_element_parent (child, element); + } + } +} + +/* Check the computation of the successor at the maximum level of the element. + * Given the element of level maxlevel-2 with linear id 0 all children of + * maximum level are computed. The successor runs through all these children. + */ +static void +t8_deep_successor (t8_element_t *element, t8_element_t *successor, t8_element_t *child, t8_eclass_scheme_c *ts) +{ + int maxlvl = ts->t8_element_maxlevel (); + int num_children = ts->t8_element_num_children (element); + + for (int ichild = 0; ichild < num_children; ichild++) { + ts->t8_element_child (element, ichild, child); + /* Go to the children at maximum level. */ + const int num_children_child = ts->t8_element_num_children (child); + for (int jchild = 0; jchild < num_children_child; jchild++) { + ts->t8_element_child (child, jchild, element); + /* Check the computation of the successor. */ + ASSERT_TRUE (ts->t8_element_equal (element, successor)) << "Wrong Successor at Maxlvl.\n"; + /* Compute the next successor. */ + EXPECT_EQ (ts->t8_element_level (successor), maxlvl); + ts->t8_element_successor (successor, successor); + } + ts->t8_element_parent (child, element); + } +} + +TEST_P (class_successor, test_recursive_and_deep_successor) +{ +#ifdef T8_ENABLE_LESS_TESTS + const int maxlvl = 3; +#else + const int maxlvl = 4; +#endif + + /* Test at lower level. */ + for (int ilevel = 1; ilevel <= maxlvl; ilevel++) { + ts->t8_element_set_linear_id (successor, ilevel, 0); + ts->t8_element_last_descendant (element, last, ilevel); + t8_recursive_successor (element, successor, child, last, ts, ilevel); + } + /* Test at Maxlevel. */ + ts->t8_element_set_linear_id (element, ts->t8_element_maxlevel () - 2, 0); + ts->t8_element_set_linear_id (successor, ts->t8_element_maxlevel (), 0); + t8_deep_successor (element, successor, last, ts); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_successor, class_successor, testing::Values (T8_ECLASS_HEX), print_eclass);