diff --git a/example/forest/t8_extruded_hex.cxx b/example/forest/t8_extruded_hex.cxx index cf21e5b60d..8032063efb 100644 --- a/example/forest/t8_extruded_hex.cxx +++ b/example/forest/t8_extruded_hex.cxx @@ -100,7 +100,6 @@ main (int argc, char **argv) t8_cmesh_t cmesh; t8_cmesh_init (&cmesh); t8_cmesh_new_brick_3d (cmesh, num_x, num_y, num_z, 0, 0, 0, comm); - SC_CHECK_ABORT (t8_cmesh_is_extrusion_compatible (cmesh), "The cmesh is not compatible with extruded hexes."); t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_extruded (), level, 0, comm); t8_global_productionf ("Uniform forest of level %i with %lli elements.\n", level, diff --git a/src/t8_cmesh/t8_cmesh_examples.cxx b/src/t8_cmesh/t8_cmesh_examples.cxx index ef4d49c92e..3a4c8fa57c 100644 --- a/src/t8_cmesh/t8_cmesh_examples.cxx +++ b/src/t8_cmesh/t8_cmesh_examples.cxx @@ -848,7 +848,7 @@ t8_cmesh_new_hypercube (t8_cmesh_t *pcmesh, t8_eclass_t eclass, sc_MPI_Comm comm t8_cmesh_set_tree_vertices (cmesh, 1, attr_vertices, 6); if (periodic) { t8_cmesh_set_join (cmesh, 0, 1, 0, 1, 0); - t8_cmesh_set_join (cmesh, 0, 1, 2, 0, 0); + t8_cmesh_set_join (cmesh, 0, 1, 2, 0, 1); t8_cmesh_set_join (cmesh, 0, 0, 3, 4, 0); t8_cmesh_set_join (cmesh, 1, 1, 3, 4, 0); } diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 3bef5f8e89..a84a961c69 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -3306,6 +3306,9 @@ t8_forest_commit (t8_forest_t forest) * TODO: Remove when trees access schemes via key. * Also remove the complete function t8_forest_scheme_is_valid */ T8_ASSERT (t8_forest_scheme_is_valid (forest->scheme)); + /* Check if the scheme supports the trees of the cmesh and their face connections. */ + SC_CHECK_ABORT (forest->scheme->cmesh_is_compatible (forest->cmesh), + "The cmesh is not compatible with the scheme of the forest."); /* dup communicator if requested */ if (forest->do_dup) { diff --git a/src/t8_schemes/t8_extruded/t8_extruded.cxx b/src/t8_schemes/t8_extruded/t8_extruded.cxx index cf683ecef3..d7835c3349 100644 --- a/src/t8_schemes/t8_extruded/t8_extruded.cxx +++ b/src/t8_schemes/t8_extruded/t8_extruded.cxx @@ -76,10 +76,10 @@ t8_extruded_num_lateral_faces (const t8_eclass_t eclass) } } -/** Check whether a single face connection of an extruded tree is compatible with the extruded schemes. +/** Check whether a single face connection between two extruded trees is compatible with the extruded schemes. * \param [in] eclass The eclass of the tree, hex or prism. * \param [in] face A face of the tree. - * \param [in] neigh_class The eclass of the neighbor tree. + * \param [in] neigh_class The eclass of the neighbor tree, hex or prism. * \param [in] neigh_face The face of the neighbor tree. * \param [in] orientation The orientation of the face connection. * \return True if the connection is compatible. @@ -90,10 +90,7 @@ t8_extruded_face_connection_is_compatible (const t8_eclass_t eclass, const int f const int neigh_face, const int orientation) { const int neigh_num_lateral_faces = t8_extruded_num_lateral_faces (neigh_class); - if (neigh_num_lateral_faces < 0) { - /* The neighbor tree is not extruded. */ - return false; - } + T8_ASSERT (neigh_num_lateral_faces > 0); const bool face_is_lateral = face < t8_extruded_num_lateral_faces (eclass); const bool neigh_face_is_lateral = neigh_face < neigh_num_lateral_faces; if (face_is_lateral != neigh_face_is_lateral) { @@ -116,34 +113,25 @@ t8_extruded_face_connection_is_compatible (const t8_eclass_t eclass, const int f } bool -t8_cmesh_is_extrusion_compatible (t8_cmesh_t cmesh) +t8_extruded_cmesh_tree_is_compatible (const t8_scheme *scheme, const t8_cmesh_t cmesh, const t8_locidx_t ltreeid) { T8_ASSERT (t8_cmesh_is_committed (cmesh)); - const t8_locidx_t num_local_trees = t8_cmesh_get_num_local_trees (cmesh); - for (t8_locidx_t ltree = 0; ltree < num_local_trees; ++ltree) { - const t8_eclass_t eclass = t8_cmesh_get_tree_class (cmesh, ltree); - if (t8_eclass_to_dimension[eclass] < 3) { - /* Lower dimensional trees use the default schemes. */ + const t8_eclass_t eclass = t8_cmesh_get_tree_class (cmesh, ltreeid); + T8_ASSERT (t8_eclass_scheme_is_extruded (scheme, eclass)); + for (int face = 0; face < t8_eclass_num_faces[eclass]; ++face) { + int neigh_face; + int orientation; + const t8_locidx_t neigh_tree = t8_cmesh_get_face_neighbor (cmesh, ltreeid, face, &neigh_face, &orientation); + if (neigh_tree < 0) { + /* No neighbor across this face. */ continue; } - if (t8_extruded_num_lateral_faces (eclass) < 0) { - t8_debugf ("Local tree %i of class %s cannot be extruded.\n", ltree, t8_eclass_to_string[eclass]); + const t8_eclass_t neigh_class = t8_cmesh_get_tree_face_neighbor_eclass (cmesh, ltreeid, face); + if (!t8_eclass_scheme_is_extruded (scheme, neigh_class) + || !t8_extruded_face_connection_is_compatible (eclass, face, neigh_class, neigh_face, orientation)) { + t8_debugf ("Face %i of local tree %i is not compatible with the extruded schemes.\n", face, ltreeid); return false; } - for (int face = 0; face < t8_eclass_num_faces[eclass]; ++face) { - int neigh_face; - int orientation; - const t8_locidx_t neigh_tree = t8_cmesh_get_face_neighbor (cmesh, ltree, face, &neigh_face, &orientation); - if (neigh_tree < 0) { - /* No neighbor across this face. */ - continue; - } - const t8_eclass_t neigh_class = t8_cmesh_get_tree_face_neighbor_eclass (cmesh, ltree, face); - if (!t8_extruded_face_connection_is_compatible (eclass, face, neigh_class, neigh_face, orientation)) { - t8_debugf ("Face %i of local tree %i is not compatible with the extruded schemes.\n", face, ltree); - return false; - } - } } return true; } diff --git a/src/t8_schemes/t8_extruded/t8_extruded.hxx b/src/t8_schemes/t8_extruded/t8_extruded.hxx index 7dcc395f08..8929347780 100644 --- a/src/t8_schemes/t8_extruded/t8_extruded.hxx +++ b/src/t8_schemes/t8_extruded/t8_extruded.hxx @@ -43,18 +43,3 @@ t8_scheme_new_extruded (); */ int t8_eclass_scheme_is_extruded (const t8_scheme *scheme, const t8_eclass_t eclass); - -/** Check whether a cmesh is compatible with the extruded schemes. - * All 3D trees have to be extruded, that is hexes or prisms, no tets or pyramids. - * The extruded elements span the whole tree height, so the extrusion (z-) directions of - * neighboring trees have to be parallel. That is, for each local 3D tree: - * - A lateral face (hex: 0, ..., 3, prism: 0, 1, 2) is only connected to a lateral face of another hex or prism tree, - * such that the z-axes of both trees are parallel (possibly with opposite directions). - * - The bottom and top face (hex: 4, 5, prism: 3, 4) are only connected to the bottom or top face of another tree. - * Cmeshes without 3D trees are always compatible. - * \param [in] cmesh A committed cmesh. - * \return True if all local trees and their face connections are compatible, false otherwise. - * \note This function is not collective. It only checks the local trees of \a cmesh. - */ -bool -t8_cmesh_is_extrusion_compatible (t8_cmesh_t cmesh); diff --git a/src/t8_schemes/t8_extruded/t8_extruded_scheme.hxx b/src/t8_schemes/t8_extruded/t8_extruded_scheme.hxx index c4555e5a08..d314393150 100644 --- a/src/t8_schemes/t8_extruded/t8_extruded_scheme.hxx +++ b/src/t8_schemes/t8_extruded/t8_extruded_scheme.hxx @@ -28,12 +28,12 @@ * * Face numbering: The lateral faces 0, ..., F-1 are the extrusions of the F faces of the base element. * Face F is the bottom and face F + 1 the top face. Vertex v is the base vertex v % V at the bottom (v < V) or - * the top (v >= V), where V is the number of base vertices. This matches the default hex (and prism) conventions. + * the top (v >= V), where V is the number of base vertices. This matches the default hex and prism conventions. * * The face elements of lateral faces are quads in the scheme of the face eclass. Since the element spans the whole tree * height, the lateral face element stores the in-plane coordinate in x and 0 in y. The z-part is ignored when * extruding the face again. This is only consistent, if all trees glued at lateral faces have parallel extrusion - * directions, see \ref t8_cmesh_is_extrusion_compatible. + * directions, see \ref t8_extruded_cmesh_tree_is_compatible. */ #pragma once @@ -50,6 +50,21 @@ #include #include +/** Check whether a local hex or prism tree of a cmesh is compatible with the extruded schemes. + * The extruded elements span the whole tree height, so the extrusion (z-) directions of neighboring trees have to be + * parallel. That is, for each face of the tree: + * - The neighbor tree also uses an extruded scheme. + * - A lateral face (hex: 0, ..., 3, prism: 0, 1, 2) is only connected to a lateral face, such that the z-axes of + * both trees are parallel (possibly with opposite directions). + * - The bottom and top face (hex: 4, 5, prism: 3, 4) are only connected to the bottom or top face of another tree. + * \param [in] scheme The scheme containing the extruded schemes. + * \param [in] cmesh A committed cmesh. + * \param [in] ltreeid A local tree of \a cmesh, whose class uses an extruded scheme in \a scheme. + * \return True if all face connections of the tree are compatible, false otherwise. + */ +bool +t8_extruded_cmesh_tree_is_compatible (const t8_scheme *scheme, const t8_cmesh_t cmesh, const t8_locidx_t ltreeid); + /** Scheme for extruded elements. * \tparam TEclass The 3D element class of the extruded trees. * \tparam TBaseScheme The scheme of the 2D base elements. @@ -111,6 +126,20 @@ struct t8_extruded_scheme: public t8_scheme_helperscmesh_is_compatible (cmesh); +} + size_t t8_element_get_element_size (const t8_scheme_c *scheme, const t8_eclass_t tree_class) { diff --git a/src/t8_schemes/t8_scheme.h b/src/t8_schemes/t8_scheme.h index ba70bacf28..e9852810a5 100644 --- a/src/t8_schemes/t8_scheme.h +++ b/src/t8_schemes/t8_scheme.h @@ -37,6 +37,9 @@ */ typedef struct t8_scheme t8_scheme_c; +/* Forward declaration of the cmesh, since t8_cmesh.h includes this header. */ +struct t8_cmesh; + T8_EXTERN_C_BEGIN (); /** Increase the reference counter of a scheme. @@ -58,6 +61,15 @@ t8_scheme_ref (t8_scheme_c *scheme); void t8_scheme_unref (t8_scheme_c **pscheme); +/** Check whether all local trees of a cmesh can be used with a scheme. + * \param [in] scheme A scheme. + * \param [in] cmesh A committed cmesh. + * \return True if all local trees and their face connections are supported by \a scheme. + * \note This function is not collective. It only checks the local trees of \a cmesh. + */ +int +t8_scheme_cmesh_is_compatible (const t8_scheme_c *scheme, struct t8_cmesh *cmesh); + /** Return the size of any element of a given class. * \return The size of an element of class \b ts. * We provide a default implementation of this routine that should suffice diff --git a/src/t8_schemes/t8_scheme.hxx b/src/t8_schemes/t8_scheme.hxx index 98ab02863c..6e21cc6560 100644 --- a/src/t8_schemes/t8_scheme.hxx +++ b/src/t8_schemes/t8_scheme.hxx @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -191,6 +192,36 @@ struct t8_scheme return std::visit ([&] (auto &&scheme) { return scheme.get_dimension (); }, eclass_schemes[tree_class]); } + /** Check whether a local tree of a cmesh can be used with its eclass scheme. + * \param [in] tree_class The eclass of the tree. + * \param [in] cmesh A committed cmesh. + * \param [in] ltreeid A local tree of \a cmesh of class \a tree_class. + * \return True if the tree and its face connections are supported by the eclass scheme. + */ + inline bool + cmesh_tree_is_compatible (const t8_eclass_t tree_class, const t8_cmesh_t cmesh, const t8_locidx_t ltreeid) const + { + return std::visit ([&] (auto &&scheme) { return scheme.cmesh_tree_is_compatible (this, cmesh, ltreeid); }, + eclass_schemes[tree_class]); + } + + /** Check whether all local trees of a cmesh can be used with this scheme. + * \param [in] cmesh A committed cmesh. + * \return True if all local trees and their face connections are supported. + * \note This function is not collective. It only checks the local trees of \a cmesh. + */ + inline bool + cmesh_is_compatible (const t8_cmesh_t cmesh) const + { + const t8_locidx_t num_local_trees = t8_cmesh_get_num_local_trees (cmesh); + for (t8_locidx_t ltreeid = 0; ltreeid < num_local_trees; ++ltreeid) { + if (!cmesh_tree_is_compatible (t8_cmesh_get_tree_class (cmesh, ltreeid), cmesh, ltreeid)) { + return false; + } + } + return true; + } + /** Return the size of any element of a given class. * \param [in] tree_class The eclass of the current tree. * \return The size of an element of class \a tree_class. diff --git a/src/t8_schemes/t8_scheme_helpers.hxx b/src/t8_schemes/t8_scheme_helpers.hxx index 60e4acc51a..82248c7ed0 100644 --- a/src/t8_schemes/t8_scheme_helpers.hxx +++ b/src/t8_schemes/t8_scheme_helpers.hxx @@ -30,6 +30,9 @@ #include #include #include +#include + +struct t8_scheme; /** * Class which provides helper functions and default implementations for different schemes. @@ -65,6 +68,20 @@ struct t8_scheme_helpers: public t8_crtp_basic return TEclass; } + /** Check whether a local tree of a cmesh can be used with this eclass scheme. + * By default, all trees and face connections are supported. + * \param [in] scheme The scheme this eclass scheme is part of, to look up the schemes of neighbor trees. + * \param [in] cmesh A committed cmesh. + * \param [in] ltreeid A local tree of \a cmesh of class \a TEclass. + * \return True if the tree and its face connections are supported. + */ + inline bool + cmesh_tree_is_compatible ([[maybe_unused]] const t8_scheme *scheme, [[maybe_unused]] const t8_cmesh_t cmesh, + [[maybe_unused]] const t8_locidx_t ltreeid) const + { + return true; + } + /** Given a face of an element and a level coarser than (or equal to) * the element's level, return the face number * of the ancestor of the element that matches the element's face. Or return -1 if diff --git a/test/t8_forest/t8_gtest_leaf_face_neighbors.cxx b/test/t8_forest/t8_gtest_leaf_face_neighbors.cxx index 77c5f84d5e..c2d67ceb8f 100644 --- a/test/t8_forest/t8_gtest_leaf_face_neighbors.cxx +++ b/test/t8_forest/t8_gtest_leaf_face_neighbors.cxx @@ -562,7 +562,7 @@ TEST_P (forest_face_neighbors_two_quad_mesh, check_neighbors) T8_FREE (dual_faces); } } // End face loop - } // End leaf in tree loop + } // End leaf in tree loop if (gtreeid == 0) { // Tree 0 must have >0 faces with 1 neighbor. EXPECT_GE (num_faces_with_1_neighbor, 1) << "Tree 1 must have 1 face with 2 neighbors."; @@ -775,7 +775,7 @@ TEST_P (forest_face_neighbors_subface, test_face_neighbor_subface) T8_TESTSUITE_FREE (neigh_children); } // end if(neighbor_level == level - 1) - } // end if(num_neighbors == 1) + } // end if(num_neighbors == 1) // Manually free memory allocated by t8code inside t8_forest_leaf_face_neighbors_ext. if (num_neighbors > 0) { @@ -784,8 +784,8 @@ TEST_P (forest_face_neighbors_subface, test_face_neighbor_subface) T8_FREE (dual_faces); } } // end face loop - } // end element loop - } // end tree loop + } // end element loop + } // end tree loop } // We check for all cmesh examples and scheme collections. diff --git a/test/t8_forest/t8_gtest_user_data.cxx b/test/t8_forest/t8_gtest_user_data.cxx index c134e0deb9..d83c34e706 100644 --- a/test/t8_forest/t8_gtest_user_data.cxx +++ b/test/t8_forest/t8_gtest_user_data.cxx @@ -125,7 +125,7 @@ TEST_P (forest_user_data, test_user_function) void (*funpointer_second) (void); /* Set the t8_test_function_42 as user function pointer. */ - t8_forest_set_user_function (forest, (void (*) (void)) &t8_test_function_42); + t8_forest_set_user_function (forest, (void (*) (void)) & t8_test_function_42); /* Retrieve the function pointer from the forest. */ funpointer = (double (*) (int)) t8_forest_get_user_function (forest); @@ -135,7 +135,7 @@ TEST_P (forest_user_data, test_user_function) ASSERT_EQ (funpointer (0), 42.42) << "Forest function pointer returned wrong result."; /* Overwrite the function user pointer with a second function. */ - t8_forest_set_user_function (forest, (void (*) (void)) &t8_test_function_second); + t8_forest_set_user_function (forest, (void (*) (void)) & t8_test_function_second); /* Retrieve the function pointer from the forest. */ funpointer_second = (void (*) (void)) t8_forest_get_user_function (forest); diff --git a/test/t8_gtest_schemes.hxx b/test/t8_gtest_schemes.hxx index fa62f4340e..5c07d1fc4a 100644 --- a/test/t8_gtest_schemes.hxx +++ b/test/t8_gtest_schemes.hxx @@ -52,9 +52,8 @@ create_from_scheme_id (const int scheme_id) } } -/** Check whether a cmesh is supported by the scheme of a given scheme id. - * The extruded scheme only supports cmeshes whose hex trees have parallel extrusion directions, - * see \ref t8_cmesh_is_extrusion_compatible. All other schemes support all cmeshes. +/** Check whether a cmesh is supported by the scheme of a given scheme id, see \ref t8_scheme::cmesh_is_compatible. + * For example, the extruded scheme only supports cmeshes whose extruded trees have parallel extrusion directions. * \param [in] scheme_id The scheme id, see \ref create_from_scheme_id. * \param [in] cmesh A committed cmesh. * \param [in] comm The communicator of \a cmesh. @@ -64,10 +63,9 @@ create_from_scheme_id (const int scheme_id) inline bool t8_test_scheme_supports_cmesh (const int scheme_id, t8_cmesh_t cmesh, sc_MPI_Comm comm) { - if (scheme_id != 2) { - return true; - } - int is_compatible = t8_cmesh_is_extrusion_compatible (cmesh); + const t8_scheme *scheme = create_from_scheme_id (scheme_id); + int is_compatible = scheme->cmesh_is_compatible (cmesh); + scheme->unref (); int is_compatible_all = 0; const int mpiret = sc_MPI_Allreduce (&is_compatible, &is_compatible_all, 1, sc_MPI_INT, sc_MPI_LAND, comm); SC_CHECK_MPI (mpiret);