diff --git a/src/mesh/Connectivity.cpp b/src/mesh/Connectivity.cpp index cc501c1ba034cdbebca5f91de56fb6f1f66c2a1a..0a02886dd673680a7c800d549b5ee3fc9aacd07a 100644 --- a/src/mesh/Connectivity.cpp +++ b/src/mesh/Connectivity.cpp @@ -1,6 +1,7 @@ #include <mesh/Connectivity.hpp> #include <mesh/ConnectivityDescriptor.hpp> +#include <mesh/ItemValueUtils.hpp> #include <utils/Messenger.hpp> #include <map> @@ -152,6 +153,91 @@ Connectivity<Dimension>::_buildFrom(const ConnectivityDescriptor& descriptor) } } +template <size_t Dimension> +void +Connectivity<Dimension>::_buildIsBoundaryFace() const +{ + Array<bool> is_face_boundary_array(this->numberOfFaces()); + WeakFaceValue<bool> is_boundary_face(*this, is_face_boundary_array); + const auto& face_to_cell_matrix = this->faceToCellMatrix(); + const auto& face_is_owned = this->faceIsOwned(); + parallel_for( + this->numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + is_boundary_face[face_id] = face_is_owned[face_id] and (face_to_cell_matrix[face_id].size() == 1); + }); + synchronize(is_boundary_face); + const_cast<WeakFaceValue<const bool>&>(m_is_boundary_face) = is_boundary_face; + + if constexpr (Dimension <= 2) { + const_cast<WeakEdgeValue<const bool>&>(m_is_boundary_edge) = WeakEdgeValue<bool>(*this, is_face_boundary_array); + if constexpr (Dimension == 1) { + const_cast<WeakNodeValue<const bool>&>(m_is_boundary_node) = WeakNodeValue<bool>(*this, is_face_boundary_array); + } else { + static_assert(Dimension == 2, "unexpected dimension"); + } + } +} + +template <size_t Dimension> +void +Connectivity<Dimension>::_buildIsBoundaryEdge() const +{ + if constexpr (Dimension < 3) { + this->_buildIsBoundaryFace(); + } else { + auto is_boundary_face = this->isBoundaryFace(); + WeakEdgeValue<bool> is_boundary_edge(*this); + is_boundary_edge.fill(false); + const auto& face_to_edge_matrix = this->faceToEdgeMatrix(); + for (FaceId face_id = 0; face_id < this->numberOfFaces(); ++face_id) { + if (is_boundary_face[face_id]) { + auto face_edge = face_to_edge_matrix[face_id]; + for (size_t i_edge = 0; i_edge < face_edge.size(); ++i_edge) { + is_boundary_edge[face_edge[i_edge]] = true; + } + } + } + synchronize(is_boundary_edge); + const_cast<WeakEdgeValue<const bool>&>(m_is_boundary_edge) = is_boundary_edge; + } +} + +template <size_t Dimension> +void +Connectivity<Dimension>::_buildIsBoundaryNode() const +{ + if constexpr (Dimension == 1) { + this->_buildIsBoundaryFace(); + } else { + auto is_boundary_face = this->isBoundaryFace(); + WeakNodeValue<bool> is_boundary_node(*this); + is_boundary_node.fill(false); + const auto& face_to_node_matrix = this->faceToNodeMatrix(); + for (FaceId face_id = 0; face_id < this->numberOfFaces(); ++face_id) { + if (is_boundary_face[face_id]) { + auto face_nodes = face_to_node_matrix[face_id]; + for (size_t i_node = 0; i_node < face_nodes.size(); ++i_node) { + is_boundary_node[face_nodes[i_node]] = true; + } + } + } + synchronize(is_boundary_node); + const_cast<WeakNodeValue<const bool>&>(m_is_boundary_node) = is_boundary_node; + } +} + +template void Connectivity<1>::_buildIsBoundaryFace() const; +template void Connectivity<2>::_buildIsBoundaryFace() const; +template void Connectivity<3>::_buildIsBoundaryFace() const; + +template void Connectivity<1>::_buildIsBoundaryEdge() const; +template void Connectivity<2>::_buildIsBoundaryEdge() const; +template void Connectivity<3>::_buildIsBoundaryEdge() const; + +template void Connectivity<1>::_buildIsBoundaryNode() const; +template void Connectivity<2>::_buildIsBoundaryNode() const; +template void Connectivity<3>::_buildIsBoundaryNode() const; + template Connectivity<1>::Connectivity(); template Connectivity<2>::Connectivity(); template Connectivity<3>::Connectivity(); diff --git a/src/mesh/Connectivity.hpp b/src/mesh/Connectivity.hpp index 8a1f82dfce3b331f4bff0bc7d28be0e7abfd3498..f0e250c4d2aa2e3de12c6132640086e657231e94 100644 --- a/src/mesh/Connectivity.hpp +++ b/src/mesh/Connectivity.hpp @@ -62,19 +62,22 @@ class Connectivity final : public IConnectivity WeakFaceValue<const int> m_face_owner; WeakFaceValue<const bool> m_face_is_owned; + WeakFaceValue<const bool> m_is_boundary_face; WeakEdgeValue<const int> m_edge_owner; WeakEdgeValue<const bool> m_edge_is_owned; + WeakEdgeValue<const bool> m_is_boundary_edge; WeakNodeValue<const int> m_node_owner; WeakNodeValue<const bool> m_node_is_owned; + WeakNodeValue<const bool> m_is_boundary_node; WeakFaceValuePerCell<const bool> m_cell_face_is_reversed; WeakEdgeValuePerFace<const bool> m_face_edge_is_reversed; WeakNodeValuePerCell<const unsigned short> m_cell_local_numbers_in_their_nodes; - WeakEdgeValuePerCell<const unsigned short> m_cell_local_numbers_in_their_edges; WeakFaceValuePerCell<const unsigned short> m_cell_local_numbers_in_their_faces; + WeakEdgeValuePerCell<const unsigned short> m_cell_local_numbers_in_their_edges; WeakCellValuePerFace<const unsigned short> m_face_local_numbers_in_their_cells; WeakEdgeValuePerFace<const unsigned short> m_face_local_numbers_in_their_edges; @@ -85,8 +88,8 @@ class Connectivity final : public IConnectivity WeakNodeValuePerEdge<const unsigned short> m_edge_local_numbers_in_their_nodes; WeakCellValuePerNode<const unsigned short> m_node_local_numbers_in_their_cells; - WeakEdgeValuePerNode<const unsigned short> m_node_local_numbers_in_their_edges; WeakFaceValuePerNode<const unsigned short> m_node_local_numbers_in_their_faces; + WeakEdgeValuePerNode<const unsigned short> m_node_local_numbers_in_their_edges; ConnectivityComputer m_connectivity_computer; @@ -99,6 +102,10 @@ class Connectivity final : public IConnectivity void _computeCellFaceAndFaceNodeConnectivities(); + void _buildIsBoundaryFace() const; + void _buildIsBoundaryEdge() const; + void _buildIsBoundaryNode() const; + template <typename SubItemValuePerItemType> PUGS_INLINE const SubItemValuePerItemType& _lazzyBuildItemNumberInTheirChild(const SubItemValuePerItemType& sub_item_value_per_item) const @@ -268,6 +275,52 @@ class Connectivity final : public IConnectivity } } + PUGS_INLINE + const auto& + isBoundaryFace() const + { + if (not m_is_boundary_face.isBuilt()) { + this->_buildIsBoundaryFace(); + } + return m_is_boundary_face; + } + + PUGS_INLINE + const auto& + isBoundaryEdge() const + { + if (not m_is_boundary_edge.isBuilt()) { + this->_buildIsBoundaryEdge(); + } + return m_is_boundary_edge; + } + + PUGS_INLINE + const auto& + isBoundaryNode() const + { + if (not m_is_boundary_node.isBuilt()) { + this->_buildIsBoundaryNode(); + } + return m_is_boundary_node; + } + + template <ItemType item_type> + PUGS_INLINE const auto& + isBoundary() const + { + if constexpr (item_type == ItemType::face) { + return isBoundaryFace(); + } else if constexpr (item_type == ItemType::edge) { + return isBoundaryEdge(); + } else if constexpr (item_type == ItemType::node) { + return isBoundaryNode(); + } else { + static_assert(item_type != ItemType::cell, "cell boundary makes no sense"); + static_assert(is_false_item_type_v<item_type>, "unknown ItemType"); + } + } + PUGS_INLINE const bool& isConnectivityMatrixBuilt(ItemType item_type_0, ItemType item_type_1) const diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cbd6b32bc1010a44fe13ed71cb55d03d10052895..085f012fb6d0e1bd9ad519d594e6bc8121f27303 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -156,6 +156,7 @@ add_executable (unit_tests add_executable (mpi_unit_tests mpi_test_main.cpp + test_Connectivity.cpp test_DiscreteFunctionInterpoler.cpp test_DiscreteFunctionP0.cpp test_DiscreteFunctionP0Vector.cpp diff --git a/tests/test_Connectivity.cpp b/tests/test_Connectivity.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9abb8fdb3aa0f81bc6123daf70dd467006657f54 --- /dev/null +++ b/tests/test_Connectivity.cpp @@ -0,0 +1,558 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/ItemValue.hpp> +#include <mesh/ItemValueUtils.hpp> +#include <mesh/Mesh.hpp> +#include <utils/Messenger.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("Connectivity", "[mesh]") +{ + SECTION("isOwned") + { + SECTION("1D") + { + std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_1d = named_mesh.mesh(); + const Connectivity<1>& connectivity = mesh_1d->connectivity(); + + SECTION("nodes/edges/faces") + { + auto node_owner = connectivity.nodeOwner(); + REQUIRE(decltype(node_owner)::item_t == ItemType::node); + auto node_owner_generic = connectivity.owner<ItemType::node>(); + REQUIRE(decltype(node_owner_generic)::item_t == ItemType::node); + REQUIRE(&(node_owner[NodeId{0}]) == &(node_owner_generic[NodeId{0}])); + + auto node_is_owned = connectivity.nodeIsOwned(); + REQUIRE(decltype(node_is_owned)::item_t == ItemType::node); + auto node_is_owned_generic = connectivity.isOwned<ItemType::node>(); + REQUIRE(decltype(node_is_owned_generic)::item_t == ItemType::node); + REQUIRE(&(node_is_owned[NodeId{0}]) == &(node_is_owned_generic[NodeId{0}])); + + auto edge_owner = connectivity.edgeOwner(); + REQUIRE(decltype(edge_owner)::item_t == ItemType::edge); + auto edge_owner_generic = connectivity.owner<ItemType::edge>(); + REQUIRE(decltype(edge_owner_generic)::item_t == ItemType::edge); + REQUIRE(&(edge_owner[EdgeId{0}]) == &(edge_owner_generic[EdgeId{0}])); + + auto edge_is_owned = connectivity.edgeIsOwned(); + REQUIRE(decltype(edge_is_owned)::item_t == ItemType::edge); + auto edge_is_owned_generic = connectivity.isOwned<ItemType::edge>(); + REQUIRE(decltype(edge_is_owned_generic)::item_t == ItemType::edge); + REQUIRE(&(edge_is_owned[EdgeId{0}]) == &(edge_is_owned_generic[EdgeId{0}])); + + auto face_owner = connectivity.faceOwner(); + REQUIRE(decltype(face_owner)::item_t == ItemType::face); + auto face_owner_generic = connectivity.owner<ItemType::face>(); + REQUIRE(decltype(face_owner_generic)::item_t == ItemType::face); + REQUIRE(&(face_owner[FaceId{0}]) == &(face_owner_generic[FaceId{0}])); + + auto face_is_owned = connectivity.faceIsOwned(); + REQUIRE(decltype(face_is_owned)::item_t == ItemType::face); + auto face_is_owned_generic = connectivity.isOwned<ItemType::face>(); + REQUIRE(decltype(face_is_owned_generic)::item_t == ItemType::face); + REQUIRE(&(face_is_owned[FaceId{0}]) == &(face_is_owned_generic[FaceId{0}])); + + // In 1d these values are the same + REQUIRE(&(node_owner[NodeId(0)]) == &(face_owner[FaceId(0)])); + REQUIRE(&(node_owner[NodeId(0)]) == &(edge_owner[EdgeId(0)])); + + REQUIRE(isSynchronized(node_owner)); + + const int rank_number = parallel::rank(); + + bool node_is_owned_is_valid = true; + for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) { + node_is_owned_is_valid &= (node_is_owned[node_id] == (node_owner[node_id] == rank_number)); + } + REQUIRE(node_is_owned_is_valid); + } + + SECTION("cells") + { + auto cell_owner = connectivity.cellOwner(); + REQUIRE(decltype(cell_owner)::item_t == ItemType::cell); + auto cell_owner_generic = connectivity.owner<ItemType::cell>(); + REQUIRE(decltype(cell_owner_generic)::item_t == ItemType::cell); + REQUIRE(&(cell_owner[CellId{0}]) == &(cell_owner_generic[CellId{0}])); + + auto cell_is_owned = connectivity.cellIsOwned(); + REQUIRE(decltype(cell_is_owned)::item_t == ItemType::cell); + auto cell_is_owned_generic = connectivity.isOwned<ItemType::cell>(); + REQUIRE(decltype(cell_is_owned_generic)::item_t == ItemType::cell); + REQUIRE(&(cell_is_owned[CellId{0}]) == &(cell_is_owned_generic[CellId{0}])); + + REQUIRE(isSynchronized(cell_owner)); + + const int rank_number = parallel::rank(); + + bool cell_is_owned_is_valid = true; + for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { + cell_is_owned_is_valid &= (cell_is_owned[cell_id] == (cell_owner[cell_id] == rank_number)); + } + REQUIRE(cell_is_owned_is_valid); + } + } + } + } + + SECTION("2D") + { + std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_2d = named_mesh.mesh(); + const Connectivity<2>& connectivity = mesh_2d->connectivity(); + + SECTION("nodes") + { + auto node_owner = connectivity.nodeOwner(); + REQUIRE(decltype(node_owner)::item_t == ItemType::node); + auto node_owner_generic = connectivity.owner<ItemType::node>(); + REQUIRE(decltype(node_owner_generic)::item_t == ItemType::node); + REQUIRE(&(node_owner[NodeId{0}]) == &(node_owner_generic[NodeId{0}])); + + auto node_is_owned = connectivity.nodeIsOwned(); + REQUIRE(decltype(node_is_owned)::item_t == ItemType::node); + auto node_is_owned_generic = connectivity.isOwned<ItemType::node>(); + REQUIRE(decltype(node_is_owned_generic)::item_t == ItemType::node); + REQUIRE(&(node_is_owned[NodeId{0}]) == &(node_is_owned_generic[NodeId{0}])); + + REQUIRE(isSynchronized(node_owner)); + + const int rank_number = parallel::rank(); + + bool node_is_owned_is_valid = true; + for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) { + node_is_owned_is_valid &= (node_is_owned[node_id] == (node_owner[node_id] == rank_number)); + } + REQUIRE(node_is_owned_is_valid); + } + + SECTION("edges/faces") + { + auto edge_owner = connectivity.edgeOwner(); + REQUIRE(decltype(edge_owner)::item_t == ItemType::edge); + auto edge_owner_generic = connectivity.owner<ItemType::edge>(); + REQUIRE(decltype(edge_owner_generic)::item_t == ItemType::edge); + REQUIRE(&(edge_owner[EdgeId{0}]) == &(edge_owner_generic[EdgeId{0}])); + + auto edge_is_owned = connectivity.edgeIsOwned(); + REQUIRE(decltype(edge_is_owned)::item_t == ItemType::edge); + auto edge_is_owned_generic = connectivity.isOwned<ItemType::edge>(); + REQUIRE(decltype(edge_is_owned_generic)::item_t == ItemType::edge); + REQUIRE(&(edge_is_owned[EdgeId{0}]) == &(edge_is_owned_generic[EdgeId{0}])); + + auto face_owner = connectivity.faceOwner(); + REQUIRE(decltype(face_owner)::item_t == ItemType::face); + auto face_owner_generic = connectivity.owner<ItemType::face>(); + REQUIRE(decltype(face_owner_generic)::item_t == ItemType::face); + REQUIRE(&(face_owner[FaceId{0}]) == &(face_owner_generic[FaceId{0}])); + + auto face_is_owned = connectivity.faceIsOwned(); + REQUIRE(decltype(face_is_owned)::item_t == ItemType::face); + auto face_is_owned_generic = connectivity.isOwned<ItemType::face>(); + REQUIRE(decltype(face_is_owned_generic)::item_t == ItemType::face); + REQUIRE(&(face_is_owned[FaceId{0}]) == &(face_is_owned_generic[FaceId{0}])); + + // In 2d these values are the same + REQUIRE(&(face_owner[FaceId(0)]) == &(edge_owner[EdgeId(0)])); + + REQUIRE(isSynchronized(face_owner)); + + const int rank_number = parallel::rank(); + + bool face_is_owned_is_valid = true; + for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) { + face_is_owned_is_valid &= (face_is_owned[face_id] == (face_owner[face_id] == rank_number)); + } + REQUIRE(face_is_owned_is_valid); + } + + SECTION("cells") + { + auto cell_owner = connectivity.cellOwner(); + REQUIRE(decltype(cell_owner)::item_t == ItemType::cell); + auto cell_owner_generic = connectivity.owner<ItemType::cell>(); + REQUIRE(decltype(cell_owner_generic)::item_t == ItemType::cell); + REQUIRE(&(cell_owner[CellId{0}]) == &(cell_owner_generic[CellId{0}])); + + auto cell_is_owned = connectivity.cellIsOwned(); + REQUIRE(decltype(cell_is_owned)::item_t == ItemType::cell); + auto cell_is_owned_generic = connectivity.isOwned<ItemType::cell>(); + REQUIRE(decltype(cell_is_owned_generic)::item_t == ItemType::cell); + REQUIRE(&(cell_is_owned[CellId{0}]) == &(cell_is_owned_generic[CellId{0}])); + + REQUIRE(isSynchronized(cell_owner)); + + const int rank_number = parallel::rank(); + + bool cell_is_owned_is_valid = true; + for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { + cell_is_owned_is_valid &= (cell_is_owned[cell_id] == (cell_owner[cell_id] == rank_number)); + } + REQUIRE(cell_is_owned_is_valid); + } + } + } + } + + SECTION("3D") + { + std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_3d = named_mesh.mesh(); + + const Connectivity<3>& connectivity = mesh_3d->connectivity(); + + SECTION("nodes") + { + auto node_owner = connectivity.nodeOwner(); + REQUIRE(decltype(node_owner)::item_t == ItemType::node); + auto node_owner_generic = connectivity.owner<ItemType::node>(); + REQUIRE(decltype(node_owner_generic)::item_t == ItemType::node); + REQUIRE(&(node_owner[NodeId{0}]) == &(node_owner_generic[NodeId{0}])); + + auto node_is_owned = connectivity.nodeIsOwned(); + REQUIRE(decltype(node_is_owned)::item_t == ItemType::node); + auto node_is_owned_generic = connectivity.isOwned<ItemType::node>(); + REQUIRE(decltype(node_is_owned_generic)::item_t == ItemType::node); + REQUIRE(&(node_is_owned[NodeId{0}]) == &(node_is_owned_generic[NodeId{0}])); + + REQUIRE(isSynchronized(node_owner)); + + const int rank_number = parallel::rank(); + + bool node_is_owned_is_valid = true; + for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) { + node_is_owned_is_valid &= (node_is_owned[node_id] == (node_owner[node_id] == rank_number)); + } + REQUIRE(node_is_owned_is_valid); + } + + SECTION("edges") + { + auto edge_owner = connectivity.edgeOwner(); + REQUIRE(decltype(edge_owner)::item_t == ItemType::edge); + auto edge_owner_generic = connectivity.owner<ItemType::edge>(); + REQUIRE(decltype(edge_owner_generic)::item_t == ItemType::edge); + REQUIRE(&(edge_owner[EdgeId{0}]) == &(edge_owner_generic[EdgeId{0}])); + + auto edge_is_owned = connectivity.edgeIsOwned(); + REQUIRE(decltype(edge_is_owned)::item_t == ItemType::edge); + auto edge_is_owned_generic = connectivity.isOwned<ItemType::edge>(); + REQUIRE(decltype(edge_is_owned_generic)::item_t == ItemType::edge); + REQUIRE(&(edge_is_owned[EdgeId{0}]) == &(edge_is_owned_generic[EdgeId{0}])); + + REQUIRE(isSynchronized(edge_owner)); + + const int rank_number = parallel::rank(); + + bool edge_is_owned_is_valid = true; + for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) { + edge_is_owned_is_valid &= (edge_is_owned[edge_id] == (edge_owner[edge_id] == rank_number)); + } + REQUIRE(edge_is_owned_is_valid); + } + + SECTION("faces") + { + auto face_owner = connectivity.faceOwner(); + REQUIRE(decltype(face_owner)::item_t == ItemType::face); + auto face_owner_generic = connectivity.owner<ItemType::face>(); + REQUIRE(decltype(face_owner_generic)::item_t == ItemType::face); + REQUIRE(&(face_owner[FaceId{0}]) == &(face_owner_generic[FaceId{0}])); + + auto face_is_owned = connectivity.faceIsOwned(); + REQUIRE(decltype(face_is_owned)::item_t == ItemType::face); + auto face_is_owned_generic = connectivity.isOwned<ItemType::face>(); + REQUIRE(decltype(face_is_owned_generic)::item_t == ItemType::face); + REQUIRE(&(face_is_owned[FaceId{0}]) == &(face_is_owned_generic[FaceId{0}])); + + REQUIRE(isSynchronized(face_owner)); + + const int rank_number = parallel::rank(); + + bool face_is_owned_is_valid = true; + for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) { + face_is_owned_is_valid &= (face_is_owned[face_id] == (face_owner[face_id] == rank_number)); + } + REQUIRE(face_is_owned_is_valid); + } + + SECTION("cells") + { + auto cell_owner = connectivity.cellOwner(); + REQUIRE(decltype(cell_owner)::item_t == ItemType::cell); + auto cell_owner_generic = connectivity.owner<ItemType::cell>(); + REQUIRE(decltype(cell_owner_generic)::item_t == ItemType::cell); + REQUIRE(&(cell_owner[CellId{0}]) == &(cell_owner_generic[CellId{0}])); + + auto cell_is_owned = connectivity.cellIsOwned(); + REQUIRE(decltype(cell_is_owned)::item_t == ItemType::cell); + auto cell_is_owned_generic = connectivity.isOwned<ItemType::cell>(); + REQUIRE(decltype(cell_is_owned_generic)::item_t == ItemType::cell); + REQUIRE(&(cell_is_owned[CellId{0}]) == &(cell_is_owned_generic[CellId{0}])); + + REQUIRE(isSynchronized(cell_owner)); + + const int rank_number = parallel::rank(); + + bool cell_is_owned_is_valid = true; + for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { + cell_is_owned_is_valid &= (cell_is_owned[cell_id] == (cell_owner[cell_id] == rank_number)); + } + REQUIRE(cell_is_owned_is_valid); + } + } + } + } + } + + SECTION("isBoundary") + { + SECTION("1D") + { + std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_1d = named_mesh.mesh(); + const Connectivity<1>& connectivity = mesh_1d->connectivity(); + + auto is_boundary_node = connectivity.isBoundaryNode(); + REQUIRE(decltype(is_boundary_node)::item_t == ItemType::node); + auto is_boundary_node_generic = connectivity.isBoundary<ItemType::node>(); + REQUIRE(decltype(is_boundary_node_generic)::item_t == ItemType::node); + REQUIRE(&(is_boundary_node[NodeId{0}]) == &(is_boundary_node_generic[NodeId{0}])); + + auto is_boundary_edge = connectivity.isBoundaryEdge(); + REQUIRE(decltype(is_boundary_edge)::item_t == ItemType::edge); + auto is_boundary_edge_generic = connectivity.isBoundary<ItemType::edge>(); + REQUIRE(decltype(is_boundary_edge_generic)::item_t == ItemType::edge); + REQUIRE(&(is_boundary_edge[EdgeId{0}]) == &(is_boundary_edge_generic[EdgeId{0}])); + + auto is_boundary_face = connectivity.isBoundaryFace(); + REQUIRE(decltype(is_boundary_face)::item_t == ItemType::face); + auto is_boundary_face_generic = connectivity.isBoundary<ItemType::face>(); + REQUIRE(decltype(is_boundary_face_generic)::item_t == ItemType::face); + REQUIRE(&(is_boundary_face[FaceId{0}]) == &(is_boundary_face_generic[FaceId{0}])); + + // In 1d these values are the same + REQUIRE(&(is_boundary_node[NodeId(0)]) == &(is_boundary_face[FaceId(0)])); + REQUIRE(&(is_boundary_edge[EdgeId(0)]) == &(is_boundary_face[FaceId(0)])); + + REQUIRE(isSynchronized(is_boundary_face)); + + auto face_is_owned = connectivity.faceIsOwned(); + auto face_to_cell_matrix = connectivity.faceToCellMatrix(); + + bool face_boundary_is_valid = true; + for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) { + if (face_is_owned[face_id]) { + const bool is_boundary = (face_to_cell_matrix[face_id].size() == 1); + face_boundary_is_valid &= (is_boundary == is_boundary_face[face_id]); + } + } + REQUIRE(face_boundary_is_valid); + } + } + } + + SECTION("2D") + { + std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_2d = named_mesh.mesh(); + const Connectivity<2>& connectivity = mesh_2d->connectivity(); + + SECTION("faces/edges") + { + auto is_boundary_edge = connectivity.isBoundaryEdge(); + REQUIRE(decltype(is_boundary_edge)::item_t == ItemType::edge); + auto is_boundary_edge_generic = connectivity.isBoundary<ItemType::edge>(); + REQUIRE(decltype(is_boundary_edge_generic)::item_t == ItemType::edge); + REQUIRE(&(is_boundary_edge[EdgeId{0}]) == &(is_boundary_edge_generic[EdgeId{0}])); + + auto is_boundary_face = connectivity.isBoundaryFace(); + REQUIRE(decltype(is_boundary_face)::item_t == ItemType::face); + auto is_boundary_face_generic = connectivity.isBoundary<ItemType::face>(); + REQUIRE(decltype(is_boundary_face_generic)::item_t == ItemType::face); + REQUIRE(&(is_boundary_face[FaceId{0}]) == &(is_boundary_face_generic[FaceId{0}])); + + // In 2d these values are the same + REQUIRE(&(is_boundary_edge[EdgeId(0)]) == &(is_boundary_face[FaceId(0)])); + + REQUIRE(isSynchronized(is_boundary_face)); + + auto face_is_owned = connectivity.faceIsOwned(); + auto face_to_cell_matrix = connectivity.faceToCellMatrix(); + + bool face_boundary_is_valid = true; + for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) { + if (face_is_owned[face_id]) { + const bool is_boundary = (face_to_cell_matrix[face_id].size() == 1); + face_boundary_is_valid &= (is_boundary == is_boundary_face[face_id]); + } + } + REQUIRE(face_boundary_is_valid); + } + + SECTION("nodes") + { + auto is_boundary_node = connectivity.isBoundaryNode(); + REQUIRE(decltype(is_boundary_node)::item_t == ItemType::node); + auto is_boundary_node_generic = connectivity.isBoundary<ItemType::node>(); + REQUIRE(decltype(is_boundary_node_generic)::item_t == ItemType::node); + REQUIRE(&(is_boundary_node[NodeId{0}]) == &(is_boundary_node_generic[NodeId{0}])); + + REQUIRE(isSynchronized(is_boundary_node)); + + auto node_is_owned = connectivity.nodeIsOwned(); + auto node_to_face_matrix = connectivity.nodeToFaceMatrix(); + + auto is_boundary_face = connectivity.isBoundaryFace(); + + bool node_boundary_is_valid = true; + for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) { + if (node_is_owned[node_id]) { + bool is_boundary = false; + auto node_face_list = node_to_face_matrix[node_id]; + for (size_t i_face = 0; i_face < node_face_list.size(); ++i_face) { + if (is_boundary_face[node_face_list[i_face]]) { + is_boundary = true; + break; + } + } + + node_boundary_is_valid &= (is_boundary == is_boundary_node[node_id]); + } + } + REQUIRE(node_boundary_is_valid); + } + } + } + } + + SECTION("3D") + { + std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_3d = named_mesh.mesh(); + + const Connectivity<3>& connectivity = mesh_3d->connectivity(); + + SECTION("faces") + { + auto is_boundary_face = connectivity.isBoundaryFace(); + REQUIRE(decltype(is_boundary_face)::item_t == ItemType::face); + auto is_boundary_face_generic = connectivity.isBoundary<ItemType::face>(); + REQUIRE(decltype(is_boundary_face_generic)::item_t == ItemType::face); + REQUIRE(&(is_boundary_face[FaceId{0}]) == &(is_boundary_face_generic[FaceId{0}])); + + REQUIRE(isSynchronized(is_boundary_face)); + + auto face_is_owned = connectivity.faceIsOwned(); + auto face_to_cell_matrix = connectivity.faceToCellMatrix(); + + bool face_boundary_is_valid = true; + for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) { + if (face_is_owned[face_id]) { + const bool is_boundary = (face_to_cell_matrix[face_id].size() == 1); + face_boundary_is_valid &= (is_boundary == is_boundary_face[face_id]); + } + } + REQUIRE(face_boundary_is_valid); + } + + SECTION("edges") + { + auto is_boundary_edge = connectivity.isBoundaryEdge(); + REQUIRE(decltype(is_boundary_edge)::item_t == ItemType::edge); + auto is_boundary_edge_generic = connectivity.isBoundary<ItemType::edge>(); + REQUIRE(decltype(is_boundary_edge_generic)::item_t == ItemType::edge); + REQUIRE(&(is_boundary_edge[EdgeId{0}]) == &(is_boundary_edge_generic[EdgeId{0}])); + + REQUIRE(isSynchronized(is_boundary_edge)); + + auto edge_is_owned = connectivity.edgeIsOwned(); + auto edge_to_face_matrix = connectivity.edgeToFaceMatrix(); + + auto is_boundary_face = connectivity.isBoundaryFace(); + + bool edge_boundary_is_valid = true; + for (EdgeId edge_id = 0; edge_id < connectivity.numberOfNodes(); ++edge_id) { + if (edge_is_owned[edge_id]) { + bool is_boundary = false; + auto edge_face_list = edge_to_face_matrix[edge_id]; + for (size_t i_face = 0; i_face < edge_face_list.size(); ++i_face) { + if (is_boundary_face[edge_face_list[i_face]]) { + is_boundary = true; + break; + } + } + + edge_boundary_is_valid &= (is_boundary == is_boundary_edge[edge_id]); + } + } + REQUIRE(edge_boundary_is_valid); + } + + SECTION("nodes") + { + auto is_boundary_node = connectivity.isBoundaryNode(); + REQUIRE(decltype(is_boundary_node)::item_t == ItemType::node); + auto is_boundary_node_generic = connectivity.isBoundary<ItemType::node>(); + REQUIRE(decltype(is_boundary_node_generic)::item_t == ItemType::node); + REQUIRE(&(is_boundary_node[NodeId{0}]) == &(is_boundary_node_generic[NodeId{0}])); + + REQUIRE(isSynchronized(is_boundary_node)); + + auto node_is_owned = connectivity.nodeIsOwned(); + auto node_to_face_matrix = connectivity.nodeToFaceMatrix(); + + auto is_boundary_face = connectivity.isBoundaryFace(); + + bool node_boundary_is_valid = true; + for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) { + if (node_is_owned[node_id]) { + bool is_boundary = false; + auto node_face_list = node_to_face_matrix[node_id]; + for (size_t i_face = 0; i_face < node_face_list.size(); ++i_face) { + if (is_boundary_face[node_face_list[i_face]]) { + is_boundary = true; + break; + } + } + + node_boundary_is_valid &= (is_boundary == is_boundary_node[node_id]); + } + } + REQUIRE(node_boundary_is_valid); + } + } + } + } + } +}