From ebc8a5eaaac0d37a275db1dbddd7ca2f5bf1d10a Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 19 Aug 2026 17:19:43 +0200 Subject: [PATCH 01/14] implement a static vector to minimize heap allocations --- src/t8_data/t8_static_vector.hxx | 398 +++++++++++++++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 src/t8_data/t8_static_vector.hxx diff --git a/src/t8_data/t8_static_vector.hxx b/src/t8_data/t8_static_vector.hxx new file mode 100644 index 0000000000..9de168139d --- /dev/null +++ b/src/t8_data/t8_static_vector.hxx @@ -0,0 +1,398 @@ +/* + 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 t8_static_vector.hxx + * + * Implements the fixed-size container \ref t8_static_vector. + * + */ + +#pragma once + +#include + +#include +#include +#include +#include + +/** + * A fixed-capacity vector with statically allocated storage. + * + * The container stores up to TCapacity elements without performing dynamic + * memory allocations. The logical number of elements can be smaller than + * the maximum capacity. + * + * \tparam TType The type of elements stored in the container. + * \tparam TCapacity The maximum number of elements that can be stored. + */ +template +class t8_static_vector { + public: + using value_type = TType; + + /** + * Creates an empty static vector. + * + * \return The constructed empty static vector. + */ + constexpr t8_static_vector () noexcept = default; + + /** + * Creates a static vector from an initializer list. + * \param [in] values The elements to store in the vector. + * + * \note The number of values must not exceed the vector capacity. + */ + constexpr t8_static_vector (std::initializer_list values) + { + T8_ASSERT (values.size () <= TCapacity); + + for (const TType& value : values) { + m_data[m_size++] = value; + } + } + + /** + * Creates a static vector with a given size and initializes all elements + * with the given value. + * \param [in] size The number of elements to create. + * \param [in] value The value used to initialize all elements. + * + * \note The requested size must not exceed the vector capacity. + */ + constexpr t8_static_vector (size_t size, const TType& value) + { + T8_ASSERT (size <= TCapacity); + + m_size = size; + for (size_t i = 0; i < m_size; ++i) { + m_data[i] = value; + } + } + + /** + * Returns the current number of elements stored in the vector. + * + * \return The number of elements currently stored. + */ + constexpr size_t + size () const noexcept + { + return m_size; + } + + /** + * Returns the maximum number of elements that can be stored. + * + * \return The maximum number of elements. + */ + static constexpr size_t + capacity () noexcept + { + return TCapacity; + } + + /** + * Returns whether the vector contains no elements. + * + * \return true if the vector is empty, false otherwise. + */ + constexpr bool + empty () const noexcept + { + return m_size == 0; + } + + /** + * Returns whether the vector contains the maximum number of elements. + * + * \return true if the vector is full, false otherwise. + */ + constexpr bool + full () const noexcept + { + return m_size == TCapacity; + } + + /** + * Changes the number of elements stored in the vector. + * + * If the new size is smaller than the current size, elements at the end + * are removed. If the new size is larger, new elements are initialized + * with their default value. + * + * \param [in] new_size The new number of elements. + * + * \note The requested size must not exceed the vector capacity. + */ + constexpr void + resize (size_t new_size) + { + T8_ASSERT (new_size <= TCapacity); + + if (new_size > m_size) { + for (size_t i = m_size; i < new_size; ++i) { + m_data[i] = TType {}; + } + } + + m_size = new_size; + } + + /** + * Changes the number of elements stored in the vector. + * + * If the new size is smaller than the current size, elements at the end + * are removed. If the new size is larger, new elements are initialized + * with the given value. + * + * \param [in] new_size The new number of elements. + * \param [in] value The value used to initialize new elements. + * + * \note The requested size must not exceed the vector capacity. + */ + constexpr void + resize (size_t new_size, const TType& value) + { + T8_ASSERT (new_size <= TCapacity); + + for (size_t i = m_size; i < new_size; ++i) { + m_data[i] = value; + } + + m_size = new_size; + } + + /** + * Adds an element to the end of the vector. + * + * \param [in] value The element to add. + * + * \note The vector must not be full. + */ + constexpr void + push_back (const TType& value) + { + T8_ASSERT (!full ()); + m_data[m_size++] = value; + } + + /** + * Adds an element to the end of the vector by moving it. + * + * \param [in] value The element to move into the vector. + * + * \note The vector must not be full. + */ + constexpr void + push_back (TType&& value) + { + T8_ASSERT (!full ()); + m_data[m_size++] = std::move (value); + } + + /** + * Constructs and adds an element to the end of the vector. + * + * \tparam Args The types of the arguments forwarded to the constructor. + * \param [in] args The arguments forwarded to the element constructor. + * \return A reference to the newly constructed element. + * + * \note The vector must not be full. + */ + template + constexpr TType& + emplace_back (TArgs&&... args) + { + T8_ASSERT (!full ()); + + m_data[m_size] = TType (std::forward (args)...); + return m_data[m_size++]; + } + + /** + * Replaces the contents of the vector with a given number of copies + * of a value. + * \param [in] size The number of elements to create. + * \param [in] value The value used to initialize all elements. + * + * \note The requested size must not exceed the vector capacity. + */ + constexpr void + assign (size_t size, const TType& value) + { + T8_ASSERT (size <= TCapacity); + + m_size = size; + for (size_t i = 0; i < m_size; ++i) { + m_data[i] = value; + } + } + + /** + * Removes the last element from the vector. + * + * \note The vector must not be empty. + */ + constexpr void + pop_back () noexcept + { + T8_ASSERT (m_size > 0); + --m_size; + } + + /** + * Removes all elements from the vector. + */ + constexpr void + clear () noexcept + { + m_size = 0; + } + + /** + * Assigns the contents of an initializer list to the vector. + * + * \param [in] values The elements to store in the vector. + * \return A reference to this vector. + * + * \note The number of values must not exceed the vector capacity. + */ + constexpr t8_static_vector& + operator= (std::initializer_list values) + { + T8_ASSERT (values.size () <= TCapacity); + + m_size = 0; + + for (const TType& value : values) { + m_data[m_size++] = value; + } + + return *this; + } + + /** + * Returns a reference to the element at the given index. + * + * \param [in] index The index of the element to access. + * \return A reference to the requested element. + * + * \note \a index must be smaller than the current number of elements. + */ + constexpr TType& + operator[] (size_t index) noexcept + { + T8_ASSERT (index < m_size); + return m_data[index]; + } + + /** + * Returns a constant reference to the element at the given index. + * + * \param [in] index The index of the element to access. + * \return A constant reference to the requested element. + * + * \note \a index must be smaller than the current number of elements. + */ + constexpr const TType& + operator[] (size_t index) const noexcept + { + T8_ASSERT (index < m_size); + return m_data[index]; + } + + /** + * Returns a pointer to the underlying element storage. + * + * \return A pointer to the first element in the vector. + */ + constexpr TType* + data () noexcept + { + return m_data.data (); + } + + /** + * Returns a pointer to the underlying element storage. + * + * \return A constant pointer to the first element in the vector. + */ + constexpr const TType* + data () const noexcept + { + return m_data.data (); + } + + /** + * Returns an iterator to the first element. + * + * \return An iterator to the first element. + */ + constexpr auto + begin () noexcept + { + return m_data.begin (); + } + + /** + * Returns a constant iterator to the first element. + * + * \return A constant iterator to the first element. + */ + constexpr auto + begin () const noexcept + { + return m_data.begin (); + } + + /** + * Returns an iterator past the last element. + * + * \return An iterator past the last element. + */ + constexpr auto + end () noexcept + { + return m_data.begin () + m_size; + } + + /** + * Returns a constant iterator past the last element. + * + * \return A constant iterator past the last element. + */ + constexpr auto + end () const noexcept + { + return m_data.begin () + m_size; + } + + private: + /** Storage for the maximum number of elements. */ + std::array m_data {}; + + /** Current number of elements stored in the vector. */ + size_t m_size = 0; +}; From eb6922e7177a0285b18ba989d6dad9143b70ff89 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 19 Aug 2026 17:20:02 +0200 Subject: [PATCH 02/14] typo --- example/remove/t8_example_gauss_blob.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/remove/t8_example_gauss_blob.cxx b/example/remove/t8_example_gauss_blob.cxx index 3604cb7a7b..3e63127dce 100644 --- a/example/remove/t8_example_gauss_blob.cxx +++ b/example/remove/t8_example_gauss_blob.cxx @@ -36,7 +36,7 @@ struct t8_adapt_data }; static double -t8_gausss_blob (const t8_3D_vec ¢er_elem, const t8_3D_vec ¢er_cube, const double radius) +t8_gauss_blob (const t8_3D_vec ¢er_elem, const t8_3D_vec ¢er_cube, const double radius) { double expo = 0; for (int i = 0; i < 3; i++) { @@ -68,7 +68,7 @@ t8_create_element_data (t8_forest_t forest, const t8_3D_vec &sphere_center, cons element = t8_forest_get_leaf_element_in_tree (forest, itree, ielement); t8_3D_vec center; t8_forest_element_centroid (forest, itree, element, center.data ()); - element_data[current_index] = t8_gausss_blob (center, sphere_center, sphere_radius); + element_data[current_index] = t8_gauss_blob (center, sphere_center, sphere_radius); } } return element_data; From d2b17281cd1db6b8d4289c4bb651f08c4ac3b573 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 19 Aug 2026 17:22:43 +0200 Subject: [PATCH 03/14] implement a boundary condition handler --- src/CMakeLists.txt | 5 +- .../t8_cmesh_boundary_condition_handler.cxx | 93 ++++++++ .../t8_cmesh_boundary_condition_handler.hxx | 212 ++++++++++++++++++ ...cmesh_boundary_condition_handler_types.cxx | 48 ++++ ...8_cmesh_boundary_condition_handler_types.h | 74 ++++++ .../t8_cmesh_boundary_conditions.cxx | 133 +++++++++++ .../t8_cmesh_boundary_conditions.hxx | 106 +++++++++ ...t8_cmesh_boundary_conditions_c_interface.h | 102 +++++++++ .../{t8_cmesh_stash.c => t8_cmesh_stash.cxx} | 22 +- .../t8_cmesh_internal/t8_cmesh_stash.h | 44 ++++ .../t8_cmesh_internal/t8_cmesh_types.h | 13 +- 11 files changed, 846 insertions(+), 6 deletions(-) create mode 100644 src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.cxx create mode 100644 src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx create mode 100644 src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.cxx create mode 100644 src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.h create mode 100644 src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.cxx create mode 100644 src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.hxx create mode 100644 src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h rename src/t8_cmesh/t8_cmesh_internal/{t8_cmesh_stash.c => t8_cmesh_stash.cxx} (94%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6d5197d07e..4d7589d6f2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -149,11 +149,14 @@ target_sources( T8 PRIVATE t8_cmesh/t8_cmesh_internal/t8_cmesh_copy.cxx t8_cmesh/t8_cmesh_internal/t8_cmesh_offset.c t8_cmesh/t8_cmesh_internal/t8_cmesh_partition.cxx - t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.c + t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.cxx t8_cmesh/t8_cmesh_internal/t8_cmesh_trees.cxx t8_cmesh/t8_cmesh_vertex_connectivity/t8_cmesh_vertex_conn_tree_to_vertex.cxx t8_cmesh/t8_cmesh_vertex_connectivity/t8_cmesh_vertex_conn_vertex_to_tree.cxx t8_cmesh/t8_cmesh_vertex_connectivity/t8_cmesh_vertex_connectivity.cxx + t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.cxx + t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.cxx + t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.cxx t8_cmesh/t8_cmesh_io/t8_cmesh_readmshfile.cxx t8_cmesh/t8_cmesh_io/deprecated/deprecated_t8_cmesh_save.cxx t8_cmesh/t8_cmesh_io/deprecated/deprecated_t8_cmesh_triangle.cxx diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.cxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.cxx new file mode 100644 index 0000000000..022a1f8185 --- /dev/null +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.cxx @@ -0,0 +1,93 @@ +/* + 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 t8_cmesh_boundary_condition_manager.cxx + * Implementation context of \ref t8_cmesh_boundary_condition_manager.hxx + */ + +#include +#include +#include +#include + +using namespace detail; + +#if T8_ENABLE_DEBUG +int +t8_cmesh_boundary_condition_handler::verify () const +{ + T8_ASSERT (!t8_cmesh_is_committed (m_cmesh, 0)); + + /* Retrieve eclasses and boundary conditions from stash */ + std::vector>> boundary_conditions + = t8_stash_extract_attribute_list (m_cmesh->stash, t8_get_package_id (), + T8_CMESH_BOUNDARY_CONDITION_ATTRIBUTE_KEY); + std::vector> eclasses = t8_stash_extract_eclasses (m_cmesh->stash); + + /* Check that every tree has a bc */ + if (eclasses.size () != boundary_conditions.size ()) { + t8_errorf ("ERROR: Number of cmesh eclasses does not match the number of cmesh boundary conditions.\n" + "If boundary conditions are applied, all trees have to get them assigned. They can be left empty.\n"); + return false; + } + + /* Sort the bcs and eclasses by tree id. */ + std::sort (boundary_conditions.begin (), boundary_conditions.end (), + [] (const auto &a, const auto &b) { return a.first < b.first; }); + std::sort (eclasses.begin (), eclasses.end (), [] (const auto &a, const auto &b) { return a.first < b.first; }); + + auto eclasses_it = eclasses.cbegin (); + auto bc_it = boundary_conditions.cbegin (); + for (; eclasses_it != eclasses.end (); ++eclasses_it, ++bc_it) { + /* Make sure, that the eclasses and bcs tree id are the same. */ + if (eclasses_it->first != bc_it->first) { + t8_errorf ("ERROR: eclass tree id and boundary condition tree id do not match.\nProbably the boundary condition " + "got a wrong tree id. Boundary condition tree id: %li \n", + bc_it->first); + return false; + } + /* Make sure that the amount of registered bcs matches th number of tree faces. */ + if (static_cast (t8_eclass_num_faces[eclasses_it->second]) != bc_it->second.size ()) { + t8_errorf ("ERROR: Tree %li has a mismatch in its face count and boundary condition count.\n" + "Face count: %i, boundary condition count: %li\n", + eclasses_it->first, t8_eclass_num_faces[eclasses_it->second], bc_it->second.size ()); + return false; + } + /* Check, that the hash of the bc attribute is present in the bc map. This failure cannot be caused by the user. */ + for (const auto bc : bc_it->second) { + if (!get_boundary_condition_name_safe (bc).has_value ()) { + t8_errorf ("ERROR: Could not find name of applied boundary condition. You probably found a bug.\n"); + return false; + } + } + } + return true; +} + +#endif /* T8_ENABLE_DEBUG */ + +int +t8_cmesh_boundary_condition_handler::get_boundary_condition_attribute_key () const +{ + return T8_CMESH_BOUNDARY_CONDITION_ATTRIBUTE_KEY; +} diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx new file mode 100644 index 0000000000..ea5cedc390 --- /dev/null +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx @@ -0,0 +1,212 @@ +/* + 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 t8_cmesh_boundary_condition_handler.hxx + * Implements a data structure for the assignment of boundary conditions to the cmesh. + * The handler can also query boundary conditions of mesh elements. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/** + * A container to store boundary conditions. * + * \tparam TType The type the boundary conditions are saved in. + */ +template +using t8_boundary_conditions = t8_static_vector; + +namespace detail +{ + +/** + * Struct to efficiently store boundary condition labels inside the cmesh. + * It uses an internal map to convert boundary condition names into hashes and vice versa. + * We do this since we do not want to save strings of dynamic size in the cmesh. Hashes are + * smaller in their memory footprint and also fixed-size. + */ +struct t8_cmesh_boundary_condition_handler +{ + private: + /** + * Tag for boundary condition hash strong type. + */ + struct boundary_condition_hash_tag + { + }; + /** + * Strong type for boundary condition hashes. + */ + using boundary_condition_hash = T8Type; + + public: + /** + * Standard constructor. Assotiates the handler with a cmesh + * \param [in] cmesh + */ + t8_cmesh_boundary_condition_handler (t8_cmesh_t cmesh): m_cmesh (cmesh) + { + } + + template + requires std::convertible_to, std::string_view> + inline void + add_boundary_conditions (t8_gloidx_t gtreeid, TStringRange boundary_conditions) + { + std::vector hashes; + hashes.reserve (std::size (boundary_conditions)); + for (const auto &boundary_condition : boundary_conditions) { + const boundary_condition_hash hash = hash_boundary_condition_name (boundary_condition); + m_boundary_conditions.try_emplace (hash, boundary_condition); + hashes.emplace_back (std::move (hash)); + } + t8_cmesh_set_attribute (m_cmesh, gtreeid, t8_get_package_id (), get_boundary_condition_attribute_key (), + hashes.data (), sizeof (boundary_condition_hash) * hashes.size (), 0); + } + + inline t8_boundary_conditions + get_boundary_conditions (t8_locidx_t ltreeid) const + { + T8_ASSERT (t8_cmesh_is_committed (m_cmesh, 0)); + const std::span hashes = fetch_boundary_condition_hashes (ltreeid); + t8_boundary_conditions boundary_conditions; + for (const auto &hash : hashes) { + boundary_conditions.emplace_back (get_boundary_condition_name (hash)); + } + return boundary_conditions; + } + + inline std::string_view + get_boundary_condition (t8_locidx_t ltreeid, int face) const + { + T8_ASSERT (face >= 0); + T8_ASSERT (face < T8_ECLASS_MAX_FACES); + T8_ASSERT (t8_cmesh_is_committed (m_cmesh, 0)); + const std::span hashes = fetch_boundary_condition_hashes (ltreeid); + return get_boundary_condition_name (hashes[face]); + } + + inline t8_boundary_conditions> + get_boundary_conditions (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element) const + { + T8_ASSERT (t8_cmesh_is_committed (t8_forest_get_cmesh (forest))); + /* The forest should be associated with the same cmesh this handler is associated with. */ + T8_ASSERTF (m_cmesh == t8_forest_get_cmesh (forest), + "Called get_boundary_conditions on a forest with a different cmesh.\n"); + const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, ltreeid); + const t8_scheme *scheme = t8_forest_get_scheme (forest); + const int num_faces = scheme->element_get_num_faces (tree_class, element); + t8_boundary_conditions> boundary_conditions; + for (int iface = 0; iface < num_faces; ++iface) { + boundary_conditions.emplace_back (get_boundary_condition (forest, ltreeid, element, iface)); + } + return boundary_conditions; + } + + inline std::optional + get_boundary_condition (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face) const + { + T8_ASSERT (face >= 0); + T8_ASSERT (face < T8_ECLASS_MAX_FACES); + const t8_scheme *scheme = t8_forest_get_scheme (forest); + const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, ltreeid); + if (scheme->element_is_root_boundary (tree_class, element, face)) { + /* We retrieve the face it lies on */ + const int tree_face = scheme->element_get_tree_face (tree_class, element, face); + const t8_locidx_t cmesh_ltreeid = t8_forest_ltreeid_to_cmesh_ltreeid (forest, ltreeid); + return get_boundary_condition (cmesh_ltreeid, tree_face); + } + return std::nullopt; + } + +#if T8_ENABLE_DEBUG + /** Verifies the proper attribution of boundary conditions. Can only be called on a cmesh + * during commit. + * \return 1 if boundary conditions are valid, 0 otherwise. + */ + int + verify () const; + +#endif /* T8_ENABLE_DEBUG */ + + private: + /** + * Gets the attribute key for boundary conditions. + * \return The attribute key for boundary conditions. + */ + int + get_boundary_condition_attribute_key () const; + + inline std::span + fetch_boundary_condition_hashes (t8_locidx_t ltreeid) const + { + const void *hashes + = t8_cmesh_get_attribute (m_cmesh, t8_get_package_id (), get_boundary_condition_attribute_key (), ltreeid); + const t8_eclass eclass = t8_cmesh_get_tree_class (m_cmesh, ltreeid); + const int num_faces = t8_eclass_num_faces[eclass]; + return { static_cast (hashes), static_cast (num_faces) }; + } + + inline boundary_condition_hash + hash_boundary_condition_name (const std::string &boundary_condition_name) const + { + return boundary_condition_hash (std::hash {}(boundary_condition_name)); + } + + inline std::optional + get_boundary_condition_name_safe (boundary_condition_hash hash) const + { + auto position = m_boundary_conditions.find (hash); + if (position == m_boundary_conditions.end ()) { + return std::nullopt; + } + return position->second; + } + + inline std::string_view + get_boundary_condition_name (boundary_condition_hash hash) const + { + return m_boundary_conditions.at (hash); + } + + /** The associated cmesh of this struct */ + t8_cmesh_t m_cmesh; + + /** Map for storing boundary_condition_hash -> boundary condition name */ + std::map m_boundary_conditions; +}; + +} /* namespace detail */ diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.cxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.cxx new file mode 100644 index 0000000000..6f9230dc29 --- /dev/null +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.cxx @@ -0,0 +1,48 @@ +/* + 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 t8_cmesh_boundary_condition_handler_type.cxx + * Implements functionality for working with private headers and c types. + */ + +#include +#include +#include + +T8_EXTERN_C_BEGIN (); + +t8_cmesh_boundary_condition_handler_c * +t8_cmesh_get_boundary_condition_handler (t8_cmesh_t cmesh) +{ + return cmesh->boundary_condition_handler; +} + +t8_cmesh_boundary_condition_handler_c * +t8_cmesh_add_boundary_condition_handler (t8_cmesh_t cmesh) +{ + T8_ASSERT (cmesh->boundary_condition_handler == nullptr); + cmesh->boundary_condition_handler = new detail::t8_cmesh_boundary_condition_handler (cmesh); + return cmesh->boundary_condition_handler; +} + +T8_EXTERN_C_END (); diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.h b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.h new file mode 100644 index 0000000000..7c6fdf6b32 --- /dev/null +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.h @@ -0,0 +1,74 @@ +/* + 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 t8_cmesh_boundary_condition_handler_type.h + * Implements functionality for working with private headers and c types. + */ + +#pragma once + +#include + +#ifdef __cplusplus +#include + +/** This typedef is used for the opaque pointers to the handler. + * We need it so that we can use t8_cmesh_boundary_condition_handler_c pointers in .c files + * without them seeing the actual C++ code (and then not compiling). + * We have one cpp version with the correct namespace and one c version pointing to nothing. + * TODO: Delete this when the cmesh is a proper cpp class. + */ +typedef struct detail::t8_cmesh_boundary_condition_handler t8_cmesh_boundary_condition_handler_c; + +#else + +/** This typedef is used for the opaque pointers to the handler. + * We need it so that we can use t8_cmesh_boundary_condition_handler_c pointers in .c files + * without them seeing the actual C++ code (and then not compiling). + * We have one cpp version with the correct namespace and one c version pointing to nothing. + * TODO: Delete this when the cmesh is a proper cpp class. + */ +typedef struct t8_cmesh_boundary_condition_handler t8_cmesh_boundary_condition_handler_c; + +#endif + +T8_EXTERN_C_BEGIN (); + +/** + * Returns the boundary condition handler of the cmesh. + * This is needed because we implement templated functions which need to access the handler, but the cmesh type is not installed. + * \param [in] cmesh The cmesh + * \return A pointer to the boundary condition handler. nullptr if none was set. + */ +t8_cmesh_boundary_condition_handler_c * +t8_cmesh_get_boundary_condition_handler (t8_cmesh_t cmesh); + +/** + * Adds a boundary condition handler to a cmesh. The cmesh cannot have a handler yet. + * \param [in,out] cmesh The cmesh + * \return A pointer to the newly created handler inside the cmesh. + */ +t8_cmesh_boundary_condition_handler_c * +t8_cmesh_add_boundary_condition_handler (t8_cmesh_t cmesh); + +T8_EXTERN_C_END (); diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.cxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.cxx new file mode 100644 index 0000000000..33a645f966 --- /dev/null +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.cxx @@ -0,0 +1,133 @@ +/* + 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 t8_cmesh_boundary_conditions.cxx + * Implementation context for \ref t8_cmesh_boundary_conditions.hxx and \ref t8_cmesh_boundary_conditions_c_interface.h. + */ + +#include "t8_cmesh_boundary_conditions.hxx" +#include "t8_cmesh_boundary_conditions_c_interface.h" + +#include + +/**************************************** FUNCTION DEFINITIONS ****************************************/ + +t8_boundary_conditions +t8_cmesh_get_boundary_conditions (t8_cmesh_t cmesh, t8_locidx_t ltreeid) +{ + const detail::t8_cmesh_boundary_condition_handler *handler = t8_cmesh_get_boundary_condition_handler (cmesh); + SC_CHECK_ABORTF (handler != NULL, "ERROR: Trying to retrieve boundary conditions, even though none were set.\n"); + return handler->get_boundary_conditions (ltreeid); +}; + +std::string_view +t8_cmesh_get_boundary_condition (t8_cmesh_t cmesh, t8_locidx_t ltreeid, int face) +{ + const detail::t8_cmesh_boundary_condition_handler *handler = t8_cmesh_get_boundary_condition_handler (cmesh); + SC_CHECK_ABORTF (handler != NULL, "ERROR: Trying to retrieve boundary conditions, even though none were set.\n"); + return handler->get_boundary_condition (ltreeid, face); +}; + +t8_boundary_conditions> +t8_forest_get_boundary_conditions (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element) +{ + T8_ASSERT (t8_forest_is_committed (forest)); + const t8_cmesh_t cmesh = t8_forest_get_cmesh (forest); + const detail::t8_cmesh_boundary_condition_handler *handler = t8_cmesh_get_boundary_condition_handler (cmesh); + SC_CHECK_ABORTF (handler != NULL, "ERROR: Trying to retrieve boundary conditions, even though none were set.\n"); + return handler->get_boundary_conditions (forest, ltreeid, element); +}; + +std::optional +t8_forest_get_boundary_condition (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face) +{ + T8_ASSERT (t8_forest_is_committed (forest)); + const t8_cmesh_t cmesh = t8_forest_get_cmesh (forest); + const detail::t8_cmesh_boundary_condition_handler *handler = t8_cmesh_get_boundary_condition_handler (cmesh); + SC_CHECK_ABORTF (handler != NULL, "ERROR: Trying to retrieve boundary conditions, even though none were set.\n"); + return handler->get_boundary_condition (forest, ltreeid, element, face); +}; + +/**************************************** C INTERFACE ****************************************/ + +T8_EXTERN_C_BEGIN (); + +void +t8_cmesh_set_boundary_conditions (t8_cmesh_t cmesh, t8_gloidx_t gtreeid, const char *boundary_conditions[], + size_t length) +{ + T8_ASSERT (boundary_conditions != nullptr); + T8_ASSERT (length > 0); + T8_ASSERT (length <= T8_ECLASS_MAX_FACES); + const auto boundary_conditions_cpp = std::span { boundary_conditions, length }; + t8_cmesh_set_boundary_conditions (cmesh, gtreeid, boundary_conditions_cpp); +} + +void +t8_cmesh_get_boundary_conditions (t8_cmesh_t cmesh, t8_locidx_t ltreeid, + const char *boundary_conditions[T8_ECLASS_MAX_FACES], size_t *length) +{ + const auto boundary_conditions_cpp = t8_cmesh_get_boundary_conditions (cmesh, ltreeid); + *length = boundary_conditions_cpp.size (); + for (size_t i_condition = 0; i_condition < *length; ++i_condition) { + boundary_conditions[i_condition] = boundary_conditions_cpp[i_condition].data (); + } +}; + +void +t8_cmesh_get_boundary_condition (t8_cmesh_t cmesh, t8_locidx_t ltreeid, int face, + [[maybe_unused]] const char *boundary_condition) +{ + boundary_condition = t8_cmesh_get_boundary_condition (cmesh, ltreeid, face).data (); +}; + +void +t8_forest_get_boundary_conditions (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, + const char *boundary_conditions[T8_ECLASS_MAX_FACES], size_t *length) +{ + const auto boundary_conditions_cpp = t8_forest_get_boundary_conditions (forest, ltreeid, element); + *length = boundary_conditions_cpp.size (); + for (size_t i_condition = 0; i_condition < *length; ++i_condition) { + if (boundary_conditions_cpp[i_condition].has_value ()) { + boundary_conditions[i_condition] = boundary_conditions_cpp[i_condition]->data (); + } + else { + boundary_conditions[i_condition] = nullptr; + } + } +}; + +void +t8_forest_get_boundary_condition (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, + [[maybe_unused]] const char *boundary_condition) +{ + const auto boundary_condition_cpp = t8_forest_get_boundary_condition (forest, ltreeid, element, face); + if (boundary_condition_cpp.has_value ()) { + boundary_condition = boundary_condition_cpp->data (); + } + else { + boundary_condition = nullptr; + } +}; + +T8_EXTERN_C_END (); diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.hxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.hxx new file mode 100644 index 0000000000..f4efb2cd9f --- /dev/null +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.hxx @@ -0,0 +1,106 @@ +/* + 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 t8_cmesh_boundary_conditions.h + * Public interface for the definition and retrieval of boundary conditions. + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +/** + * Applies boundary conditions to the faces of a cmesh cell. + * + * \tparam TStringRange An iterable container filled with string like values. + * \param [in] cmesh The cmesh. + * \param [in] gtreeid The global id of the tree the boundary conditions should be set for. + * \param [in] boundary_conditions The boundary conditions to set. Container must have the same length + * as the eclass of the cell has faces. + */ +template + requires std::convertible_to, std::string_view> +void +t8_cmesh_set_boundary_conditions (t8_cmesh_t cmesh, t8_gloidx_t gtreeid, TStringRange boundary_conditions) +{ + detail::t8_cmesh_boundary_condition_handler *handler = t8_cmesh_get_boundary_condition_handler (cmesh); + if (handler == nullptr) { + handler = t8_cmesh_add_boundary_condition_handler (cmesh); + } + handler->add_boundary_conditions (gtreeid, boundary_conditions); +} + +/** + * Retrieves the boundary conditions of a cmesh cell. + * + * \param [in] cmesh The cmesh the cell lives in. + * \param [in] ltreeid The local cmesh id of the cell. + * \note The cmesh local cell id is a different one as the tree id inside the forest. + * \return A container with the boundary conditions. + */ +t8_boundary_conditions +t8_cmesh_get_boundary_conditions (t8_cmesh_t cmesh, t8_locidx_t ltreeid); + +/** + * Retrieves the boundary condition of one face of a cmesh cell. + * Retrieving all boundary conditions at once via \ref t8_cmesh_get_boundary_conditions will be faster. + * + * \param [in] cmesh The cmesh the cell lives in. + * \param [in] ltreeid The local cmesh id of the cell. + * \param [in] face The face id of the cell. + * \note The cmesh local cell id is a different one as the tree id inside the forest. + * \return The boundary condition of the tree face. + */ +std::string_view +t8_cmesh_get_boundary_condition (t8_cmesh_t cmesh, t8_locidx_t ltreeid, int face); + +/** + * Retrieves the boundary conditions of a forest element. + * + * \param [in] forest The forest the element lives in. + * \param [in] ltreeid The local id of the forest tree. + * \param [in] element The element. + * \return A container with the boundary conditions. Note, that only elements faces at the boundary of a + * tree will have boundary conditions. Internal faces will return an empty optional. + */ +t8_boundary_conditions> +t8_forest_get_boundary_conditions (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element); + +/** + * Retrieves the boundary condition of a face of a forest element. + * Retrieving all boundary conditions at once via \ref t8_forest_get_boundary_conditions will be faster. + * + * \param [in] forest The forest the element lives in. + * \param [in] ltreeid The local id of the forest tree. + * \param [in] element The element. + * \param [in] face The face id of the element. + * \return The boundary condition. It will be empty if the element is not touching the boundary of the tree. + */ +std::optional +t8_forest_get_boundary_condition (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face); diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h new file mode 100644 index 0000000000..e3e18fbd29 --- /dev/null +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h @@ -0,0 +1,102 @@ +/* + 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 t8_cmesh_boundary_conditions.h + * Public interface for the definition and retrieval of boundary conditions. + */ + +#pragma once + +T8_EXTERN_C_BEGIN (); + +/** + * Applies boundary conditions to the faces of a cmesh cell. + * + * \param [in] cmesh The cmesh. + * \param [in] gtreeid The global id of the tree the boundary conditions should be set for. + * \param [in] boundary_conditions The boundary conditions to set. + * \param [in] length The length of \a boundary_conditions. Container must have the same length + * as the eclass of the cell has faces. + */ +void +t8_cmesh_set_boundary_conditions (t8_cmesh_t cmesh, t8_gloidx_t gtreeid, const char *boundary_conditions[], + size_t length); + +/** + * Retrieves the boundary conditions of a cmesh cell. + * + * \param [in] cmesh The cmesh the cell lives in. + * \param [in] ltreeid The local cmesh id of the cell. + * \param [out] boundary_conditions The boundary conditions of the faces. + * \param [out] length The length of \a boundary_conditions. + * \note The cmesh local cell id is a different one as the tree id inside the forest. + */ +void +t8_cmesh_get_boundary_conditions (t8_cmesh_t cmesh, t8_locidx_t ltreeid, + const char *boundary_conditions[T8_ECLASS_MAX_FACES], size_t *length); + +/** + * Retrieves the boundary condition of one face of a cmesh cell. + * Retrieving all boundary conditions at once via \ref t8_cmesh_get_boundary_conditions will be faster. + * + * \param [in] cmesh The cmesh the cell lives in. + * \param [in] ltreeid The local cmesh id of the cell. + * \param [in] face The face id of the cell. + * \param [out] boundary_condition The boundary condition of the tree face. + * \note The cmesh local cell id is a different one as the tree id inside the forest. + */ +void +t8_cmesh_get_boundary_condition (t8_cmesh_t cmesh, t8_locidx_t ltreeid, int face, const char *boundary_condition); + +/** + * Retrieves the boundary conditions of a forest element. + * + * \param [in] forest The forest the element lives in. + * \param [in] ltreeid The local id of the forest tree. + * \param [in] element The element. + * \param [out] boundary_conditions The boundary conditions of the element. String will be nullptr if the elements + * face is internal; if it does not touch the trees face, since only the tree faces carry boundary + * conditions. All inner element faces have neighbors anyways. + * \param [out] length The length of \a boundary_conditions. + */ +void +t8_forest_get_boundary_conditions (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, + const char *boundary_conditions[T8_ECLASS_MAX_FACES], size_t *length); + +/** + * Retrieves the boundary condition of a face of a forest element. + * Retrieving all boundary conditions at once via \ref t8_forest_get_boundary_conditions will be faster. + * + * \param [in] forest The forest the element lives in. + * \param [in] ltreeid The local id of the forest tree. + * \param [in] element The element. + * \param [in] face The face id of the element. + * \param [out] boundary_conditions The boundary condition of the element. Will be nullptr if the elements + * face is internal; if it does not touch the trees face, since only the tree faces carry boundary + * conditions. All inner element faces have neighbors anyways. + */ +void +t8_forest_get_boundary_condition (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, + const char *boundary_condition); + +T8_EXTERN_C_END (); diff --git a/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.c b/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.cxx similarity index 94% rename from src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.c rename to src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.cxx index ca7571dd68..51a0f3aa92 100644 --- a/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.c +++ b/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.cxx @@ -20,7 +20,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/** \file t8_cmesh_stash.c +/** \file t8_cmesh_stash.cxx * We define the data structures and routines for temporary storage before commit */ @@ -28,6 +28,8 @@ #include #include +T8_EXTERN_C_BEGIN (); + void t8_stash_init (t8_stash_t *pstash) { @@ -339,3 +341,21 @@ t8_stash_is_equal (const t8_stash_t stash_a, const t8_stash_t stash_b) && sc_array_is_equal (&stash_a->classes, &stash_b->classes) && sc_array_is_equal (&stash_a->joinfaces, &stash_b->joinfaces)); } + +T8_EXTERN_C_END (); + +std::vector> +t8_stash_extract_eclasses (const t8_stash_t &stash) +{ + const t8_gloidx_t ntrees = stash->classes.elem_count; + std::vector> eclasses; + eclasses.reserve (ntrees); + + for (t8_gloidx_t itree = 0; itree < ntrees; itree++) { + const t8_stash_class_struct_t *entry + = (const t8_stash_class_struct_t *) t8_sc_array_index_locidx (&stash->classes, itree); + eclasses.emplace_back (entry->id, entry->eclass); + } + + return eclasses; +} diff --git a/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.h b/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.h index 270a29d5ad..3f68cb5ce7 100644 --- a/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.h +++ b/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.h @@ -250,4 +250,48 @@ t8_stash_is_equal (t8_stash_t stash_a, t8_stash_t stash_b); T8_EXTERN_C_END (); +#ifdef __cplusplus + +#include +#include + +/** Reads the eclasses from a stash and returns them along with their global tree id. + * \param [in] stash A stash. + * \return Vector of pairs with [global tree id, eclass]. + */ +std::vector> +t8_stash_extract_eclasses (const t8_stash_t &stash); + +/** Extracts all attributes belonging to an attribute key and package id. + * The attributes are casted into a vector of views of the template parameter \p TType. \p TType is also used + * to determine the size of the elements. If a tree is not listed, no attributes with this package id and key were assigned. + * \param [in] stash A stash. + * \return A vector of pairs with the global tree id and a list of the attributes. + */ +template +std::vector>> +t8_stash_extract_attribute_list (const t8_stash_t &stash, const int package_id, const int key) +{ + /* Reserve memory */ + const t8_gloidx_t ntrees = stash->classes.elem_count; + std::vector>> attributes; + attributes.reserve (ntrees); + + /* Iterate over all attributes and filter by package id and attribute key */ + for (size_t iattribute = 0; iattribute < stash->attributes.elem_count; iattribute++) { + const t8_stash_attribute_struct_t *entry + = (const t8_stash_attribute_struct_t *) t8_sc_array_index_locidx (&stash->attributes, iattribute); + if (entry->key == key && entry->package_id == package_id) { + /* Make sure that the element size matches and construct view. */ + T8_ASSERT (entry->attr_size % sizeof (TType) == 0); + const size_t num_elements = entry->attr_size / sizeof (TType); + attributes.emplace_back (entry->id, std::span (static_cast (entry->attr_data), num_elements)); + } + } + attributes.shrink_to_fit (); + return attributes; +} + +#endif /* __cplusplus */ + #endif /* !T8_CMESH_STASH_H */ diff --git a/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_types.h b/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_types.h index b7e47b1c41..d9cee777ec 100644 --- a/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_types.h +++ b/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_types.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -58,10 +59,11 @@ typedef struct t8_cprofile t8_cprofile_t; /* Defined below */ #define T8_CMESH_VERTICES_ATTRIBUTE_KEY 0 /**< Used to store vertex coordinates. */ #define T8_CMESH_GLOBAL_VERTICES_ATTRIBUTE_KEY 1 /**< Used to store global vertex ids. */ #define T8_CMESH_GEOMETRY_ATTRIBUTE_KEY 2 /**< Used to store the name of a tree's geometry. */ -#define T8_CMESH_NODE_GEOMETRY_ATTRIBUTE_KEY 3 /**< Used to store the geometry dimension and tag of the nodes of a tree. */ -#define T8_CMESH_NODE_PARAMETERS_ATTRIBUTE_KEY 4 /**< Used to store node parameters of a tree. Used in combination with T8_CMESH_NODE_GEOMETRY_ATTRIBUTE_KEY */ -#define T8_CMESH_CAD_EDGE_ATTRIBUTE_KEY 5 /**< Used to store which edge is linked to which geometry */ -#define T8_CMESH_CAD_EDGE_PARAMETERS_ATTRIBUTE_KEY 6 /**< Used to store edge parameters */ +#define T8_CMESH_BOUNDARY_CONDITION_ATTRIBUTE_KEY 3 /**< Used to store the boundary condition of each tree's face. */ +#define T8_CMESH_NODE_GEOMETRY_ATTRIBUTE_KEY 4 /**< Used to store the geometry dimension and tag of the nodes of a tree. */ +#define T8_CMESH_NODE_PARAMETERS_ATTRIBUTE_KEY 5 /**< Used to store node parameters of a tree. Used in combination with T8_CMESH_NODE_GEOMETRY_ATTRIBUTE_KEY */ +#define T8_CMESH_CAD_EDGE_ATTRIBUTE_KEY 6 /**< Used to store which edge is linked to which geometry */ +#define T8_CMESH_CAD_EDGE_PARAMETERS_ATTRIBUTE_KEY 7 /**< Used to store edge parameters */ #define T8_CMESH_CAD_FACE_ATTRIBUTE_KEY T8_CMESH_CAD_EDGE_PARAMETERS_ATTRIBUTE_KEY + T8_ECLASS_MAX_EDGES /**< Used to store which face is linked to which surface */ #define T8_CMESH_CAD_FACE_PARAMETERS_ATTRIBUTE_KEY T8_CMESH_CAD_FACE_ATTRIBUTE_KEY + 1 /**< Used to store face parameters */ #define T8_CMESH_LAGRANGE_POLY_DEGREE_KEY T8_CMESH_CAD_FACE_PARAMETERS_ATTRIBUTE_KEY + T8_ECLASS_MAX_FACES /**< Used to store parameters of lagrangian polynomials */ @@ -140,6 +142,9 @@ typedef struct t8_cmesh t8_geometry_handler_c *geometry_handler; /**< Handles all geometries that are used by trees in this cmesh. */ + t8_cmesh_boundary_condition_handler_c + *boundary_condition_handler; /**< Handles all boundary conditions that are used by trees in this cmesh. */ + struct t8_cmesh_vertex_connectivity *vertex_connectivity; /**< Structure that manages tree_to_vertex and vertex_to_tree connectivity. */ From 30a0e5279aec5e417bbbab1aa239f0e4ce3d6b40 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 19 Aug 2026 17:23:13 +0200 Subject: [PATCH 04/14] implement tests for the boundary condition handler --- src/t8_cmesh/t8_cmesh_examples.cxx | 93 +++++++- src/t8_cmesh/t8_cmesh_examples.h | 2 + test/CMakeLists.txt | 1 + .../t8_gtest_cmesh_boundary_conditions.cxx | 210 ++++++++++++++++++ 4 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 test/t8_cmesh/t8_gtest_cmesh_boundary_conditions.cxx diff --git a/src/t8_cmesh/t8_cmesh_examples.cxx b/src/t8_cmesh/t8_cmesh_examples.cxx index afe23d4d55..80bb6e7da8 100644 --- a/src/t8_cmesh/t8_cmesh_examples.cxx +++ b/src/t8_cmesh/t8_cmesh_examples.cxx @@ -35,7 +35,8 @@ #include #include #include -#include /* default refinement scheme. */ +#include +#include /** * This function calculates an 'equal' partition for the cmesh based on the \a number_trees supplied @@ -510,15 +511,22 @@ t8_cmesh_new_hypercube_hybrid (t8_cmesh_t cmesh, sc_MPI_Comm comm, int periodic) }; /* clang-format on */ + std::string internal = "internal"; + std::string boundary = "boundary"; + std::array, 16> boundary_conditions; + /* This cmesh consists of 6 tets, 6 prisms and 3 hexes */ for (i = 0; i < 6; i++) { t8_cmesh_set_tree_class (cmesh, i, T8_ECLASS_TET); + boundary_conditions[i].assign (t8_eclass_num_faces[T8_ECLASS_TET], boundary); } for (i = 6; i < 12; i++) { t8_cmesh_set_tree_class (cmesh, i, T8_ECLASS_PRISM); + boundary_conditions[i].assign (t8_eclass_num_faces[T8_ECLASS_PRISM], boundary); } for (i = 12; i < 16; i++) { t8_cmesh_set_tree_class (cmesh, i, T8_ECLASS_HEX); + boundary_conditions[i].assign (t8_eclass_num_faces[T8_ECLASS_HEX], boundary); } /* We use standard linear geometry */ @@ -531,11 +539,24 @@ t8_cmesh_new_hypercube_hybrid (t8_cmesh_t cmesh, sc_MPI_Comm comm, int periodic) * They are essentially the tetrahedral hypercube scaled by 0.5 */ t8_cmesh_coords_axb (vertices_coords, vertices_coords_temp, 8, 0.5, null_vec); t8_cmesh_set_join (cmesh, 0, 1, 2, 1, 0); + boundary_conditions[0][2] = internal; + boundary_conditions[1][1] = internal; t8_cmesh_set_join (cmesh, 1, 2, 2, 1, 0); + boundary_conditions[1][2] = internal; + boundary_conditions[2][1] = internal; t8_cmesh_set_join (cmesh, 2, 3, 2, 1, 0); + boundary_conditions[2][2] = internal; + boundary_conditions[3][1] = internal; t8_cmesh_set_join (cmesh, 3, 4, 2, 1, 0); + boundary_conditions[3][2] = internal; + boundary_conditions[4][1] = internal; t8_cmesh_set_join (cmesh, 4, 5, 2, 1, 0); + boundary_conditions[4][2] = internal; + boundary_conditions[5][1] = internal; t8_cmesh_set_join (cmesh, 5, 0, 2, 1, 0); + boundary_conditions[5][2] = internal; + boundary_conditions[0][1] = internal; + vertices[0] = 0; vertices[1] = 1; vertices[2] = 5; @@ -587,6 +608,8 @@ t8_cmesh_new_hypercube_hybrid (t8_cmesh_t cmesh, sc_MPI_Comm comm, int periodic) t8_cmesh_set_tree_vertices (cmesh, 7, attr_vertices, 6); t8_cmesh_set_join (cmesh, 6, 7, 2, 1, 0); + boundary_conditions[6][2] = internal; + boundary_conditions[7][1] = internal; /* trees 8 and 9 */ t8_cmesh_coords_axb (vertices_coords, vertices_coords_temp, 8, 0.5, shift[1]); vertices[0] = 0; @@ -604,6 +627,8 @@ t8_cmesh_new_hypercube_hybrid (t8_cmesh_t cmesh, sc_MPI_Comm comm, int periodic) t8_cmesh_new_translate_vertices_to_attributes (vertices, vertices_coords_temp, attr_vertices, 6); t8_cmesh_set_tree_vertices (cmesh, 9, attr_vertices, 6); t8_cmesh_set_join (cmesh, 8, 9, 2, 1, 0); + boundary_conditions[8][2] = internal; + boundary_conditions[9][1] = internal; /* trees 10 an 11 */ t8_cmesh_coords_axb (vertices_coords, vertices_coords_temp, 8, 0.5, shift[2]); vertices[0] = 0; @@ -621,14 +646,28 @@ t8_cmesh_new_hypercube_hybrid (t8_cmesh_t cmesh, sc_MPI_Comm comm, int periodic) t8_cmesh_new_translate_vertices_to_attributes (vertices, vertices_coords_temp, attr_vertices, 6); t8_cmesh_set_tree_vertices (cmesh, 11, attr_vertices, 6); t8_cmesh_set_join (cmesh, 10, 11, 1, 2, 0); + boundary_conditions[10][1] = internal; + boundary_conditions[11][2] = internal; /* Connect prisms and tets */ t8_cmesh_set_join (cmesh, 0, 6, 0, 3, 0); + boundary_conditions[0][0] = internal; + boundary_conditions[6][3] = internal; t8_cmesh_set_join (cmesh, 1, 7, 0, 3, 1); + boundary_conditions[1][0] = internal; + boundary_conditions[7][3] = internal; t8_cmesh_set_join (cmesh, 2, 8, 0, 3, 0); + boundary_conditions[2][0] = internal; + boundary_conditions[8][3] = internal; t8_cmesh_set_join (cmesh, 3, 9, 0, 3, 1); + boundary_conditions[3][0] = internal; + boundary_conditions[9][3] = internal; t8_cmesh_set_join (cmesh, 4, 11, 0, 3, 0); + boundary_conditions[4][0] = internal; + boundary_conditions[11][3] = internal; t8_cmesh_set_join (cmesh, 5, 10, 0, 3, 1); + boundary_conditions[5][0] = internal; + boundary_conditions[10][3] = internal; /************************************/ /* The hexahedra */ @@ -645,37 +684,89 @@ t8_cmesh_new_hypercube_hybrid (t8_cmesh_t cmesh, sc_MPI_Comm comm, int periodic) } /* Join the hexes */ t8_cmesh_set_join (cmesh, 12, 14, 5, 4, 0); + boundary_conditions[12][5] = internal; + boundary_conditions[14][4] = internal; t8_cmesh_set_join (cmesh, 13, 14, 3, 2, 0); + boundary_conditions[13][3] = internal; + boundary_conditions[14][2] = internal; t8_cmesh_set_join (cmesh, 14, 15, 0, 1, 0); + boundary_conditions[14][0] = internal; + boundary_conditions[15][1] = internal; /* Join the prisms and hexes */ t8_cmesh_set_join (cmesh, 6, 13, 0, 4, 1); + boundary_conditions[6][0] = internal; + boundary_conditions[13][4] = internal; t8_cmesh_set_join (cmesh, 7, 12, 0, 2, 0); + boundary_conditions[7][0] = internal; + boundary_conditions[12][2] = internal; t8_cmesh_set_join (cmesh, 8, 12, 0, 0, 1); + boundary_conditions[8][0] = internal; + boundary_conditions[12][0] = internal; t8_cmesh_set_join (cmesh, 9, 15, 0, 4, 0); + boundary_conditions[9][0] = internal; + boundary_conditions[15][4] = internal; t8_cmesh_set_join (cmesh, 10, 13, 0, 0, 0); + boundary_conditions[10][0] = internal; + boundary_conditions[13][0] = internal; t8_cmesh_set_join (cmesh, 11, 15, 0, 2, 1); + boundary_conditions[11][0] = internal; + boundary_conditions[15][2] = internal; if (periodic) { /* Connect the sides of the cube to make it periodic */ /* tets to prisms */ t8_cmesh_set_join (cmesh, 0, 8, 3, 4, 0); + boundary_conditions[0][3] = internal; + boundary_conditions[8][4] = internal; t8_cmesh_set_join (cmesh, 5, 9, 3, 4, 0); + boundary_conditions[5][3] = internal; + boundary_conditions[9][4] = internal; t8_cmesh_set_join (cmesh, 3, 7, 3, 4, 0); + boundary_conditions[3][3] = internal; + boundary_conditions[7][4] = internal; t8_cmesh_set_join (cmesh, 4, 6, 3, 4, 0); + boundary_conditions[4][3] = internal; + boundary_conditions[6][4] = internal; t8_cmesh_set_join (cmesh, 1, 10, 3, 4, 0); + boundary_conditions[1][3] = internal; + boundary_conditions[10][4] = internal; t8_cmesh_set_join (cmesh, 2, 11, 3, 4, 0); + boundary_conditions[2][3] = internal; + boundary_conditions[11][4] = internal; /* prism to hex */ t8_cmesh_set_join (cmesh, 6, 12, 1, 3, 0); + boundary_conditions[6][1] = internal; + boundary_conditions[12][3] = internal; t8_cmesh_set_join (cmesh, 9, 12, 2, 1, 0); + boundary_conditions[9][2] = internal; + boundary_conditions[12][1] = internal; t8_cmesh_set_join (cmesh, 7, 13, 2, 5, 0); + boundary_conditions[7][2] = internal; + boundary_conditions[13][5] = internal; t8_cmesh_set_join (cmesh, 11, 13, 1, 1, 0); + boundary_conditions[11][1] = internal; + boundary_conditions[13][1] = internal; t8_cmesh_set_join (cmesh, 8, 15, 1, 5, 0); + boundary_conditions[8][1] = internal; + boundary_conditions[15][5] = internal; t8_cmesh_set_join (cmesh, 10, 15, 2, 3, 0); + boundary_conditions[10][2] = internal; + boundary_conditions[15][3] = internal; /* hex to hex */ t8_cmesh_set_join (cmesh, 12, 14, 4, 5, 0); + boundary_conditions[12][4] = internal; + boundary_conditions[14][5] = internal; t8_cmesh_set_join (cmesh, 13, 14, 2, 3, 0); + boundary_conditions[13][2] = internal; + boundary_conditions[14][3] = internal; t8_cmesh_set_join (cmesh, 14, 15, 1, 0, 0); + boundary_conditions[14][1] = internal; + boundary_conditions[15][0] = internal; + } + + for (size_t itree = 0; itree < 16; ++itree) { + t8_cmesh_set_boundary_conditions (cmesh, itree, boundary_conditions[itree]); } t8_cmesh_commit (cmesh, comm); diff --git a/src/t8_cmesh/t8_cmesh_examples.h b/src/t8_cmesh/t8_cmesh_examples.h index fcfb38609a..19afde6040 100644 --- a/src/t8_cmesh/t8_cmesh_examples.h +++ b/src/t8_cmesh/t8_cmesh_examples.h @@ -205,6 +205,8 @@ t8_cmesh_new_hypercube_pad_ext (t8_cmesh_t cmesh, const t8_eclass_t eclass, sc_M const int use_axis_aligned, const int set_partition, t8_gloidx_t offset); /** Hybercube with 6 Tets, 6 Prism, 4 Hex. + * Also sets boundary conditions on internal tree faces to "internal" and boundary faces to "boundary". + * For periodic meshes all tree faces are "internal". * \param [in,out] cmesh An initialized, but not committed cmesh, as created by \ref t8_cmesh_init. * Filled and committed in place with 6 Tets, 6 prism and 4 hex, together * forming a cube. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ee0f214509..fdc8409356 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -139,6 +139,7 @@ add_t8_cpp_test( NAME t8_gtest_compute_first_element_serial SOUR add_t8_cpp_test( NAME t8_gtest_multiple_attributes_parallel SOURCES t8_cmesh/t8_gtest_multiple_attributes.cxx ) add_t8_cpp_test( NAME t8_gtest_attribute_gloidx_array_serial SOURCES t8_cmesh/t8_gtest_attribute_gloidx_array.cxx ) add_t8_cpp_test( NAME t8_gtest_cmesh_bounding_box_serial SOURCES t8_cmesh/t8_gtest_cmesh_bounding_box.cxx ) +add_t8_cpp_test( NAME t8_gtest_cmesh_boundary_conditions_serial SOURCES t8_cmesh/t8_gtest_cmesh_boundary_conditions.cxx ) add_t8_cpp_test( NAME t8_gtest_shmem_parallel SOURCES t8_data/t8_gtest_shmem.cxx ) add_t8_cpp_test( NAME t8_gtest_data_pack_parallel SOURCES t8_data/t8_gtest_data_handler.cxx t8_data/t8_data_handler_specs.cxx) diff --git a/test/t8_cmesh/t8_gtest_cmesh_boundary_conditions.cxx b/test/t8_cmesh/t8_gtest_cmesh_boundary_conditions.cxx new file mode 100644 index 0000000000..5325a50950 --- /dev/null +++ b/test/t8_cmesh/t8_gtest_cmesh_boundary_conditions.cxx @@ -0,0 +1,210 @@ +/* + 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** \file In this file we test the global cmesh vertex numbers. + * + * We build a test cmesh consisting of two coarse triangles joined together + * and associate global vertex numbers with the cmesh's vertices. + * This cmesh has 4 global vertices in total. + * + * We then perform three tests + * + * 1) check_tree_to_vertex + * Here we test the tree_to_vertex connectivity. + * That is, given a tree id, we get a list of the global vertices of that tree + * (in local vertex order) and check whether this list is correct. + * + * 2) check_vertex_to_tree + * Here we test the vertex_to_tree connectivity. + * Given a global vertex index, the vertex_to_tree connectivity returns a list + * of pairs (local tree_id, local_vertex_id) of all the local trees and their local + * vertices that are connected to the global vertex. + * We check whether this list is correct. + * + * 3) check_global_vertex_number + * We verify that the number of global vertices is 4. + * We additionally verify that the process local number of global vertices is 4 as well. + * This is true, since the cmesh is not partitioned. + * + * Additionally, t8_test_cmesh_vertex_conn_partitioned is the start of a test + * suite with partitioned cmesh that is currently disabled and could be enabled and extended + * when cmesh vertex connectivity supports partitioned cmeshes. + * Note that the test itself then has to be set to parallel in the CMake file. + */ + +/** + * Test fixture for the cmesh boundary condition module. It applies boundary conditions + * to single tree cmeshes in accordance to their face number. + */ +struct t8_cmesh_single_tree_bc: public testing::TestWithParam +{ + protected: + void + SetUp () override + { + eclass = GetParam (); + boundary_conditions = { "bc_0", "bc_1", "bc_2", "bc_3", "bc_4", "bc_5" }; + boundary_conditions.resize (static_cast (t8_eclass_num_faces[eclass])); + t8_cmesh_init (&cmesh); + t8_cmesh_set_tree_class (cmesh, 0, eclass); + t8_cmesh_set_boundary_conditions (cmesh, 0, boundary_conditions); + t8_cmesh_commit (cmesh, sc_MPI_COMM_WORLD); + } + + void + TearDown () override + { + t8_cmesh_unref (&cmesh); + } + + t8_boundary_conditions boundary_conditions; + t8_cmesh_t cmesh; + t8_eclass eclass; +}; + +TEST_P (t8_cmesh_single_tree_bc, test_single_tree_boundary_conditions) +{ + const auto retrieved_boundary_conditions = t8_cmesh_get_boundary_conditions (cmesh, 0); + for (size_t i_boundary_condition = 0; i_boundary_condition < boundary_conditions.size (); ++i_boundary_condition) { + EXPECT_EQ (boundary_conditions[i_boundary_condition], retrieved_boundary_conditions[i_boundary_condition]); + } +} + +TEST_P (t8_cmesh_single_tree_bc, test_single_tree_element_boundary_conditions) +{ + t8_cmesh_ref (cmesh); + t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_standalone (), 2, 0, sc_MPI_COMM_WORLD); + t8_locidx_t num_elements = t8_forest_get_local_num_leaf_elements (forest); + const t8_scheme *scheme = t8_forest_get_scheme (forest); + const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, 0); + + /* Some variables for t8_forest_leaf_face_neighbors */ + int *dual_faces; + int num_neighbors = 0; + t8_locidx_t *element_indices; + t8_eclass_t neigh_class; + + for (t8_locidx_t ielem = 0; ielem < num_elements; ++ielem) { + const t8_element_t *elem = t8_forest_get_leaf_element_in_tree (forest, 0, ielem); + const size_t num_faces = scheme->element_get_num_faces (tree_class, elem); + for (size_t iface = 0; iface < num_faces; ++iface) { + t8_forest_leaf_face_neighbors (forest, 0, elem, NULL, iface, &dual_faces, &num_neighbors, &element_indices, + &neigh_class); + T8_FREE (element_indices); + T8_FREE (dual_faces); + const auto boundary_condition = t8_forest_get_boundary_condition (forest, 0, elem, iface); + if (num_neighbors > 0) { + EXPECT_FALSE (boundary_condition.has_value ()); + } + else { + EXPECT_TRUE (boundary_condition.has_value ()); + } + } + } + + t8_forest_unref (&forest); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_cmesh_boundary_conditions, t8_cmesh_single_tree_bc, AllEclasses); + +TEST (t8_gtest_cmesh_boundary_conditions, test_hybrid_hypercube_boundary_conditions) +{ + t8_cmesh_t cmesh; + t8_cmesh_init (&cmesh); + t8_cmesh_new_hypercube_hybrid (cmesh, sc_MPI_COMM_WORLD, 0); + + /* Test the boundary conditions of the trees. All faces with neighbors should have the bc "internal". All other faces are "boundary". */ + const t8_locidx_t num_trees = t8_cmesh_get_num_local_trees (cmesh); + + /* Iterate over all trees. */ + for (t8_locidx_t itree = 0; itree < num_trees; ++itree) { + const t8_eclass_t tree_class = t8_cmesh_get_tree_class (cmesh, itree); + const int num_faces = t8_eclass_num_faces[tree_class]; + + /* Iterate over all faces of the tree. */ + for (int iface = 0; iface < num_faces; ++iface) { + /* Grab the neighbor eclass and the boundary condition. */ + const t8_eclass neigh_class = t8_cmesh_get_tree_face_neighbor_eclass (cmesh, itree, iface); + const auto boundary_condition = t8_cmesh_get_boundary_condition (cmesh, itree, iface); + /* If a face is internal, the boundary condition should be "internal". */ + if (neigh_class == T8_ECLASS_INVALID) { + EXPECT_EQ (boundary_condition, "boundary"); + } + else { + EXPECT_EQ (boundary_condition, "internal"); + } + } + } + + /* Do the same test with the forest interface. We set the refinement level to 0 so that there are no internal faces inside trees. + This way every element face will carry a boundary condition. Internal element faces are checked in another test (test_single_tree_element_boundary_conditions). */ + t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_standalone (), 0, 0, sc_MPI_COMM_WORLD); + const t8_scheme *scheme = t8_forest_get_scheme (forest); + + /* Some variables for t8_forest_leaf_face_neighbors */ + int *dual_faces; + int num_neighbors = 0; + t8_locidx_t *element_indices; + t8_eclass_t neigh_class; + + /* Iterate over all trees. */ + for (t8_locidx_t itree = 0; itree < num_trees; ++itree) { + const t8_eclass_t tree_class = t8_cmesh_get_tree_class (cmesh, itree); + + /* We will not iterate over the elements, because there is only one per tree. */ + const t8_element_t *elem = t8_forest_get_leaf_element_in_tree (forest, itree, 0); + const size_t num_faces = scheme->element_get_num_faces (tree_class, elem); + + /* Retrieve the boundary conditions. */ + const auto boundary_conditions = t8_forest_get_boundary_conditions (forest, itree, elem); + + for (size_t iface = 0; iface < num_faces; ++iface) { + /* Since we have a level 0 forest every face should have a boundary condition. */ + ASSERT_TRUE (boundary_conditions[iface].has_value ()); + + /* Find out if we have neighbors. */ + t8_forest_leaf_face_neighbors (forest, itree, elem, NULL, iface, &dual_faces, &num_neighbors, &element_indices, + &neigh_class); + T8_FREE (element_indices); + T8_FREE (dual_faces); + + /* If we have neighbors, the bc should be "internal". It should be "boundary" otherwise. */ + if (num_neighbors > 0) { + EXPECT_EQ (boundary_conditions[iface].value (), "internal"); + } + else { + EXPECT_EQ (boundary_conditions[iface].value (), "boundary"); + } + } + } + + t8_forest_unref (&forest); +} From 2895d80d3f2b78f4ea49ba573e28d771e4df71fb Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 19 Aug 2026 17:32:36 +0200 Subject: [PATCH 05/14] typo --- .../internal/t8_cmesh_boundary_condition_handler.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx index ea5cedc390..19f6361628 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx @@ -74,7 +74,7 @@ struct t8_cmesh_boundary_condition_handler public: /** - * Standard constructor. Assotiates the handler with a cmesh + * Standard constructor. Associates the handler with a cmesh * \param [in] cmesh */ t8_cmesh_boundary_condition_handler (t8_cmesh_t cmesh): m_cmesh (cmesh) From 1c1aece2355ad070b835907b292b2181159fdcac Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 19 Aug 2026 17:49:30 +0200 Subject: [PATCH 06/14] fix documentation for static vector --- src/t8_data/t8_static_vector.hxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/t8_data/t8_static_vector.hxx b/src/t8_data/t8_static_vector.hxx index 9de168139d..56f1895865 100644 --- a/src/t8_data/t8_static_vector.hxx +++ b/src/t8_data/t8_static_vector.hxx @@ -49,12 +49,11 @@ template class t8_static_vector { public: + /** The type of the stored values. */ using value_type = TType; /** * Creates an empty static vector. - * - * \return The constructed empty static vector. */ constexpr t8_static_vector () noexcept = default; From 1a5b5958ccadacf8c35a7f25663d8dd27a65e774 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 19 Aug 2026 17:49:40 +0200 Subject: [PATCH 07/14] documentation fixes --- .../t8_cmesh_boundary_condition_handler.cxx | 4 +- .../t8_cmesh_boundary_condition_handler.hxx | 67 +++++++++++++++++++ ...cmesh_boundary_condition_handler_types.cxx | 2 +- ...8_cmesh_boundary_condition_handler_types.h | 2 +- .../t8_cmesh_boundary_conditions.hxx | 4 +- ...t8_cmesh_boundary_conditions_c_interface.h | 6 +- 6 files changed, 76 insertions(+), 9 deletions(-) diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.cxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.cxx index 022a1f8185..20800df38e 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.cxx +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.cxx @@ -21,8 +21,8 @@ */ /** - * \file t8_cmesh_boundary_condition_manager.cxx - * Implementation context of \ref t8_cmesh_boundary_condition_manager.hxx + * \file t8_cmesh_boundary_condition_handler.cxx + * Implementation context of \ref t8_cmesh_boundary_condition_handler.hxx */ #include diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx index 19f6361628..2c652362ab 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx @@ -81,6 +81,14 @@ struct t8_cmesh_boundary_condition_handler { } + /** + * Applies boundary conditions to the faces of a cmesh cell. + * + * \tparam TStringRange An iterable container filled with string like values. + * \param [in] gtreeid The global id of the tree the boundary conditions should be set for. + * \param [in] boundary_conditions The boundary conditions to set. Container must have the same length + * as the eclass of the cell has faces. + */ template requires std::convertible_to, std::string_view> inline void @@ -97,6 +105,14 @@ struct t8_cmesh_boundary_condition_handler hashes.data (), sizeof (boundary_condition_hash) * hashes.size (), 0); } + /** + * Retrieves the boundary conditions of a cmesh cell. + * + * \param [in] cmesh The cmesh the cell lives in. + * \param [in] ltreeid The local cmesh id of the cell. + * \note The cmesh local cell id is a different one as the tree id inside the forest. + * \return A container with the boundary conditions. + */ inline t8_boundary_conditions get_boundary_conditions (t8_locidx_t ltreeid) const { @@ -109,6 +125,15 @@ struct t8_cmesh_boundary_condition_handler return boundary_conditions; } + /** + * Retrieves the boundary condition of one face of a cmesh cell. + * Retrieving all boundary conditions at once via \ref get_boundary_condition() will be faster. + * + * \param [in] ltreeid The local cmesh id of the cell. + * \param [in] face The face id of the cell. + * \note The cmesh local cell id is a different one as the tree id inside the forest. + * \return The boundary condition of the tree face. + */ inline std::string_view get_boundary_condition (t8_locidx_t ltreeid, int face) const { @@ -119,6 +144,15 @@ struct t8_cmesh_boundary_condition_handler return get_boundary_condition_name (hashes[face]); } + /** + * Retrieves the boundary conditions of a forest element. + * + * \param [in] forest The forest the element lives in. + * \param [in] ltreeid The local id of the forest tree. + * \param [in] element The element. + * \return A container with the boundary conditions. Note, that only elements faces at the boundary of a + * tree will have boundary conditions. Internal faces will return an empty optional. + */ inline t8_boundary_conditions> get_boundary_conditions (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element) const { @@ -136,6 +170,16 @@ struct t8_cmesh_boundary_condition_handler return boundary_conditions; } + /** + * Retrieves the boundary condition of a face of a forest element. + * Retrieving all boundary conditions at once via \ref get_boundary_conditions() will be faster. + * + * \param [in] forest The forest the element lives in. + * \param [in] ltreeid The local id of the forest tree. + * \param [in] element The element. + * \param [in] face The face id of the element. + * \return The boundary condition. It will be empty if the element is not touching the boundary of the tree. + */ inline std::optional get_boundary_condition (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face) const { @@ -170,6 +214,11 @@ struct t8_cmesh_boundary_condition_handler int get_boundary_condition_attribute_key () const; + /** + * Retrieves the boundary conditions hashes of a tree from the cmeshes attributes. + * \param [in] ltreeid The local tree id. + * \return The boundary condition hashes. + */ inline std::span fetch_boundary_condition_hashes (t8_locidx_t ltreeid) const { @@ -180,12 +229,23 @@ struct t8_cmesh_boundary_condition_handler return { static_cast (hashes), static_cast (num_faces) }; } + /** + * Hashes a boundary condition name. + * \param [in] boundary_condition_name The name. + * \return The hash of the name. + */ inline boundary_condition_hash hash_boundary_condition_name (const std::string &boundary_condition_name) const { return boundary_condition_hash (std::hash {}(boundary_condition_name)); } + /** + * Retrieves the boundary condition name to a hash. + * If the hash is not registered with a name, nullopt is returned. + * \param [in] hash The hash. + * \return The boundary condition name on success. nullopt otherwise. + */ inline std::optional get_boundary_condition_name_safe (boundary_condition_hash hash) const { @@ -196,6 +256,13 @@ struct t8_cmesh_boundary_condition_handler return position->second; } + /** + * Retrieves the boundary condition name to a hash. + * Crashes if the hash is not registered with a name. + * Faster than \ref get_boundary_condition_name_safe(). + * \param [in] hash The hash. + * \return The boundary condition name. + */ inline std::string_view get_boundary_condition_name (boundary_condition_hash hash) const { diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.cxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.cxx index 6f9230dc29..bd71b2af64 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.cxx +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.cxx @@ -21,7 +21,7 @@ */ /** - * \file t8_cmesh_boundary_condition_handler_type.cxx + * \file t8_cmesh_boundary_condition_handler_types.cxx * Implements functionality for working with private headers and c types. */ diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.h b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.h index 7c6fdf6b32..b38d903f68 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.h +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler_types.h @@ -21,7 +21,7 @@ */ /** - * \file t8_cmesh_boundary_condition_handler_type.h + * \file t8_cmesh_boundary_condition_handler_types.h * Implements functionality for working with private headers and c types. */ diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.hxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.hxx index f4efb2cd9f..0a37625987 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.hxx +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.hxx @@ -69,7 +69,7 @@ t8_cmesh_get_boundary_conditions (t8_cmesh_t cmesh, t8_locidx_t ltreeid); /** * Retrieves the boundary condition of one face of a cmesh cell. - * Retrieving all boundary conditions at once via \ref t8_cmesh_get_boundary_conditions will be faster. + * Retrieving all boundary conditions at once via \ref t8_cmesh_get_boundary_conditions() will be faster. * * \param [in] cmesh The cmesh the cell lives in. * \param [in] ltreeid The local cmesh id of the cell. @@ -94,7 +94,7 @@ t8_forest_get_boundary_conditions (t8_forest_t forest, t8_locidx_t ltreeid, cons /** * Retrieves the boundary condition of a face of a forest element. - * Retrieving all boundary conditions at once via \ref t8_forest_get_boundary_conditions will be faster. + * Retrieving all boundary conditions at once via \ref t8_forest_get_boundary_conditions() will be faster. * * \param [in] forest The forest the element lives in. * \param [in] ltreeid The local id of the forest tree. diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h index e3e18fbd29..c2e74c943a 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h @@ -57,7 +57,7 @@ t8_cmesh_get_boundary_conditions (t8_cmesh_t cmesh, t8_locidx_t ltreeid, /** * Retrieves the boundary condition of one face of a cmesh cell. - * Retrieving all boundary conditions at once via \ref t8_cmesh_get_boundary_conditions will be faster. + * Retrieving all boundary conditions at once via \ref t8_cmesh_get_boundary_conditions() will be faster. * * \param [in] cmesh The cmesh the cell lives in. * \param [in] ltreeid The local cmesh id of the cell. @@ -85,13 +85,13 @@ t8_forest_get_boundary_conditions (t8_forest_t forest, t8_locidx_t ltreeid, cons /** * Retrieves the boundary condition of a face of a forest element. - * Retrieving all boundary conditions at once via \ref t8_forest_get_boundary_conditions will be faster. + * Retrieving all boundary conditions at once via \ref t8_forest_get_boundary_conditions() will be faster. * * \param [in] forest The forest the element lives in. * \param [in] ltreeid The local id of the forest tree. * \param [in] element The element. * \param [in] face The face id of the element. - * \param [out] boundary_conditions The boundary condition of the element. Will be nullptr if the elements + * \param [out] boundary_condition The boundary condition of the element. Will be nullptr if the elements * face is internal; if it does not touch the trees face, since only the tree faces carry boundary * conditions. All inner element faces have neighbors anyways. */ From 909a43fe0e48d2e861fc5d9c20331d13738c3f3e Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 19 Aug 2026 17:57:05 +0200 Subject: [PATCH 08/14] more documentation fixes --- .../internal/t8_cmesh_boundary_condition_handler.hxx | 3 +-- .../t8_cmesh_boundary_conditions.hxx | 2 +- .../t8_cmesh_boundary_conditions_c_interface.h | 2 +- src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.cxx | 4 ++++ 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx index 2c652362ab..f288b62d82 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx @@ -108,7 +108,6 @@ struct t8_cmesh_boundary_condition_handler /** * Retrieves the boundary conditions of a cmesh cell. * - * \param [in] cmesh The cmesh the cell lives in. * \param [in] ltreeid The local cmesh id of the cell. * \note The cmesh local cell id is a different one as the tree id inside the forest. * \return A container with the boundary conditions. @@ -259,7 +258,7 @@ struct t8_cmesh_boundary_condition_handler /** * Retrieves the boundary condition name to a hash. * Crashes if the hash is not registered with a name. - * Faster than \ref get_boundary_condition_name_safe(). + * Faster than \ref t8_cmesh_boundary_condition_handler::get_boundary_condition_name_safe(). * \param [in] hash The hash. * \return The boundary condition name. */ diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.hxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.hxx index 0a37625987..8561fdc6d9 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.hxx +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.hxx @@ -21,7 +21,7 @@ */ /** - * \file t8_cmesh_boundary_conditions.h + * \file t8_cmesh_boundary_conditions.hxx * Public interface for the definition and retrieval of boundary conditions. */ diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h index c2e74c943a..61c3164c2c 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h @@ -21,7 +21,7 @@ */ /** - * \file t8_cmesh_boundary_conditions.h + * \file t8_cmesh_boundary_conditions_c_interface.h: * Public interface for the definition and retrieval of boundary conditions. */ diff --git a/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.cxx b/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.cxx index 51a0f3aa92..222c2d0dc0 100644 --- a/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.cxx +++ b/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_stash.cxx @@ -344,6 +344,10 @@ t8_stash_is_equal (const t8_stash_t stash_a, const t8_stash_t stash_b) T8_EXTERN_C_END (); +/** Reads the eclasses from a stash and returns them along with their global tree id. + * \param [in] stash A stash. + * \return Vector of pairs with [global tree id, eclass]. + */ std::vector> t8_stash_extract_eclasses (const t8_stash_t &stash) { From e8e34aae7c7bcba9e43e86b38b39557a387b3215 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 19 Aug 2026 18:00:18 +0200 Subject: [PATCH 09/14] even more documentation fixes --- .../internal/t8_cmesh_boundary_condition_handler.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx index f288b62d82..b09a4d6bff 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx @@ -258,7 +258,7 @@ struct t8_cmesh_boundary_condition_handler /** * Retrieves the boundary condition name to a hash. * Crashes if the hash is not registered with a name. - * Faster than \ref t8_cmesh_boundary_condition_handler::get_boundary_condition_name_safe(). + * Faster than \ref detail::t8_cmesh_boundary_condition_handler::get_boundary_condition_name_safe(). * \param [in] hash The hash. * \return The boundary condition name. */ From 2fc62384a57d23ee06275ad08dce5b8b51c86ae5 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 19 Aug 2026 18:57:30 +0200 Subject: [PATCH 10/14] bugfix: as expected the new test unveiled a bug --- .../t8_cmesh_boundary_conditions.cxx | 10 +++++----- .../t8_cmesh_boundary_conditions_c_interface.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.cxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.cxx index 33a645f966..f595e4af92 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.cxx +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions.cxx @@ -96,9 +96,9 @@ t8_cmesh_get_boundary_conditions (t8_cmesh_t cmesh, t8_locidx_t ltreeid, void t8_cmesh_get_boundary_condition (t8_cmesh_t cmesh, t8_locidx_t ltreeid, int face, - [[maybe_unused]] const char *boundary_condition) + [[maybe_unused]] const char **boundary_condition) { - boundary_condition = t8_cmesh_get_boundary_condition (cmesh, ltreeid, face).data (); + *boundary_condition = t8_cmesh_get_boundary_condition (cmesh, ltreeid, face).data (); }; void @@ -119,14 +119,14 @@ t8_forest_get_boundary_conditions (t8_forest_t forest, t8_locidx_t ltreeid, cons void t8_forest_get_boundary_condition (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, - [[maybe_unused]] const char *boundary_condition) + [[maybe_unused]] const char **boundary_condition) { const auto boundary_condition_cpp = t8_forest_get_boundary_condition (forest, ltreeid, element, face); if (boundary_condition_cpp.has_value ()) { - boundary_condition = boundary_condition_cpp->data (); + *boundary_condition = boundary_condition_cpp->data (); } else { - boundary_condition = nullptr; + *boundary_condition = nullptr; } }; diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h index 61c3164c2c..4d5bb227ae 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/t8_cmesh_boundary_conditions_c_interface.h @@ -66,7 +66,7 @@ t8_cmesh_get_boundary_conditions (t8_cmesh_t cmesh, t8_locidx_t ltreeid, * \note The cmesh local cell id is a different one as the tree id inside the forest. */ void -t8_cmesh_get_boundary_condition (t8_cmesh_t cmesh, t8_locidx_t ltreeid, int face, const char *boundary_condition); +t8_cmesh_get_boundary_condition (t8_cmesh_t cmesh, t8_locidx_t ltreeid, int face, const char **boundary_condition); /** * Retrieves the boundary conditions of a forest element. @@ -97,6 +97,6 @@ t8_forest_get_boundary_conditions (t8_forest_t forest, t8_locidx_t ltreeid, cons */ void t8_forest_get_boundary_condition (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, int face, - const char *boundary_condition); + const char **boundary_condition); T8_EXTERN_C_END (); From 98e1d02171893d9f25ebea3db6ca2f5f767dfe9a Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 19 Aug 2026 18:57:58 +0200 Subject: [PATCH 11/14] add a test for the boundary condition modules c interface --- .../t8_gtest_cmesh_boundary_conditions.cxx | 87 ++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/test/t8_cmesh/t8_gtest_cmesh_boundary_conditions.cxx b/test/t8_cmesh/t8_gtest_cmesh_boundary_conditions.cxx index 5325a50950..f9b8a563f6 100644 --- a/test/t8_cmesh/t8_gtest_cmesh_boundary_conditions.cxx +++ b/test/t8_cmesh/t8_gtest_cmesh_boundary_conditions.cxx @@ -90,6 +90,9 @@ struct t8_cmesh_single_tree_bc: public testing::TestWithParam t8_eclass eclass; }; +/** + * Tests for each tree class if the boundary conditions input and output remain constant. + */ TEST_P (t8_cmesh_single_tree_bc, test_single_tree_boundary_conditions) { const auto retrieved_boundary_conditions = t8_cmesh_get_boundary_conditions (cmesh, 0); @@ -98,11 +101,14 @@ TEST_P (t8_cmesh_single_tree_bc, test_single_tree_boundary_conditions) } } +/** + * Tests if internal element faces return empty boundary conditions and if extrior element faces return a filled bc. + */ TEST_P (t8_cmesh_single_tree_bc, test_single_tree_element_boundary_conditions) { t8_cmesh_ref (cmesh); t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_standalone (), 2, 0, sc_MPI_COMM_WORLD); - t8_locidx_t num_elements = t8_forest_get_local_num_leaf_elements (forest); + t8_locidx_t num_elements = t8_forest_get_tree_num_leaf_elements (forest, 0); const t8_scheme *scheme = t8_forest_get_scheme (forest); const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, 0); @@ -135,6 +141,10 @@ TEST_P (t8_cmesh_single_tree_bc, test_single_tree_element_boundary_conditions) INSTANTIATE_TEST_SUITE_P (t8_gtest_cmesh_boundary_conditions, t8_cmesh_single_tree_bc, AllEclasses); +/** + * Tests of a hybrid cmesh with multiple trees if the correct boundary conditions are returned. + * For the cmesh all faces without a neighbor get the bc "boundary". All faces with a registered neighbor get the bc "internal". + */ TEST (t8_gtest_cmesh_boundary_conditions, test_hybrid_hypercube_boundary_conditions) { t8_cmesh_t cmesh; @@ -208,3 +218,78 @@ TEST (t8_gtest_cmesh_boundary_conditions, test_hybrid_hypercube_boundary_conditi t8_forest_unref (&forest); } + +/** + * Tests the c interface of the boundary condition module by setting and retrieving the boundary condition of a hex tree. + * More complex tests are performed for the cpp interface. This test is just to test if th conversion routines are working. + */ +TEST (t8_gtest_cmesh_boundary_conditions, test_boundary_condition_c_interface) +{ + /* Create a cmesh with one hex and apply boundary conditions */ + const char *boundary_conditions[6] = { "bc_0", "bc_1", "bc_2", "bc_3", "bc_4", "bc_5" }; + t8_cmesh_t cmesh; + t8_cmesh_init (&cmesh); + t8_cmesh_set_tree_class (cmesh, 0, T8_ECLASS_HEX); + t8_cmesh_set_boundary_conditions (cmesh, 0, boundary_conditions, 6); + t8_cmesh_commit (cmesh, sc_MPI_COMM_WORLD); + + /* Retrieve boundary conditions via t8_cmesh_get_boundary_conditions and t8_cmesh_get_boundary_condition() and check them. */ + const char *retrieved_boundary_conditions[6]; + const char *retrieved_single_boundary_condition; + size_t length = 0; + t8_cmesh_get_boundary_conditions (cmesh, 0, retrieved_boundary_conditions, &length); + for (size_t i_boundary_condition = 0; i_boundary_condition < length; ++i_boundary_condition) { + /* Check t8_cmesh_get_boundary_conditions */ + EXPECT_STREQ (boundary_conditions[i_boundary_condition], retrieved_boundary_conditions[i_boundary_condition]); + + /* Check t8_cmesh_get_boundary_condition */ + t8_cmesh_get_boundary_condition (cmesh, 0, i_boundary_condition, &retrieved_single_boundary_condition); + EXPECT_STREQ (boundary_conditions[i_boundary_condition], retrieved_single_boundary_condition); + } + + /* We now test the interface for a level 1 forest. This way we should get internal and external faces. + We assume, that all hex elements inside the tree have the same orientation and face numeration. */ + t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_standalone (), 1, 0, sc_MPI_COMM_WORLD); + const t8_scheme *scheme = t8_forest_get_scheme (forest); + t8_locidx_t num_elements = t8_forest_get_tree_num_leaf_elements (forest, 0); + + /* Some variables for t8_forest_leaf_face_neighbors */ + int *dual_faces; + int num_neighbors = 0; + t8_locidx_t *element_indices; + t8_eclass_t neigh_class; + + /* Iterate over all elements. */ + for (t8_locidx_t ielem = 0; ielem < num_elements; ++ielem) { + const t8_element_t *elem = t8_forest_get_leaf_element_in_tree (forest, 0, ielem); + const size_t num_faces = scheme->element_get_num_faces (T8_ECLASS_HEX, elem); + /* Fetch boundary conditions via t8_forest_get_boundary_conditions */ + t8_forest_get_boundary_conditions (forest, 0, elem, retrieved_boundary_conditions, &length); + + for (size_t iface = 0; iface < num_faces; ++iface) { + t8_forest_leaf_face_neighbors (forest, 0, elem, NULL, iface, &dual_faces, &num_neighbors, &element_indices, + &neigh_class); + T8_FREE (element_indices); + T8_FREE (dual_faces); + + /* Fetch boundary conditions via t8_forest_get_boundary_condition */ + t8_forest_get_boundary_condition (forest, 0, elem, iface, &retrieved_single_boundary_condition); + + /* The boundary conditions should be nullptr for internal faces */ + if (num_neighbors > 0) { + EXPECT_EQ (retrieved_boundary_conditions[iface], nullptr); + EXPECT_EQ (retrieved_single_boundary_condition, nullptr); + } + /* For boundary faces, they should match the bcs of the cmesh cell. */ + else { + ASSERT_TRUE (retrieved_boundary_conditions[iface] != nullptr); + ASSERT_TRUE (retrieved_single_boundary_condition != nullptr); + + EXPECT_STREQ (retrieved_boundary_conditions[iface], boundary_conditions[iface]); + EXPECT_STREQ (retrieved_single_boundary_condition, boundary_conditions[iface]); + } + } + } + + t8_forest_unref (&forest); +} From 56ad1c00bceada1ae2963022864053537b5e66db Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 19 Aug 2026 18:58:36 +0200 Subject: [PATCH 12/14] add boundary condition modules verify function to the cmesh commit routine --- src/t8_cmesh/t8_cmesh_internal/t8_cmesh_commit.cxx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_commit.cxx b/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_commit.cxx index 7d0f789302..289b9a0b3d 100644 --- a/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_commit.cxx +++ b/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_commit.cxx @@ -522,6 +522,13 @@ t8_cmesh_commit_from_stash (t8_cmesh_t cmesh, sc_MPI_Comm comm) { T8_ASSERT (cmesh != NULL); +#if T8_ENABLE_DEBUG + /* Verify the boundary condition handler if there is one. */ + if (cmesh->boundary_condition_handler != nullptr) { + T8_ASSERT (cmesh->boundary_condition_handler->verify ()); + } +#endif + if (cmesh->set_partition) { /* partitioned commit */ t8_cmesh_commit_partitioned_new (cmesh, comm); From 29c9570a6b654eb1e1dbcc47b25fcc302a1dc3ee Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 19 Aug 2026 19:05:29 +0200 Subject: [PATCH 13/14] documentation [run ci] --- .../internal/t8_cmesh_boundary_condition_handler.hxx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx index b09a4d6bff..167bd3dba4 100644 --- a/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx +++ b/src/t8_cmesh/t8_cmesh_boundary_conditions/internal/t8_cmesh_boundary_condition_handler.hxx @@ -258,7 +258,6 @@ struct t8_cmesh_boundary_condition_handler /** * Retrieves the boundary condition name to a hash. * Crashes if the hash is not registered with a name. - * Faster than \ref detail::t8_cmesh_boundary_condition_handler::get_boundary_condition_name_safe(). * \param [in] hash The hash. * \return The boundary condition name. */ From b91480875b93a827956194f76a2afa306f4411b2 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Thu, 20 Aug 2026 11:04:27 +0200 Subject: [PATCH 14/14] make boundary condition test work in parallel --- .../t8_gtest_cmesh_boundary_conditions.cxx | 315 ++++++++++-------- 1 file changed, 173 insertions(+), 142 deletions(-) diff --git a/test/t8_cmesh/t8_gtest_cmesh_boundary_conditions.cxx b/test/t8_cmesh/t8_gtest_cmesh_boundary_conditions.cxx index f9b8a563f6..aac4d4836d 100644 --- a/test/t8_cmesh/t8_gtest_cmesh_boundary_conditions.cxx +++ b/test/t8_cmesh/t8_gtest_cmesh_boundary_conditions.cxx @@ -95,9 +95,13 @@ struct t8_cmesh_single_tree_bc: public testing::TestWithParam */ TEST_P (t8_cmesh_single_tree_bc, test_single_tree_boundary_conditions) { - const auto retrieved_boundary_conditions = t8_cmesh_get_boundary_conditions (cmesh, 0); - for (size_t i_boundary_condition = 0; i_boundary_condition < boundary_conditions.size (); ++i_boundary_condition) { - EXPECT_EQ (boundary_conditions[i_boundary_condition], retrieved_boundary_conditions[i_boundary_condition]); + /* Only check if the tree is local to our process. */ + if (t8_cmesh_get_num_local_trees (cmesh)) { + /* Retrieve bcs and check that input == output. */ + const auto retrieved_boundary_conditions = t8_cmesh_get_boundary_conditions (cmesh, 0); + for (size_t i_boundary_condition = 0; i_boundary_condition < boundary_conditions.size (); ++i_boundary_condition) { + EXPECT_EQ (boundary_conditions[i_boundary_condition], retrieved_boundary_conditions[i_boundary_condition]); + } } } @@ -107,35 +111,38 @@ TEST_P (t8_cmesh_single_tree_bc, test_single_tree_boundary_conditions) TEST_P (t8_cmesh_single_tree_bc, test_single_tree_element_boundary_conditions) { t8_cmesh_ref (cmesh); - t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_standalone (), 2, 0, sc_MPI_COMM_WORLD); - t8_locidx_t num_elements = t8_forest_get_tree_num_leaf_elements (forest, 0); - const t8_scheme *scheme = t8_forest_get_scheme (forest); - const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, 0); - - /* Some variables for t8_forest_leaf_face_neighbors */ - int *dual_faces; - int num_neighbors = 0; - t8_locidx_t *element_indices; - t8_eclass_t neigh_class; - - for (t8_locidx_t ielem = 0; ielem < num_elements; ++ielem) { - const t8_element_t *elem = t8_forest_get_leaf_element_in_tree (forest, 0, ielem); - const size_t num_faces = scheme->element_get_num_faces (tree_class, elem); - for (size_t iface = 0; iface < num_faces; ++iface) { - t8_forest_leaf_face_neighbors (forest, 0, elem, NULL, iface, &dual_faces, &num_neighbors, &element_indices, - &neigh_class); - T8_FREE (element_indices); - T8_FREE (dual_faces); - const auto boundary_condition = t8_forest_get_boundary_condition (forest, 0, elem, iface); - if (num_neighbors > 0) { - EXPECT_FALSE (boundary_condition.has_value ()); - } - else { - EXPECT_TRUE (boundary_condition.has_value ()); + t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_default (), 2, 1, sc_MPI_COMM_WORLD); + + /* Only check if the tree is local to our process. */ + if (t8_forest_get_num_local_trees (forest)) { + t8_locidx_t num_local_elements = t8_forest_get_tree_num_leaf_elements (forest, 0); + const t8_scheme *scheme = t8_forest_get_scheme (forest); + const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, 0); + + /* Some variables for t8_forest_leaf_face_neighbors */ + int *dual_faces; + int num_neighbors = 0; + t8_locidx_t *element_indices; + t8_eclass_t neigh_class; + + for (t8_locidx_t ielem = 0; ielem < num_local_elements; ++ielem) { + const t8_element_t *elem = t8_forest_get_leaf_element_in_tree (forest, 0, ielem); + const size_t num_faces = scheme->element_get_num_faces (tree_class, elem); + for (size_t iface = 0; iface < num_faces; ++iface) { + t8_forest_leaf_face_neighbors (forest, 0, elem, NULL, iface, &dual_faces, &num_neighbors, &element_indices, + &neigh_class); + T8_FREE (element_indices); + T8_FREE (dual_faces); + const auto boundary_condition = t8_forest_get_boundary_condition (forest, 0, elem, iface); + if (num_neighbors > 0) { + EXPECT_FALSE (boundary_condition.has_value ()); + } + else { + EXPECT_TRUE (boundary_condition.has_value ()); + } } } } - t8_forest_unref (&forest); } @@ -147,76 +154,86 @@ INSTANTIATE_TEST_SUITE_P (t8_gtest_cmesh_boundary_conditions, t8_cmesh_single_tr */ TEST (t8_gtest_cmesh_boundary_conditions, test_hybrid_hypercube_boundary_conditions) { - t8_cmesh_t cmesh; - t8_cmesh_init (&cmesh); - t8_cmesh_new_hypercube_hybrid (cmesh, sc_MPI_COMM_WORLD, 0); - - /* Test the boundary conditions of the trees. All faces with neighbors should have the bc "internal". All other faces are "boundary". */ - const t8_locidx_t num_trees = t8_cmesh_get_num_local_trees (cmesh); - - /* Iterate over all trees. */ - for (t8_locidx_t itree = 0; itree < num_trees; ++itree) { - const t8_eclass_t tree_class = t8_cmesh_get_tree_class (cmesh, itree); - const int num_faces = t8_eclass_num_faces[tree_class]; - - /* Iterate over all faces of the tree. */ - for (int iface = 0; iface < num_faces; ++iface) { - /* Grab the neighbor eclass and the boundary condition. */ - const t8_eclass neigh_class = t8_cmesh_get_tree_face_neighbor_eclass (cmesh, itree, iface); - const auto boundary_condition = t8_cmesh_get_boundary_condition (cmesh, itree, iface); - /* If a face is internal, the boundary condition should be "internal". */ - if (neigh_class == T8_ECLASS_INVALID) { - EXPECT_EQ (boundary_condition, "boundary"); - } - else { - EXPECT_EQ (boundary_condition, "internal"); + /* We test this with a non periodic, as well as a periodic hypercube. The periodic one should have only "internal" bcs. */ + for (int periodic = 0; periodic < 2; ++periodic) { + t8_cmesh_t cmesh; + t8_cmesh_init (&cmesh); + t8_cmesh_new_hypercube_hybrid (cmesh, sc_MPI_COMM_WORLD, periodic); + + /* Test the boundary conditions of the trees. All faces with neighbors should have the bc "internal". All other faces are "boundary". */ + const t8_locidx_t num_local_cmesh_trees = t8_cmesh_get_num_local_trees (cmesh); + + /* Iterate over all cmesh trees. */ + for (t8_locidx_t itree = 0; itree < num_local_cmesh_trees; ++itree) { + const t8_eclass_t tree_class = t8_cmesh_get_tree_class (cmesh, itree); + const int num_faces = t8_eclass_num_faces[tree_class]; + + /* Iterate over all faces of the tree. */ + for (int iface = 0; iface < num_faces; ++iface) { + /* Grab the neighbor eclass and the boundary condition. */ + const t8_eclass neigh_class = t8_cmesh_get_tree_face_neighbor_eclass (cmesh, itree, iface); + const auto boundary_condition = t8_cmesh_get_boundary_condition (cmesh, itree, iface); + /* If a face is internal, the boundary condition should be "internal". */ + if (neigh_class == T8_ECLASS_INVALID) { + EXPECT_EQ (boundary_condition, "boundary"); + } + else { + EXPECT_EQ (boundary_condition, "internal"); + } } } - } - /* Do the same test with the forest interface. We set the refinement level to 0 so that there are no internal faces inside trees. + /* Do the same test with the forest interface. We set the refinement level to 0 so that there are no internal faces inside trees. This way every element face will carry a boundary condition. Internal element faces are checked in another test (test_single_tree_element_boundary_conditions). */ - t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_standalone (), 0, 0, sc_MPI_COMM_WORLD); - const t8_scheme *scheme = t8_forest_get_scheme (forest); - - /* Some variables for t8_forest_leaf_face_neighbors */ - int *dual_faces; - int num_neighbors = 0; - t8_locidx_t *element_indices; - t8_eclass_t neigh_class; - - /* Iterate over all trees. */ - for (t8_locidx_t itree = 0; itree < num_trees; ++itree) { - const t8_eclass_t tree_class = t8_cmesh_get_tree_class (cmesh, itree); - - /* We will not iterate over the elements, because there is only one per tree. */ - const t8_element_t *elem = t8_forest_get_leaf_element_in_tree (forest, itree, 0); - const size_t num_faces = scheme->element_get_num_faces (tree_class, elem); - - /* Retrieve the boundary conditions. */ - const auto boundary_conditions = t8_forest_get_boundary_conditions (forest, itree, elem); - - for (size_t iface = 0; iface < num_faces; ++iface) { - /* Since we have a level 0 forest every face should have a boundary condition. */ - ASSERT_TRUE (boundary_conditions[iface].has_value ()); - - /* Find out if we have neighbors. */ - t8_forest_leaf_face_neighbors (forest, itree, elem, NULL, iface, &dual_faces, &num_neighbors, &element_indices, - &neigh_class); - T8_FREE (element_indices); - T8_FREE (dual_faces); - - /* If we have neighbors, the bc should be "internal". It should be "boundary" otherwise. */ - if (num_neighbors > 0) { - EXPECT_EQ (boundary_conditions[iface].value (), "internal"); - } - else { - EXPECT_EQ (boundary_conditions[iface].value (), "boundary"); + t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_default (), 0, 1, sc_MPI_COMM_WORLD); + const t8_scheme *scheme = t8_forest_get_scheme (forest); + + /* Some variables for t8_forest_leaf_face_neighbors */ + int *dual_faces; + int num_neighbors = 0; + t8_locidx_t *element_indices; + t8_eclass_t neigh_class; + + /* Iterate over all trees. */ + const t8_locidx_t num_local_forest_trees = t8_forest_get_num_local_trees (forest); + for (t8_locidx_t itree = 0; itree < num_local_forest_trees; ++itree) { + const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, itree); + + const t8_locidx_t num_local_elements = t8_forest_get_tree_num_leaf_elements (forest, itree); + /* The forest is level 0, there should only be one element. */ + T8_ASSERT (num_local_elements < 2); + + /* Iterate over our one element. */ + for (t8_locidx_t ielem = 0; ielem < num_local_elements; ++ielem) { + const t8_element_t *elem = t8_forest_get_leaf_element_in_tree (forest, itree, ielem); + const size_t num_faces = scheme->element_get_num_faces (tree_class, elem); + + /* Retrieve the boundary conditions. */ + const auto boundary_conditions = t8_forest_get_boundary_conditions (forest, itree, elem); + + for (size_t iface = 0; iface < num_faces; ++iface) { + /* Since we have a level 0 forest every face should have a boundary condition. */ + ASSERT_TRUE (boundary_conditions[iface].has_value ()); + + /* Find out if we have neighbors. */ + t8_forest_leaf_face_neighbors (forest, itree, elem, NULL, iface, &dual_faces, &num_neighbors, + &element_indices, &neigh_class); + T8_FREE (element_indices); + T8_FREE (dual_faces); + + /* If we have neighbors, the bc should be "internal". It should be "boundary" otherwise. */ + if (num_neighbors > 0) { + EXPECT_EQ (boundary_conditions[iface].value (), "internal"); + } + else { + EXPECT_EQ (boundary_conditions[iface].value (), "boundary"); + } + } } } - } - t8_forest_unref (&forest); + t8_forest_unref (&forest); + } } /** @@ -233,60 +250,74 @@ TEST (t8_gtest_cmesh_boundary_conditions, test_boundary_condition_c_interface) t8_cmesh_set_boundary_conditions (cmesh, 0, boundary_conditions, 6); t8_cmesh_commit (cmesh, sc_MPI_COMM_WORLD); - /* Retrieve boundary conditions via t8_cmesh_get_boundary_conditions and t8_cmesh_get_boundary_condition() and check them. */ - const char *retrieved_boundary_conditions[6]; - const char *retrieved_single_boundary_condition; - size_t length = 0; - t8_cmesh_get_boundary_conditions (cmesh, 0, retrieved_boundary_conditions, &length); - for (size_t i_boundary_condition = 0; i_boundary_condition < length; ++i_boundary_condition) { - /* Check t8_cmesh_get_boundary_conditions */ - EXPECT_STREQ (boundary_conditions[i_boundary_condition], retrieved_boundary_conditions[i_boundary_condition]); - - /* Check t8_cmesh_get_boundary_condition */ - t8_cmesh_get_boundary_condition (cmesh, 0, i_boundary_condition, &retrieved_single_boundary_condition); - EXPECT_STREQ (boundary_conditions[i_boundary_condition], retrieved_single_boundary_condition); + /* Only check if the cmesh tree is local to our process. */ + if (t8_cmesh_get_num_local_trees (cmesh)) { + /* Some variables for retrieving and checking the boundary conditions. */ + const char *retrieved_boundary_conditions[6]; + const char *retrieved_single_boundary_condition; + size_t length = 0; + + /* Retrieve boundary conditions via t8_cmesh_get_boundary_conditions and t8_cmesh_get_boundary_condition() and check them. */ + t8_cmesh_get_boundary_conditions (cmesh, 0, retrieved_boundary_conditions, &length); + for (size_t i_boundary_condition = 0; i_boundary_condition < length; ++i_boundary_condition) { + /* Check t8_cmesh_get_boundary_conditions */ + EXPECT_STREQ (boundary_conditions[i_boundary_condition], retrieved_boundary_conditions[i_boundary_condition]); + + /* Check t8_cmesh_get_boundary_condition */ + t8_cmesh_get_boundary_condition (cmesh, 0, i_boundary_condition, &retrieved_single_boundary_condition); + EXPECT_STREQ (boundary_conditions[i_boundary_condition], retrieved_single_boundary_condition); + } } /* We now test the interface for a level 1 forest. This way we should get internal and external faces. We assume, that all hex elements inside the tree have the same orientation and face numeration. */ - t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_standalone (), 1, 0, sc_MPI_COMM_WORLD); - const t8_scheme *scheme = t8_forest_get_scheme (forest); - t8_locidx_t num_elements = t8_forest_get_tree_num_leaf_elements (forest, 0); - - /* Some variables for t8_forest_leaf_face_neighbors */ - int *dual_faces; - int num_neighbors = 0; - t8_locidx_t *element_indices; - t8_eclass_t neigh_class; - - /* Iterate over all elements. */ - for (t8_locidx_t ielem = 0; ielem < num_elements; ++ielem) { - const t8_element_t *elem = t8_forest_get_leaf_element_in_tree (forest, 0, ielem); - const size_t num_faces = scheme->element_get_num_faces (T8_ECLASS_HEX, elem); - /* Fetch boundary conditions via t8_forest_get_boundary_conditions */ - t8_forest_get_boundary_conditions (forest, 0, elem, retrieved_boundary_conditions, &length); - - for (size_t iface = 0; iface < num_faces; ++iface) { - t8_forest_leaf_face_neighbors (forest, 0, elem, NULL, iface, &dual_faces, &num_neighbors, &element_indices, - &neigh_class); - T8_FREE (element_indices); - T8_FREE (dual_faces); - - /* Fetch boundary conditions via t8_forest_get_boundary_condition */ - t8_forest_get_boundary_condition (forest, 0, elem, iface, &retrieved_single_boundary_condition); - - /* The boundary conditions should be nullptr for internal faces */ - if (num_neighbors > 0) { - EXPECT_EQ (retrieved_boundary_conditions[iface], nullptr); - EXPECT_EQ (retrieved_single_boundary_condition, nullptr); - } - /* For boundary faces, they should match the bcs of the cmesh cell. */ - else { - ASSERT_TRUE (retrieved_boundary_conditions[iface] != nullptr); - ASSERT_TRUE (retrieved_single_boundary_condition != nullptr); - - EXPECT_STREQ (retrieved_boundary_conditions[iface], boundary_conditions[iface]); - EXPECT_STREQ (retrieved_single_boundary_condition, boundary_conditions[iface]); + t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_default (), 1, 1, sc_MPI_COMM_WORLD); + + /* Only check if the forest tree is local to our process. */ + if (t8_forest_get_num_local_trees (forest)) { + const t8_scheme *scheme = t8_forest_get_scheme (forest); + t8_locidx_t num_local_elements = t8_forest_get_tree_num_leaf_elements (forest, 0); + + /* Some variables for t8_forest_leaf_face_neighbors */ + int *dual_faces; + int num_neighbors = 0; + t8_locidx_t *element_indices; + t8_eclass_t neigh_class; + + /* Some variables for retrieving and checking the boundary conditions. */ + const char *retrieved_boundary_conditions[6]; + const char *retrieved_single_boundary_condition; + size_t length = 0; + + /* Iterate over all elements. */ + for (t8_locidx_t ielem = 0; ielem < num_local_elements; ++ielem) { + const t8_element_t *elem = t8_forest_get_leaf_element_in_tree (forest, 0, ielem); + const size_t num_faces = scheme->element_get_num_faces (T8_ECLASS_HEX, elem); + /* Fetch boundary conditions via t8_forest_get_boundary_conditions */ + t8_forest_get_boundary_conditions (forest, 0, elem, retrieved_boundary_conditions, &length); + + for (size_t iface = 0; iface < num_faces; ++iface) { + t8_forest_leaf_face_neighbors (forest, 0, elem, NULL, iface, &dual_faces, &num_neighbors, &element_indices, + &neigh_class); + T8_FREE (element_indices); + T8_FREE (dual_faces); + + /* Fetch boundary conditions via t8_forest_get_boundary_condition */ + t8_forest_get_boundary_condition (forest, 0, elem, iface, &retrieved_single_boundary_condition); + + /* The boundary conditions should be nullptr for internal faces */ + if (num_neighbors > 0) { + EXPECT_EQ (retrieved_boundary_conditions[iface], nullptr); + EXPECT_EQ (retrieved_single_boundary_condition, nullptr); + } + /* For boundary faces, they should match the bcs of the cmesh cell. */ + else { + ASSERT_TRUE (retrieved_boundary_conditions[iface] != nullptr); + ASSERT_TRUE (retrieved_single_boundary_condition != nullptr); + + EXPECT_STREQ (retrieved_boundary_conditions[iface], boundary_conditions[iface]); + EXPECT_STREQ (retrieved_single_boundary_condition, boundary_conditions[iface]); + } } } }