Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mesh_handle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ The folder's most important files are:
- The [element.hxx](element.hxx) defines the elements (mesh or ghost elements) of the mesh handle.
- The [competences.hxx](competences.hxx) defines additional competences/functionality of an element to access additional data.
- The [competence_pack.hxx](competence_pack.hxx) is needed to pack element or mesh competences to pass it as template parameter to the mesh.
- The [data_handler.hxx](data_handler.hxx) provides competences to work with element data.

Headers in the [internal/](internal/) folder are only intended to implement details of the mesh handle, so they should not need to be included for any other purpose.
88 changes: 66 additions & 22 deletions mesh_handle/competence_pack.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -21,44 +21,88 @@
*/

/** \file competence_pack.hxx
* Define to pack different competences into one template parameter for the \ref t8_mesh_handle::mesh class.
* Define classes to pack different competences into one template parameter for the \ref t8_mesh_handle::mesh class.
* We have one class for element competences and one for mesh competences.
* There are several predefined competence packs for different use cases, e.g. for all caching competences or
* all data related competences.
* Use the union operator to combine different competence packs.
*/

#pragma once

#include "competences.hxx"
#include "data_handler.hxx"
#include "internal/competence_pack_union.hxx"
namespace t8_mesh_handle
{
/** Class to pack different competences into one template parameter for the \ref mesh class.
* \tparam TCompetence The competences to be packed.
// --- Element competence pack. ---
/** Class to pack different element competences into one template parameter for the \ref mesh class.
* \tparam TElementCompetence The competences to be packed.
*/
template <template <typename> class... TCompetence>
struct competence_pack
template <template <typename> class... TElementCompetence>
struct element_competence_pack
{
/** Apply the competence pack to a template class, e.g. the \ref element class.
* \tparam Target The target template class to apply the \a TCompetence pack to.
* \tparam TMeshClass The mesh class given to the element class.
* \tparam Target The target template class to apply the \a TElementCompetence pack to.
*/
template <typename mesh_class, template <typename, template <typename> class...> class Target>
using apply = Target<mesh_class, TCompetence...>;
template <typename TMeshClass, template <typename, template <typename> class...> class Target>
using apply = Target<TMeshClass, TElementCompetence...>;

using is_competence_pack = void; /**< Tag to identify this class. */
using is_element_competence_pack = void; /**< Tag to identify this class. */
};

/** Predefined competence pack combining all caching competences. */
using all_cache_competences
= competence_pack<cache_volume, cache_diameter, cache_vertex_coordinates, cache_centroid, cache_face_areas,
cache_face_centroids, cache_face_normals, cache_neighbors>;
/** Empty competence pack. */
using empty_element_competences = element_competence_pack<>;

/** Predefined competence pack combining all competences related to faces. */
using cache_face_competences
= competence_pack<cache_face_areas, cache_face_centroids, cache_face_normals, cache_neighbors>;
/** Predefined element competence pack combining all caching competences. */
using all_cache_element_competences
= element_competence_pack<cache_volume, cache_diameter, cache_vertex_coordinates, cache_centroid, cache_face_areas,
cache_face_centroids, cache_face_normals, cache_neighbors>;

/** Compute the unique union of the competences of several \ref t8_mesh_handle::competence_pack 's.
* This produces a new \ref t8_mesh_handle::competence_pack containing all competences of the competence packs
* with duplicates removed.
* \tparam TPacks The competence pack for which we should compute the unique union of the competences.
* Each competence pack is expected to be of type \ref t8_mesh_handle::competence_pack.
/** Predefined element competence pack combining all competences related to faces. */
using cache_face_element_competences
= element_competence_pack<cache_face_areas, cache_face_centroids, cache_face_normals, cache_neighbors>;

/** Predefined element competence pack combining all competences related to data.
* Please note that you must combine this with \ref t8_mesh_handle::data_mesh_competences. */
using data_element_competences = element_competence_pack<element_data_element_competence>;

// --- 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.
*/
template <template <typename> class... TMeshCompetence>
struct mesh_competence_pack
{
/** Apply the mesh competence pack to a mesh type.
* By inheriting from all mesh competences, the functionality of the competences gets added to the mesh type.
* \tparam TMesh The mesh type to which the competences are applied.
*/
template <typename TMesh>
struct apply: public TMeshCompetence<TMesh>...
{
};

using is_mesh_competence_pack = void; /**< Tag to identify this class. */
};

/** Empty competence pack. */
using empty_mesh_competences = 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 t8_mesh_handle::data_element_competences.
*/
template <T8MPISafeType TElementDataType>
using data_mesh_competences = mesh_competence_pack<element_data_mesh_competence<TElementDataType>::template type>;

// --- Compute union of competence packs. ---
/** Compute the unique union of the competences of several competence_pack. This could be
* \ref t8_mesh_handle::element_competence_pack or \ref t8_mesh_handle::mesh_competence_pack.
* A new competence pack is produced containing all competences of the given competence packs with duplicates removed.
* \tparam TPacks The competence packs for which we should compute the unique union of the competences.
* Each competence pack is expected to be of the same type and of type
* \ref t8_mesh_handle::element_competence_pack or \ref t8_mesh_handle::mesh_competence_pack.
*/
template <typename... TPacks>
using union_competence_packs_type = typename detail::union_competence_packs<TPacks...>::type;
Expand Down
173 changes: 173 additions & 0 deletions mesh_handle/data_handler.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
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) 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
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 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.
*/
#pragma once

#include <t8.h>
#include <t8_forest/t8_forest_general.h>
#include <t8_types/t8_crtp.hxx>
#include <type_traits>
#include <vector>

namespace t8_mesh_handle
{
// --- Mesh competence for element data management. ---
/** Concept to ensure that a type is MPI safe. */
template <typename TType>
concept T8MPISafeType
= std::is_void_v<TType> || (std::is_trivially_copyable_v<TType> && std::is_standard_layout_v<TType>);

/** 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.
* 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<element_competence_pack<element_data_element_competence>,
* mesh_competence_pack<element_data_mesh_competence<YourElementDataType>::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.
* The data type has to be MPI safe as the data for ghost elements will be exchanged via MPI.
*/
template <typename TUnderlying, T8MPISafeType TElementDataType>
class element_data_mesh_competence_impl: public t8_crtp_basic<TUnderlying> {
public:
using ElementDataType = TElementDataType; /**< Make Type of the element data publicly accessible. */

/** 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_element_data (std::vector<TElementDataType> element_data)
{
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<size_t> (num_local_elements));
m_element_data.reserve (num_local_elements + num_ghosts);
m_element_data = std::move (element_data);
}

/** 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 std::vector<TElementDataType>&
get_element_data () const
{
return m_element_data;
}

/** Exchange the element data for ghost elements between processes.
* This routine has to be called on each process after setting the element data for all local elements.
*/
void
exchange_ghost_data ()
{
// t8_forest_ghost_exchange_data expects an sc_array, so we need to wrap our data array to one.
sc_array* sc_array_wrapper;
const auto num_local_elements = this->underlying ().get_num_local_elements ();
const auto num_ghosts = this->underlying ().get_num_ghosts ();
m_element_data.resize (num_local_elements + num_ghosts);
sc_array_wrapper
= sc_array_new_data (m_element_data.data (), sizeof (ElementDataType), num_local_elements + num_ghosts);

// Data exchange: entries with indices > num_local_elements will get overwritten.
t8_forest_ghost_exchange_data (this->underlying ().get_forest (), sc_array_wrapper);

sc_array_destroy (sc_array_wrapper);
}

protected:
std::vector<TElementDataType> 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.
* Use mesh_competence_pack<element_data_mesh_competence<YourElementDataType>::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 <T8MPISafeType TElementDataType>
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 <typename TUnderlying>
using type = element_data_mesh_competence_impl<TUnderlying, TElementDataType>;
};

// --- 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
* (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 <typename TUnderlying>
struct element_data_element_competence: public t8_crtp_basic<TUnderlying>
{
public:
// --- Getter and setter for element data. ---
/** 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_element_data (auto element_data)
{
T8_ASSERT (this->underlying ().m_mesh->has_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_element_data.reserve (this->underlying ().m_mesh->get_num_local_elements ()
+ this->underlying ().m_mesh->get_num_ghosts ());
this->underlying ().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<size_t> (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];
}
};

} // namespace t8_mesh_handle
51 changes: 16 additions & 35 deletions mesh_handle/element.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ along with t8code; if not, write to the Free Software Foundation, Inc.,
#pragma once

#include <t8.h>
#include <t8_element/t8_element.h>
#include "data_handler.hxx"
#include <t8_eclass/t8_eclass.h>
#include <t8_element/t8_element.h>
#include <t8_forest/t8_forest_general.h>
#include <t8_forest/t8_forest_geometrical.h>
#include <t8_schemes/t8_scheme.hxx>
Expand Down Expand Up @@ -60,9 +61,12 @@ namespace t8_mesh_handle
template <typename TMeshClass, template <typename> class... TCompetences>
class element: public TCompetences<element<TMeshClass, TCompetences...>>... {
private:
using SelfType
= element<TMeshClass, TCompetences...>; /**< Type of the current class with all template parameters specified. */
using SelfType = element<TMeshClass, TCompetences...>; /**< 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. */

/** 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).
Expand Down Expand Up @@ -178,6 +182,15 @@ class element: public TCompetences<element<TMeshClass, TCompetences...>>... {
return requires (SelfType& element) { element.face_normal_cache_filled (0); };
}

/** Function that checks if a competence for element data handling is given to the element.
* \return true if element has a data handler, false otherwise.
*/
static constexpr bool
has_element_data_handler_competence ()
{
return requires (SelfType& element) { element.get_element_data (); };
}

// --- Functionality of the element. In each function, it is checked if a cached version exists (and is used then). ---
/** Getter for the refinement level of the element.
* For this easily accessible variable, it makes no sense to provide a cached version.
Expand Down Expand Up @@ -538,38 +551,6 @@ class element: public TCompetences<element<TMeshClass, TCompetences...>>... {
return m_is_ghost_element;
}

// --- Getter and setter for element data. ---
/** Getter for the element data.
* For ghost elements ensure that \ref mesh::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.
*/
template <typename TElementDataType = typename TMeshClass::ElementDataType,
typename = std::enable_if_t<!std::is_void<TElementDataType>::value>>
const TElementDataType&
get_element_data () const
{
const t8_locidx_t handle_id = get_element_handle_id ();
T8_ASSERTF (static_cast<size_t> (handle_id) < m_mesh->m_element_data.size (), "Element data not set.\n");
return 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.
*/
template <typename TElementDataType = typename TMeshClass::ElementDataType,
typename = std::enable_if_t<!std::is_void<TElementDataType>::value>>
void
set_element_data (TElementDataType element_data)
{
SC_CHECK_ABORT (!m_is_ghost_element, "Element data cannot be set for ghost elements.\n");
// Resize for the case that no data vector has been set previously.
m_mesh->m_element_data.reserve (m_mesh->get_num_local_elements () + m_mesh->get_num_ghosts ());
m_mesh->m_element_data.resize (m_mesh->get_num_local_elements ());
m_mesh->m_element_data[get_element_handle_id ()] = std::move (element_data);
}

private:
// --- Private member variables. ---
TMeshClass* m_mesh; /**< Pointer to the mesh the element is defined for. */
Expand Down
Loading
Loading