Skip to content
Closed
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 @@ -11,4 +11,6 @@ The folder's most important files are:
- 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 [competence_pack.hxx](competence_pack.hxx) is needed to pack element or mesh competences to pass it as template parameter to the mesh.
- The [competence_pack.hxx](competence_pack.hxx) is needed to pack element or mesh competences to pass it as template parameter to the mesh.

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.
73 changes: 33 additions & 40 deletions mesh_handle/adapt.hxx → mesh_handle/internal/adapt.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@

#include <t8.h>
#include <t8_forest/t8_forest_general.h>
#include "mesh.hxx"
#include <vector>
#include <mesh_handle/mesh.hxx>
#include <memory>
#include <span>

namespace t8_mesh_handle
{
Expand All @@ -41,70 +41,63 @@ namespace detail
{

/** Virtual base class for mesh adaptation contexts.
* We need this base class and not only \ref MeshAdaptContext for the \ref AdaptRegistry.
* AdaptRegistry should not be templated because we need to access registered contexts in \ref mesh_adapt_callback_wrapper,
* We need this base class and not only \ref mesh_adapt_context for the \ref adapt_registry.
* adapt_registry should not be templated because we need to access registered contexts in \ref mesh_adapt_callback_wrapper,
* where we do not know the type of the mesh. Therefore, we work with a map of forests to instances of this base class to remain template free.
*/
struct MeshAdaptContextBase
struct mesh_adapt_context_base
{
/** Virtual destructor for safe polymorphic deletion.
*/
virtual ~MeshAdaptContextBase () = default;
virtual ~mesh_adapt_context_base () = default;

/** Pure virtual callback for mesh adaptation.
* \param [in] lelement_handle_id Local flat element ID in the mesh handle of the first element.
* \param [in] is_family If 1, the entries with indices \a lelement_handle_id,..., \a lelement_handle_id+ \a num_elements-1
* form a family. If 0, they do not.
* \param [in] num_elements The number of elements that form a family or 1 if \a is_family is 0.
* \param [in] lelement_handle_id Local flat element ID in the mesh handle of the first element selected for adaptation.
* \param [in] is_family true if the entries with indices \a lelement_handle_id,...,
* \a lelement_handle_id+ \a num_elements-1 form a family, false otherwise.
* \param [in] num_elements The number of elements that form a family or 1 if \a is_family is false.
* \return 1 if the element with index \a lelement_handle_id should be refined,
* -1 if the family shall be coarsened,
* 0 else.
*/
virtual int
adapt_mesh (const t8_locidx_t lelement_handle_id, const int is_family, const int num_elements)
adapt_mesh (const t8_locidx_t lelement_handle_id, const bool is_family, const int num_elements)
= 0;
};

/** Templated mesh adaptation context holding the mesh handle and the user defined callback.
* Class inherits from \ref MeshAdaptContextBase and implements the virtual adapt callback using the mesh and the callback.
* Struct inherits from \ref mesh_adapt_context_base and implements the virtual adapt callback using the mesh and the callback.
* \tparam TMesh The mesh handle class.
*/
template <typename TMesh>
struct MeshAdaptContext final: MeshAdaptContextBase
struct mesh_adapt_context final: mesh_adapt_context_base
{
/** Constructor of the context with the mesh handle and the user defined callback.
* \param [in] mesh_handle The mesh handle to adapt.
* \param [in] adapt_callback The adapt callback.
*/
MeshAdaptContext (TMesh& mesh_handle, typename TMesh::adapt_callback_type adapt_callback)
mesh_adapt_context (TMesh& mesh_handle, typename TMesh::adapt_callback_type adapt_callback)
: m_mesh_handle (mesh_handle), m_adapt_callback (std::move (adapt_callback))
{
}

/** Callback for mesh adaptation using the user defined adapt callback.
* \param [in] lelement_handle_id Local flat element ID in the mesh handle of the first element.
* \param [in] is_family If 1, the entries with indices \a lelement_handle_id,..., \a lelement_handle_id+ \a num_elements-1
* form a family. If 0, they do not.
* \param [in] num_elements The number of elements that form a family or 1 if \a is_family is 0.
* \param [in] is_family true if the entries with indices \a lelement_handle_id,...,
* \a lelement_handle_id+ \a num_elements-1 form a family, false otherwise.
* \param [in] num_elements The number of elements that form a family or 1 if \a is_family is false.
* \return 1 if the element with index \a lelement_handle_id should be refined,
* -1 if the family shall be coarsened,
* 0 else.
*/
int
adapt_mesh (const t8_locidx_t lelement_handle_id, const int is_family, const int num_elements) override
adapt_mesh (const t8_locidx_t lelement_handle_id, const bool is_family, const int num_elements) override
{
// Check if adapt callback is set and call it using the correct mesh handle function arguments.
T8_ASSERTF (m_adapt_callback, "No adapt callback set.");
std::vector<typename TMesh::element_class> element_vec;
if (is_family) {
for (int i = 0; i < num_elements; i++) {
element_vec.push_back (m_mesh_handle[lelement_handle_id + i]);
}
}
else {
element_vec.push_back (m_mesh_handle[lelement_handle_id]);
}
return m_adapt_callback (m_mesh_handle, element_vec);
const int count = is_family ? num_elements : 1;
std::span<const typename TMesh::element_class> element_view (&m_mesh_handle[lelement_handle_id], count);
return m_adapt_callback (m_mesh_handle, element_view);
}

private:
Expand All @@ -116,21 +109,21 @@ struct MeshAdaptContext final: MeshAdaptContextBase
* This globally accessible static class is required to get the handle and the callback in the forest callback,
* as the predefined header permits to give these as function arguments.
*/
class AdaptRegistry {
class adapt_registry {
public:
/** Static function to register \a context using \a forest as identifier.
* This makes the context publicly available using the Registry.
* This makes the context publicly available using the registry.
* \param [in] forest The forest identifier. In our case, this is the forest from which we adapt the mesh because
* we do not change this forest during adaptation such that it is valid unique identifier.
* we do not change this forest during adaptation such that it is valid unique identifier.
* \param [in] context The context to register. Use unique pointer to ensure proper memory management and ownership.
*/
static void
register_context (t8_forest_t forest, std::unique_ptr<MeshAdaptContextBase> context)
register_context (t8_forest_t forest, std::unique_ptr<mesh_adapt_context_base> context)
{
auto& map = get_map ();
auto [it, inserted] = map.emplace (forest, std::move (context));
if (!inserted) {
throw std::logic_error ("Context already registered");
t8_global_productionf ("Context already registered!");
}
}

Expand All @@ -149,7 +142,7 @@ class AdaptRegistry {
* \param [in] forest The forest identifier. In our case, this is the forest from which we adapt.
* \return Pointer to the context registered with the id \a forest if found, nullptr otherwise.
*/
static MeshAdaptContextBase*
static mesh_adapt_context_base*
get (t8_forest_t forest)
{
auto& map = get_map ();
Expand All @@ -158,14 +151,14 @@ class AdaptRegistry {
}

private:
/** Get the static map associating t8_forest_t with MeshAdaptContextBase references.
/** Get the static map associating t8_forest_t with mesh_adapt_context_base references.
* We use a getter instead of private member variable to ensure single initialization.
* \return Reference to the static unordered map of t8_forest_t to MeshAdaptContextBase references.
* \return Reference to the static unordered map of t8_forest_t to mesh_adapt_context_base references.
*/
static std::unordered_map<t8_forest_t, std::unique_ptr<MeshAdaptContextBase>>&
static std::unordered_map<t8_forest_t, std::unique_ptr<mesh_adapt_context_base>>&
get_map ()
{
static std::unordered_map<t8_forest_t, std::unique_ptr<MeshAdaptContextBase>> map;
static std::unordered_map<t8_forest_t, std::unique_ptr<mesh_adapt_context_base>> map;
return map;
}
};
Expand Down Expand Up @@ -193,9 +186,9 @@ mesh_adapt_callback_wrapper ([[maybe_unused]] t8_forest_t forest, t8_forest_t fo
{
// Get static adapt context from the registry.
// Via this, we can access the mesh handle and the user defined adapt callback that uses mesh handle functionality.
auto* context = AdaptRegistry::get (forest_from);
auto* context = adapt_registry::get (forest_from);
if (!context) {
t8_global_infof (
t8_global_productionf (
"Something went wrong while registering the adaptation callbacks. Please check your implementation.");
return 0; // No adaptation as default.
}
Expand Down
74 changes: 66 additions & 8 deletions mesh_handle/mesh.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@
#include <t8.h>
#include "element.hxx"
#include "competence_pack.hxx"
#include "adapt.hxx"
#include "internal/adapt.hxx"
#include "t8_forest/t8_forest_balance.h"
#include "t8_forest/t8_forest_types.h"
#include <t8_forest/t8_forest_general.h>
#include <t8_forest/t8_forest_ghost.h>
#include <vector>
#include <type_traits>
#include <functional>
#include <memory>
#include <span>

namespace t8_mesh_handle
{
Expand Down Expand Up @@ -82,7 +85,7 @@ class mesh {
* -1 if the family \a elements shall be coarsened,
* 0 else.
*/
using adapt_callback_type = std::function<int (const SelfType& mesh, const std::vector<element_class>& elements)>;
using adapt_callback_type = std::function<int (const SelfType& mesh, std::span<const element_class> elements)>;

/** Templated callback function prototype to decide for refining and coarsening of a family of elements
* or one element in a mesh handle including user data.
Expand All @@ -99,7 +102,7 @@ class mesh {
*/
template <typename TUserDataType>
using adapt_callback_type_with_userdata
= std::function<int (const SelfType& mesh, const std::vector<element_class>& elements, TUserDataType user_data)>;
= std::function<int (const SelfType& mesh, std::span<const element_class> elements, TUserDataType user_data)>;

/**
* Constructor for a mesh of the handle.
Expand Down Expand Up @@ -163,6 +166,17 @@ class mesh {
return m_forest;
}

/** Check if the local elements of the mesh are balanced.
* The mesh is said to be balanced if each element has face neighbors of level
* at most +1 or -1 of the element's level.
* \return true if the local elements are balanced, false otherwise.
*/
bool
is_balanced ()
{
return t8_forest_is_balanced (m_forest);
}

// --- Methods to access elements. ---
/**
* Returns a constant iterator to the first (local) mesh element.
Expand Down Expand Up @@ -249,7 +263,7 @@ class mesh {
mesh_adapt_callback_wrapper (adapt_callback_type_with_userdata<TUserDataType> adapt_callback_with_userdata,
const TUserDataType& user_data)
{
return [=] (const SelfType& mesh, const std::vector<element_class>& elements) {
return [=] (const SelfType& mesh, std::span<const element_class> elements) {
return adapt_callback_with_userdata (mesh, elements, user_data);
};
}
Expand All @@ -270,13 +284,57 @@ class mesh {
m_uncommitted_forest = new_forest;
}
// Create and register adaptation context holding the mesh handle and the user defined callback.
detail::AdaptRegistry::register_context (
m_forest, std::make_unique<detail::MeshAdaptContext<SelfType>> (*this, std::move (adapt_callback)));
detail::adapt_registry::register_context (
m_forest, std::make_unique<detail::mesh_adapt_context<SelfType>> (*this, std::move (adapt_callback)));

// Set up the forest for adaptation using the wrapper callback.
t8_forest_set_adapt (m_uncommitted_forest.value (), m_forest, detail::mesh_adapt_callback_wrapper, recursive);
}

/** If this function is called, the mesh will be partitioned on committing.
* The partitioning is done according to the SFC and each rank is assigned
* the same (maybe +1) number of elements.
* \note The partition is carried out only when \ref commit is called.
* \note This setting can be combined with \ref set_adapt and \ref set_balance. The order in which
* these operations are executed is always 1) Adapt 2) Partition 3) Balance.
* \param [in] set_for_coarsening If true, the partitions are choose such that coarsening
* an element once is a process local operation. Default is false.
*/
void
set_partition (bool set_for_coarsening = false)
{
if (!m_uncommitted_forest.has_value ()) {
t8_forest_t new_forest;
t8_forest_init (&new_forest);
m_uncommitted_forest = new_forest;
}
t8_forest_set_partition (m_uncommitted_forest.value (), m_forest, set_for_coarsening);
}

/** If this function is called, the mesh will be balanced on committing.
* The mesh is said to be balanced if each element has face neighbors of level
* at most +1 or -1 of the element's level.
* \note The balance is carried out only when \ref commit is called.
* \param [in] no_repartition Balance constructs several intermediate steps that
* are refined from each other. In order to maintain a balanced load, a repartitioning is performed in each
* round and the resulting mesh is load-balanced per default.
* Set \a no_repartition to true if this behaviour is not desired.
* If \a no_repartition is false (default), an additional call of \ref set_partition is not necessary.
* \note This setting can be combined with \ref set_adapt and \ref set_partition. The order in which
* these operations are executed is always 1) Adapt 2) Partition 3) Balance.
*/
void
set_balance (bool no_repartition = false)
{
if (!m_uncommitted_forest.has_value ()) {
t8_forest_t new_forest;
t8_forest_init (&new_forest);
m_uncommitted_forest = new_forest;
}
// Disable repartitioning and let the user call set_partition if desired.
t8_forest_set_balance (m_uncommitted_forest.value (), m_forest, no_repartition);
}

/** Enable or disable the creation of a layer of ghost elements.
* \param [in] do_ghost If true a ghost layer will be created.
* \param [in] ghost_type Controls which neighbors count as ghost elements,
Expand Down Expand Up @@ -316,8 +374,8 @@ class mesh {
t8_forest_ref (m_forest);
t8_forest_commit (m_uncommitted_forest.value ());
// Check if we adapted and unregister the adapt context if so.
if (detail::AdaptRegistry::get (m_uncommitted_forest.value ()) != nullptr) {
detail::AdaptRegistry::unregister_context (m_forest);
if (detail::adapt_registry::get (m_uncommitted_forest.value ()) != nullptr) {
detail::adapt_registry::unregister_context (m_forest);
if (!std::is_void<TElementDataType>::value) {
t8_global_infof (
"Please note that the element data is not interpolated automatically during adaptation. Use the "
Expand Down
14 changes: 7 additions & 7 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,11 @@ endif()
add_t8_cpp_test( NAME t8_gtest_vector_split_serial SOURCES t8_helper_functions/t8_gtest_vector_split.cxx )

if( T8CODE_BUILD_MESH_HANDLE )
add_t8_cpp_test( NAME t8_gtest_mesh_handle_parallel SOURCES mesh_handle/t8_gtest_mesh_handle.cxx )
add_t8_cpp_test( NAME t8_gtest_compare_handle_to_forest_serial SOURCES mesh_handle/t8_gtest_compare_handle_to_forest.cxx )
add_t8_cpp_test( NAME t8_gtest_handle_ghost_parallel SOURCES mesh_handle/t8_gtest_ghost.cxx )
add_t8_cpp_test( NAME t8_gtest_custom_competence_serial SOURCES mesh_handle/t8_gtest_custom_competence.cxx )
add_t8_cpp_test( NAME t8_gtest_cache_competence_serial SOURCES mesh_handle/t8_gtest_cache_competence.cxx )
add_t8_cpp_test( NAME t8_gtest_handle_data_parallel SOURCES mesh_handle/t8_gtest_handle_data.cxx )
add_t8_cpp_test( NAME t8_gtest_handle_adapt_serial SOURCES mesh_handle/t8_gtest_adapt.cxx )
add_t8_cpp_test( NAME t8_gtest_mesh_handle_parallel SOURCES mesh_handle/t8_gtest_mesh_handle.cxx )
add_t8_cpp_test( NAME t8_gtest_compare_handle_to_forest_serial SOURCES mesh_handle/t8_gtest_compare_handle_to_forest.cxx )
add_t8_cpp_test( NAME t8_gtest_handle_ghost_parallel SOURCES mesh_handle/t8_gtest_ghost.cxx )
add_t8_cpp_test( NAME t8_gtest_custom_competence_serial SOURCES mesh_handle/t8_gtest_custom_competence.cxx )
add_t8_cpp_test( NAME t8_gtest_cache_competence_serial SOURCES mesh_handle/t8_gtest_cache_competence.cxx )
add_t8_cpp_test( NAME t8_gtest_handle_data_parallel SOURCES mesh_handle/t8_gtest_handle_data.cxx )
add_t8_cpp_test( NAME t8_gtest_adapt_partition_balance_parallel SOURCES mesh_handle/t8_gtest_adapt_partition_balance.cxx )
endif()
Loading