From 0d2ad43eb7fae3b45eeb01b205c61ce56e98676f Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Mon, 22 Dec 2025 13:31:17 +0100 Subject: [PATCH 1/7] add constructor wrappers --- mesh_handle/constructor_wrapper.hxx | 78 +++++++++++++++++++ .../mesh_handle/t8_gtest_cache_competence.cxx | 50 ++++-------- .../t8_gtest_custom_competence.cxx | 16 ++-- test/mesh_handle/t8_gtest_handle_data.cxx | 41 ++++------ 4 files changed, 112 insertions(+), 73 deletions(-) create mode 100644 mesh_handle/constructor_wrapper.hxx diff --git a/mesh_handle/constructor_wrapper.hxx b/mesh_handle/constructor_wrapper.hxx new file mode 100644 index 0000000000..483ffe9a27 --- /dev/null +++ b/mesh_handle/constructor_wrapper.hxx @@ -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) 2025 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 constructor_wrapper.hxx + * Wrapper for the mesh constructor from a cmesh and some default cmeshes. + */ + +#ifndef T8_CONSTRUCTOR_WRAPPER_HXX +#define T8_CONSTRUCTOR_WRAPPER_HXX + +#include +#include +#include +#include +#include +#include + +namespace t8_mesh_handle +{ + +/** Build a uniformly refined mesh handle on a coarse mesh using a default scheme. + * \param [in] cmesh A coarse mesh. + * \param [in] level An initial uniform refinement level. + * \param [in] comm MPI communicator to use. + * \param [in] do_face_ghost If true, a layer of ghost elements is created. + * \tparam TMesh The mesh handle class. + * \return Unique pointer to a uniformly refined mesh handle with coarse mesh \a cmesh and refinement level \a level. + */ +template +std::unique_ptr +handle_new_uniform_default (const t8_cmesh_t cmesh, const int level, const sc_MPI_Comm comm, + const bool do_face_ghost = false) +{ + const t8_scheme *scheme = t8_scheme_new_default (); + t8_forest_t forest = t8_forest_new_uniform (cmesh, scheme, level, do_face_ghost, sc_MPI_COMM_WORLD); + return std::make_unique (forest); +} + +/** Hybercube with 6 Tets, 6 Prism, 4 Hex. Refined uniformly to given level. + * \param [in] level An initial uniform refinement level. + * \param [in] comm MPI communicator to use. + * \param [in] do_partition If non-zero create a partitioned cmesh. + * \param [in] do_face_ghost If true, a layer of ghost elements is created. + * \param [in] periodic If non-zero create a periodic cmesh in each direction. + * \tparam TMesh The mesh handle class. + * \return Unique pointer to a uniformly refined mesh handle initially consisting of 6 Tets, 6 prism and 4 hex. + * Together, they form a cube. +*/ +template +std::unique_ptr +handle_hypercube_uniform_default (const int level, const sc_MPI_Comm comm, const bool do_partition = false, + const bool do_face_ghost = false, const bool periodic = false) +{ + t8_cmesh_t cmesh = t8_cmesh_new_hypercube_hybrid (comm, do_partition, periodic); + return handle_new_uniform_default (cmesh, level, comm, do_face_ghost); +} + +} // namespace t8_mesh_handle +#endif /* !T8_CONSTRUCTOR_WRAPPER_HXX */ diff --git a/test/mesh_handle/t8_gtest_cache_competence.cxx b/test/mesh_handle/t8_gtest_cache_competence.cxx index 5c881fae64..5906518512 100644 --- a/test/mesh_handle/t8_gtest_cache_competence.cxx +++ b/test/mesh_handle/t8_gtest_cache_competence.cxx @@ -31,10 +31,7 @@ along with t8code; if not, write to the Free Software Foundation, Inc., #include #include #include -#include -#include -#include -#include +#include #include #include @@ -83,43 +80,20 @@ struct cache_centroid_overwrite: public t8_mesh_handle::cache_centroidrc.refcount > 0) { - t8_forest_unref (&forest); - } - } - - t8_forest_t forest; - int level; -}; - /** Use child class of \ref t8_mesh_handle::cache_volume class to check that the cache is actually set * and accessed correctly. This is done by modifying the cache to an unrealistic value and * checking that the functionality actually outputs this unrealistic value. */ -TEST_F (t8_gtest_cache_competence, cache_volume) +TEST (t8_gtest_cache_competence, cache_volume) { + const int level = 1; using mesh_class = t8_mesh_handle::mesh>; using element_class = typename mesh_class::abstract_element_class; - mesh_class mesh = mesh_class (forest); + auto mesh = t8_mesh_handle::handle_hypercube_uniform_default (level, sc_MPI_COMM_WORLD); EXPECT_TRUE (element_class::has_volume_cache ()); double unrealistic_volume = -3000; - for (auto it = mesh.begin (); it != mesh.end (); ++it) { + for (auto it = mesh->begin (); it != mesh->end (); ++it) { // Check that cache is empty at the beginning. EXPECT_FALSE (it->volume_cache_filled ()); // Fill cache and check that volume is valid. @@ -137,15 +111,16 @@ TEST_F (t8_gtest_cache_competence, cache_volume) * and accessed correctly. This is done by modifying the cache to an unrealistic value and * checking that the functionality actually outputs this unrealistic value. */ -TEST_F (t8_gtest_cache_competence, cache_vertex_coordinates) +TEST (t8_gtest_cache_competence, cache_vertex_coordinates) { + const int level = 1; using mesh_class = t8_mesh_handle::mesh>; using element_class = typename mesh_class::abstract_element_class; - mesh_class mesh = mesh_class (forest); + auto mesh = t8_mesh_handle::handle_hypercube_uniform_default (level, sc_MPI_COMM_WORLD); EXPECT_TRUE (element_class::has_vertex_cache ()); std::vector unrealistic_vertex = { t8_3D_point ({ 41, 42, 43 }), t8_3D_point ({ 99, 100, 101 }) }; - for (auto it = mesh.begin (); it != mesh.end (); ++it) { + for (auto it = mesh->begin (); it != mesh->end (); ++it) { // Check that cache is empty at the beginning. EXPECT_FALSE (it->vertex_cache_filled ()); // Check that values are valid. @@ -169,15 +144,16 @@ TEST_F (t8_gtest_cache_competence, cache_vertex_coordinates) * and accessed correctly. This is done by modifying the cache to an unrealistic value and * checking that the functionality actually outputs this unrealistic value. */ -TEST_F (t8_gtest_cache_competence, cache_centroid) +TEST (t8_gtest_cache_competence, cache_centroid) { + const int level = 1; using mesh_class = t8_mesh_handle::mesh>; using element_class = mesh_class::abstract_element_class; - mesh_class mesh = mesh_class (forest); + auto mesh = t8_mesh_handle::handle_hypercube_uniform_default (level, sc_MPI_COMM_WORLD); EXPECT_TRUE (element_class::has_centroid_cache ()); t8_3D_point unrealistic_centroid ({ 999, 1000, 998 }); - for (auto it = mesh.begin (); it != mesh.end (); ++it) { + for (auto it = mesh->begin (); it != mesh->end (); ++it) { // Check that cache is empty at the beginning. EXPECT_FALSE (it->centroid_cache_filled ()); // Check that values are valid. diff --git a/test/mesh_handle/t8_gtest_custom_competence.cxx b/test/mesh_handle/t8_gtest_custom_competence.cxx index 788b35ab74..183ac20b95 100644 --- a/test/mesh_handle/t8_gtest_custom_competence.cxx +++ b/test/mesh_handle/t8_gtest_custom_competence.cxx @@ -31,10 +31,8 @@ along with t8code; if not, write to the Free Software Foundation, Inc., #include #include #include -#include -#include +#include #include -#include #include /** @@ -85,26 +83,22 @@ TEST (t8_gtest_custom_competence, custom_competence) { // Define forest to construct mesh. const int level = 1; - t8_cmesh_t cmesh = t8_cmesh_new_hypercube_hybrid (sc_MPI_COMM_WORLD, 0, 0); - const t8_scheme *scheme = t8_scheme_new_default (); - t8_forest_t forest = t8_forest_new_uniform (cmesh, scheme, level, 0, sc_MPI_COMM_WORLD); // Check mesh with custom defined competence. using mesh_class_custom = t8_mesh_handle::mesh>; - mesh_class_custom mesh = mesh_class_custom (forest); + auto mesh = t8_mesh_handle::handle_hypercube_uniform_default (level, sc_MPI_COMM_WORLD); - for (auto it = mesh.begin (); it != mesh.end (); ++it) { + for (auto it = mesh->begin (); it != mesh->end (); ++it) { EXPECT_EQ (it->get_level (), it->get_level_dummy ()); EXPECT_EQ (level, it->get_level_dummy ()); } - t8_forest_ref (forest); // Test with two custom competences and a predefined competence. using competences = t8_mesh_handle::competence_pack; using mesh_class = t8_mesh_handle::mesh; - mesh_class mesh_more_competences = mesh_class (forest); + auto mesh_more_competences = t8_mesh_handle::handle_hypercube_uniform_default (level, sc_MPI_COMM_WORLD); - for (auto it = mesh_more_competences.begin (); it != mesh_more_competences.end (); ++it) { + for (auto it = mesh_more_competences->begin (); it != mesh_more_competences->end (); ++it) { EXPECT_EQ (it->get_level (), it->get_level_dummy ()); EXPECT_EQ (it->get_value_dummy (), 1); EXPECT_FALSE (it->centroid_cache_filled ()); diff --git a/test/mesh_handle/t8_gtest_handle_data.cxx b/test/mesh_handle/t8_gtest_handle_data.cxx index 53adab5eac..b07bad833f 100644 --- a/test/mesh_handle/t8_gtest_handle_data.cxx +++ b/test/mesh_handle/t8_gtest_handle_data.cxx @@ -30,10 +30,7 @@ along with t8code; if not, write to the Free Software Foundation, Inc., #include #include -#include -#include -#include -#include +#include #include #include @@ -50,14 +47,10 @@ struct dummy_user_data */ TEST (t8_gtest_handle_data, set_and_get_user_data) { - // Define forest and mesh handle. + // Define mesh handle. const int level = 2; - t8_cmesh_t cmesh = t8_cmesh_new_hypercube_hybrid (sc_MPI_COMM_WORLD, 0, 0); - const t8_scheme *init_scheme = t8_scheme_new_default (); - t8_forest_t forest = t8_forest_new_uniform (cmesh, init_scheme, level, 0, sc_MPI_COMM_WORLD); - using mesh_class = t8_mesh_handle::mesh, dummy_user_data>; - mesh_class mesh = mesh_class (forest); + auto mesh = t8_mesh_handle::handle_hypercube_uniform_default (level, sc_MPI_COMM_WORLD); struct dummy_user_data user_data = { t8_3D_point ({ 41, 42, 43 }), /* Midpoints of the sphere. */ @@ -66,8 +59,8 @@ TEST (t8_gtest_handle_data, set_and_get_user_data) }; // Set user data for the mesh handle and check that the getter returns the same data. - mesh.set_user_data (&user_data); - auto mesh_user_data = mesh.get_user_data (); + mesh->set_user_data (&user_data); + auto mesh_user_data = mesh->get_user_data (); EXPECT_EQ (mesh_user_data.midpoint, user_data.midpoint); EXPECT_EQ (mesh_user_data.refine_if_inside_radius, user_data.refine_if_inside_radius); EXPECT_EQ (mesh_user_data.coarsen_if_outside_radius, user_data.coarsen_if_outside_radius); @@ -86,28 +79,26 @@ struct data_per_element */ TEST (t8_gtest_handle_data, set_and_get_element_data) { - // Define forest and mesh handle. + // Define mesh handle. const int level = 2; - t8_cmesh_t cmesh = t8_cmesh_new_hypercube_hybrid (sc_MPI_COMM_WORLD, 0, 0); - const t8_scheme *init_scheme = t8_scheme_new_default (); - t8_forest_t forest = t8_forest_new_uniform (cmesh, init_scheme, level, 1, sc_MPI_COMM_WORLD); - using mesh_class = t8_mesh_handle::mesh, void, data_per_element>; - mesh_class mesh = mesh_class (forest); - if ((mesh.get_dimension () > 1) && (mesh.get_num_local_elements () > 1)) { + auto mesh + = t8_mesh_handle::handle_hypercube_uniform_default (level, sc_MPI_COMM_WORLD, true, true, false); + + if ((mesh->get_dimension () > 1) && (mesh->get_num_local_elements () > 1)) { // Ensure that we actually test with ghost elements. - EXPECT_GT (mesh.get_num_local_ghosts (), 0); + EXPECT_GT (mesh->get_num_local_ghosts (), 0); } // Create element data for all local mesh elements. std::vector element_data; - for (const auto &elem : mesh) { + for (const auto &elem : *mesh) { element_data.push_back ({ elem.get_level (), elem.get_volume () }); } - mesh.set_element_data (element_data); + mesh->set_element_data (element_data); // Get element data and check that the data for all elements (including ghosts) is correct. - auto mesh_element_data = mesh.get_element_data (); - for (t8_locidx_t ielem = 0; ielem < mesh.get_num_local_elements () + mesh.get_num_local_ghosts (); ielem++) { + auto mesh_element_data = mesh->get_element_data (); + for (t8_locidx_t ielem = 0; ielem < mesh->get_num_local_elements () + mesh->get_num_local_ghosts (); ielem++) { EXPECT_EQ (mesh_element_data[ielem].level, level) << "ielem = " << ielem; - EXPECT_EQ (mesh_element_data[ielem].volume, mesh[ielem].get_volume ()) << "ielem = " << ielem; + EXPECT_EQ (mesh_element_data[ielem].volume, (*mesh)[ielem].get_volume ()) << "ielem = " << ielem; } } From 554be6c9ec71bc5d058bf9083133f626e3538df3 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Fri, 2 Jan 2026 15:33:28 +0100 Subject: [PATCH 2/7] added some functionality --- mesh_handle/abstract_element.hxx | 262 ++++++++++++++++++++++++++-- mesh_handle/competence_pack.hxx | 7 +- mesh_handle/competences.hxx | 94 +++++++++- mesh_handle/mesh_element.hxx | 12 +- test/mesh_handle/t8_gtest_ghost.cxx | 241 +++++++++++++------------ 5 files changed, 485 insertions(+), 131 deletions(-) diff --git a/mesh_handle/abstract_element.hxx b/mesh_handle/abstract_element.hxx index 2c23172243..1484242bf0 100644 --- a/mesh_handle/abstract_element.hxx +++ b/mesh_handle/abstract_element.hxx @@ -91,6 +91,20 @@ class abstract_element: public TCompetence ()); + /** Helper function to check if class T implements the function diameter_cache_defined. + * \tparam T The competence to be checked. + * \return true if T implements the function, false if not. + */ + template