From ba23921711e21b883edd7d2b12d0c543ea4f7086 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Mon, 2 Mar 2026 13:28:40 +0100 Subject: [PATCH 1/6] WIP add competence --- mesh_handle/data_handler.hxx | 66 ++++++++++++++++++++++- test/mesh_handle/t8_gtest_handle_data.cxx | 2 +- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/mesh_handle/data_handler.hxx b/mesh_handle/data_handler.hxx index 7c98c19164..958021cd21 100644 --- a/mesh_handle/data_handler.hxx +++ b/mesh_handle/data_handler.hxx @@ -69,9 +69,8 @@ class handle_element_data: public t8_crtp_basic { const auto num_local_elements = this->underlying ().get_num_local_elements (); const auto num_ghosts = this->underlying ().get_num_ghosts (); T8_ASSERT (element_data.size () == static_cast (num_local_elements)); - m_element_data = std::move (element_data); m_element_data.reserve (num_local_elements + num_ghosts); - m_element_data.resize (num_local_elements); + m_element_data = std::move (element_data); } /** Get the element data vector. @@ -168,4 +167,67 @@ struct access_element_data: public t8_crtp_basic } }; +// --- Competences for new element data. --- +/** With the mesh competence \ref element_data_competence and the element competence \ref access_element_data, the element data + * are updated in place such that we cannot access the old data afterwards. Use the following competences to store the old element data. + */ +/** TODO: new element data to store + +Handler for the element data of a \ref mesh. + * Use this competence if you want to manage element data for the elements of the mesh. + * Use the helper \ref element_data_competence to get this competence with the correct template parameters form for the mesh. + * If you want to access the data not only in vector form but also directly for each element, + * you can combine this competence with the \ref access_element_data competence. + * In summary you can use the competences like this: + * mesh, + * mesh_competence_pack::template type>>; + * + * \tparam TUnderlying Use the \ref mesh class here. + * \tparam TElementDataType The element data type you want to use for each element of the mesh. + * The data type has to be MPI safe as the data for ghost elements will be exchanged via MPI. + */ +template +class mesh_second_element_data_vector: public t8_crtp_basic { + public: + /** Set the element data vector. The vector should have the length of num_local_elements. + * \param [in] element_data The element data vector to set with one entry of class TElementDataType + * for each local mesh element (excluding ghosts). + */ + void + set_second_vec_element_data (std::vector element_data) + { + T8_ASSERT (this->underlying ().has_element_data_handler_competence ()); + const auto num_local_elements = this->underlying ().get_num_local_elements (); + T8_ASSERT (element_data.size () == static_cast (num_local_elements)); + m_new_element_data = std::move (element_data); + m_new_element_data.resize (num_local_elements); + } + + /** Get the element data vector. + * The element data of the local mesh elements can be set using \ref set_element_data. + * If ghost entries should be filled, one should call \ref exchange_ghost_data on each process first. + * \return Element data vector with data of Type TElementDataType. + */ + const auto& + get_second_vec_element_data () const + { + T8_ASSERT (this->underlying ().has_element_data_handler_competence ()); + return m_new_element_data; + } + + /** TODO + */ + void + write_to_element_data () const + { + T8_ASSERT (this->underlying ().has_element_data_handler_competence ()); + m_new_element_data=std::move(m_new_element_data); + m_new_element_data.clear(); + } + + protected: + std::vector m_new_element_data; /**< Vector storing the (local) element data. */ +}; + + } // namespace t8_mesh_handle diff --git a/test/mesh_handle/t8_gtest_handle_data.cxx b/test/mesh_handle/t8_gtest_handle_data.cxx index ba6c7abe9b..1310077755 100644 --- a/test/mesh_handle/t8_gtest_handle_data.cxx +++ b/test/mesh_handle/t8_gtest_handle_data.cxx @@ -49,7 +49,7 @@ TEST (t8_gtest_handle_data, set_and_get_element_data) const int level = 2; using mesh_class = t8_mesh_handle::mesh< t8_mesh_handle::element_competence_pack, - t8_mesh_handle::mesh_competence_pack::template type>>; + t8_mesh_handle::mesh_competence_pack::template type, t8_mesh_handle::mesh_second_element_data_vector>>; auto mesh = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (level, sc_MPI_COMM_WORLD, true, true, false); From 197c25e982c11559bbab4ac86ffe58d3b10bb71e Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Mon, 2 Mar 2026 13:29:07 +0100 Subject: [PATCH 2/6] WIP add competence --- mesh_handle/data_handler.hxx | 12 ++++++------ test/mesh_handle/t8_gtest_handle_data.cxx | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/mesh_handle/data_handler.hxx b/mesh_handle/data_handler.hxx index 958021cd21..e2b9313718 100644 --- a/mesh_handle/data_handler.hxx +++ b/mesh_handle/data_handler.hxx @@ -211,7 +211,7 @@ class mesh_second_element_data_vector: public t8_crtp_basic { const auto& get_second_vec_element_data () const { - T8_ASSERT (this->underlying ().has_element_data_handler_competence ()); + T8_ASSERT (this->underlying ().has_element_data_handler_competence ()); return m_new_element_data; } @@ -219,15 +219,15 @@ class mesh_second_element_data_vector: public t8_crtp_basic { */ void write_to_element_data () const - { + { T8_ASSERT (this->underlying ().has_element_data_handler_competence ()); - m_new_element_data=std::move(m_new_element_data); - m_new_element_data.clear(); + m_new_element_data = std::move (m_new_element_data); + m_new_element_data.clear (); } protected: - std::vector m_new_element_data; /**< Vector storing the (local) element data. */ + std::vector + m_new_element_data; /**< Vector storing the (local) element data. */ }; - } // namespace t8_mesh_handle diff --git a/test/mesh_handle/t8_gtest_handle_data.cxx b/test/mesh_handle/t8_gtest_handle_data.cxx index 1310077755..9c60bab3f1 100644 --- a/test/mesh_handle/t8_gtest_handle_data.cxx +++ b/test/mesh_handle/t8_gtest_handle_data.cxx @@ -49,7 +49,8 @@ TEST (t8_gtest_handle_data, set_and_get_element_data) const int level = 2; using mesh_class = t8_mesh_handle::mesh< t8_mesh_handle::element_competence_pack, - t8_mesh_handle::mesh_competence_pack::template type, t8_mesh_handle::mesh_second_element_data_vector>>; + t8_mesh_handle::mesh_competence_pack::template type, + t8_mesh_handle::mesh_second_element_data_vector>>; auto mesh = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (level, sc_MPI_COMM_WORLD, true, true, false); From 6f6b6e91436b14715ea10d0952a58d41d1907f36 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Tue, 3 Mar 2026 16:30:57 +0100 Subject: [PATCH 3/6] First working version --- mesh_handle/data_handler.hxx | 120 ++++++++++++++---- mesh_handle/element.hxx | 2 +- mesh_handle/mesh.hxx | 42 ++++-- .../t8_gtest_adapt_partition_balance.cxx | 4 +- test/mesh_handle/t8_gtest_handle_data.cxx | 60 +++++++-- 5 files changed, 176 insertions(+), 52 deletions(-) diff --git a/mesh_handle/data_handler.hxx b/mesh_handle/data_handler.hxx index e2b9313718..4797bccb17 100644 --- a/mesh_handle/data_handler.hxx +++ b/mesh_handle/data_handler.hxx @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -43,19 +44,19 @@ concept T8MPISafeType /** Handler for the element data of a \ref mesh. * Use this competence if you want to manage element data for the elements of the mesh. - * Use the helper \ref element_data_competence to get this competence with the correct template parameters form for the mesh. + * Use the helper \ref element_data_mesh_competence to get this competence with the correct template parameters form for the mesh. * If you want to access the data not only in vector form but also directly for each element, - * you can combine this competence with the \ref access_element_data competence. + * you can combine this competence with the \ref element_data_element_competence competence. * In summary you can use the competences like this: - * mesh, - * mesh_competence_pack::template type>>; + * mesh, + * mesh_competence_pack::template type>>; * * \tparam TUnderlying Use the \ref mesh class here. * \tparam TElementDataType The element data type you want to use for each element of the mesh. * The data type has to be MPI safe as the data for ghost elements will be exchanged via MPI. */ template -class handle_element_data: public t8_crtp_basic { +class element_data_mesh_competence_impl: public t8_crtp_basic { public: using ElementDataType = TElementDataType; /**< Make Type of the element data publicly accessible. */ @@ -108,35 +109,35 @@ class handle_element_data: public t8_crtp_basic { std::vector m_element_data; /**< Vector storing the (local) element data. */ }; -/** Wrapper for \ref handle_element_data to hide TUnderlying and provide the form needed to pass it as a mesh competence. - * Use mesh_competence_pack::template type> +/** Wrapper for \ref element_data_mesh_competence_impl to hide TUnderlying and provide the form needed to pass it as a mesh competence. + * Use mesh_competence_pack::template type> * to get this competence with the correct template parameter form for the mesh. * \tparam TElementDataType The element data type you want to use for each element of the mesh. * The data type has to be MPI safe as the data for ghost elements will be exchanged via MPI. */ template -struct element_data_competence +struct element_data_mesh_competence { /** Type to provide the form needed for the mesh competence pack. * \tparam TUnderlying Use the \ref mesh class here. */ template - using type = handle_element_data; + using type = element_data_mesh_competence_impl; }; // --- Element competence for element data management. --- /** Element competence to enable that element data can be accessed directly for each element of the mesh. - * \note This competence requires that the mesh has the \ref handle_element_data competence to manage the + * \note This competence requires that the mesh has the \ref element_data_mesh_competence_impl competence to manage the * element data vector and exchange ghost data. * \tparam TUnderlying Use the \ref element with specified competences as template parameter. */ template -struct access_element_data: public t8_crtp_basic +struct element_data_element_competence: public t8_crtp_operator { public: // --- Getter and setter for element data. --- /** Getter for the element data. - * For ghost elements ensure that \ref handle_element_data::exchange_ghost_data is called on each process first. + * For ghost elements ensure that \ref element_data_mesh_competence_impl::exchange_ghost_data is called on each process first. * Element data for non-ghost elements can be accessed (if set) directly. * \return Element data with data of Type TMeshClass::ElementDataType. */ @@ -168,35 +169,42 @@ struct access_element_data: public t8_crtp_basic }; // --- Competences for new element data. --- -/** With the mesh competence \ref element_data_competence and the element competence \ref access_element_data, the element data +namespace detail +{ +template +struct new_element_data_helper +{ +}; +} // namespace detail +/** With the mesh competence \ref element_data_mesh_competence and the element competence \ref element_data_element_competence, the element data * are updated in place such that we cannot access the old data afterwards. Use the following competences to store the old element data. + * IDEA: The new interpolated element data is stored here (because we need the ghost data for calculation. This new element data is always used to update the elemtent data at the end of commit(). ) */ /** TODO: new element data to store Handler for the element data of a \ref mesh. * Use this competence if you want to manage element data for the elements of the mesh. - * Use the helper \ref element_data_competence to get this competence with the correct template parameters form for the mesh. + * Use the helper \ref element_data_mesh_competence to get this competence with the correct template parameters form for the mesh. * If you want to access the data not only in vector form but also directly for each element, - * you can combine this competence with the \ref access_element_data competence. + * you can combine this competence with the \ref element_data_element_competence competence. * In summary you can use the competences like this: - * mesh, - * mesh_competence_pack::template type>>; + * mesh, + * mesh_competence_pack::template type>>; * * \tparam TUnderlying Use the \ref mesh class here. * \tparam TElementDataType The element data type you want to use for each element of the mesh. * The data type has to be MPI safe as the data for ghost elements will be exchanged via MPI. */ -template -class mesh_second_element_data_vector: public t8_crtp_basic { +template +class new_element_data_mesh_competence_impl: public t8_crtp_operator { public: /** Set the element data vector. The vector should have the length of num_local_elements. * \param [in] element_data The element data vector to set with one entry of class TElementDataType * for each local mesh element (excluding ghosts). */ void - set_second_vec_element_data (std::vector element_data) + set_new_element_data (std::vector element_data) { - T8_ASSERT (this->underlying ().has_element_data_handler_competence ()); const auto num_local_elements = this->underlying ().get_num_local_elements (); T8_ASSERT (element_data.size () == static_cast (num_local_elements)); m_new_element_data = std::move (element_data); @@ -209,25 +217,83 @@ class mesh_second_element_data_vector: public t8_crtp_basic { * \return Element data vector with data of Type TElementDataType. */ const auto& - get_second_vec_element_data () const + get_new_element_data () const { - T8_ASSERT (this->underlying ().has_element_data_handler_competence ()); return m_new_element_data; } /** TODO */ void - write_to_element_data () const + write_new_to_element_data () { T8_ASSERT (this->underlying ().has_element_data_handler_competence ()); - m_new_element_data = std::move (m_new_element_data); + this->underlying ().set_element_data (m_new_element_data); m_new_element_data.clear (); } protected: - std::vector - m_new_element_data; /**< Vector storing the (local) element data. */ + std::vector m_new_element_data; /**< Vector storing the (local) element data. */ +}; + +/** Wrapper for \ref new_element_data_mesh_competence_impl to hide TUnderlying and provide the form needed to pass it as a mesh competence. + * Use mesh_competence_pack::template type> + * to get this competence with the correct template parameter form for the mesh. + * \tparam TElementDataType The element data type you want to use for each element of the mesh. + * The data type has to be MPI safe as the data for ghost elements will be exchanged via MPI. + */ +template +struct new_element_data_mesh_competence +{ + /** Type to provide the form needed for the mesh competence pack. + * \tparam TUnderlying Use the \ref mesh class here. + */ + template + using type = new_element_data_mesh_competence_impl; +}; + +// --- Element competence for element data management. --- +/** Element competence to enable that element data can be accessed directly for each element of the mesh. + * \note This competence requires that the mesh has the \ref element_data_mesh_competence_impl competence to manage the + * element data vector and exchange ghost data. + * \tparam TUnderlying Use the \ref element with specified competences as template parameter. + */ +template +struct new_element_data_element_competence: public t8_crtp_operator +{ + public: + // --- Getter and setter for element data. --- + /** Getter for the element data. + * For ghost elements ensure that \ref element_data_mesh_competence_impl::exchange_ghost_data is called on each process first. + * Element data for non-ghost elements can be accessed (if set) directly. + * \return Element data with data of Type TMeshClass::ElementDataType. + */ + const auto& + get_new_element_data () const + { + T8_ASSERT (this->underlying ().m_mesh->has_new_element_data_handler_competence ()); + const t8_locidx_t handle_id = this->underlying ().get_element_handle_id (); + T8_ASSERTF (static_cast (handle_id) < this->underlying ().m_mesh->m_new_element_data.size (), + "Element data not set.\n"); + return this->underlying ().m_mesh->m_new_element_data[handle_id]; + } + + /** Set the element data for the element. + * \note You can only set element data for non-ghost elements. + * \param [in] element_data The element data to be set of Type TMeshClass::ElementDataType. + */ + void + set_new_element_data (auto element_data) + { + T8_ASSERT (this->underlying ().m_mesh->has_new_element_data_handler_competence ()); + SC_CHECK_ABORT (!this->underlying ().is_ghost_element (), "Element data cannot be set for ghost elements.\n"); + // Resize for the case that no data vector has been set previously. + this->underlying ().m_mesh->m_new_element_data.reserve (this->underlying ().m_mesh->get_num_local_elements () + + this->underlying ().m_mesh->get_num_ghosts ()); + this->underlying ().m_mesh->m_new_element_data.resize (this->underlying ().m_mesh->get_num_local_elements ()); + this->underlying ().m_mesh->m_new_element_data[this->underlying ().get_element_handle_id ()] + = std::move (element_data); + } }; } // namespace t8_mesh_handle diff --git a/mesh_handle/element.hxx b/mesh_handle/element.hxx index 49123e3c4f..8a47e3d384 100644 --- a/mesh_handle/element.hxx +++ b/mesh_handle/element.hxx @@ -65,7 +65,7 @@ class element: public TCompetences>... { using SelfType = element; /**< Type of the current class with all template parameters specified. */ friend TMeshClass; /**< Define TMeshClass as friend to be able to access e.g. the constructor. */ - friend struct access_element_data< + friend struct element_data_element_competence< SelfType>; /**< Define the competence to access element data as friend to be able to access e.g. the mesh. */ /** Private constructor for an element of a mesh. This could be a simple mesh element or a ghost element. diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index 463a0c9e24..dcd6a7b9c9 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -58,7 +58,7 @@ concept MeshCompetencePack = requires { typename TType::is_mesh_competence_pack; * \note Please pack your competences using the \ref element_competence_pack class. * \tparam TMeshCompetences The competences you want to add to the default functionality of the mesh. * \note Please pack your competences using the \ref t8_mesh_handle::mesh_competence_pack class. - * One of the most important competences to add is \ref handle_element_data. + * One of the most important competences to add is \ref element_data_mesh_competence. */ template , MeshCompetencePack TMeshCompetencePack = mesh_competence_pack<>> @@ -71,8 +71,10 @@ class mesh: public TMeshCompetencePack::template apply::const_iterator; /**< Constant iterator type for the mesh elements. */ using mesh_iterator = - typename std::vector::iterator; /**< Non-const iterator type for the mesh elements. */ - friend struct access_element_data; /**< Friend struct to access its element data vector. */ + typename std::vector::iterator; /**< Non-const iterator type for the mesh elements. */ + friend struct element_data_element_competence; /**< Friend struct to access its element data vector. */ + friend struct new_element_data_element_competence< + element_class>; /**< Friend struct to access its element data vector. */ /** Callback function prototype to decide for refining and coarsening of a family of elements * or one element in a mesh handle. @@ -249,6 +251,25 @@ class mesh: public TMeshCompetencePack::template apply (static_cast (this)->operator[] (local_index)); } + // --- Methods to check for mesh competences. --- + /** Function that checks if a competence for element data handling is given. + * \return true if mesh has a data handler, false otherwise. + */ + static constexpr bool + has_element_data_handler_competence () + { + return requires (SelfType& mesh) { mesh.get_element_data (); }; + } + + /** Function that checks if a competence for element data handling is given. + * \return true if mesh has a data handler, false otherwise. + */ + static constexpr bool + has_new_element_data_handler_competence () + { + return requires (SelfType& mesh) { mesh.get_new_element_data (); }; + } + // --- Methods to change the mesh, e.g. adapt, partition, balance, ... --- /** Wrapper to convert an adapt callback with user data of type \ref adapt_callback_type_with_userdata * into a callback without user data of type \ref adapt_callback_type using the defined user data \a user_data. @@ -376,7 +397,7 @@ class mesh: public TMeshCompetencePack::template applywrite_new_to_element_data (); + } } private: diff --git a/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx b/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx index df460cd05a..21634e374e 100644 --- a/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx +++ b/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx @@ -113,8 +113,8 @@ TEST (t8_gtest_handle_adapt, compare_adapt_with_forest) 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< - t8_mesh_handle::element_competence_pack, - t8_mesh_handle::mesh_competence_pack::template type>>; + t8_mesh_handle::element_competence_pack, + t8_mesh_handle::mesh_competence_pack::template type>>; mesh_class mesh_handle = mesh_class (forest); struct dummy_user_data user_data = { t8_3D_vec ({ 0.5, 0.5, 1 }), /**< Midpoints of the sphere. */ diff --git a/test/mesh_handle/t8_gtest_handle_data.cxx b/test/mesh_handle/t8_gtest_handle_data.cxx index 9c60bab3f1..8a6b24c32d 100644 --- a/test/mesh_handle/t8_gtest_handle_data.cxx +++ b/test/mesh_handle/t8_gtest_handle_data.cxx @@ -41,16 +41,20 @@ struct data_per_element { int level; double volume; + + bool + operator== (const data_per_element &) const + = default; }; /** Check that element data can be set for the handle and that exchanging data for the ghosts works. */ TEST (t8_gtest_handle_data, set_and_get_element_data) { const int level = 2; - using mesh_class = t8_mesh_handle::mesh< - t8_mesh_handle::element_competence_pack, - t8_mesh_handle::mesh_competence_pack::template type, - t8_mesh_handle::mesh_second_element_data_vector>>; + using mesh_class + = t8_mesh_handle::mesh, + t8_mesh_handle::mesh_competence_pack< + t8_mesh_handle::element_data_mesh_competence::template type>>; auto mesh = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (level, sc_MPI_COMM_WORLD, true, true, false); @@ -58,14 +62,16 @@ TEST (t8_gtest_handle_data, set_and_get_element_data) // Ensure that we actually test with ghost elements. EXPECT_GT (mesh->get_num_ghosts (), 0); } + EXPECT_TRUE (mesh->has_element_data_handler_competence ()); + EXPECT_FALSE (mesh->has_new_element_data_handler_competence ()); - // Create element data for all local mesh elements. + // Create element data for all local mesh elements and set via mesh competence. std::vector element_data; for (const auto &elem : *mesh) { element_data.push_back ({ elem.get_level (), elem.get_volume () }); } mesh->set_element_data (std::move (element_data)); - // Get element data and check that the data for all elements (including ghosts) is correct. + // Exchange element data for ghosts and check that the data for all elements (including ghosts) is correct. mesh->exchange_ghost_data (); auto mesh_element_data = mesh->get_element_data (); for (t8_locidx_t ielem = 0; ielem < mesh->get_num_local_elements () + mesh->get_num_ghosts (); ielem++) { @@ -73,8 +79,7 @@ TEST (t8_gtest_handle_data, set_and_get_element_data) EXPECT_EQ (mesh_element_data[ielem].volume, (*mesh)[ielem].get_volume ()) << "ielem = " << ielem; } - // Modify element data for elements that are in the first half of the global trees. - EXPECT_TRUE (mesh->has_element_data_handler_competence ()); + // Modify element data via the element competence for elements that are in the first half of the global trees. auto forest = mesh->get_forest (); t8_gloidx_t barrier = t8_forest_get_num_global_trees (forest) / 2.0; const int newlevel = 42; @@ -85,6 +90,7 @@ TEST (t8_gtest_handle_data, set_and_get_element_data) elem.set_element_data (elem_data); } } + // Exchange data for ghosts and check that the data for all elements (including ghosts) is correct. mesh->exchange_ghost_data (); for (auto &elem : *mesh) { if (t8_forest_global_tree_id (forest, elem.get_local_tree_id ()) < barrier) { @@ -111,3 +117,41 @@ TEST (t8_gtest_handle_data, set_and_get_element_data) } } } + +/** Check that element data can be set for the handle and that exchanging data for the ghosts works. */ +TEST (t8_gtest_handle_data, set_and_get_new_element_data) +{ + const int level = 2; + using mesh_class + = t8_mesh_handle::mesh, + t8_mesh_handle::mesh_competence_pack< + t8_mesh_handle::element_data_mesh_competence::template type, + t8_mesh_handle::new_element_data_mesh_competence::template type>>; + auto mesh + = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (level, sc_MPI_COMM_WORLD, true, true, false); + + EXPECT_TRUE (mesh->has_new_element_data_handler_competence ()); + // Create element data for all local mesh elements and set via mesh competence. + std::vector element_data; + for (const auto &elem : *mesh) { + element_data.push_back ({ elem.get_level (), elem.get_volume () }); + } + mesh->set_element_data (element_data); + + // Modify element data via the element competence for elements that are in the first half of the global trees. + const int newlevel = 42; + const double newvolume = 42.42; + std::vector new_element_data (mesh->get_num_local_elements (), { newlevel, newvolume }); + + mesh->set_new_element_data (new_element_data); + EXPECT_EQ (mesh->get_element_data (), element_data); + EXPECT_EQ (mesh->get_new_element_data (), new_element_data); + + mesh->commit (); + for (auto &elem : *mesh) { + EXPECT_EQ (elem.get_element_data ().level, newlevel); + EXPECT_EQ (elem.get_element_data ().volume, newvolume); + } + EXPECT_TRUE (mesh->get_new_element_data ().empty ()); +} From 46418bae6d66910ce5c708aa65f72016f8527e86 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Wed, 4 Mar 2026 15:38:33 +0100 Subject: [PATCH 4/6] added new element data competences --- mesh_handle/competence_pack.hxx | 15 +- mesh_handle/data_handler.hxx | 162 ++++++++++-------- mesh_handle/element.hxx | 4 +- mesh_handle/mesh.hxx | 1 + .../t8_gtest_adapt_partition_balance.cxx | 4 +- test/mesh_handle/t8_gtest_handle_data.cxx | 20 +-- 6 files changed, 116 insertions(+), 90 deletions(-) diff --git a/mesh_handle/competence_pack.hxx b/mesh_handle/competence_pack.hxx index e3963566b7..ef022b573b 100644 --- a/mesh_handle/competence_pack.hxx +++ b/mesh_handle/competence_pack.hxx @@ -28,6 +28,7 @@ #pragma once #include "competences.hxx" +#include "data_handler.hxx" namespace t8_mesh_handle { // --- Element competence pack. --- @@ -52,10 +53,15 @@ using all_cache_element_competences = element_competence_pack; -/** Predefined competence pack combining all competences related to faces. */ +/** Predefined element competence pack combining all competences related to faces. */ using cache_face_element_competences = element_competence_pack; +/** Predefined element competence pack combining all competences related to data. + * Please note that you must combine this with \ref data_mesh_competence. */ +using data_element_competences + = element_competence_pack; + // --- Mesh competence pack. --- /** Class to pack different mesh competences into one template parameter for the \ref mesh class. * \tparam TMeshCompetence The mesh competences to be packed. @@ -75,4 +81,11 @@ struct mesh_competence_pack using is_mesh_competence_pack = void; /**< Tag to identify this class. */ }; +/** Predefined mesh competence pack combining all competences related to data. + * If you want to access the data also via the elements, combine this with \ref data_element_competence. + */ +template +using data_mesh_competences = mesh_competence_pack::template type, + new_element_data_mesh_competence::template type>; + } // namespace t8_mesh_handle diff --git a/mesh_handle/data_handler.hxx b/mesh_handle/data_handler.hxx index 4797bccb17..cb6ef83912 100644 --- a/mesh_handle/data_handler.hxx +++ b/mesh_handle/data_handler.hxx @@ -22,8 +22,11 @@ /** \file data_handler.hxx * Handler for the element data of a \ref t8_mesh_handle::mesh. - * The file defines a mesh and an element competence for element data handling. - * Use both competences together if you want to manage element data for the elements of the mesh and access it directly for each element. + * The file defines mesh and element competences for element data handling. + * The mesh competences make it possible to manage element data and exchange it for ghost elements between processes. + * The element competences makes it possible to access these element data directly for each element of the mesh. + * The competences with new element data additionally provide the possibility to set new element data that will + * be used to update the element data on commit (or on the related function call). */ #pragma once @@ -44,12 +47,13 @@ concept T8MPISafeType /** Handler for the element data of a \ref mesh. * Use this competence if you want to manage element data for the elements of the mesh. - * Use the helper \ref element_data_mesh_competence to get this competence with the correct template parameters form for the mesh. + * Use the helper \ref element_data_mesh_competence to get this competence with the correct template parameters form. * If you want to access the data not only in vector form but also directly for each element, - * you can combine this competence with the \ref element_data_element_competence competence. + * you can combine this competence with \ref element_data_element_competence. * In summary you can use the competences like this: * mesh, * mesh_competence_pack::template type>>; + * Some predefined competences are also defined in \ref competence_pack.hxx. * * \tparam TUnderlying Use the \ref mesh class here. * \tparam TElementDataType The element data type you want to use for each element of the mesh. @@ -109,7 +113,8 @@ class element_data_mesh_competence_impl: public t8_crtp_basic { std::vector m_element_data; /**< Vector storing the (local) element data. */ }; -/** Wrapper for \ref element_data_mesh_competence_impl to hide TUnderlying and provide the form needed to pass it as a mesh competence. +/** Wrapper for \ref element_data_mesh_competence_impl to hide TUnderlying and provide the form needed to pass it + * as a mesh competence. * Use mesh_competence_pack::template type> * to get this competence with the correct template parameter form for the mesh. * \tparam TElementDataType The element data type you want to use for each element of the mesh. @@ -127,33 +132,17 @@ struct element_data_mesh_competence // --- Element competence for element data management. --- /** Element competence to enable that element data can be accessed directly for each element of the mesh. - * \note This competence requires that the mesh has the \ref element_data_mesh_competence_impl competence to manage the - * element data vector and exchange ghost data. + * \note This competence requires that the mesh has \ref element_data_mesh_competence_impl + * (or \ref element_data_mesh_competence) competence that defines the element data vector and the element data type. * \tparam TUnderlying Use the \ref element with specified competences as template parameter. */ template struct element_data_element_competence: public t8_crtp_operator { public: - // --- Getter and setter for element data. --- - /** Getter for the element data. - * For ghost elements ensure that \ref element_data_mesh_competence_impl::exchange_ghost_data is called on each process first. - * Element data for non-ghost elements can be accessed (if set) directly. - * \return Element data with data of Type TMeshClass::ElementDataType. - */ - const auto& - get_element_data () const - { - T8_ASSERT (this->underlying ().m_mesh->has_element_data_handler_competence ()); - const t8_locidx_t handle_id = this->underlying ().get_element_handle_id (); - T8_ASSERTF (static_cast (handle_id) < this->underlying ().m_mesh->m_element_data.size (), - "Element data not set.\n"); - return this->underlying ().m_mesh->m_element_data[handle_id]; - } - /** Set the element data for the element. * \note You can only set element data for non-ghost elements. - * \param [in] element_data The element data to be set of Type TMeshClass::ElementDataType. + * \param [in] element_data The element data to be set of type TMeshClass::ElementDataType. */ void set_element_data (auto element_data) @@ -166,55 +155,80 @@ struct element_data_element_competence: public t8_crtp_operatorunderlying ().m_mesh->m_element_data.resize (this->underlying ().m_mesh->get_num_local_elements ()); this->underlying ().m_mesh->m_element_data[this->underlying ().get_element_handle_id ()] = std::move (element_data); } + + /** Getter for the element data. + * For ghost elements ensure that \ref element_data_mesh_competence_impl::exchange_ghost_data + * is called on each process first. + * Element data for non-ghost elements can be accessed (if set) directly. + * \return Element data with data of Type TMeshClass::ElementDataType. + */ + const auto& + get_element_data () const + { + T8_ASSERT (this->underlying ().m_mesh->has_element_data_handler_competence ()); + const t8_locidx_t handle_id = this->underlying ().get_element_handle_id (); + T8_ASSERTF (static_cast (handle_id) < this->underlying ().m_mesh->m_element_data.size (), + "Element data not set.\n"); + return this->underlying ().m_mesh->m_element_data[handle_id]; + } }; // --- Competences for new element data. --- +/* Using setter of \ref element_data_mesh_competence and \ref element_data_element_competence, the element data + * are updated in place such that we cannot access the old data afterwards. With the following competences, + * an additional data vector is defined for the mesh where new element data can be stored that will replace the element + * data vector on commit. + */ + +/** Detail namespace should be uninteresting for users. */ namespace detail { +/** Dummy for the inheritance of \ref new_element_data_mesh_competence_impl. + * The dummy class is used in the inheritance pattern to avoid diamond shaped inheritance + * if the competence is used together with \ref element_data_mesh_competence_impl. + * \tparam TUnderlying Use the \ref mesh class here. + */ template struct new_element_data_helper { }; } // namespace detail -/** With the mesh competence \ref element_data_mesh_competence and the element competence \ref element_data_element_competence, the element data - * are updated in place such that we cannot access the old data afterwards. Use the following competences to store the old element data. - * IDEA: The new interpolated element data is stored here (because we need the ghost data for calculation. This new element data is always used to update the elemtent data at the end of commit(). ) - */ -/** TODO: new element data to store -Handler for the element data of a \ref mesh. - * Use this competence if you want to manage element data for the elements of the mesh. - * Use the helper \ref element_data_mesh_competence to get this competence with the correct template parameters form for the mesh. +/** Define new element data vector for the mesh. + * Using setter of \ref element_data_mesh_competence and \ref element_data_element_competence, the element data + * are updated in place such that we cannot access the old data afterwards. + * Use this competence if you want to manage new element data separately that will be used to update the element data + * on commit (or if \ref write_new_to_element_data is called). + * \note This competence only makes sense if the mesh also has \ref element_data_mesh_competence. + * You can use the predefined competence pack \ref data_mesh_competences to get both competences together. + * Use the helper \ref new_element_data_mesh_competence to get this competence with the correct template parameters form. * If you want to access the data not only in vector form but also directly for each element, - * you can combine this competence with the \ref element_data_element_competence competence. - * In summary you can use the competences like this: - * mesh, - * mesh_competence_pack::template type>>; - * + * you can combine this competence with \ref new_element_data_element_competence. + * * \tparam TUnderlying Use the \ref mesh class here. * \tparam TElementDataType The element data type you want to use for each element of the mesh. * The data type has to be MPI safe as the data for ghost elements will be exchanged via MPI. + * \note TElementDataType must be the same as the datatype in \ref element_data_mesh_competence_impl. */ template class new_element_data_mesh_competence_impl: public t8_crtp_operator { public: - /** Set the element data vector. The vector should have the length of num_local_elements. - * \param [in] element_data The element data vector to set with one entry of class TElementDataType + /** Set the new element data vector. The vector should have the length of num_local_elements. + * \param [in] new_element_data The element data vector to set with one entry of class TElementDataType * for each local mesh element (excluding ghosts). */ void - set_new_element_data (std::vector element_data) + set_new_element_data (std::vector new_element_data) { const auto num_local_elements = this->underlying ().get_num_local_elements (); - T8_ASSERT (element_data.size () == static_cast (num_local_elements)); - m_new_element_data = std::move (element_data); + T8_ASSERT (new_element_data.size () == static_cast (num_local_elements)); + m_new_element_data = std::move (new_element_data); m_new_element_data.resize (num_local_elements); } - /** Get the element data vector. - * The element data of the local mesh elements can be set using \ref set_element_data. - * If ghost entries should be filled, one should call \ref exchange_ghost_data on each process first. - * \return Element data vector with data of Type TElementDataType. + /** Get the new element data vector. + * The new element data of the local mesh elements can be set using \ref set_new_element_data. + * \return New element data vector with data of Type TElementDataType. */ const auto& get_new_element_data () const @@ -222,7 +236,7 @@ class new_element_data_mesh_competence_impl: public t8_crtp_operator m_new_element_data; /**< Vector storing the (local) element data. */ + std::vector m_new_element_data; /**< Vector storing the (local) new element data. */ }; -/** Wrapper for \ref new_element_data_mesh_competence_impl to hide TUnderlying and provide the form needed to pass it as a mesh competence. - * Use mesh_competence_pack::template type> +/** Wrapper for \ref new_element_data_mesh_competence_impl to hide TUnderlying and provide the form needed to pass + * it as a mesh competence. + * Use mesh_competence_pack::template type> * to get this competence with the correct template parameter form for the mesh. * \tparam TElementDataType The element data type you want to use for each element of the mesh. * The data type has to be MPI safe as the data for ghost elements will be exchanged via MPI. + * \note TElementDataType must be the same as the datatype in \ref element_data_mesh_competence. */ template struct new_element_data_mesh_competence @@ -252,48 +268,44 @@ struct new_element_data_mesh_competence using type = new_element_data_mesh_competence_impl; }; -// --- Element competence for element data management. --- +// --- Element competence for new element data. --- /** Element competence to enable that element data can be accessed directly for each element of the mesh. - * \note This competence requires that the mesh has the \ref element_data_mesh_competence_impl competence to manage the - * element data vector and exchange ghost data. + * \note This competence requires that the mesh has \ref new_element_data_mesh_competence_impl such that + * the new data vector is available and the element data type is defined. * \tparam TUnderlying Use the \ref element with specified competences as template parameter. */ template struct new_element_data_element_competence: public t8_crtp_operator { public: - // --- Getter and setter for element data. --- - /** Getter for the element data. - * For ghost elements ensure that \ref element_data_mesh_competence_impl::exchange_ghost_data is called on each process first. - * Element data for non-ghost elements can be accessed (if set) directly. - * \return Element data with data of Type TMeshClass::ElementDataType. + /** Set the new element data for the element. + * \note You can only set element data for non-ghost elements. + * \param [in] new_element_data New element data to be set of Type TMeshClass::ElementDataType. + */ + void + set_new_element_data (auto new_element_data) + { + T8_ASSERT (this->underlying ().m_mesh->has_new_element_data_handler_competence ()); + SC_CHECK_ABORT (!this->underlying ().is_ghost_element (), "New element data cannot be set for ghost elements.\n"); + // Resize for the case that no data vector has been set previously. + this->underlying ().m_mesh->m_new_element_data.resize (this->underlying ().m_mesh->get_num_local_elements ()); + this->underlying ().m_mesh->m_new_element_data[this->underlying ().get_element_handle_id ()] + = std::move (new_element_data); + } + + /** Getter for new element data. + * \return New element data with data of Type TMeshClass::ElementDataType. */ const auto& get_new_element_data () const { T8_ASSERT (this->underlying ().m_mesh->has_new_element_data_handler_competence ()); + const t8_locidx_t handle_id = this->underlying ().get_element_handle_id (); T8_ASSERTF (static_cast (handle_id) < this->underlying ().m_mesh->m_new_element_data.size (), "Element data not set.\n"); return this->underlying ().m_mesh->m_new_element_data[handle_id]; } - - /** Set the element data for the element. - * \note You can only set element data for non-ghost elements. - * \param [in] element_data The element data to be set of Type TMeshClass::ElementDataType. - */ - void - set_new_element_data (auto element_data) - { - T8_ASSERT (this->underlying ().m_mesh->has_new_element_data_handler_competence ()); - SC_CHECK_ABORT (!this->underlying ().is_ghost_element (), "Element data cannot be set for ghost elements.\n"); - // Resize for the case that no data vector has been set previously. - this->underlying ().m_mesh->m_new_element_data.reserve (this->underlying ().m_mesh->get_num_local_elements () - + this->underlying ().m_mesh->get_num_ghosts ()); - this->underlying ().m_mesh->m_new_element_data.resize (this->underlying ().m_mesh->get_num_local_elements ()); - this->underlying ().m_mesh->m_new_element_data[this->underlying ().get_element_handle_id ()] - = std::move (element_data); - } }; } // namespace t8_mesh_handle diff --git a/mesh_handle/element.hxx b/mesh_handle/element.hxx index 8a47e3d384..1774080849 100644 --- a/mesh_handle/element.hxx +++ b/mesh_handle/element.hxx @@ -66,7 +66,9 @@ class element: public TCompetences>... { = element; /**< Type of the current class with all template parameters specified. */ friend TMeshClass; /**< Define TMeshClass as friend to be able to access e.g. the constructor. */ friend struct element_data_element_competence< - SelfType>; /**< Define the competence to access element data as friend to be able to access e.g. the mesh. */ + SelfType>; /**< Define the competence as friend to be able to access e.g. the mesh from competence. */ + friend struct new_element_data_element_competence< + SelfType>; /**< Define the competence as friend to be able to access e.g. the mesh from competence. */ /** Private constructor for an element of a mesh. This could be a simple mesh element or a ghost element. * This constructor should only be called by the TMeshClass (and invisible for the user). diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index dcd6a7b9c9..68daf4140c 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -378,6 +378,7 @@ class mesh: public TMeshCompetencePack::template apply, - t8_mesh_handle::mesh_competence_pack::template type>>; + using mesh_class = t8_mesh_handle::mesh<>; mesh_class mesh_handle = mesh_class (forest); struct dummy_user_data user_data = { t8_3D_vec ({ 0.5, 0.5, 1 }), /**< Midpoints of the sphere. */ diff --git a/test/mesh_handle/t8_gtest_handle_data.cxx b/test/mesh_handle/t8_gtest_handle_data.cxx index 8a6b24c32d..915c8f0552 100644 --- a/test/mesh_handle/t8_gtest_handle_data.cxx +++ b/test/mesh_handle/t8_gtest_handle_data.cxx @@ -118,16 +118,12 @@ TEST (t8_gtest_handle_data, set_and_get_element_data) } } -/** Check that element data can be set for the handle and that exchanging data for the ghosts works. */ +/** Check that new element data works and element data is updated on commit. */ TEST (t8_gtest_handle_data, set_and_get_new_element_data) { const int level = 2; - using mesh_class - = t8_mesh_handle::mesh, - t8_mesh_handle::mesh_competence_pack< - t8_mesh_handle::element_data_mesh_competence::template type, - t8_mesh_handle::new_element_data_mesh_competence::template type>>; + using mesh_class = t8_mesh_handle::mesh>; auto mesh = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (level, sc_MPI_COMM_WORLD, true, true, false); @@ -139,15 +135,19 @@ TEST (t8_gtest_handle_data, set_and_get_new_element_data) } mesh->set_element_data (element_data); - // Modify element data via the element competence for elements that are in the first half of the global trees. + // Set new element data and check that data is correctly stored in the mesh. const int newlevel = 42; const double newvolume = 42.42; std::vector new_element_data (mesh->get_num_local_elements (), { newlevel, newvolume }); - mesh->set_new_element_data (new_element_data); EXPECT_EQ (mesh->get_element_data (), element_data); EXPECT_EQ (mesh->get_new_element_data (), new_element_data); - + // Also check that we can access the new element data via the elements. + for (auto &elem : *mesh) { + EXPECT_EQ (elem.get_new_element_data ().level, newlevel); + EXPECT_EQ (elem.get_new_element_data ().volume, newvolume); + } + // Commit mesh and check if element data is updated and new element data vector is cleared. mesh->commit (); for (auto &elem : *mesh) { EXPECT_EQ (elem.get_element_data ().level, newlevel); From 9c3de8eef54cf680abfcec4c3dda53e9af22d241 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Wed, 4 Mar 2026 15:49:59 +0100 Subject: [PATCH 5/6] doxygen --- mesh_handle/competence_pack.hxx | 6 +++--- test/mesh_handle/t8_gtest_handle_data.cxx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mesh_handle/competence_pack.hxx b/mesh_handle/competence_pack.hxx index ef022b573b..b311361851 100644 --- a/mesh_handle/competence_pack.hxx +++ b/mesh_handle/competence_pack.hxx @@ -3,7 +3,7 @@ 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 + Copyright (C) 2026 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 @@ -58,7 +58,7 @@ using cache_face_element_competences = element_competence_pack; /** Predefined element competence pack combining all competences related to data. - * Please note that you must combine this with \ref data_mesh_competence. */ + * Please note that you must combine this with \ref data_mesh_competences. */ using data_element_competences = element_competence_pack; @@ -82,7 +82,7 @@ struct mesh_competence_pack }; /** Predefined mesh competence pack combining all competences related to data. - * If you want to access the data also via the elements, combine this with \ref data_element_competence. + * If you want to access the data also via the elements, combine this with \ref data_element_competences. */ template using data_mesh_competences = mesh_competence_pack::template type, diff --git a/test/mesh_handle/t8_gtest_handle_data.cxx b/test/mesh_handle/t8_gtest_handle_data.cxx index 915c8f0552..4038cdbfea 100644 --- a/test/mesh_handle/t8_gtest_handle_data.cxx +++ b/test/mesh_handle/t8_gtest_handle_data.cxx @@ -3,7 +3,7 @@ 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 +Copyright (C) 2026 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 From 9c9e6781ea7baf74d6f417b663a6f3d0476ba35f Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Wed, 4 Mar 2026 15:57:59 +0100 Subject: [PATCH 6/6] doxygen --- mesh_handle/competence_pack.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesh_handle/competence_pack.hxx b/mesh_handle/competence_pack.hxx index b311361851..b3f148ffbb 100644 --- a/mesh_handle/competence_pack.hxx +++ b/mesh_handle/competence_pack.hxx @@ -58,7 +58,7 @@ using cache_face_element_competences = element_competence_pack; /** Predefined element competence pack combining all competences related to data. - * Please note that you must combine this with \ref data_mesh_competences. */ + * Please note that you must combine this with \ref t8_mesh_handle::data_mesh_competences. */ using data_element_competences = element_competence_pack; @@ -82,7 +82,7 @@ struct mesh_competence_pack }; /** Predefined mesh competence pack combining all competences related to data. - * If you want to access the data also via the elements, combine this with \ref data_element_competences. + * If you want to access the data also via the elements, combine this with \ref t8_mesh_handle::data_element_competences. */ template using data_mesh_competences = mesh_competence_pack::template type,