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
10 changes: 10 additions & 0 deletions mesh_handle/competence_pack.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#pragma once

#include "competences.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.
Expand All @@ -53,4 +54,13 @@ using all_cache_competences
using cache_face_competences
= competence_pack<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.
*/
template <typename... TPacks>
using union_competence_packs_type = typename detail::union_competence_packs<TPacks...>::type;

} // namespace t8_mesh_handle
157 changes: 157 additions & 0 deletions mesh_handle/internal/competence_pack_union.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
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 competence_pack_union.hxx
* Define the implementation of the unique union of competences of several \ref t8_mesh_handle::competence_pack 's.
* Users should use \ref t8_mesh_handle::union_competence_packs_type in \ref competence_pack.hxx.
*/

#pragma once

#include <type_traits>

namespace t8_mesh_handle
{

/** Forward declaration of the competence pack class.
* \tparam TCompetence The competences to be packed.
*/
template <template <typename> class... TCompetence>
struct competence_pack;

/** Namespace to hide detail from user. */
namespace detail
{

// --- Unique insertion of one competence into a competence pack. ---
/** Helper: Wraps a template-template parameter into a type for comparison.
* Necessary because our competences are templated on the underlying type.
* This allows safe comparison of template templates using std::is_same_v.
* \tparam TType Template-template parameter to wrap.
*/
template <template <class> class TType>
struct tag
{
};

/** Insert competence TCompetence into a pack with competences TUnionCompetences if not already present.
* A new competence_pack is produced.
* \tparam TCompetence Competence to insert.
* \tparam TUnionCompetences Existing competences in the pack.
*/
template <template <class> class TCompetence, template <class> class... TUnionCompetences>
using insert_unique
= std::conditional_t<(std::is_same_v<tag<TCompetence>, tag<TUnionCompetences>> || ...),
competence_pack<TUnionCompetences...>, competence_pack<TUnionCompetences..., TCompetence>>;

//--- Unique fold operation to fold competences into one competence pack without duplication. ---
/** Fold operation to accumulate unique competences.
* Recursively inserts all TCompetences into the competence pack.
* \tparam TCompetencePack Current accumulated competence_pack.
* \tparam TCompetences Competences to process.
*/
template <typename TCompetencePack, template <class> class... TCompetences>
struct fold_unique;

/** Termination case: no more competences to process.
* Specialization for the case when the competence pack is already fully processed and
* no more competences are left to insert.
* \tparam TUnionCompetences Existing competences in the pack.
*/
template <template <class> class... TUnionCompetences>
struct fold_unique<competence_pack<TUnionCompetences...>>
{
/** Final competence pack with all competences inserted. */
using type = competence_pack<TUnionCompetences...>;
};

/** Recursive case: insert the first competence uniquely, then process the rest recursively until
* termination (no competences left).
* Specialization for the case when there is still at least one competence left to process.
* \tparam TUnionCompetences Existing competences in the pack.
* \tparam TCompetence Competence to insert in this recursion cycle.
* \tparam TOtherCompetences Remaining competences to process in the next recursion cycles.
*/
template <template <class> class... TUnionCompetences, template <class> class TCompetence,
template <class> class... TOtherCompetences>
struct fold_unique<competence_pack<TUnionCompetences...>, TCompetence, TOtherCompetences...>
{
/** Competence pack after inserting the first competence TCompetence uniquely. */
using type = typename fold_unique<insert_unique<TCompetence, TUnionCompetences...>, TOtherCompetences...>::type;
};

//--- Unique union of two competence packs. ---
/** Compute the unique union of the competences of two \ref competence_pack 's.
* This produces a new \ref competence_pack containing all competences with duplicates removed.
* \tparam TPack1 First competence pack.
* \tparam TPack2 Second competence pack.
*/
template <typename TPack1, typename TPack2>
struct union_two_competence_packs;

/** Specialization for two \ref competence_pack types.
* This is necessary because this way, we can access the competences directly.
* This specialization of the class above is used if both template parameters are of type \ref competence_pack.
* \tparam TCompetences1 Competences of the first competence pack.
* \tparam TCompetences2 Competences of the second competence pack.
*/
template <template <class> class... TCompetences1, template <class> class... TCompetences2>
struct union_two_competence_packs<competence_pack<TCompetences1...>, competence_pack<TCompetences2...>>
{
/** The resulting competence_pack type with all competences from both packs, but without duplicates.
* The type is computed by folding the unique insertion of all competences from both packs into an
* initially empty pack.
*/
using type = typename fold_unique<competence_pack<>, TCompetences1..., TCompetences2...>::type;
};

//--- Recursive union of multiple competence packs using the implementation for two packs. ---
/** Recursive case: Compute the unique union of the competences of more than one \ref competence_pack 's.
* Specialization for the case when there are still at least two competence packs left to process.
* Uses \ref union_two_competence_packs recursively for pairwise combination.
* \tparam TPack First competence pack to combine with the union of the rest of the competence packs
* in this recursion cycle.
* \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 competence_pack.
*/
template <typename TPack, typename... TPacks>
struct union_competence_packs
{
/** This is the type of a new \ref competence_pack containing all competences of the competence packs
* with duplicates removed.
*/
using type = typename union_two_competence_packs<TPack, typename union_competence_packs<TPacks...>::type>::type;
};

/** Termination case: Only one pack left.
* Specialization for the case when there is only one competence pack left to process.
* \tparam TPack The only competence pack left.
*/
template <typename TPack>
struct union_competence_packs<TPack>
{
/** Type of the last remaining competence pack. */
using type = TPack;
};

} // namespace detail
} // namespace t8_mesh_handle
24 changes: 23 additions & 1 deletion test/mesh_handle/t8_gtest_mesh_handle.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ TEST_P (t8_mesh_handle_test, test_all_cache_competence)
/** Test mesh class with all predefined face competences using some exemplary functionality. */
TEST_P (t8_mesh_handle_test, test_cache_face_competences)
{
// --- Use all predefined competences. ---
using mesh_class = t8_mesh_handle::mesh<t8_mesh_handle::cache_face_competences>;
using element_class = typename mesh_class::element_class;
auto mesh = t8_mesh_handle::handle_hypercube_uniform_default<mesh_class> (eclass, level, sc_MPI_COMM_WORLD, true,
Expand Down Expand Up @@ -172,4 +171,27 @@ TEST_P (t8_mesh_handle_test, test_cache_face_competences)
}
}

/** Check that the unique union of multiple competence packs works as intended. */
TEST (t8_mesh_handle_test, test_union_competence_pack)
{
using namespace t8_mesh_handle;
/* Combine multiple competence packs with some overlapping competences to check that the union works correctly
* and duplicates are removed. Duplicates would cause an error because we inherit multiple times from the same class.
*/
using mesh_class
= mesh<union_competence_packs_type<competence_pack<cache_volume>,
competence_pack<cache_volume, cache_diameter, cache_vertex_coordinates>,
competence_pack<cache_centroid, cache_face_areas, cache_face_centroids>>>;
using element_class = typename mesh_class::element_class;

EXPECT_TRUE (element_class::has_volume_cache ());
EXPECT_TRUE (element_class::has_diameter_cache ());
EXPECT_TRUE (element_class::has_vertex_cache ());
EXPECT_TRUE (element_class::has_centroid_cache ());
EXPECT_TRUE (element_class::has_face_areas_cache ());
EXPECT_TRUE (element_class::has_face_centroids_cache ());
EXPECT_FALSE (element_class::has_face_normals_cache ());
EXPECT_FALSE (element_class::has_face_neighbor_cache ());
}

INSTANTIATE_TEST_SUITE_P (t8_gtest_mesh, t8_mesh_handle_test, testing::Combine (AllEclasses, testing::Range (2, 3)));