Skip to content
Merged
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
50 changes: 15 additions & 35 deletions src/t8_unstructured_mesh/t8_element_competences.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -53,28 +53,18 @@ along with t8code; if not, write to the Free Software Foundation, Inc.,
template <typename TUnderlying>
struct t8_cache_vertex_coordinates: t8_crtp_operator<TUnderlying, t8_cache_vertex_coordinates>
{
private:
public:
/**
* Returns the previously cached vector with the vertex coordinates of the unstructured mesh element.
* \return Constant reference to the cached vector with the vertex coordinates.
* Function that checks if the cache for the vertex coordinates has been filled.
* \return true if the cache for the vertex coordinates has been filled, false otherwise.
*/
const std::vector<t8_3D_vec>&
get_vertex_coordinates_cached () const
bool
vertex_cache_filled () const
{
return m_vertex_coordinates;
}

/**
* Setter for the cache.
* \param [in] new_vertex_coordinates Vector with the coordinates of the vertices of the
* unstructured mesh element that should be cached.
*/
void
set_vertex_coordinates_cached (std::vector<t8_3D_vec>&& new_vertex_coordinates)
{
m_vertex_coordinates = std::move (new_vertex_coordinates);
return !m_vertex_coordinates.empty ();
}

protected:
mutable std::vector<t8_3D_vec>
m_vertex_coordinates; /**< Cache for the vector of vertex coordinate arrays. Empty vector if not filled. */
};
Expand All @@ -86,30 +76,20 @@ struct t8_cache_vertex_coordinates: t8_crtp_operator<TUnderlying, t8_cache_verte
template <typename TUnderlying>
struct t8_cache_centroid: t8_crtp_operator<TUnderlying, t8_cache_centroid>
{
private:
/**
* Returns an optional with the centroid coordinates for an unstructured mesh element if previously cached.
* \return Optional with coordinates of the centroid of the unstructured mesh element.
*/
std::optional<t8_3D_vec>
get_centroid_cached () const
{
return m_coordinates;
}

public:
/**
* Setter for the cache.
* \param [in] new_centroid_coordinates Array with the coordinates of the centroid of the
* unstructured mesh element that should be cached.
* Function that checks if the cache for the centroid has been filled.
* \return true if the cache for the centroid has been filled, false otherwise.
*/
void
set_centroid_cached (t8_3D_vec new_centroid_coordinates)
bool
centroid_cache_filled () const
{
m_coordinates = new_centroid_coordinates;
return m_centroid.has_value ();
}

protected:
mutable std::optional<t8_3D_vec>
m_coordinates; /**< Cache for the coordinates of the centroid. Use optional to allow no value if cache is not filled. */
m_centroid; /**< Cache for the coordinates of the centroid. Use optional to allow no value if cache is not filled. */
};

#endif /* !T8_ELEMENT_COMPETENCES_HXX */
66 changes: 42 additions & 24 deletions src/t8_unstructured_mesh/t8_unstructured_element.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -65,34 +65,33 @@ class t8_unstructured_mesh_element: public TCompetence<t8_unstructured_mesh_elem

private:
// --- Variables to check which functionality is defined in TCompetence. ---
/** Helper function to check if class T implements the function get_vertex_coordinates_cached.
/** Helper function to check if class T implements the function vertex_cache_filled.
* \tparam T The competence to be checked.
* \return true if T implements the function, false if not.
*/
template <template <typename> class T>
static constexpr bool
has_get_vertex_coordinates_cached ()
vertex_cache_defined ()
{
return requires (T<SelfType>& competence) { competence.get_vertex_coordinates_cached (); };
return requires (T<SelfType>& competence) { competence.vertex_cache_filled (); };
}
/* This variable is true if any of the given competences \ref TCompetence implements
a function get_vertex_coordinates_cached. */
static constexpr bool get_vertex_coordinates_defined
= (false || ... || has_get_vertex_coordinates_cached<TCompetence> ());
a function vertex_cache_filled */
static constexpr bool vertex_cache_exists = (false || ... || vertex_cache_defined<TCompetence> ());

/** Helper function to check if class T implements the function get_centroid_cached.
/** Helper function to check if class T implements the function centroid_cache_filled.
* \tparam T The competence to be checked.
* \return true if T implements the function, false if not.
*/
template <template <typename> class T>
static constexpr bool
has_get_centroid_cached ()
centroid_cache_defined ()
{
return requires (T<SelfType>& competence) { competence.get_centroid_cached (); };
return requires (T<SelfType>& competence) { competence.centroid_cache_filled (); };
}
/* This variable is true if any of the given competences \ref TCompetence implements
a function get_centroid_cached. */
static constexpr bool get_centroid_defined = (false || ... || has_get_centroid_cached<TCompetence> ());
a function centroid_cache_filled. */
static constexpr bool centroid_cache_exists = (false || ... || centroid_cache_defined<TCompetence> ());

public:
/**
Expand All @@ -107,6 +106,27 @@ class t8_unstructured_mesh_element: public TCompetence<t8_unstructured_mesh_elem
{
}

// --- Functions to check if caches exist. ---
/**
* Function that checks if a cache for the vertex coordinates exists.
* \return true if a cache for the vertex coordinates exists, false otherwise.
*/
static constexpr bool
has_vertex_cache ()
{
return vertex_cache_exists;
}

/**
* Function that checks if a cache for the centroid exists.
* \return true if a cache for the centroid exists, false otherwise.
*/
static constexpr bool
has_centroid_cache ()
{
return centroid_cache_exists;
}

// --- 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 unstructured mesh element.
Expand All @@ -130,10 +150,9 @@ class t8_unstructured_mesh_element: public TCompetence<t8_unstructured_mesh_elem
get_vertex_coordinates () const
{
// Check if we have a cached version and if the cache has already been filled.
if constexpr (get_vertex_coordinates_defined) {
auto cached_vertex = this->get_vertex_coordinates_cached ();
if (!cached_vertex.empty ()) {
return cached_vertex;
if constexpr (vertex_cache_exists) {
if (this->vertex_cache_filled ()) {
return this->m_vertex_coordinates;
}
}
// Calculate the vertex coordinates.
Expand All @@ -148,9 +167,9 @@ class t8_unstructured_mesh_element: public TCompetence<t8_unstructured_mesh_elem
vertex_coordinates.push_back (vertex);
}
// Fill the cache in the cached version.
if constexpr (get_vertex_coordinates_defined) {
this->set_vertex_coordinates_cached (std::move (vertex_coordinates));
return this->get_vertex_coordinates_cached ();
if constexpr (vertex_cache_exists) {
this->m_vertex_coordinates = std::move (vertex_coordinates);
return this->m_vertex_coordinates;
}
return vertex_coordinates;
}
Expand All @@ -164,17 +183,16 @@ class t8_unstructured_mesh_element: public TCompetence<t8_unstructured_mesh_elem
get_centroid () const
{
// Check if we have a cached version and if the cache has already been filled.
if constexpr (get_centroid_defined) {
auto cached_centroid = this->get_centroid_cached ();
if (cached_centroid.has_value ()) {
return cached_centroid.value ();
if constexpr (centroid_cache_exists) {
if (this->centroid_cache_filled ()) {
return this->m_centroid.value ();
}
}
t8_3D_vec coordinates;
t8_forest_element_centroid (m_unstructured_mesh->m_forest, m_tree_id, get_element (), coordinates.data ());
// Fill the cache in the cached version.
if constexpr (get_centroid_defined) {
this->set_centroid_cached (coordinates);
if constexpr (centroid_cache_exists) {
this->m_centroid = coordinates;
}
return coordinates;
}
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ if( T8CODE_BUILD_FORTRAN_INTERFACE AND T8CODE_ENABLE_MPI )
endif()

add_t8_cpp_test( NAME t8_gtest_unstructured_mesh_serial SOURCES t8_unstructured_mesh/t8_gtest_unstructured_mesh.cxx )
add_t8_cpp_test( NAME t8_gtest_unstructured_custom_comp_serial SOURCES t8_unstructured_mesh/t8_gtest_custom_competence.cxx )
add_t8_cpp_test( NAME t8_gtest_vector_split_serial SOURCES t8_vector_helper/t8_gtest_vector_split.cxx )

copy_test_file( test_cube_unstructured_1.inp )
Expand Down
107 changes: 107 additions & 0 deletions test/t8_unstructured_mesh/t8_gtest_custom_competence.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
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

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 t8_gtest_custom_competence.cxx
* Checks that custom-defined competences can be used as template parameters for the unstructured mesh interface.
*/

#include <gtest/gtest.h>
#include <test/t8_gtest_schemes.hxx>
#include <t8.h>

#include <t8_unstructured_mesh/t8_unstructured_mesh.hxx>
#include <t8_unstructured_mesh/t8_unstructured_element.hxx>
#include <t8_unstructured_mesh/t8_element_competences.hxx>
#include <t8_cmesh.h>
#include <t8_cmesh/t8_cmesh_examples.h>
#include <t8_forest/t8_forest_general.h>
#include <t8_schemes/t8_default/t8_default.hxx>
#include <t8_types/t8_operators.hxx>

/** Custom competence that needs access to the public members of the elements. */
template <typename TUnderlying>
struct dummy_get_level: public t8_crtp_operator<TUnderlying, dummy_get_level>
{
public:
t8_element_level
get_level_dummy () const
{
auto forest = this->underlying ().get_unstructured_mesh ()->get_forest ();
const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, this->underlying ().get_tree_id ());
const t8_element_t *element = t8_forest_get_leaf_element_in_tree (forest, this->underlying ().get_tree_id (),
this->underlying ().get_element_id ());
return t8_forest_get_scheme (forest)->element_get_level (tree_class, element);
}
};

/** Second custom competence. */
template <typename TUnderlying>
struct dummy_trivial: public t8_crtp_operator<TUnderlying, dummy_trivial>
{
public:
int
get_value_dummy () const
{
return 1;
}
};

/** This tests checks that custom defined competences can be used for the unstructured mesh class and that we can use the functionality defined in the competence. Also checks that we can use more than one custom competence and that predefined competences can be additionally used. */
TEST (t8_gtest_custom_competence, custom_competence)
{
// Define forest to construct unstructured mesh.
const int level = 1;
t8_cmesh_t cmesh = t8_cmesh_new_hypercube_hybrid (sc_MPI_COMM_WORLD, 0, 0);
const t8_scheme *scheme = t8_scheme_new_default ();
t8_forest_t forest = t8_forest_new_uniform (cmesh, scheme, level, 0, sc_MPI_COMM_WORLD);
ASSERT_EQ (true, t8_forest_is_committed (forest));

// Check mesh with custom defined competence.
t8_unstructured_mesh<t8_unstructured_mesh_element<dummy_get_level>> unstructured_mesh
= t8_unstructured_mesh<t8_unstructured_mesh_element<dummy_get_level>> (forest);

for (auto it = unstructured_mesh.begin (); it != unstructured_mesh.end (); ++it) {
EXPECT_EQ (it->get_level (), it->get_level_dummy ());
EXPECT_EQ (level, it->get_level_dummy ());
}

// Test with two custom competences and a predefined competence.
t8_unstructured_mesh<t8_unstructured_mesh_element<dummy_get_level, dummy_trivial, t8_cache_centroid>>
unstructured_mesh_2competences
= t8_unstructured_mesh<t8_unstructured_mesh_element<dummy_get_level, dummy_trivial, t8_cache_centroid>> (forest);

for (auto it = unstructured_mesh_2competences.begin (); it != unstructured_mesh_2competences.end (); ++it) {
EXPECT_EQ (it->get_level (), it->get_level_dummy ());
EXPECT_EQ (it->get_value_dummy (), 1);
EXPECT_FALSE (it->centroid_cache_filled ());
auto centroid = it->get_centroid ();
for (const auto &coordinate : centroid) {
EXPECT_GE (1, coordinate);
EXPECT_LE (0, coordinate);
}
EXPECT_TRUE (it->centroid_cache_filled ());
}

// Unref the forest.
t8_forest_unref (&forest);
}
Loading