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
4 changes: 3 additions & 1 deletion mesh_handle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ If you want to use the handle, note that is has its own library. Turn the option

The folder's most important files are:
- The [mesh.hxx](mesh.hxx) defines the mesh of the handle. This is the central file of the mesh handle.
- The [constructor_wrappers.hxx](constructor_wrappers.hxx) allows to define a mesh handle using a cmesh instead of a forest and provides a very small number of examples where the user needs no cmesh.
- 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 [constructor_wrappers.hxx](constructor_wrappers.hxx) allows to define a mesh handle using a cmesh instead of a forest and provides a very small number of examples where the user needs no cmesh.
- 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.
52 changes: 37 additions & 15 deletions mesh_handle/competence_pack.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,58 @@
*/

/** \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.
*/

#pragma once

#include "competences.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>;
/** 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>;

/** 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>;
using cache_face_element_competences
= element_competence_pack<cache_face_areas, cache_face_centroids, cache_face_normals, cache_neighbors>;

// --- 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. */
};

} // namespace t8_mesh_handle
7 changes: 6 additions & 1 deletion mesh_handle/competences.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,20 @@ along with t8code; if not, write to the Free Software Foundation, Inc.,
* Especially, competences to cache functionalities of elements instead of calculating them each time a function
* is called are provided.
*
* \note for developers or users that want to provide their own competence:
* All competences have the same inheritance pattern:
* We use the CRTP pattern as we may need to access members of the derived classes like
* \ref t8_mesh_handle::element.
* The t8_crtp_operator is used for convenience/clear code (avoid to type a static cast explicitly each time
* The \ref t8_crtp_operator is used for convenience/clear code (avoid to type a static cast explicitly each time
* we need functionality of TUnderlying).
* Especially for the competences to cache functionality, the access of members is not necessary,
* such that it is not obvious why we use the crtp. For competences that extend the functionality of the element,
* this is required.
* We use it for all competences for consistency and compatibility with \ref t8_mesh_handle::element.
*
* We use \ref t8_crtp_operator instead of \ref t8_crtp_basic to circumvent diamond shaped inheritance pattern in
* the case that multiple competences are used together.
* The distinction of the classes is made by the second template parameter of \ref t8_crtp_operator.
*/

#pragma once
Expand Down
171 changes: 171 additions & 0 deletions mesh_handle/data_handler.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
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_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<element_competence_pack<access_element_data>,
* mesh_competence_pack<element_data_competence<YourElementDataType>::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 <typename TUnderlying, T8MPISafeType TElementDataType>
class handle_element_data: 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 = std::move (element_data);
m_element_data.reserve (num_local_elements + num_ghosts);
m_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 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 handle_element_data to hide TUnderlying and provide the form needed to pass it as a mesh competence.
* Use mesh_competence_pack<element_data_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_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 = handle_element_data<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 handle_element_data competence to manage the
* element data vector and exchange ghost data.
* \tparam TUnderlying Use the \ref element with specified competences as template parameter.
*/
template <typename TUnderlying>
struct access_element_data: public t8_crtp_basic<TUnderlying>
{
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.
* 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];
}

/** 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);
}
};

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

#include <t8.h>
#include "data_handler.hxx"
#include <t8_element.h>
#include <t8_eclass.h>
#include <t8_forest/t8_forest_general.h>
Expand Down Expand Up @@ -55,23 +56,26 @@ namespace t8_mesh_handle
* The choice between calculate and cache is a tradeoff between runtime and memory usage.
*
* \tparam TCompetences The competences you want to add to the default functionality of the element.
* \tparam TMeshClass The class of the mesh the element belongs to.
*/

template <typename mesh_class, template <typename> class... TCompetences>
class element: public TCompetences<element<mesh_class, TCompetences...>>... {
template <typename TMeshClass, template <typename> class... TCompetences>
class element: public TCompetences<element<TMeshClass, TCompetences...>>... {
private:
using SelfType
= element<mesh_class, TCompetences...>; /**< Type of the current class with all template parameters specified. */
friend mesh_class; /**< Define mesh_class as friend to be able to access e.g. the constructor. */
= 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 access_element_data<
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 mesh_class (and invisible for the user).
* This constructor should only be called by the TMeshClass (and invisible for the user).
* \param [in] mesh Pointer to the mesh the element should belong to.
* \param [in] tree_id The tree id of the element in the forest defining the mesh.
* \param [in] element_id The element id of the element in the forest defining the mesh.
* \param [in] is_ghost_element Flag to indicate that this element is a ghost element. Default is false.
*/
element (mesh_class* mesh, t8_locidx_t tree_id, t8_locidx_t element_id, bool is_ghost_element = false)
element (TMeshClass* mesh, t8_locidx_t tree_id, t8_locidx_t element_id, bool is_ghost_element = false)
: m_mesh (mesh), m_tree_id (tree_id), m_element_id (element_id), m_is_ghost_element (is_ghost_element)
{
// Cache the t8_element_t from the forest as it is often used.
Expand Down Expand Up @@ -523,7 +527,7 @@ class element: public TCompetences<element<mesh_class, TCompetences...>>... {
/** Getter for the mesh to which the element belongs.
* \return Reference to the mesh.
*/
const mesh_class*
const TMeshClass*
get_mesh () const
{
return m_mesh;
Expand All @@ -538,41 +542,10 @@ class element: public TCompetences<element<mesh_class, 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 mesh_class::ElementDataType.
*/
template <typename TElementDataType = typename mesh_class::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 mesh_class::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);
}

protected:
TMeshClass* m_mesh; /**< Pointer to the mesh the element is defined for. */
private:
// --- Private member variables. ---
mesh_class* m_mesh; /**< Pointer to the mesh the element is defined for. */
const t8_locidx_t m_tree_id; /**< The tree id of the element in the forest defined in the mesh. */
const t8_locidx_t m_element_id; /**< The element id of the element in the forest defined in the mesh. */
const bool m_is_ghost_element; /**< Flag to indicate if the element is a ghost element. */
Expand Down
Loading