diff --git a/src/mesh/CMakeLists.txt b/src/mesh/CMakeLists.txt index 7d7caaf31cdf3d791de1915660491939efcb32fd..66ce42ae6f9e490a0b3e6e406934478b0f026f4a 100644 --- a/src/mesh/CMakeLists.txt +++ b/src/mesh/CMakeLists.txt @@ -22,10 +22,14 @@ add_library( MeshBuilderBase.cpp MeshCellZone.cpp MeshDataManager.cpp + MeshEdgeBoundary.cpp MeshFaceBoundary.cpp + MeshFlatEdgeBoundary.cpp MeshFlatFaceBoundary.cpp MeshFlatNodeBoundary.cpp MeshRelaxer.cpp + MeshLineEdgeBoundary.cpp + MeshLineFaceBoundary.cpp MeshLineNodeBoundary.cpp MeshNodeBoundary.cpp MeshRandomizer.cpp diff --git a/src/mesh/CartesianMeshBuilder.cpp b/src/mesh/CartesianMeshBuilder.cpp index c0cf86a73886b59e56f417fbf1731c78e00d15d5..9f6ea50cab88390669e3471ad3c70f25e870e390 100644 --- a/src/mesh/CartesianMeshBuilder.cpp +++ b/src/mesh/CartesianMeshBuilder.cpp @@ -2,7 +2,6 @@ #include <mesh/Connectivity.hpp> #include <mesh/LogicalConnectivityBuilder.hpp> -#include <mesh/RefId.hpp> #include <utils/Array.hpp> #include <utils/Messenger.hpp> diff --git a/src/mesh/Connectivity.cpp b/src/mesh/Connectivity.cpp index 7b81eeb0326e7cd5bc5df2b9c6d45b642dcb5e56..5622294b2af633f38b1cc283eaf0b1576498f0d4 100644 --- a/src/mesh/Connectivity.cpp +++ b/src/mesh/Connectivity.cpp @@ -95,6 +95,24 @@ Connectivity<Dimension>::_buildFrom(const ConnectivityDescriptor& descriptor) m_edge_number = WeakEdgeValue<int>(*this, node_number_array); m_edge_owner = WeakEdgeValue<int>(*this, node_owner_array); m_edge_is_owned = WeakEdgeValue<bool>(*this, node_is_owned_array); + + // edge and face references are set equal to node references + m_ref_edge_list_vector.reserve(descriptor.template refItemListVector<ItemType::node>().size()); + m_ref_face_list_vector.reserve(descriptor.template refItemListVector<ItemType::node>().size()); + for (auto ref_node_list : descriptor.template refItemListVector<ItemType::node>()) { + const RefId ref_id = ref_node_list.refId(); + Array<const NodeId> node_list = ref_node_list.list(); + Array<EdgeId> edge_list(node_list.size()); + Array<FaceId> face_list(node_list.size()); + for (size_t i = 0; i < node_list.size(); ++i) { + edge_list[i] = EdgeId::base_type{node_list[i]}; + face_list[i] = FaceId::base_type{node_list[i]}; + } + + m_ref_edge_list_vector.emplace_back(RefItemList<ItemType::edge>(ref_id, edge_list, ref_node_list.isBoundary())); + m_ref_face_list_vector.emplace_back(RefItemList<ItemType::face>(ref_id, face_list, ref_node_list.isBoundary())); + } + } else { m_item_to_item_matrix[itemTId(ItemType::face)][itemTId(ItemType::node)] = descriptor.face_to_node_vector; @@ -134,6 +152,19 @@ Connectivity<Dimension>::_buildFrom(const ConnectivityDescriptor& descriptor) m_edge_owner = WeakEdgeValue<int>(*this, face_owner_array); m_edge_is_owned = WeakEdgeValue<bool>(*this, face_is_owned_array); + // edge references are set equal to face references + m_ref_edge_list_vector.reserve(descriptor.template refItemListVector<ItemType::face>().size()); + for (auto ref_face_list : descriptor.template refItemListVector<ItemType::face>()) { + const RefId ref_id = ref_face_list.refId(); + Array<const FaceId> face_list = ref_face_list.list(); + Array<EdgeId> edge_list(face_list.size()); + for (size_t i = 0; i < face_list.size(); ++i) { + edge_list[i] = EdgeId::base_type{face_list[i]}; + } + + m_ref_edge_list_vector.emplace_back(RefItemList<ItemType::edge>(ref_id, edge_list, ref_face_list.isBoundary())); + } + } else { m_item_to_item_matrix[itemTId(ItemType::edge)][itemTId(ItemType::node)] = descriptor.edge_to_node_vector; @@ -243,7 +274,7 @@ Connectivity<Dimension>::_buildIsBoundaryNode() const template <ItemType item_type, size_t Dimension> inline void -_printReference(std::ostream& os, const Connectivity<Dimension>& connectivity) +_printReference(std::ostream& os, const Connectivity<Dimension>& connectivity, std::set<std::string>& already_printed) { auto count_all_items = [](const auto& item_is_owned) -> size_t { using ItemId = typename std::decay_t<decltype(item_is_owned)>::index_type; @@ -264,10 +295,20 @@ _printReference(std::ostream& os, const Connectivity<Dimension>& connectivity) os << "- number of " << itemName(item_type) << "s: " << rang::fgB::yellow << count_all_items(connectivity.template isOwned<item_type>()) << rang::style::reset << '\n'; - os << " " << rang::fgB::yellow << connectivity.template numberOfRefItemList<item_type>() << rang::style::reset - << " references\n"; - if (connectivity.template numberOfRefItemList<item_type>() > 0) { - for (size_t i_ref_item = 0; i_ref_item < connectivity.template numberOfRefItemList<item_type>(); ++i_ref_item) { + + // This is done to avoid printing deduced references on subitems + std::vector<size_t> to_print_list; + for (size_t i_ref_item = 0; i_ref_item < connectivity.template numberOfRefItemList<item_type>(); ++i_ref_item) { + auto ref_item_list = connectivity.template refItemList<item_type>(i_ref_item); + if (already_printed.find(ref_item_list.refId().tagName()) == already_printed.end()) { + to_print_list.push_back(i_ref_item); + already_printed.insert(ref_item_list.refId().tagName()); + } + } + + os << " " << rang::fgB::yellow << to_print_list.size() << rang::style::reset << " references\n"; + if (to_print_list.size() > 0) { + for (size_t i_ref_item : to_print_list) { auto ref_item_list = connectivity.template refItemList<item_type>(i_ref_item); os << " - " << rang::fgB::green << ref_item_list.refId().tagName() << rang::style::reset << " (" << rang::fgB::green << ref_item_list.refId().tagNumber() << rang::style::reset << ") number " @@ -281,15 +322,17 @@ template <size_t Dimension> std::ostream& Connectivity<Dimension>::_write(std::ostream& os) const { + std::set<std::string> already_printed; + os << "connectivity of dimension " << Dimension << '\n'; - _printReference<ItemType::cell>(os, *this); + _printReference<ItemType::cell>(os, *this, already_printed); if constexpr (Dimension > 1) { - _printReference<ItemType::face>(os, *this); + _printReference<ItemType::face>(os, *this, already_printed); } if constexpr (Dimension > 2) { - _printReference<ItemType::edge>(os, *this); + _printReference<ItemType::edge>(os, *this, already_printed); } - _printReference<ItemType::node>(os, *this); + _printReference<ItemType::node>(os, *this, already_printed); return os; } diff --git a/src/mesh/ConnectivityDispatcher.cpp b/src/mesh/ConnectivityDispatcher.cpp index 8c10a5a8fe73e350f1c3d7e84a718344fe4d9cf5..fd5b58c6daaf5fd44c0cb440e7af472b9f9aebbf 100644 --- a/src/mesh/ConnectivityDispatcher.cpp +++ b/src/mesh/ConnectivityDispatcher.cpp @@ -435,6 +435,17 @@ ConnectivityDispatcher<Dimension>::_buildItemReferenceList() Assert(number_of_item_list_sender < parallel::size()); + // sending is boundary property + Array<bool> ref_item_list_is_boundary{number_of_item_ref_list_per_proc[sender_rank]}; + if (parallel::rank() == sender_rank) { + for (size_t i_item_ref_list = 0; i_item_ref_list < m_connectivity.template numberOfRefItemList<item_type>(); + ++i_item_ref_list) { + auto item_ref_list = m_connectivity.template refItemList<item_type>(i_item_ref_list); + ref_item_list_is_boundary[i_item_ref_list] = item_ref_list.isBoundary(); + } + } + parallel::broadcast(ref_item_list_is_boundary, sender_rank); + // sending references tags Array<RefId::TagNumberType> ref_tag_list{number_of_item_ref_list_per_proc[sender_rank]}; if (parallel::rank() == sender_rank) { @@ -553,7 +564,8 @@ ConnectivityDispatcher<Dimension>::_buildItemReferenceList() Array<const ItemId> item_id_array = convert_to_array(item_id_vector); - m_new_descriptor.addRefItemList(RefItemList<item_type>(ref_id_list[i_ref], item_id_array)); + bool is_boundary = ref_item_list_is_boundary[i_ref]; + m_new_descriptor.addRefItemList(RefItemList<item_type>(ref_id_list[i_ref], item_id_array, is_boundary)); } } } diff --git a/src/mesh/DiamondDualConnectivityBuilder.cpp b/src/mesh/DiamondDualConnectivityBuilder.cpp index 38e62b737d772d2db7d32a2baa4134c9af94f6e9..1afa7d40413f2f8c9d88a99004a78b094d2705e5 100644 --- a/src/mesh/DiamondDualConnectivityBuilder.cpp +++ b/src/mesh/DiamondDualConnectivityBuilder.cpp @@ -230,7 +230,8 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityFrom(const IConnectivit for (size_t i = 0; i < diamond_node_list.size(); ++i) { node_array[i] = diamond_node_list[i]; } - diamond_descriptor.addRefItemList(RefNodeList{primal_ref_node_list.refId(), node_array}); + diamond_descriptor.addRefItemList( + RefNodeList{primal_ref_node_list.refId(), node_array, primal_ref_node_list.isBoundary()}); } } } @@ -283,7 +284,8 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityFrom(const IConnectivit for (size_t i = 0; i < diamond_face_list.size(); ++i) { face_array[i] = diamond_face_list[i]; } - diamond_descriptor.addRefItemList(RefFaceList{primal_ref_face_list.refId(), face_array}); + diamond_descriptor.addRefItemList( + RefFaceList{primal_ref_face_list.refId(), face_array, primal_ref_face_list.isBoundary()}); } } } @@ -347,7 +349,8 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityFrom(const IConnectivit for (size_t i = 0; i < diamond_edge_list.size(); ++i) { edge_array[i] = diamond_edge_list[i]; } - diamond_descriptor.addRefItemList(RefEdgeList{primal_ref_edge_list.refId(), edge_array}); + diamond_descriptor.addRefItemList( + RefEdgeList{primal_ref_edge_list.refId(), edge_array, primal_ref_edge_list.isBoundary()}); } } } diff --git a/src/mesh/Dual1DConnectivityBuilder.cpp b/src/mesh/Dual1DConnectivityBuilder.cpp index 39dea9ddb6ee6c3c6f26660600765e1d9e779b60..ec0389d33ce17ba32141f7fcea8e251eba08ab70 100644 --- a/src/mesh/Dual1DConnectivityBuilder.cpp +++ b/src/mesh/Dual1DConnectivityBuilder.cpp @@ -128,7 +128,8 @@ Dual1DConnectivityBuilder::_buildConnectivityFrom(const IConnectivity& i_primal_ for (size_t i = 0; i < dual_node_list.size(); ++i) { node_array[i] = dual_node_list[i]; } - dual_descriptor.addRefItemList(RefNodeList{primal_ref_node_list.refId(), node_array}); + dual_descriptor.addRefItemList( + RefNodeList{primal_ref_node_list.refId(), node_array, primal_ref_node_list.isBoundary()}); } } } diff --git a/src/mesh/GmshReader.cpp b/src/mesh/GmshReader.cpp index 15b34e1613586a38385d7a4051103b0713dafef2..f12e6ab3c6b17e3ae31110b91789ba8188b53d7c 100644 --- a/src/mesh/GmshReader.cpp +++ b/src/mesh/GmshReader.cpp @@ -55,13 +55,31 @@ GmshConnectivityBuilder<1>::GmshConnectivityBuilder(const GmshReader::GmshData& ref_points_map[ref].push_back(point_number); } + Array<size_t> node_nb_cell(descriptor.node_number_vector.size()); + node_nb_cell.fill(0); + + for (size_t j = 0; j < nb_cells; ++j) { + for (int r = 0; r < 2; ++r) { + node_nb_cell[descriptor.cell_to_node_vector[j][r]] += 1; + } + } + for (const auto& ref_point_list : ref_points_map) { Array<NodeId> point_list(ref_point_list.second.size()); for (size_t j = 0; j < ref_point_list.second.size(); ++j) { point_list[j] = ref_point_list.second[j]; } const GmshReader::PhysicalRefId& physical_ref_id = gmsh_data.m_physical_ref_map.at(ref_point_list.first); - descriptor.addRefItemList(RefNodeList(physical_ref_id.refId(), point_list)); + + bool is_boundary = true; + for (size_t i_node = 0; i_node < point_list.size(); ++i_node) { + if (node_nb_cell[point_list[i_node]] > 1) { + is_boundary = false; + break; + } + } + + descriptor.addRefItemList(RefNodeList(physical_ref_id.refId(), point_list, is_boundary)); } std::map<unsigned int, std::vector<unsigned int>> ref_cells_map; @@ -77,7 +95,7 @@ GmshConnectivityBuilder<1>::GmshConnectivityBuilder(const GmshReader::GmshData& cell_list[j] = ref_cell_list.second[j]; } const GmshReader::PhysicalRefId& physical_ref_id = gmsh_data.m_physical_ref_map.at(ref_cell_list.first); - descriptor.addRefItemList(RefCellList(physical_ref_id.refId(), cell_list)); + descriptor.addRefItemList(RefCellList(physical_ref_id.refId(), cell_list, false)); } descriptor.cell_owner_vector.resize(nb_cells); @@ -139,7 +157,7 @@ GmshConnectivityBuilder<2>::GmshConnectivityBuilder(const GmshReader::GmshData& cell_list[j] = ref_cell_list.second[j]; } const GmshReader::PhysicalRefId& physical_ref_id = gmsh_data.m_physical_ref_map.at(ref_cell_list.first); - descriptor.addRefItemList(RefCellList(physical_ref_id.refId(), cell_list)); + descriptor.addRefItemList(RefCellList(physical_ref_id.refId(), cell_list, false)); } ConnectivityBuilderBase::_computeCellFaceAndFaceNodeConnectivities<2>(descriptor); @@ -196,13 +214,42 @@ GmshConnectivityBuilder<2>::GmshConnectivityBuilder(const GmshReader::GmshData& } } + Array<size_t> face_nb_cell(descriptor.face_number_vector.size()); + face_nb_cell.fill(0); + + for (size_t j = 0; j < descriptor.cell_to_face_vector.size(); ++j) { + for (size_t l = 0; l < descriptor.cell_to_face_vector[j].size(); ++l) { + face_nb_cell[descriptor.cell_to_face_vector[j][l]] += 1; + } + } + for (const auto& ref_face_list : ref_faces_map) { Array<FaceId> face_list(ref_face_list.second.size()); for (size_t j = 0; j < ref_face_list.second.size(); ++j) { face_list[j] = ref_face_list.second[j]; } const GmshReader::PhysicalRefId& physical_ref_id = gmsh_data.m_physical_ref_map.at(ref_face_list.first); - descriptor.addRefItemList(RefFaceList{physical_ref_id.refId(), face_list}); + + bool is_boundary = true; + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + if (face_nb_cell[face_list[i_face]] > 1) { + is_boundary = false; + break; + } + } + + descriptor.addRefItemList(RefFaceList{physical_ref_id.refId(), face_list, is_boundary}); + } + + Array<bool> is_boundary_node(descriptor.node_number_vector.size()); + is_boundary_node.fill(false); + + for (size_t i_face = 0; i_face < face_nb_cell.size(); ++i_face) { + if (face_nb_cell[i_face] == 1) { + for (size_t node_id : descriptor.face_to_node_vector[i_face]) { + is_boundary_node[node_id] = true; + } + } } std::map<unsigned int, std::vector<unsigned int>> ref_points_map; @@ -218,7 +265,15 @@ GmshConnectivityBuilder<2>::GmshConnectivityBuilder(const GmshReader::GmshData& point_list[j] = ref_point_list.second[j]; } const GmshReader::PhysicalRefId& physical_ref_id = gmsh_data.m_physical_ref_map.at(ref_point_list.first); - descriptor.addRefItemList(RefNodeList(physical_ref_id.refId(), point_list)); + + bool is_boundary = true; + for (size_t i_node = 0; i_node < point_list.size(); ++i_node) { + if (not is_boundary_node[point_list[i_node]]) { + is_boundary = false; + } + } + + descriptor.addRefItemList(RefNodeList(physical_ref_id.refId(), point_list, is_boundary)); } descriptor.cell_owner_vector.resize(nb_cells); @@ -317,13 +372,22 @@ GmshConnectivityBuilder<3>::GmshConnectivityBuilder(const GmshReader::GmshData& cell_list[j] = ref_cell_list.second[j]; } const GmshReader::PhysicalRefId& physical_ref_id = gmsh_data.m_physical_ref_map.at(ref_cell_list.first); - descriptor.addRefItemList(RefCellList(physical_ref_id.refId(), cell_list)); + descriptor.addRefItemList(RefCellList(physical_ref_id.refId(), cell_list, false)); } ConnectivityBuilderBase::_computeCellFaceAndFaceNodeConnectivities<3>(descriptor); const auto& node_number_vector = descriptor.node_number_vector; + Array<size_t> face_nb_cell(descriptor.face_number_vector.size()); + face_nb_cell.fill(0); + + for (size_t j = 0; j < descriptor.cell_to_face_vector.size(); ++j) { + for (size_t l = 0; l < descriptor.cell_to_face_vector[j].size(); ++l) { + face_nb_cell[descriptor.cell_to_face_vector[j][l]] += 1; + } + } + { using Face = ConnectivityFace<3>; const std::unordered_map<Face, FaceId, typename Face::Hash> face_to_id_map = [&] { @@ -417,13 +481,33 @@ GmshConnectivityBuilder<3>::GmshConnectivityBuilder(const GmshReader::GmshData& face_list[j] = ref_face_list.second[j]; } const GmshReader::PhysicalRefId& physical_ref_id = gmsh_data.m_physical_ref_map.at(ref_face_list.first); - descriptor.addRefItemList(RefFaceList{physical_ref_id.refId(), face_list}); + + bool is_boundary = true; + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + if (face_nb_cell[face_list[i_face]] > 1) { + is_boundary = false; + break; + } + } + + descriptor.addRefItemList(RefFaceList{physical_ref_id.refId(), face_list, is_boundary}); } } ConnectivityBuilderBase::_computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities<3>(descriptor); { + Array<bool> is_boundary_edge(descriptor.edge_number_vector.size()); + is_boundary_edge.fill(false); + + for (size_t i_face = 0; i_face < face_nb_cell.size(); ++i_face) { + if (face_nb_cell[i_face] == 1) { + for (size_t node_id : descriptor.face_to_edge_vector[i_face]) { + is_boundary_edge[node_id] = true; + } + } + } + using Edge = ConnectivityFace<2>; const auto& node_number_vector = descriptor.node_number_vector; const std::unordered_map<Edge, EdgeId, typename Edge::Hash> edge_to_id_map = [&] { @@ -482,7 +566,26 @@ GmshConnectivityBuilder<3>::GmshConnectivityBuilder(const GmshReader::GmshData& edge_list[j] = ref_edge_list.second[j]; } const GmshReader::PhysicalRefId& physical_ref_id = gmsh_data.m_physical_ref_map.at(ref_edge_list.first); - descriptor.addRefItemList(RefEdgeList{physical_ref_id.refId(), edge_list}); + + bool is_boundary = true; + for (size_t i_node = 0; i_node < edge_list.size(); ++i_node) { + if (not is_boundary_edge[edge_list[i_node]]) { + is_boundary = false; + } + } + + descriptor.addRefItemList(RefEdgeList{physical_ref_id.refId(), edge_list, is_boundary}); + } + } + + Array<bool> is_boundary_node(descriptor.node_number_vector.size()); + is_boundary_node.fill(false); + + for (size_t i_face = 0; i_face < face_nb_cell.size(); ++i_face) { + if (face_nb_cell[i_face] == 1) { + for (size_t node_id : descriptor.face_to_node_vector[i_face]) { + is_boundary_node[node_id] = true; + } } } @@ -499,7 +602,15 @@ GmshConnectivityBuilder<3>::GmshConnectivityBuilder(const GmshReader::GmshData& point_list[j] = ref_point_list.second[j]; } const GmshReader::PhysicalRefId& physical_ref_id = gmsh_data.m_physical_ref_map.at(ref_point_list.first); - descriptor.addRefItemList(RefNodeList(physical_ref_id.refId(), point_list)); + + bool is_boundary = true; + for (size_t i_node = 0; i_node < point_list.size(); ++i_node) { + if (not is_boundary_node[point_list[i_node]]) { + is_boundary = false; + } + } + + descriptor.addRefItemList(RefNodeList(physical_ref_id.refId(), point_list, is_boundary)); } descriptor.cell_owner_vector.resize(nb_cells); @@ -1010,8 +1121,7 @@ GmshReader::__readPeriodic2_2() // std::fill(descriptor.cell_owner_vector.begin(), descriptor.cell_owner_vector.end(), parallel::rank()); // descriptor.face_owner_vector.resize(descriptor.face_number_vector.size()); -// std::fill(descriptor.face_owner_vector.begin(), descriptor.face_owner_vector.end(), parallel::rank()); - +// std::fill(descriptor.face_owner_vector.begin(), descriptor.face_owner_vector.end(), parallel::rank());// // descriptor.edge_owner_vector.resize(descriptor.edge_number_vector.size()); // std::fill(descriptor.edge_owner_vector.begin(), descriptor.edge_owner_vector.end(), parallel::rank()); diff --git a/src/mesh/LogicalConnectivityBuilder.cpp b/src/mesh/LogicalConnectivityBuilder.cpp index db14ff1df24cede3afcc7d77e96950b185274546..41092faaf90a9a3f9a24ac1c561304373ba748d0 100644 --- a/src/mesh/LogicalConnectivityBuilder.cpp +++ b/src/mesh/LogicalConnectivityBuilder.cpp @@ -24,13 +24,13 @@ LogicalConnectivityBuilder::_buildBoundaryNodeList( { // xmin Array<NodeId> boundary_nodes(1); boundary_nodes[0] = 0; - descriptor.addRefItemList(RefNodeList{RefId{0, "XMIN"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{0, "XMIN"}, boundary_nodes, true}); } { // xmax Array<NodeId> boundary_nodes(1); boundary_nodes[0] = cell_size[0]; - descriptor.addRefItemList(RefNodeList{RefId{1, "XMAX"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{1, "XMAX"}, boundary_nodes, true}); } } @@ -47,25 +47,25 @@ LogicalConnectivityBuilder::_buildBoundaryNodeList( { // xminymin Array<NodeId> boundary_nodes(1); boundary_nodes[0] = node_number(0, 0); - descriptor.addRefItemList(RefNodeList{RefId{10, "XMINYMIN"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{10, "XMINYMIN"}, boundary_nodes, true}); } { // xmaxymin Array<NodeId> boundary_nodes(1); boundary_nodes[0] = node_number(cell_size[0], 0); - descriptor.addRefItemList(RefNodeList{RefId{11, "XMAXYMIN"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{11, "XMAXYMIN"}, boundary_nodes, true}); } { // xmaxymax Array<NodeId> boundary_nodes(1); boundary_nodes[0] = node_number(cell_size[0], cell_size[1]); - descriptor.addRefItemList(RefNodeList{RefId{12, "XMAXYMAX"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{12, "XMAXYMAX"}, boundary_nodes, true}); } { // xminymax Array<NodeId> boundary_nodes(1); boundary_nodes[0] = node_number(0, cell_size[1]); - descriptor.addRefItemList(RefNodeList{RefId{13, "XMINYMAX"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{13, "XMINYMAX"}, boundary_nodes, true}); } } @@ -83,49 +83,49 @@ LogicalConnectivityBuilder::_buildBoundaryNodeList(const TinyVector<3, uint64_t> { // xminyminzmin Array<NodeId> boundary_nodes(1); boundary_nodes[0] = node_number(0, 0, 0); - descriptor.addRefItemList(RefNodeList{RefId{10, "XMINYMINZMIN"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{10, "XMINYMINZMIN"}, boundary_nodes, true}); } { // xmaxyminzmin Array<NodeId> boundary_nodes(1); boundary_nodes[0] = node_number(cell_size[0], 0, 0); - descriptor.addRefItemList(RefNodeList{RefId{11, "XMAXYMINZMIN"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{11, "XMAXYMINZMIN"}, boundary_nodes, true}); } { // xmaxymaxzmin Array<NodeId> boundary_nodes(1); boundary_nodes[0] = node_number(cell_size[0], cell_size[1], 0); - descriptor.addRefItemList(RefNodeList{RefId{12, "XMAXYMAXZMIN"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{12, "XMAXYMAXZMIN"}, boundary_nodes, true}); } { // xminymaxzmin Array<NodeId> boundary_nodes(1); boundary_nodes[0] = node_number(0, cell_size[1], 0); - descriptor.addRefItemList(RefNodeList{RefId{13, "XMINYMAXZMIN"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{13, "XMINYMAXZMIN"}, boundary_nodes, true}); } { // xminyminzmax Array<NodeId> boundary_nodes(1); boundary_nodes[0] = node_number(0, 0, cell_size[2]); - descriptor.addRefItemList(RefNodeList{RefId{14, "XMINYMINZMAX"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{14, "XMINYMINZMAX"}, boundary_nodes, true}); } { // xmaxyminzmax Array<NodeId> boundary_nodes(1); boundary_nodes[0] = node_number(cell_size[0], 0, cell_size[2]); - descriptor.addRefItemList(RefNodeList{RefId{15, "XMAXYMINZMAX"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{15, "XMAXYMINZMAX"}, boundary_nodes, true}); } { // xmaxymaxzmax Array<NodeId> boundary_nodes(1); boundary_nodes[0] = node_number(cell_size[0], cell_size[1], cell_size[2]); - descriptor.addRefItemList(RefNodeList{RefId{16, "XMAXYMAXZMAX"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{16, "XMAXYMAXZMAX"}, boundary_nodes, true}); } { // xminymaxzmax Array<NodeId> boundary_nodes(1); boundary_nodes[0] = node_number(0, cell_size[1], cell_size[2]); - descriptor.addRefItemList(RefNodeList{RefId{17, "XMINYMAXZMAX"}, boundary_nodes}); + descriptor.addRefItemList(RefNodeList{RefId{17, "XMINYMAXZMAX"}, boundary_nodes, true}); } } @@ -181,7 +181,7 @@ LogicalConnectivityBuilder::_buildBoundaryEdgeList(const TinyVector<3, uint64_t> boundary_edges[l++] = i_edge->second; } Assert(l == cell_size[2]); - descriptor.addRefItemList(RefEdgeList{RefId{ref_id, ref_name}, boundary_edges}); + descriptor.addRefItemList(RefEdgeList{RefId{ref_id, ref_name}, boundary_edges, true}); }; add_ref_item_list_along_z(0, 0, 20, "XMINYMIN"); @@ -205,7 +205,7 @@ LogicalConnectivityBuilder::_buildBoundaryEdgeList(const TinyVector<3, uint64_t> boundary_edges[l++] = i_edge->second; } Assert(l == cell_size[1]); - descriptor.addRefItemList(RefEdgeList{RefId{ref_id, ref_name}, boundary_edges}); + descriptor.addRefItemList(RefEdgeList{RefId{ref_id, ref_name}, boundary_edges, true}); }; add_ref_item_list_along_y(0, 0, 24, "XMINZMIN"); @@ -229,7 +229,7 @@ LogicalConnectivityBuilder::_buildBoundaryEdgeList(const TinyVector<3, uint64_t> boundary_edges[l++] = i_edge->second; } Assert(l == cell_size[0]); - descriptor.addRefItemList(RefEdgeList{RefId{ref_id, ref_name}, boundary_edges}); + descriptor.addRefItemList(RefEdgeList{RefId{ref_id, ref_name}, boundary_edges, true}); }; add_ref_item_list_along_x(0, 0, 28, "YMINZMIN"); @@ -267,7 +267,7 @@ LogicalConnectivityBuilder::_buildBoundaryFaceList( boundary_faces[j] = face_id; } - descriptor.addRefItemList(RefFaceList{RefId{0, "XMIN"}, boundary_faces}); + descriptor.addRefItemList(RefFaceList{RefId{0, "XMIN"}, boundary_faces, true}); } { // xmax @@ -281,7 +281,7 @@ LogicalConnectivityBuilder::_buildBoundaryFaceList( boundary_faces[j] = face_id; } - descriptor.addRefItemList(RefFaceList{RefId{1, "XMAX"}, boundary_faces}); + descriptor.addRefItemList(RefFaceList{RefId{1, "XMAX"}, boundary_faces, true}); } { // ymin @@ -295,7 +295,7 @@ LogicalConnectivityBuilder::_buildBoundaryFaceList( boundary_faces[i] = face_id; } - descriptor.addRefItemList(RefFaceList{RefId{2, "YMIN"}, boundary_faces}); + descriptor.addRefItemList(RefFaceList{RefId{2, "YMIN"}, boundary_faces, true}); } { // ymax @@ -309,7 +309,7 @@ LogicalConnectivityBuilder::_buildBoundaryFaceList( boundary_faces[i] = face_id; } - descriptor.addRefItemList(RefFaceList{RefId{3, "YMAX"}, boundary_faces}); + descriptor.addRefItemList(RefFaceList{RefId{3, "YMAX"}, boundary_faces, true}); } } @@ -362,7 +362,7 @@ LogicalConnectivityBuilder::_buildBoundaryFaceList(const TinyVector<3, uint64_t> } } Assert(l == cell_size[1] * cell_size[2]); - descriptor.addRefItemList(RefFaceList{RefId{ref_id, ref_name}, boundary_faces}); + descriptor.addRefItemList(RefFaceList{RefId{ref_id, ref_name}, boundary_faces, true}); }; add_ref_item_list_for_x(0, 0, "XMIN"); @@ -388,7 +388,7 @@ LogicalConnectivityBuilder::_buildBoundaryFaceList(const TinyVector<3, uint64_t> } } Assert(l == cell_size[0] * cell_size[2]); - descriptor.addRefItemList(RefFaceList{RefId{ref_id, ref_name}, boundary_faces}); + descriptor.addRefItemList(RefFaceList{RefId{ref_id, ref_name}, boundary_faces, true}); }; add_ref_item_list_for_y(0, 2, "YMIN"); @@ -414,7 +414,7 @@ LogicalConnectivityBuilder::_buildBoundaryFaceList(const TinyVector<3, uint64_t> } } Assert(l == cell_size[0] * cell_size[1]); - descriptor.addRefItemList(RefFaceList{RefId{ref_id, ref_name}, boundary_faces}); + descriptor.addRefItemList(RefFaceList{RefId{ref_id, ref_name}, boundary_faces, true}); }; add_ref_item_list_for_z(0, 4, "ZMIN"); diff --git a/src/mesh/MedianDualConnectivityBuilder.cpp b/src/mesh/MedianDualConnectivityBuilder.cpp index 4ed951bc374c4f639b89e20e56264f838dfcf625..8ba8046c074355d702b6c3ec599d13232e080a0c 100644 --- a/src/mesh/MedianDualConnectivityBuilder.cpp +++ b/src/mesh/MedianDualConnectivityBuilder.cpp @@ -291,7 +291,8 @@ MedianDualConnectivityBuilder::_buildConnectivityFrom<2>(const IConnectivity& i_ }(); if (parallel::allReduceOr(dual_node_list.size() > 0)) { - dual_descriptor.addRefItemList(RefNodeList{primal_ref_node_list.refId(), convert_to_array(dual_node_list)}); + dual_descriptor.addRefItemList(RefNodeList{primal_ref_node_list.refId(), convert_to_array(dual_node_list), + primal_ref_node_list.isBoundary()}); } } } @@ -344,8 +345,9 @@ MedianDualConnectivityBuilder::_buildConnectivityFrom<2>(const IConnectivity& i_ }(); if (parallel::allReduceOr(boundary_dual_face_id_list.size() > 0)) { - dual_descriptor.addRefItemList( - RefFaceList{primal_ref_face_list.refId(), convert_to_array(boundary_dual_face_id_list)}); + dual_descriptor.addRefItemList(RefFaceList{primal_ref_face_list.refId(), + convert_to_array(boundary_dual_face_id_list), + primal_ref_face_list.isBoundary()}); } } diff --git a/src/mesh/MeshCellZone.cpp b/src/mesh/MeshCellZone.cpp index 240d8ea4f1be27e879d0455d312f12f67545e7fb..ac63998b6edc658f39b9b9a1a11cf72f3bcf1c0a 100644 --- a/src/mesh/MeshCellZone.cpp +++ b/src/mesh/MeshCellZone.cpp @@ -9,9 +9,6 @@ MeshCellZone<Dimension>::MeshCellZone(const Mesh<Connectivity<Dimension>>&, cons : m_cell_list(ref_cell_list.list()), m_zone_name(ref_cell_list.refId().tagName()) {} -template MeshCellZone<2>::MeshCellZone(const Mesh<Connectivity<2>>&, const RefCellList&); -template MeshCellZone<3>::MeshCellZone(const Mesh<Connectivity<3>>&, const RefCellList&); - template <size_t Dimension> MeshCellZone<Dimension> getMeshCellZone(const Mesh<Connectivity<Dimension>>& mesh, const IZoneDescriptor& zone_descriptor) @@ -26,17 +23,7 @@ getMeshCellZone(const Mesh<Connectivity<Dimension>>& mesh, const IZoneDescriptor } std::ostringstream ost; - ost << "cannot find zone with name " << rang::fgB::red << zone_descriptor << rang::style::reset << '\n'; - ost << "The mesh contains " << mesh.connectivity().template numberOfRefItemList<ItemType::cell>() << " zones: "; - for (size_t i_ref_cell_list = 0; i_ref_cell_list < mesh.connectivity().template numberOfRefItemList<ItemType::cell>(); - ++i_ref_cell_list) { - const auto& ref_cell_list = mesh.connectivity().template refItemList<ItemType::cell>(i_ref_cell_list); - const RefId& ref = ref_cell_list.refId(); - if (i_ref_cell_list > 0) { - ost << ", "; - } - ost << rang::fgB::yellow << ref << rang::style::reset; - } + ost << "cannot find cell set with name \"" << rang::fgB::red << zone_descriptor << rang::style::reset << '\"'; throw NormalError(ost.str()); } diff --git a/src/mesh/MeshEdgeBoundary.cpp b/src/mesh/MeshEdgeBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb522e5b01d3b2498459ec4bdd187693f8cf809f --- /dev/null +++ b/src/mesh/MeshEdgeBoundary.cpp @@ -0,0 +1,110 @@ +#include <mesh/MeshEdgeBoundary.hpp> + +#include <Kokkos_Vector.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <utils/Messenger.hpp> + +template <size_t Dimension> +MeshEdgeBoundary<Dimension>::MeshEdgeBoundary(const Mesh<Connectivity<Dimension>>&, const RefEdgeList& ref_edge_list) + : m_ref_edge_list(ref_edge_list) +{} + +template MeshEdgeBoundary<1>::MeshEdgeBoundary(const Mesh<Connectivity<1>>&, const RefEdgeList&); +template MeshEdgeBoundary<2>::MeshEdgeBoundary(const Mesh<Connectivity<2>>&, const RefEdgeList&); +template MeshEdgeBoundary<3>::MeshEdgeBoundary(const Mesh<Connectivity<3>>&, const RefEdgeList&); + +template <size_t Dimension> +MeshEdgeBoundary<Dimension>::MeshEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, + const RefFaceList& ref_face_list) +{ + const Array<const FaceId>& face_list = ref_face_list.list(); + static_assert(Dimension > 1, "conversion from to edge from face is valid in dimension > 1"); + + if constexpr (Dimension > 2) { + Kokkos::vector<unsigned int> edge_ids; + // not enough but should reduce significantly the number of resizing + edge_ids.reserve(Dimension * face_list.size()); + const auto& face_to_edge_matrix = mesh.connectivity().faceToEdgeMatrix(); + + for (size_t l = 0; l < face_list.size(); ++l) { + const FaceId face_number = face_list[l]; + const auto& face_edges = face_to_edge_matrix[face_number]; + + for (size_t e = 0; e < face_edges.size(); ++e) { + edge_ids.push_back(face_edges[e]); + } + } + std::sort(edge_ids.begin(), edge_ids.end()); + auto last = std::unique(edge_ids.begin(), edge_ids.end()); + edge_ids.resize(std::distance(edge_ids.begin(), last)); + + Array<EdgeId> edge_list(edge_ids.size()); + parallel_for( + edge_ids.size(), PUGS_LAMBDA(int r) { edge_list[r] = edge_ids[r]; }); + m_ref_edge_list = RefEdgeList{ref_face_list.refId(), edge_list, ref_face_list.isBoundary()}; + } else if constexpr (Dimension == 2) { + Array<EdgeId> edge_list(face_list.size()); + parallel_for( + face_list.size(), PUGS_LAMBDA(int r) { edge_list[r] = static_cast<FaceId::base_type>(face_list[r]); }); + m_ref_edge_list = RefEdgeList{ref_face_list.refId(), edge_list, ref_face_list.isBoundary()}; + } + + // This is quite dirty but it allows a non negligible performance + // improvement + const_cast<Connectivity<Dimension>&>(mesh.connectivity()).addRefItemList(m_ref_edge_list); +} + +template MeshEdgeBoundary<2>::MeshEdgeBoundary(const Mesh<Connectivity<2>>&, const RefFaceList&); +template MeshEdgeBoundary<3>::MeshEdgeBoundary(const Mesh<Connectivity<3>>&, const RefFaceList&); + +template <size_t Dimension> +MeshEdgeBoundary<Dimension> +getMeshEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor) +{ + for (size_t i_ref_edge_list = 0; i_ref_edge_list < mesh.connectivity().template numberOfRefItemList<ItemType::edge>(); + ++i_ref_edge_list) { + const auto& ref_edge_list = mesh.connectivity().template refItemList<ItemType::edge>(i_ref_edge_list); + const RefId& ref = ref_edge_list.refId(); + + if (ref == boundary_descriptor) { + auto edge_list = ref_edge_list.list(); + if (not ref_edge_list.isBoundary()) { + std::ostringstream ost; + ost << "invalid boundary " << rang::fgB::yellow << boundary_descriptor << rang::style::reset + << ": inner edges cannot be used to define mesh boundaries"; + throw NormalError(ost.str()); + } + + return MeshEdgeBoundary<Dimension>{mesh, ref_edge_list}; + } + } + if constexpr (Dimension > 1) { + for (size_t i_ref_face_list = 0; + i_ref_face_list < mesh.connectivity().template numberOfRefItemList<ItemType::face>(); ++i_ref_face_list) { + const auto& ref_face_list = mesh.connectivity().template refItemList<ItemType::face>(i_ref_face_list); + const RefId& ref = ref_face_list.refId(); + + if (ref == boundary_descriptor) { + auto face_list = ref_face_list.list(); + if (not ref_face_list.isBoundary()) { + std::ostringstream ost; + ost << "invalid boundary " << rang::fgB::yellow << boundary_descriptor << rang::style::reset + << ": inner edges cannot be used to define mesh boundaries"; + throw NormalError(ost.str()); + } + + return MeshEdgeBoundary<Dimension>{mesh, ref_face_list}; + } + } + } + + std::ostringstream ost; + ost << "cannot find edge list with name " << rang::fgB::red << boundary_descriptor << rang::style::reset; + + throw NormalError(ost.str()); +} + +template MeshEdgeBoundary<1> getMeshEdgeBoundary(const Mesh<Connectivity<1>>&, const IBoundaryDescriptor&); +template MeshEdgeBoundary<2> getMeshEdgeBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&); +template MeshEdgeBoundary<3> getMeshEdgeBoundary(const Mesh<Connectivity<3>>&, const IBoundaryDescriptor&); diff --git a/src/mesh/MeshEdgeBoundary.hpp b/src/mesh/MeshEdgeBoundary.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2f2cd9a5222dd2e09e063965c2949c392bde12b0 --- /dev/null +++ b/src/mesh/MeshEdgeBoundary.hpp @@ -0,0 +1,60 @@ +#ifndef MESH_EDGE_BOUNDARY_HPP +#define MESH_EDGE_BOUNDARY_HPP + +#include <algebra/TinyVector.hpp> +#include <mesh/IBoundaryDescriptor.hpp> +#include <mesh/RefItemList.hpp> +#include <utils/Array.hpp> + +template <size_t Dimension> +class Connectivity; + +template <typename ConnectivityType> +class Mesh; + +template <size_t Dimension> +class [[nodiscard]] MeshEdgeBoundary // clazy:exclude=copyable-polymorphic +{ + protected: + RefEdgeList m_ref_edge_list; + + std::array<TinyVector<Dimension>, Dimension*(Dimension - 1)> _getBounds(const Mesh<Connectivity<Dimension>>& mesh) + const; + + public: + template <size_t MeshDimension> + friend MeshEdgeBoundary<MeshDimension> getMeshEdgeBoundary(const Mesh<Connectivity<MeshDimension>>& mesh, + const IBoundaryDescriptor& boundary_descriptor); + + MeshEdgeBoundary& operator=(const MeshEdgeBoundary&) = default; + MeshEdgeBoundary& operator=(MeshEdgeBoundary&&) = default; + + PUGS_INLINE + const RefEdgeList& refEdgeList() const + { + return m_ref_edge_list; + } + + PUGS_INLINE + const Array<const EdgeId>& edgeList() const + { + return m_ref_edge_list.list(); + } + + protected: + MeshEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefEdgeList& ref_edge_list); + MeshEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefFaceList& ref_face_list); + + public: + MeshEdgeBoundary(const MeshEdgeBoundary&) = default; // LCOV_EXCL_LINE + MeshEdgeBoundary(MeshEdgeBoundary &&) = default; // LCOV_EXCL_LINE + + MeshEdgeBoundary() = default; + virtual ~MeshEdgeBoundary() = default; +}; + +template <size_t Dimension> +MeshEdgeBoundary<Dimension> getMeshEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, + const IBoundaryDescriptor& boundary_descriptor); + +#endif // MESH_EDGE_BOUNDARY_HPP diff --git a/src/mesh/MeshFaceBoundary.cpp b/src/mesh/MeshFaceBoundary.cpp index 3690c5c0c614a844060fa4c266e75fdcea42e377..ce18bd150ebb0810ae00b15bb7fc59ea919be7bd 100644 --- a/src/mesh/MeshFaceBoundary.cpp +++ b/src/mesh/MeshFaceBoundary.cpp @@ -6,9 +6,10 @@ template <size_t Dimension> MeshFaceBoundary<Dimension>::MeshFaceBoundary(const Mesh<Connectivity<Dimension>>&, const RefFaceList& ref_face_list) - : m_face_list(ref_face_list.list()), m_boundary_name(ref_face_list.refId().tagName()) + : m_ref_face_list(ref_face_list) {} +template MeshFaceBoundary<1>::MeshFaceBoundary(const Mesh<Connectivity<1>>&, const RefFaceList&); template MeshFaceBoundary<2>::MeshFaceBoundary(const Mesh<Connectivity<2>>&, const RefFaceList&); template MeshFaceBoundary<3>::MeshFaceBoundary(const Mesh<Connectivity<3>>&, const RefFaceList&); @@ -20,13 +21,22 @@ getMeshFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDe ++i_ref_face_list) { const auto& ref_face_list = mesh.connectivity().template refItemList<ItemType::face>(i_ref_face_list); const RefId& ref = ref_face_list.refId(); + if (ref == boundary_descriptor) { + auto face_list = ref_face_list.list(); + if (not ref_face_list.isBoundary()) { + std::ostringstream ost; + ost << "invalid boundary " << rang::fgB::yellow << boundary_descriptor << rang::style::reset + << ": inner faces cannot be used to define mesh boundaries"; + throw NormalError(ost.str()); + } + return MeshFaceBoundary<Dimension>{mesh, ref_face_list}; } } std::ostringstream ost; - ost << "cannot find surface with name " << rang::fgB::red << boundary_descriptor << rang::style::reset; + ost << "cannot find face list with name " << rang::fgB::red << boundary_descriptor << rang::style::reset; throw NormalError(ost.str()); } diff --git a/src/mesh/MeshFaceBoundary.hpp b/src/mesh/MeshFaceBoundary.hpp index a45108aa5893c798230f41e14a8cacb071c60c0b..fd52128a3c960b43c5e432416c7e46a900d8eade 100644 --- a/src/mesh/MeshFaceBoundary.hpp +++ b/src/mesh/MeshFaceBoundary.hpp @@ -16,8 +16,7 @@ template <size_t Dimension> class [[nodiscard]] MeshFaceBoundary // clazy:exclude=copyable-polymorphic { protected: - Array<const FaceId> m_face_list; - std::string m_boundary_name; + RefFaceList m_ref_face_list; std::array<TinyVector<Dimension>, Dimension*(Dimension - 1)> _getBounds(const Mesh<Connectivity<Dimension>>& mesh) const; @@ -30,17 +29,24 @@ class [[nodiscard]] MeshFaceBoundary // clazy:exclude=copyable-polymorphic MeshFaceBoundary& operator=(const MeshFaceBoundary&) = default; MeshFaceBoundary& operator=(MeshFaceBoundary&&) = default; + PUGS_INLINE + const RefFaceList& refFaceList() const + { + return m_ref_face_list; + } + + PUGS_INLINE const Array<const FaceId>& faceList() const { - return m_face_list; + return m_ref_face_list.list(); } protected: MeshFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefFaceList& ref_face_list); public: - MeshFaceBoundary(const MeshFaceBoundary&) = default; - MeshFaceBoundary(MeshFaceBoundary &&) = default; + MeshFaceBoundary(const MeshFaceBoundary&) = default; // LCOV_EXCL_LINE + MeshFaceBoundary(MeshFaceBoundary &&) = default; // LCOV_EXCL_LINE MeshFaceBoundary() = default; virtual ~MeshFaceBoundary() = default; diff --git a/src/mesh/MeshFlatEdgeBoundary.cpp b/src/mesh/MeshFlatEdgeBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d4c1ec155a0e8c4d863facbf6280d71265944933 --- /dev/null +++ b/src/mesh/MeshFlatEdgeBoundary.cpp @@ -0,0 +1,20 @@ +#include <mesh/MeshFlatEdgeBoundary.hpp> + +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshFlatNodeBoundary.hpp> + +template <size_t Dimension> +MeshFlatEdgeBoundary<Dimension> +getMeshFlatEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor) +{ + MeshEdgeBoundary<Dimension> mesh_edge_boundary = getMeshEdgeBoundary(mesh, boundary_descriptor); + MeshFlatNodeBoundary<Dimension> mesh_flat_node_boundary = getMeshFlatNodeBoundary(mesh, boundary_descriptor); + + return MeshFlatEdgeBoundary<Dimension>{mesh, mesh_edge_boundary.refEdgeList(), + mesh_flat_node_boundary.outgoingNormal()}; +} + +template MeshFlatEdgeBoundary<1> getMeshFlatEdgeBoundary(const Mesh<Connectivity<1>>&, const IBoundaryDescriptor&); +template MeshFlatEdgeBoundary<2> getMeshFlatEdgeBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&); +template MeshFlatEdgeBoundary<3> getMeshFlatEdgeBoundary(const Mesh<Connectivity<3>>&, const IBoundaryDescriptor&); diff --git a/src/mesh/MeshFlatEdgeBoundary.hpp b/src/mesh/MeshFlatEdgeBoundary.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a104d1413a7319f08b74bf1ef103a5157b803f18 --- /dev/null +++ b/src/mesh/MeshFlatEdgeBoundary.hpp @@ -0,0 +1,46 @@ +#ifndef MESH_FLAT_EDGE_BOUNDARY_HPP +#define MESH_FLAT_EDGE_BOUNDARY_HPP + +#include <mesh/MeshEdgeBoundary.hpp> + +template <size_t Dimension> +class MeshFlatEdgeBoundary final : public MeshEdgeBoundary<Dimension> // clazy:exclude=copyable-polymorphic +{ + public: + using Rd = TinyVector<Dimension, double>; + + private: + const Rd m_outgoing_normal; + + public: + const Rd& + outgoingNormal() const + { + return m_outgoing_normal; + } + + MeshFlatEdgeBoundary& operator=(const MeshFlatEdgeBoundary&) = default; + MeshFlatEdgeBoundary& operator=(MeshFlatEdgeBoundary&&) = default; + + template <size_t MeshDimension> + friend MeshFlatEdgeBoundary<MeshDimension> getMeshFlatEdgeBoundary(const Mesh<Connectivity<MeshDimension>>& mesh, + const IBoundaryDescriptor& boundary_descriptor); + + private: + template <typename MeshType> + MeshFlatEdgeBoundary(const MeshType& mesh, const RefEdgeList& ref_edge_list, const Rd& outgoing_normal) + : MeshEdgeBoundary<Dimension>(mesh, ref_edge_list), m_outgoing_normal(outgoing_normal) + {} + + public: + MeshFlatEdgeBoundary() = default; + MeshFlatEdgeBoundary(const MeshFlatEdgeBoundary&) = default; + MeshFlatEdgeBoundary(MeshFlatEdgeBoundary&&) = default; + ~MeshFlatEdgeBoundary() = default; +}; + +template <size_t Dimension> +MeshFlatEdgeBoundary<Dimension> getMeshFlatEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, + const IBoundaryDescriptor& boundary_descriptor); + +#endif // MESH_FLAT_EDGE_BOUNDARY_HPP diff --git a/src/mesh/MeshFlatFaceBoundary.cpp b/src/mesh/MeshFlatFaceBoundary.cpp index 0deefac0a8e9fc768634d53bfd5afa6d2323f5ec..87d353e25404c3b5996a63945a9e220e49e97e55 100644 --- a/src/mesh/MeshFlatFaceBoundary.cpp +++ b/src/mesh/MeshFlatFaceBoundary.cpp @@ -8,22 +8,13 @@ template <size_t Dimension> MeshFlatFaceBoundary<Dimension> getMeshFlatFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor) { - for (size_t i_ref_face_list = 0; i_ref_face_list < mesh.connectivity().template numberOfRefItemList<ItemType::face>(); - ++i_ref_face_list) { - const auto& ref_face_list = mesh.connectivity().template refItemList<ItemType::face>(i_ref_face_list); - const RefId& ref = ref_face_list.refId(); - if (ref == boundary_descriptor) { - MeshFlatNodeBoundary<Dimension> mesh_flat_node_boundary = getMeshFlatNodeBoundary(mesh, boundary_descriptor); + MeshFaceBoundary<Dimension> mesh_face_boundary = getMeshFaceBoundary(mesh, boundary_descriptor); + MeshFlatNodeBoundary<Dimension> mesh_flat_node_boundary = getMeshFlatNodeBoundary(mesh, boundary_descriptor); - return MeshFlatFaceBoundary<Dimension>{mesh, ref_face_list, mesh_flat_node_boundary.outgoingNormal()}; - } - } - - std::ostringstream ost; - ost << "cannot find surface with name " << rang::fgB::red << boundary_descriptor << rang::style::reset; - - throw NormalError(ost.str()); + return MeshFlatFaceBoundary<Dimension>{mesh, mesh_face_boundary.refFaceList(), + mesh_flat_node_boundary.outgoingNormal()}; } +template MeshFlatFaceBoundary<1> getMeshFlatFaceBoundary(const Mesh<Connectivity<1>>&, const IBoundaryDescriptor&); template MeshFlatFaceBoundary<2> getMeshFlatFaceBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&); template MeshFlatFaceBoundary<3> getMeshFlatFaceBoundary(const Mesh<Connectivity<3>>&, const IBoundaryDescriptor&); diff --git a/src/mesh/MeshFlatNodeBoundary.cpp b/src/mesh/MeshFlatNodeBoundary.cpp index 059d6b004e1f3eaff9c9940dec3f2af7b8fbf57c..b7de42715eff88510119b54507ed32323365c817 100644 --- a/src/mesh/MeshFlatNodeBoundary.cpp +++ b/src/mesh/MeshFlatNodeBoundary.cpp @@ -15,17 +15,18 @@ MeshFlatNodeBoundary<Dimension>::_checkBoundaryIsFlat(const TinyVector<Dimension bool is_bad = false; - parallel_for(this->m_node_list.size(), [=, &is_bad](int r) { - const Rd& x = xr[this->m_node_list[r]]; - if (dot(x - origin, normal) > 1E-13 * length) { + auto node_list = this->m_ref_node_list.list(); + parallel_for(node_list.size(), [=, &is_bad](int r) { + const Rd& x = xr[node_list[r]]; + if (std::abs(dot(x - origin, normal)) > 1E-13 * length) { is_bad = true; } }); if (parallel::allReduceOr(is_bad)) { std::ostringstream ost; - ost << "invalid boundary " << rang::fgB::yellow << this->m_boundary_name << rang::style::reset - << ": boundary is not flat!"; + ost << "invalid boundary \"" << rang::fgB::yellow << this->m_ref_node_list.refId() << rang::style::reset + << "\": boundary is not flat!"; throw NormalError(ost.str()); } } @@ -39,15 +40,16 @@ MeshFlatNodeBoundary<1>::_getNormal(const Mesh<Connectivity<1>>& mesh) const size_t number_of_bc_nodes = [&]() { size_t number_of_bc_nodes = 0; auto node_is_owned = mesh.connectivity().nodeIsOwned(); - for (size_t i_node = 0; i_node < m_node_list.size(); ++i_node) { - number_of_bc_nodes += (node_is_owned[m_node_list[i_node]]); + auto node_list = m_ref_node_list.list(); + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + number_of_bc_nodes += (node_is_owned[node_list[i_node]]); } return parallel::allReduceMax(number_of_bc_nodes); }(); if (number_of_bc_nodes != 1) { std::ostringstream ost; - ost << "invalid boundary " << rang::fgB::yellow << m_boundary_name << rang::style::reset + ost << "invalid boundary " << rang::fgB::yellow << m_ref_node_list.refId() << rang::style::reset << ": node boundaries in 1D require to have exactly 1 node"; throw NormalError(ost.str()); } @@ -68,8 +70,8 @@ MeshFlatNodeBoundary<2>::_getNormal(const Mesh<Connectivity<2>>& mesh) if (xmin == xmax) { std::ostringstream ost; - ost << "invalid boundary " << rang::fgB::yellow << this->m_boundary_name << rang::style::reset - << ": unable to compute normal"; + ost << "invalid boundary \"" << rang::fgB::yellow << m_ref_node_list.refId() << rang::style::reset + << "\": unable to compute normal"; throw NormalError(ost.str()); } @@ -126,8 +128,8 @@ MeshFlatNodeBoundary<3>::_getNormal(const Mesh<Connectivity<3>>& mesh) if (normal_l2 == 0) { std::ostringstream ost; - ost << "invalid boundary " << rang::fgB::yellow << this->m_boundary_name << rang::style::reset - << ": unable to compute normal"; + ost << "invalid boundary \"" << rang::fgB::yellow << m_ref_node_list.refId() << rang::style::reset + << "\": unable to compute normal"; throw NormalError(ost.str()); } @@ -149,14 +151,14 @@ MeshFlatNodeBoundary<1>::_getOutgoingNormal(const Mesh<Connectivity<1>>& mesh) const R normal = this->_getNormal(mesh); double max_height = 0; - - if (m_node_list.size() > 0) { + auto node_list = m_ref_node_list.list(); + if (node_list.size() > 0) { const NodeValue<const R>& xr = mesh.xr(); const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); const auto& node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); - const NodeId r0 = m_node_list[0]; + const NodeId r0 = node_list[0]; const CellId j0 = node_to_cell_matrix[r0][0]; const auto& j0_nodes = cell_to_node_matrix[j0]; @@ -193,13 +195,14 @@ MeshFlatNodeBoundary<2>::_getOutgoingNormal(const Mesh<Connectivity<2>>& mesh) double max_height = 0; - if (m_node_list.size() > 0) { + auto node_list = m_ref_node_list.list(); + if (node_list.size() > 0) { const NodeValue<const R2>& xr = mesh.xr(); const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); const auto& node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); - const NodeId r0 = m_node_list[0]; + const NodeId r0 = node_list[0]; const CellId j0 = node_to_cell_matrix[r0][0]; const auto& j0_nodes = cell_to_node_matrix[j0]; for (size_t r = 0; r < j0_nodes.size(); ++r) { @@ -234,14 +237,14 @@ MeshFlatNodeBoundary<3>::_getOutgoingNormal(const Mesh<Connectivity<3>>& mesh) const R3 normal = this->_getNormal(mesh); double max_height = 0; - - if (m_node_list.size() > 0) { + auto node_list = m_ref_node_list.list(); + if (node_list.size() > 0) { const NodeValue<const R3>& xr = mesh.xr(); const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); const auto& node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); - const NodeId r0 = m_node_list[0]; + const NodeId r0 = node_list[0]; const CellId j0 = node_to_cell_matrix[r0][0]; const auto& j0_nodes = cell_to_node_matrix[j0]; diff --git a/src/mesh/MeshLineEdgeBoundary.cpp b/src/mesh/MeshLineEdgeBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..16e2e3e9764d1465cd767c130fbf27b0e7589afd --- /dev/null +++ b/src/mesh/MeshLineEdgeBoundary.cpp @@ -0,0 +1,19 @@ +#include <mesh/MeshLineEdgeBoundary.hpp> + +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshLineNodeBoundary.hpp> +#include <utils/Messenger.hpp> + +template <size_t Dimension> +MeshLineEdgeBoundary<Dimension> +getMeshLineEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor) +{ + MeshEdgeBoundary<Dimension> mesh_edge_boundary = getMeshEdgeBoundary(mesh, boundary_descriptor); + MeshLineNodeBoundary<Dimension> mesh_line_node_boundary = getMeshLineNodeBoundary(mesh, boundary_descriptor); + + return MeshLineEdgeBoundary<Dimension>{mesh, mesh_edge_boundary.refEdgeList(), mesh_line_node_boundary.direction()}; +} + +template MeshLineEdgeBoundary<2> getMeshLineEdgeBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&); +template MeshLineEdgeBoundary<3> getMeshLineEdgeBoundary(const Mesh<Connectivity<3>>&, const IBoundaryDescriptor&); diff --git a/src/mesh/MeshLineEdgeBoundary.hpp b/src/mesh/MeshLineEdgeBoundary.hpp new file mode 100644 index 0000000000000000000000000000000000000000..160444648524f266941a8d2903124bf91b77ee8b --- /dev/null +++ b/src/mesh/MeshLineEdgeBoundary.hpp @@ -0,0 +1,49 @@ +#ifndef MESH_LINE_EDGE_BOUNDARY_HPP +#define MESH_LINE_EDGE_BOUNDARY_HPP + +#include <algebra/TinyMatrix.hpp> +#include <mesh/MeshEdgeBoundary.hpp> + +template <size_t Dimension> +class [[nodiscard]] MeshLineEdgeBoundary final + : public MeshEdgeBoundary<Dimension> // clazy:exclude=copyable-polymorphic +{ + public: + static_assert(Dimension > 1, "MeshLineEdgeBoundary makes only sense in dimension 1"); + + using Rd = TinyVector<Dimension, double>; + + private: + const Rd m_direction; + + public: + template <size_t MeshDimension> + friend MeshLineEdgeBoundary<MeshDimension> getMeshLineEdgeBoundary(const Mesh<Connectivity<MeshDimension>>& mesh, + const IBoundaryDescriptor& boundary_descriptor); + + PUGS_INLINE + const Rd& direction() const + { + return m_direction; + } + + MeshLineEdgeBoundary& operator=(const MeshLineEdgeBoundary&) = default; + MeshLineEdgeBoundary& operator=(MeshLineEdgeBoundary&&) = default; + + private: + MeshLineEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefEdgeList& ref_edge_list, const Rd& direction) + : MeshEdgeBoundary<Dimension>(mesh, ref_edge_list), m_direction(direction) + {} + + public: + MeshLineEdgeBoundary() = default; + MeshLineEdgeBoundary(const MeshLineEdgeBoundary&) = default; + MeshLineEdgeBoundary(MeshLineEdgeBoundary &&) = default; + ~MeshLineEdgeBoundary() = default; +}; + +template <size_t Dimension> +MeshLineEdgeBoundary<Dimension> getMeshLineEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, + const IBoundaryDescriptor& boundary_descriptor); + +#endif // MESH_LINE_EDGE_BOUNDARY_HPP diff --git a/src/mesh/MeshLineFaceBoundary.cpp b/src/mesh/MeshLineFaceBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd67176349d0edb475001c58acc99d9cac9792ec --- /dev/null +++ b/src/mesh/MeshLineFaceBoundary.cpp @@ -0,0 +1,18 @@ +#include <mesh/MeshLineFaceBoundary.hpp> + +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshLineNodeBoundary.hpp> +#include <utils/Messenger.hpp> + +template <size_t Dimension> +MeshLineFaceBoundary<Dimension> +getMeshLineFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor) +{ + MeshFaceBoundary<Dimension> mesh_face_boundary = getMeshFaceBoundary(mesh, boundary_descriptor); + MeshLineNodeBoundary<Dimension> mesh_line_node_boundary = getMeshLineNodeBoundary(mesh, boundary_descriptor); + + return MeshLineFaceBoundary<Dimension>{mesh, mesh_face_boundary.refFaceList(), mesh_line_node_boundary.direction()}; +} + +template MeshLineFaceBoundary<2> getMeshLineFaceBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&); diff --git a/src/mesh/MeshLineFaceBoundary.hpp b/src/mesh/MeshLineFaceBoundary.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5631361f5f995883d80c5317a178eab122ddcda9 --- /dev/null +++ b/src/mesh/MeshLineFaceBoundary.hpp @@ -0,0 +1,49 @@ +#ifndef MESH_LINE_FACE_BOUNDARY_HPP +#define MESH_LINE_FACE_BOUNDARY_HPP + +#include <algebra/TinyMatrix.hpp> +#include <mesh/MeshFaceBoundary.hpp> + +template <size_t Dimension> +class [[nodiscard]] MeshLineFaceBoundary final + : public MeshFaceBoundary<Dimension> // clazy:exclude=copyable-polymorphic +{ + public: + static_assert(Dimension == 2, "MeshLineFaceBoundary makes only sense in dimension 2"); + + using Rd = TinyVector<Dimension, double>; + + private: + const Rd m_direction; + + public: + template <size_t MeshDimension> + friend MeshLineFaceBoundary<MeshDimension> getMeshLineFaceBoundary(const Mesh<Connectivity<MeshDimension>>& mesh, + const IBoundaryDescriptor& boundary_descriptor); + + PUGS_INLINE + const Rd& direction() const + { + return m_direction; + } + + MeshLineFaceBoundary& operator=(const MeshLineFaceBoundary&) = default; + MeshLineFaceBoundary& operator=(MeshLineFaceBoundary&&) = default; + + private: + MeshLineFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefFaceList& ref_face_list, const Rd& direction) + : MeshFaceBoundary<Dimension>(mesh, ref_face_list), m_direction(direction) + {} + + public: + MeshLineFaceBoundary() = default; + MeshLineFaceBoundary(const MeshLineFaceBoundary&) = default; + MeshLineFaceBoundary(MeshLineFaceBoundary &&) = default; + ~MeshLineFaceBoundary() = default; +}; + +template <size_t Dimension> +MeshLineFaceBoundary<Dimension> getMeshLineFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh, + const IBoundaryDescriptor& boundary_descriptor); + +#endif // MESH_LINE_FACE_BOUNDARY_HPP diff --git a/src/mesh/MeshLineNodeBoundary.cpp b/src/mesh/MeshLineNodeBoundary.cpp index cae862f33dd9a60d5b0e895aba4907cf5a5b102a..1b126f6207719d648415edbc41be6bb83c36abe7 100644 --- a/src/mesh/MeshLineNodeBoundary.cpp +++ b/src/mesh/MeshLineNodeBoundary.cpp @@ -15,33 +15,26 @@ MeshLineNodeBoundary<Dimension>::_checkBoundaryIsLine(const TinyVector<Dimension const NodeValue<const Rd>& xr = mesh.xr(); - Rdxd P = Rdxd{identity} - tensorProduct(direction, direction); - - bool is_bad = false; - parallel_for(this->m_node_list.size(), [=, &is_bad](int node_id) { - const Rd& x = xr[this->m_node_list[node_id]]; - const Rd delta = x - origin; - if (dot(P * delta, direction) > 1E-13 * length) { + const Rdxd P = Rdxd{identity} - tensorProduct(direction, direction); + + bool is_bad = false; + auto node_list = this->m_ref_node_list.list(); + parallel_for(node_list.size(), [=, &is_bad](int i_node) { + const Rd& x = xr[node_list[i_node]]; + const Rd delta = P * (x - origin); + if (dot(delta, delta) > 1E-13 * length) { is_bad = true; } }); if (parallel::allReduceOr(is_bad)) { std::ostringstream ost; - ost << "invalid boundary " << rang::fgB::yellow << this->m_boundary_name << rang::style::reset - << ": boundary is not a line!"; + ost << "invalid boundary \"" << rang::fgB::yellow << this->m_ref_node_list.refId() << rang::style::reset + << "\": boundary is not a line!"; throw NormalError(ost.str()); } } -template <> -template <> -TinyVector<1> -MeshLineNodeBoundary<1>::_getDirection(const Mesh<Connectivity<1>>&) -{ - throw UnexpectedError("MeshLineNodeBoundary makes no sense in dimension 1"); -} - template <> template <> TinyVector<2> @@ -56,8 +49,8 @@ MeshLineNodeBoundary<2>::_getDirection(const Mesh<Connectivity<2>>& mesh) if (xmin == xmax) { std::ostringstream ost; - ost << "invalid boundary " << rang::fgB::yellow << this->m_boundary_name << rang::style::reset - << ": unable to compute direction"; + ost << "invalid boundary \"" << rang::fgB::yellow << m_ref_node_list.refId() << rang::style::reset + << "\": unable to compute direction"; throw NormalError(ost.str()); } @@ -100,6 +93,12 @@ MeshLineNodeBoundary<3>::_getDirection(const Mesh<Connectivity<3>>& mesh) } const double length = l2Norm(direction); + if (length == 0) { + std::ostringstream ost; + ost << "invalid boundary \"" << rang::fgB::yellow << this->m_ref_node_list.refId() << rang::style::reset + << "\": unable to compute direction"; + throw NormalError(ost.str()); + } direction *= 1. / length; this->_checkBoundaryIsLine(direction, xmin, length, mesh); @@ -142,6 +141,5 @@ getMeshLineNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBounda throw NormalError(ost.str()); } -template MeshLineNodeBoundary<1> getMeshLineNodeBoundary(const Mesh<Connectivity<1>>&, const IBoundaryDescriptor&); template MeshLineNodeBoundary<2> getMeshLineNodeBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&); template MeshLineNodeBoundary<3> getMeshLineNodeBoundary(const Mesh<Connectivity<3>>&, const IBoundaryDescriptor&); diff --git a/src/mesh/MeshLineNodeBoundary.hpp b/src/mesh/MeshLineNodeBoundary.hpp index 8694dd257247eb5aeaf2224d113a34c9c4c8fa32..730b9f536bbf5563d656b59c0d246ff71193ea57 100644 --- a/src/mesh/MeshLineNodeBoundary.hpp +++ b/src/mesh/MeshLineNodeBoundary.hpp @@ -9,6 +9,8 @@ class [[nodiscard]] MeshLineNodeBoundary final : public MeshNodeBoundary<Dimension> // clazy:exclude=copyable-polymorphic { public: + static_assert(Dimension > 1, "MeshLineNodeBoundary makes only sense in dimension greater than 1"); + using Rd = TinyVector<Dimension, double>; private: diff --git a/src/mesh/MeshNodeBoundary.cpp b/src/mesh/MeshNodeBoundary.cpp index 56ee047545d51e20f11e7b0421dae839615bf2cc..3e01616c66670706449018107aa3d417ab1d5007 100644 --- a/src/mesh/MeshNodeBoundary.cpp +++ b/src/mesh/MeshNodeBoundary.cpp @@ -32,8 +32,9 @@ MeshNodeBoundary<2>::_getBounds(const Mesh<Connectivity<2>>& mesh) const } }; - for (size_t r = 0; r < m_node_list.size(); ++r) { - const R2& x = xr[m_node_list[r]]; + auto node_list = m_ref_node_list.list(); + for (size_t r = 0; r < node_list.size(); ++r) { + const R2& x = xr[node_list[r]]; update_xmin(x, xmin); update_xmax(x, xmax); } @@ -85,23 +86,23 @@ MeshNodeBoundary<3>::_getBounds(const Mesh<Connectivity<3>>& mesh) const auto update_ymax = [](const R3& x, R3& ymax) { // YMAX: X.ymax X.zmin X.xmax if ((x[1] > ymax[1]) or ((x[1] == ymax[1]) and (x[2] < ymax[2])) or - ((x[1] == ymax[1]) and (x[2] == ymax[1]) and (x[0] > ymax[0]))) { + ((x[1] == ymax[1]) and (x[2] == ymax[2]) and (x[0] > ymax[0]))) { ymax = x; } }; auto update_zmin = [](const R3& x, R3& zmin) { // ZMIN: X.zmin X.xmin X.ymin - if ((x[2] < zmin[2]) or ((x[2] == zmin[2]) and (x[2] < zmin[2])) or - ((x[1] == zmin[1]) and (x[2] == zmin[1]) and (x[0] < zmin[0]))) { + if ((x[2] < zmin[2]) or ((x[2] == zmin[2]) and (x[0] < zmin[0])) or + ((x[2] == zmin[2]) and (x[0] == zmin[0]) and (x[1] < zmin[1]))) { zmin = x; } }; auto update_zmax = [](const R3& x, R3& zmax) { // ZMAX: X.zmax X.xmax X.ymax - if ((x[2] > zmax[2]) or ((x[2] == zmax[2]) and (x[2] > zmax[2])) or - ((x[1] == zmax[1]) and (x[2] == zmax[1]) and (x[0] > zmax[0]))) { + if ((x[2] > zmax[2]) or ((x[2] == zmax[2]) and (x[0] > zmax[0])) or + ((x[2] == zmax[2]) and (x[0] == zmax[0]) and (x[1] > zmax[1]))) { zmax = x; } }; @@ -115,31 +116,35 @@ MeshNodeBoundary<3>::_getBounds(const Mesh<Connectivity<3>>& mesh) const R3& zmax = bounds[5]; xmin = R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()}; - ymin = xmin; - zmin = xmin; + ymin = R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()}; + zmin = R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()}; - xmax = -xmin; - ymax = xmax; - zmax = xmax; + xmax = + -R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()}; + ymax = + -R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()}; + zmax = + -R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()}; const NodeValue<const R3>& xr = mesh.xr(); - for (size_t r = 0; r < m_node_list.size(); ++r) { - const R3& x = xr[m_node_list[r]]; + auto node_list = m_ref_node_list.list(); + for (size_t r = 0; r < node_list.size(); ++r) { + const R3& x = xr[node_list[r]]; update_xmin(x, xmin); - update_xmax(x, xmax); update_ymin(x, ymin); - update_ymax(x, ymax); update_zmin(x, zmin); + update_xmax(x, xmax); + update_ymax(x, ymax); update_zmax(x, zmax); } - if (parallel::size() > 0) { + if (parallel::size() > 1) { Array<const R3> xmin_array = parallel::allGather(xmin); - Array<const R3> xmax_array = parallel::allGather(xmax); Array<const R3> ymin_array = parallel::allGather(ymin); - Array<const R3> ymax_array = parallel::allGather(ymax); Array<const R3> zmin_array = parallel::allGather(zmin); + Array<const R3> xmax_array = parallel::allGather(xmax); + Array<const R3> ymax_array = parallel::allGather(ymax); Array<const R3> zmax_array = parallel::allGather(zmax); for (size_t i = 0; i < xmin_array.size(); ++i) { @@ -168,24 +173,12 @@ MeshNodeBoundary<3>::_getBounds(const Mesh<Connectivity<3>>& mesh) const template <size_t Dimension> MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefFaceList& ref_face_list) - : m_boundary_name(ref_face_list.refId().tagName()) { - const auto& face_to_cell_matrix = mesh.connectivity().faceToCellMatrix(); - const Array<const FaceId>& face_list = ref_face_list.list(); - - bool is_bad = false; - parallel_for(face_list.size(), [=, &is_bad](int l) { - const auto& face_cells = face_to_cell_matrix[face_list[l]]; - if (face_cells.size() > 1) { - is_bad = true; - } - }); - - if (parallel::allReduceOr(is_bad)) { + if (not ref_face_list.isBoundary()) { std::ostringstream ost; - ost << "invalid boundary " << rang::fgB::yellow << this->m_boundary_name << rang::style::reset - << ": inner faces cannot be used to define mesh boundaries"; + ost << "invalid boundary \"" << rang::fgB::yellow << ref_face_list.refId() << rang::style::reset + << "\": inner faces cannot be used to define mesh boundaries"; throw NormalError(ost.str()); } @@ -210,62 +203,28 @@ MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension> Array<NodeId> node_list(node_ids.size()); parallel_for( node_ids.size(), PUGS_LAMBDA(int r) { node_list[r] = node_ids[r]; }); - m_node_list = node_list; + m_ref_node_list = RefNodeList{ref_face_list.refId(), node_list, ref_face_list.isBoundary()}; } else { Array<NodeId> node_list(face_list.size()); parallel_for( face_list.size(), PUGS_LAMBDA(int r) { node_list[r] = static_cast<FaceId::base_type>(face_list[r]); }); - m_node_list = node_list; + m_ref_node_list = RefNodeList{ref_face_list.refId(), node_list, ref_face_list.isBoundary()}; } + + // This is quite dirty but it allows a non negligible performance + // improvement + const_cast<Connectivity<Dimension>&>(mesh.connectivity()).addRefItemList(m_ref_node_list); } template <size_t Dimension> MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefEdgeList& ref_edge_list) - : m_boundary_name(ref_edge_list.refId().tagName()) { const Array<const EdgeId>& edge_list = ref_edge_list.list(); - const auto& edge_is_owned = mesh.connectivity().edgeIsOwned(); - - bool is_bad = false; - - if constexpr ((Dimension == 1) or (Dimension == 2)) { - const auto& edge_to_cell_matrix = mesh.connectivity().edgeToCellMatrix(); - parallel_for(edge_list.size(), [=, &is_bad](int l) { - const EdgeId edge_id = edge_list[l]; - if (edge_is_owned[edge_id]) { - if (edge_to_cell_matrix[edge_id].size() != 1) { - is_bad = true; - } - } - }); - } else { - static_assert(Dimension == 3); - const auto& edge_to_face_matrix = mesh.connectivity().edgeToFaceMatrix(); - const auto& face_to_cell_matrix = mesh.connectivity().faceToCellMatrix(); - - parallel_for(edge_list.size(), [=, &is_bad](int l) { - const EdgeId edge_id = edge_list[l]; - if (edge_is_owned[edge_id]) { - const auto& edge_faces = edge_to_face_matrix[edge_id]; - bool is_connected_to_boundary_face = false; - for (size_t i_edge_face = 0; i_edge_face < edge_faces.size(); ++i_edge_face) { - const FaceId edge_face_id = edge_faces[i_edge_face]; - if (face_to_cell_matrix[edge_face_id].size() == 1) { - is_connected_to_boundary_face = true; - } - } - if (not is_connected_to_boundary_face) { - is_bad = true; - } - } - }); - } - - if (parallel::allReduceOr(is_bad)) { + if (not ref_edge_list.isBoundary()) { std::ostringstream ost; - ost << "invalid boundary " << rang::fgB::yellow << this->m_boundary_name << rang::style::reset - << ": inner edges cannot be used to define mesh boundaries"; + ost << "invalid boundary \"" << rang::fgB::yellow << ref_edge_list.refId() << rang::style::reset + << "\": inner edges cannot be used to define mesh boundaries"; throw NormalError(ost.str()); } @@ -289,19 +248,30 @@ MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension> Array<NodeId> node_list(node_ids.size()); parallel_for( node_ids.size(), PUGS_LAMBDA(int r) { node_list[r] = node_ids[r]; }); - m_node_list = node_list; + m_ref_node_list = RefNodeList{ref_edge_list.refId(), node_list, ref_edge_list.isBoundary()}; } else { Array<NodeId> node_list(edge_list.size()); parallel_for( edge_list.size(), PUGS_LAMBDA(int r) { node_list[r] = static_cast<EdgeId::base_type>(edge_list[r]); }); - m_node_list = node_list; + m_ref_node_list = RefNodeList{ref_edge_list.refId(), node_list, ref_edge_list.isBoundary()}; } + + // This is quite dirty but it allows a non negligible performance + // improvement + const_cast<Connectivity<Dimension>&>(mesh.connectivity()).addRefItemList(m_ref_node_list); } template <size_t Dimension> MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension>>&, const RefNodeList& ref_node_list) - : m_node_list(ref_node_list.list()), m_boundary_name(ref_node_list.refId().tagName()) -{} + : m_ref_node_list(ref_node_list) +{ + if (not ref_node_list.isBoundary()) { + std::ostringstream ost; + ost << "invalid boundary \"" << rang::fgB::yellow << this->m_ref_node_list.refId() << rang::style::reset + << "\": inner nodes cannot be used to define mesh boundaries"; + throw NormalError(ost.str()); + } +} template MeshNodeBoundary<1>::MeshNodeBoundary(const Mesh<Connectivity<1>>&, const RefFaceList&); template MeshNodeBoundary<2>::MeshNodeBoundary(const Mesh<Connectivity<2>>&, const RefFaceList&); @@ -345,7 +315,7 @@ getMeshNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDe } std::ostringstream ost; - ost << "cannot find surface with name " << rang::fgB::red << boundary_descriptor << rang::style::reset; + ost << "cannot find node list with name " << rang::fgB::red << boundary_descriptor << rang::style::reset; throw NormalError(ost.str()); } diff --git a/src/mesh/MeshNodeBoundary.hpp b/src/mesh/MeshNodeBoundary.hpp index 0422118ffc14abbc85030d4d56ca2f2da29eadb9..ce56579e853171e4b5be0d1f195936a75b838f78 100644 --- a/src/mesh/MeshNodeBoundary.hpp +++ b/src/mesh/MeshNodeBoundary.hpp @@ -16,8 +16,7 @@ template <size_t Dimension> class [[nodiscard]] MeshNodeBoundary // clazy:exclude=copyable-polymorphic { protected: - Array<const NodeId> m_node_list; - std::string m_boundary_name; + RefNodeList m_ref_node_list; std::array<TinyVector<Dimension>, Dimension*(Dimension - 1)> _getBounds(const Mesh<Connectivity<Dimension>>& mesh) const; @@ -30,9 +29,16 @@ class [[nodiscard]] MeshNodeBoundary // clazy:exclude=copyable-polymorphic MeshNodeBoundary& operator=(const MeshNodeBoundary&) = default; MeshNodeBoundary& operator=(MeshNodeBoundary&&) = default; + PUGS_INLINE + const RefNodeList& refNodeList() const + { + return m_ref_node_list; + } + + PUGS_INLINE const Array<const NodeId>& nodeList() const { - return m_node_list; + return m_ref_node_list.list(); } protected: diff --git a/src/mesh/MeshRandomizer.cpp b/src/mesh/MeshRandomizer.cpp index 34087ff69d78f1c2d2bfc83589350c376d5fea70..06028bab1c002c1898bed0e94d3e3d091d1db15c 100644 --- a/src/mesh/MeshRandomizer.cpp +++ b/src/mesh/MeshRandomizer.cpp @@ -96,17 +96,21 @@ class MeshRandomizerHandler::MeshRandomizer }); } else if constexpr (std::is_same_v<BCType, AxisBoundaryCondition>) { - const Rd& t = bc.direction(); + if constexpr (Dimension > 1) { + const Rd& t = bc.direction(); - const Rdxd txt = tensorProduct(t, t); + const Rdxd txt = tensorProduct(t, t); - const Array<const NodeId>& node_list = bc.nodeList(); - parallel_for( - node_list.size(), PUGS_LAMBDA(const size_t i_node) { - const NodeId node_id = node_list[i_node]; + const Array<const NodeId>& node_list = bc.nodeList(); + parallel_for( + node_list.size(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; - shift[node_id] = txt * shift[node_id]; - }); + shift[node_id] = txt * shift[node_id]; + }); + } else { + throw UnexpectedError("AxisBoundaryCondition make no sense in dimension 1"); + } } else if constexpr (std::is_same_v<BCType, FixedBoundaryCondition>) { const Array<const NodeId>& node_list = bc.nodeList(); @@ -300,6 +304,14 @@ class MeshRandomizerHandler::MeshRandomizer<Dimension>::AxisBoundaryCondition ~AxisBoundaryCondition() = default; }; +template <> +class MeshRandomizerHandler::MeshRandomizer<1>::AxisBoundaryCondition +{ + public: + AxisBoundaryCondition() = default; + ~AxisBoundaryCondition() = default; +}; + template <size_t Dimension> class MeshRandomizerHandler::MeshRandomizer<Dimension>::FixedBoundaryCondition { diff --git a/src/mesh/RefItemList.hpp b/src/mesh/RefItemList.hpp index 594b5ad25dc4a9769cd1d4645202cceeec6273e6..326e3278c948baae0ad8dd4d562399563a6f993a 100644 --- a/src/mesh/RefItemList.hpp +++ b/src/mesh/RefItemList.hpp @@ -15,6 +15,7 @@ class RefItemList private: RefId m_ref_id; Array<const ItemId> m_item_id_list; + bool m_is_boundary; public: const RefId& @@ -29,8 +30,14 @@ class RefItemList return m_item_id_list; } - RefItemList(const RefId& ref_id, const Array<const ItemId>& item_id_list) - : m_ref_id(ref_id), m_item_id_list(item_id_list) + bool + isBoundary() const + { + return m_is_boundary; + } + + RefItemList(const RefId& ref_id, const Array<const ItemId>& item_id_list, bool is_boundary) + : m_ref_id{ref_id}, m_item_id_list{item_id_list}, m_is_boundary{is_boundary} { ; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 52a41bcb69fc8181fce73b92059b49cf1cccf314..196938400cab3735d0633b1d1ba41efc00358f4b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -175,6 +175,15 @@ add_executable (mpi_unit_tests test_ItemArrayUtils.cpp test_ItemValue.cpp test_ItemValueUtils.cpp + test_MeshEdgeBoundary.cpp + test_MeshFaceBoundary.cpp + test_MeshFlatEdgeBoundary.cpp + test_MeshFlatFaceBoundary.cpp + test_MeshFlatNodeBoundary.cpp + test_MeshLineEdgeBoundary.cpp + test_MeshLineFaceBoundary.cpp + test_MeshLineNodeBoundary.cpp + test_MeshNodeBoundary.cpp test_Messenger.cpp test_OFStream.cpp test_Partitioner.cpp diff --git a/tests/test_MeshEdgeBoundary.cpp b/tests/test_MeshEdgeBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..32c1d1c6e33c2c109b5c18f67172e3f15136f0f9 --- /dev/null +++ b/tests/test_MeshEdgeBoundary.cpp @@ -0,0 +1,335 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> + +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshEdgeBoundary.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/NumberedBoundaryDescriptor.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("MeshEdgeBoundary", "[mesh]") +{ + auto is_same = [](const auto& a, const auto& b) -> bool { + if (a.size() > 0 and b.size() > 0) { + return (a.size() == b.size()) and (&(a[0]) == &(b[0])); + } else { + return (a.size() == b.size()); + } + }; + + auto get_edge_list_from_tag = [](const size_t tag, const auto& connectivity) -> Array<const EdgeId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::edge>(); ++i) { + const auto& ref_edge_list = connectivity.template refItemList<ItemType::edge>(i); + const RefId ref_id = ref_edge_list.refId(); + if (ref_id.tagNumber() == tag) { + return ref_edge_list.list(); + } + } + return {}; + }; + + auto get_edge_list_from_name = [](const std::string& name, const auto& connectivity) -> Array<const EdgeId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::edge>(); ++i) { + const auto& ref_edge_list = connectivity.template refItemList<ItemType::edge>(i); + const RefId ref_id = ref_edge_list.refId(); + if (ref_id.tagName() == name) { + return ref_edge_list.list(); + } + } + return {}; + }; + + SECTION("1D") + { + static constexpr size_t Dimension = 1; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + SECTION("cartesian 1d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian1DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + } + } + } + + SECTION("unordered 1d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + } + } + } + } + + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor numbered_boundary_descriptor(name); + const auto& edge_boundary = getMeshEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "YMIN", "XMAX", "YMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor numbered_boundary_descriptor(name); + const auto& edge_boundary = getMeshEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3, 4, 5}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor numbered_boundary_descriptor(name); + const auto& edge_boundary = getMeshEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {22, 23, 24, 25, 26, 27}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor numbered_boundary_descriptor(name); + const auto& edge_boundary = getMeshEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + } + } + } + } + + SECTION("errors") + { + SECTION("cannot find boundary") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + NamedBoundaryDescriptor named_boundary_descriptor("invalid_boundary"); + + REQUIRE_THROWS_WITH(getMeshEdgeBoundary(mesh, named_boundary_descriptor), + "error: cannot find edge list with name \"invalid_boundary\""); + } + + SECTION("suredge is inside") + { + SECTION("1D") + { + static constexpr size_t Dimension = 1; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh(); + const MeshType& mesh = *p_mesh; + + NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE"); + + REQUIRE_THROWS_WITH(getMeshEdgeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"INTERFACE\": inner edges cannot be used to define mesh " + "boundaries"); + } + + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + const MeshType& mesh = *p_mesh; + + NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE"); + + REQUIRE_THROWS_WITH(getMeshEdgeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"INTERFACE\": inner edges cannot be used to define mesh " + "boundaries"); + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE1"); + + REQUIRE_THROWS_WITH(getMeshEdgeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"INTERFACE1\": inner edges cannot be used to define mesh " + "boundaries"); + } + } + } +} diff --git a/tests/test_MeshFaceBoundary.cpp b/tests/test_MeshFaceBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..472427aaf0c7f9c85f566dd610020e91759aad84 --- /dev/null +++ b/tests/test_MeshFaceBoundary.cpp @@ -0,0 +1,335 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> + +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/NumberedBoundaryDescriptor.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("MeshFaceBoundary", "[mesh]") +{ + auto is_same = [](const auto& a, const auto& b) -> bool { + if (a.size() > 0 and b.size() > 0) { + return (a.size() == b.size()) and (&(a[0]) == &(b[0])); + } else { + return (a.size() == b.size()); + } + }; + + auto get_face_list_from_tag = [](const size_t tag, const auto& connectivity) -> Array<const FaceId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::face>(); ++i) { + const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i); + const RefId ref_id = ref_face_list.refId(); + if (ref_id.tagNumber() == tag) { + return ref_face_list.list(); + } + } + return {}; + }; + + auto get_face_list_from_name = [](const std::string& name, const auto& connectivity) -> Array<const FaceId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::face>(); ++i) { + const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i); + const RefId ref_id = ref_face_list.refId(); + if (ref_id.tagName() == name) { + return ref_face_list.list(); + } + } + return {}; + }; + + SECTION("1D") + { + static constexpr size_t Dimension = 1; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + SECTION("cartesian 1d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian1DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + } + } + } + + SECTION("unordered 1d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + } + } + } + } + + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor numbered_boundary_descriptor(name); + const auto& face_boundary = getMeshFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "YMIN", "XMAX", "YMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor numbered_boundary_descriptor(name); + const auto& face_boundary = getMeshFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3, 4, 5}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor numbered_boundary_descriptor(name); + const auto& face_boundary = getMeshFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {22, 23, 24, 25, 26, 27}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor numbered_boundary_descriptor(name); + const auto& face_boundary = getMeshFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + } + } + } + } + + SECTION("errors") + { + SECTION("cannot find boundary") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + NamedBoundaryDescriptor named_boundary_descriptor("invalid_boundary"); + + REQUIRE_THROWS_WITH(getMeshFaceBoundary(mesh, named_boundary_descriptor), + "error: cannot find face list with name \"invalid_boundary\""); + } + + SECTION("surface is inside") + { + SECTION("1D") + { + static constexpr size_t Dimension = 1; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh(); + const MeshType& mesh = *p_mesh; + + NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE"); + + REQUIRE_THROWS_WITH(getMeshFaceBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"INTERFACE\": inner faces cannot be used to define mesh " + "boundaries"); + } + + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + const MeshType& mesh = *p_mesh; + + NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE"); + + REQUIRE_THROWS_WITH(getMeshFaceBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"INTERFACE\": inner faces cannot be used to define mesh " + "boundaries"); + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE1"); + + REQUIRE_THROWS_WITH(getMeshFaceBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"INTERFACE1\": inner faces cannot be used to define mesh " + "boundaries"); + } + } + } +} diff --git a/tests/test_MeshFlatEdgeBoundary.cpp b/tests/test_MeshFlatEdgeBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..94700598238232b1e2a5e98692f57291f568dc39 --- /dev/null +++ b/tests/test_MeshFlatEdgeBoundary.cpp @@ -0,0 +1,1471 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshFlatEdgeBoundary.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/NumberedBoundaryDescriptor.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("MeshFlatEdgeBoundary", "[mesh]") +{ + auto is_same = [](const auto& a, const auto& b) -> bool { + if (a.size() > 0 and b.size() > 0) { + return (a[0] == b[0]); + } else { + return (a.size() == b.size()); + } + }; + + auto get_edge_list_from_tag = [](const size_t tag, const auto& connectivity) -> Array<const EdgeId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::edge>(); ++i) { + const auto& ref_edge_list = connectivity.template refItemList<ItemType::edge>(i); + const RefId ref_id = ref_edge_list.refId(); + if (ref_id.tagNumber() == tag) { + return ref_edge_list.list(); + } + } + return {}; + }; + + auto get_edge_list_from_name = [](const std::string& name, const auto& connectivity) -> Array<const EdgeId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::edge>(); ++i) { + const auto& ref_edge_list = connectivity.template refItemList<ItemType::edge>(i); + const RefId ref_id = ref_edge_list.refId(); + if (ref_id.tagName() == name) { + return ref_edge_list.list(); + } + } + return {}; + }; + + SECTION("aligned axis") + { + SECTION("1D") + { + static constexpr size_t Dimension = 1; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R1 = TinyVector<1>; + + SECTION("cartesian 1d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian1DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R1 normal = zero; + + switch (tag) { + case 0: { + normal = R1{-1}; + break; + } + case 1: { + normal = R1{1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R1 normal = zero; + + if (name == "XMIN") { + normal = R1{-1}; + } else if (name == "XMAX") { + normal = R1{1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("unordered 1d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R1 normal = zero; + + switch (tag) { + case 1: { + normal = R1{-1}; + break; + } + case 2: { + normal = R1{1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R1 normal = zero; + + if (name == "XMIN") { + normal = R1{-1}; + } else if (name == "XMAX") { + normal = R1{1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 normal = zero; + + switch (tag) { + case 0: { + normal = R2{-1, 0}; + break; + } + case 1: { + normal = R2{1, 0}; + break; + } + case 2: { + normal = R2{0, -1}; + break; + } + case 3: { + normal = R2{0, 1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: cannot find edge list with name \"10\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: cannot find edge list with name \"11\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(12)), + "error: cannot find edge list with name \"12\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(13)), + "error: cannot find edge list with name \"13\""); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 normal = zero; + + if (name == "XMIN") { + normal = R2{-1, 0}; + } else if (name == "XMAX") { + normal = R2{1, 0}; + } else if (name == "YMIN") { + normal = R2{0, -1}; + } else if (name == "YMAX") { + normal = R2{0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: cannot find edge list with name \"XMINYMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find edge list with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMIN")), + "error: cannot find edge list with name \"XMAXYMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: cannot find edge list with name \"XMAXYMAX\""); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 normal = zero; + + switch (tag) { + case 1: { + normal = R2{-1, 0}; + break; + } + case 2: { + normal = R2{1, 0}; + break; + } + case 3: { + normal = R2{0, 1}; + break; + } + case 4: { + normal = R2{0, -1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(8)), + "error: cannot find edge list with name \"8\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(9)), + "error: cannot find edge list with name \"9\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: cannot find edge list with name \"10\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: cannot find edge list with name \"11\""); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + R2 normal = zero; + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + if (name == "XMIN") { + normal = R2{-1, 0}; + } else if (name == "XMAX") { + normal = R2{1, 0}; + } else if (name == "YMIN") { + normal = R2{0, -1}; + } else if (name == "YMAX") { + normal = R2{0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: cannot find edge list with name \"XMINYMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find edge list with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMIN")), + "error: cannot find edge list with name \"XMAXYMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: cannot find edge list with name \"XMAXYMAX\""); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3, 4, 5}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 normal = zero; + + switch (tag) { + case 0: { + normal = R3{-1, 0, 0}; + break; + } + case 1: { + normal = R3{1, 0, 0}; + break; + } + case 2: { + normal = R3{0, -1, 0}; + break; + } + case 3: { + normal = R3{0, 1, 0}; + break; + } + case 4: { + normal = R3{0, 0, -1}; + break; + } + case 5: { + normal = R3{0, 0, 1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(20)), + "error: cannot find surface with name \"20\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(21)), + "error: cannot find surface with name \"21\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(22)), + "error: cannot find surface with name \"22\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(23)), + "error: cannot find surface with name \"23\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(24)), + "error: cannot find surface with name \"24\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(25)), + "error: cannot find surface with name \"25\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(26)), + "error: cannot find surface with name \"26\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(27)), + "error: cannot find surface with name \"27\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(28)), + "error: cannot find surface with name \"28\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(29)), + "error: cannot find surface with name \"29\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(30)), + "error: cannot find surface with name \"30\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(31)), + "error: cannot find surface with name \"31\""); + + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: cannot find edge list with name \"10\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: cannot find edge list with name \"11\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(12)), + "error: cannot find edge list with name \"12\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(13)), + "error: cannot find edge list with name \"13\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(14)), + "error: cannot find edge list with name \"14\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(15)), + "error: cannot find edge list with name \"15\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(16)), + "error: cannot find edge list with name \"16\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(17)), + "error: cannot find edge list with name \"17\""); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R3{0, -1, 0}; + } else if (name == "YMAX") { + normal = R3{0, 1, 0}; + } else if (name == "ZMIN") { + normal = R3{0, 0, -1}; + } else if (name == "ZMAX") { + normal = R3{0, 0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: cannot find surface with name \"XMINYMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find surface with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: cannot find surface with name \"XMAXYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find surface with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINZMIN")), + "error: cannot find surface with name \"XMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINZMAX")), + "error: cannot find surface with name \"XMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXZMAX")), + "error: cannot find surface with name \"XMAXZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINZMAX")), + "error: cannot find surface with name \"XMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("YMINZMIN")), + "error: cannot find surface with name \"YMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("YMINZMAX")), + "error: cannot find surface with name \"YMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("YMAXZMAX")), + "error: cannot find surface with name \"YMAXZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("YMINZMAX")), + "error: cannot find surface with name \"YMINZMAX\""); + + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMIN")), + "error: cannot find edge list with name \"XMINYMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMIN")), + "error: cannot find edge list with name \"XMAXYMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMIN")), + "error: cannot find edge list with name \"XMAXYMAXZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMIN")), + "error: cannot find edge list with name \"XMINYMAXZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMAX")), + "error: cannot find edge list with name \"XMINYMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMAX")), + "error: cannot find edge list with name \"XMAXYMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMAX")), + "error: cannot find edge list with name \"XMAXYMAXZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMAX")), + "error: cannot find edge list with name \"XMINYMAXZMAX\""); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {22, 23, 24, 25, 26, 27}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 normal = zero; + + switch (tag) { + case 22: { + normal = R3{-1, 0, 0}; + break; + } + case 23: { + normal = R3{1, 0, 0}; + break; + } + case 24: { + normal = R3{0, 0, 1}; + break; + } + case 25: { + normal = R3{0, 0, -1}; + break; + } + case 26: { + normal = R3{0, 1, 0}; + break; + } + case 27: { + normal = R3{0, -1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(28)), + "error: cannot find surface with name \"28\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(29)), + "error: cannot find surface with name \"29\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(30)), + "error: cannot find surface with name \"30\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(31)), + "error: cannot find surface with name \"31\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(32)), + "error: cannot find surface with name \"32\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(33)), + "error: cannot find surface with name \"33\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(34)), + "error: cannot find surface with name \"34\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(35)), + "error: cannot find surface with name \"35\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(36)), + "error: cannot find surface with name \"36\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(37)), + "error: cannot find surface with name \"37\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(38)), + "error: cannot find surface with name \"38\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(39)), + "error: cannot find surface with name \"39\""); + + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(40)), + "error: cannot find edge list with name \"40\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(41)), + "error: cannot find edge list with name \"41\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(42)), + "error: cannot find edge list with name \"42\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(43)), + "error: cannot find edge list with name \"43\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(44)), + "error: cannot find edge list with name \"44\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(45)), + "error: cannot find edge list with name \"45\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(47)), + "error: cannot find edge list with name \"47\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NumberedBoundaryDescriptor(51)), + "error: cannot find edge list with name \"51\""); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R3{0, -1, 0}; + } else if (name == "YMAX") { + normal = R3{0, 1, 0}; + } else if (name == "ZMIN") { + normal = R3{0, 0, -1}; + } else if (name == "ZMAX") { + normal = R3{0, 0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: cannot find surface with name \"XMINYMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find surface with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: cannot find surface with name \"XMAXYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find surface with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINZMIN")), + "error: cannot find surface with name \"XMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINZMAX")), + "error: cannot find surface with name \"XMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXZMAX")), + "error: cannot find surface with name \"XMAXZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINZMAX")), + "error: cannot find surface with name \"XMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("YMINZMIN")), + "error: cannot find surface with name \"YMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("YMINZMAX")), + "error: cannot find surface with name \"YMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("YMAXZMAX")), + "error: cannot find surface with name \"YMAXZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("YMINZMAX")), + "error: cannot find surface with name \"YMINZMAX\""); + + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMIN")), + "error: cannot find edge list with name \"XMINYMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMIN")), + "error: cannot find edge list with name \"XMAXYMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMIN")), + "error: cannot find edge list with name \"XMAXYMAXZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMIN")), + "error: cannot find edge list with name \"XMINYMAXZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMAX")), + "error: cannot find edge list with name \"XMINYMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMAX")), + "error: cannot find edge list with name \"XMAXYMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMAX")), + "error: cannot find edge list with name \"XMAXYMAXZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMAX")), + "error: cannot find edge list with name \"XMINYMAXZMAX\""); + } + } + } + } + } + + SECTION("rotated axis") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + const double theta = 0.3; + const TinyMatrix<2> R{std::cos(theta), -std::sin(theta), // + std::sin(theta), std::cos(theta)}; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R2> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 normal = zero; + + switch (tag) { + case 0: { + normal = R * R2{-1, 0}; + break; + } + case 1: { + normal = R * R2{1, 0}; + break; + } + case 2: { + normal = R * R2{0, -1}; + break; + } + case 3: { + normal = R * R2{0, 1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 normal = zero; + + if (name == "XMIN") { + normal = R * R2{-1, 0}; + } else if (name == "XMAX") { + normal = R * R2{1, 0}; + } else if (name == "YMIN") { + normal = R * R2{0, -1}; + } else if (name == "YMAX") { + normal = R * R2{0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R2> rotated_xr{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 normal = zero; + + switch (tag) { + case 1: { + normal = R * R2{-1, 0}; + break; + } + case 2: { + normal = R * R2{1, 0}; + break; + } + case 3: { + normal = R * R2{0, 1}; + break; + } + case 4: { + normal = R * R2{0, -1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + R2 normal = zero; + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + if (name == "XMIN") { + normal = R * R2{-1, 0}; + } else if (name == "XMAX") { + normal = R * R2{1, 0}; + } else if (name == "YMIN") { + normal = R * R2{0, -1}; + } else if (name == "YMAX") { + normal = R * R2{0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + const double theta = 0.3; + const double phi = 0.4; + const TinyMatrix<3> R = + TinyMatrix<3>{std::cos(theta), -std::sin(theta), 0, std::sin(theta), std::cos(theta), 0, 0, 0, 1} * + TinyMatrix<3>{0, std::cos(phi), -std::sin(phi), 0, std::sin(phi), std::cos(phi), 1, 0, 0}; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R3> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {0, 1, 2, 3, 4, 5}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 normal = zero; + + switch (tag) { + case 0: { + normal = R * R3{-1, 0, 0}; + break; + } + case 1: { + normal = R * R3{1, 0, 0}; + break; + } + case 2: { + normal = R * R3{0, -1, 0}; + break; + } + case 3: { + normal = R * R3{0, 1, 0}; + break; + } + case 4: { + normal = R * R3{0, 0, -1}; + break; + } + case 5: { + normal = R * R3{0, 0, 1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R * R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R * R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R * R3{0, -1, 0}; + } else if (name == "YMAX") { + normal = R * R3{0, 1, 0}; + } else if (name == "ZMIN") { + normal = R * R3{0, 0, -1}; + } else if (name == "ZMAX") { + normal = R * R3{0, 0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R3> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {22, 23, 24, 25, 26, 27}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 normal = zero; + + switch (tag) { + case 22: { + normal = R * R3{-1, 0, 0}; + break; + } + case 23: { + normal = R * R3{1, 0, 0}; + break; + } + case 24: { + normal = R * R3{0, 0, 1}; + break; + } + case 25: { + normal = R * R3{0, 0, -1}; + break; + } + case 26: { + normal = R * R3{0, 1, 0}; + break; + } + case 27: { + normal = R * R3{0, -1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R * R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R * R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R * R3{0, -1, 0}; + } else if (name == "YMAX") { + normal = R * R3{0, 1, 0}; + } else if (name == "ZMIN") { + normal = R * R3{0, 0, -1}; + } else if (name == "ZMAX") { + normal = R * R3{0, 0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + } + + SECTION("curved mesh") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + auto curve = [](const R2& X) -> R2 { return R2{X[0], (1 + X[0] * X[0]) * X[1]}; }; + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<TinyVector<2>> curved_xr{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {1, 2, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 normal = zero; + + switch (tag) { + case 1: { + normal = R2{-1, 0}; + break; + } + case 2: { + normal = R2{1, 0}; + break; + } + case 4: { + normal = R2{0, -1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(3); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not flat!"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + R2 normal = zero; + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + if (name == "XMIN") { + normal = R2{-1, 0}; + } else if (name == "XMAX") { + normal = R2{1, 0}; + } else if (name == "YMIN") { + normal = R2{0, -1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("YMAX"); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not flat!"); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + auto curve = [](const R3& X) -> R3 { + return R3{X[0], (1 + X[0] * X[0]) * (X[1] + 1), (1 - 0.2 * X[0] * X[0]) * X[2]}; + }; + + NodeValue<R3> curved_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {0, 1, 2, 4}; + + for (auto tag : tag_set) { + auto edge_list = get_edge_list_from_tag(tag, connectivity); + + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 normal = zero; + + switch (tag) { + case 0: { + normal = R3{-1, 0, 0}; + break; + } + case 1: { + normal = R3{1, 0, 0}; + break; + } + case 2: { + normal = R3{0, -1, 0}; + break; + } + case 4: { + normal = R3{0, 0, -1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(3); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not flat!"); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(5); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"ZMAX(5)\": boundary is not flat!"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "ZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R3{0, -1, 0}; + } else if (name == "ZMIN") { + normal = R3{0, 0, -1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("YMAX"); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not flat!"); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("ZMAX"); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"ZMAX(5)\": boundary is not flat!"); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + auto curve = [](const R3& X) -> R3 { + return R3{X[0], (1 + X[0] * X[0]) * X[1], (1 - 0.2 * X[0] * X[0]) * X[2]}; + }; + + NodeValue<R3> curved_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {22, 23, 25, 27}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 normal = zero; + + switch (tag) { + case 22: { + normal = R3{-1, 0, 0}; + break; + } + case 23: { + normal = R3{1, 0, 0}; + break; + } + case 25: { + normal = R3{0, 0, -1}; + break; + } + case 27: { + normal = R3{0, -1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(24); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"ZMAX(24)\": boundary is not flat!"); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(26); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"YMAX(26)\": boundary is not flat!"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "ZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R3{0, -1, 0}; + } else if (name == "ZMIN") { + normal = R3{0, 0, -1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("YMAX"); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"YMAX(26)\": boundary is not flat!"); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("ZMAX"); + REQUIRE_THROWS_WITH(getMeshFlatEdgeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"ZMAX(24)\": boundary is not flat!"); + } + } + } + } + } +} diff --git a/tests/test_MeshFlatFaceBoundary.cpp b/tests/test_MeshFlatFaceBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e01d79d1dc456f6f193d42db86c4f1f5ce08b75f --- /dev/null +++ b/tests/test_MeshFlatFaceBoundary.cpp @@ -0,0 +1,1251 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshFlatFaceBoundary.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/NumberedBoundaryDescriptor.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("MeshFlatFaceBoundary", "[mesh]") +{ + auto is_same = [](const auto& a, const auto& b) -> bool { + if (a.size() > 0 and b.size() > 0) { + return (a[0] == b[0]); + } else { + return (a.size() == b.size()); + } + }; + + auto get_face_list_from_tag = [](const size_t tag, const auto& connectivity) -> Array<const FaceId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::face>(); ++i) { + const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i); + const RefId ref_id = ref_face_list.refId(); + if (ref_id.tagNumber() == tag) { + return ref_face_list.list(); + } + } + return {}; + }; + + auto get_face_list_from_name = [](const std::string& name, const auto& connectivity) -> Array<const FaceId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::face>(); ++i) { + const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i); + const RefId ref_id = ref_face_list.refId(); + if (ref_id.tagName() == name) { + return ref_face_list.list(); + } + } + return {}; + }; + + SECTION("aligned axis") + { + SECTION("1D") + { + static constexpr size_t Dimension = 1; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R1 = TinyVector<1>; + + SECTION("cartesian 1d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian1DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R1 normal = zero; + + switch (tag) { + case 0: { + normal = R1{-1}; + break; + } + case 1: { + normal = R1{1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R1 normal = zero; + + if (name == "XMIN") { + normal = R1{-1}; + } else if (name == "XMAX") { + normal = R1{1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("unordered 1d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R1 normal = zero; + + switch (tag) { + case 1: { + normal = R1{-1}; + break; + } + case 2: { + normal = R1{1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R1 normal = zero; + + if (name == "XMIN") { + normal = R1{-1}; + } else if (name == "XMAX") { + normal = R1{1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 normal = zero; + + switch (tag) { + case 0: { + normal = R2{-1, 0}; + break; + } + case 1: { + normal = R2{1, 0}; + break; + } + case 2: { + normal = R2{0, -1}; + break; + } + case 3: { + normal = R2{0, 1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 normal = zero; + + if (name == "XMIN") { + normal = R2{-1, 0}; + } else if (name == "XMAX") { + normal = R2{1, 0}; + } else if (name == "YMIN") { + normal = R2{0, -1}; + } else if (name == "YMAX") { + normal = R2{0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 normal = zero; + + switch (tag) { + case 1: { + normal = R2{-1, 0}; + break; + } + case 2: { + normal = R2{1, 0}; + break; + } + case 3: { + normal = R2{0, 1}; + break; + } + case 4: { + normal = R2{0, -1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + R2 normal = zero; + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + if (name == "XMIN") { + normal = R2{-1, 0}; + } else if (name == "XMAX") { + normal = R2{1, 0}; + } else if (name == "YMIN") { + normal = R2{0, -1}; + } else if (name == "YMAX") { + normal = R2{0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3, 4, 5}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R3 normal = zero; + + switch (tag) { + case 0: { + normal = R3{-1, 0, 0}; + break; + } + case 1: { + normal = R3{1, 0, 0}; + break; + } + case 2: { + normal = R3{0, -1, 0}; + break; + } + case 3: { + normal = R3{0, 1, 0}; + break; + } + case 4: { + normal = R3{0, 0, -1}; + break; + } + case 5: { + normal = R3{0, 0, 1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R3{0, -1, 0}; + } else if (name == "YMAX") { + normal = R3{0, 1, 0}; + } else if (name == "ZMIN") { + normal = R3{0, 0, -1}; + } else if (name == "ZMAX") { + normal = R3{0, 0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {22, 23, 24, 25, 26, 27}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R3 normal = zero; + + switch (tag) { + case 22: { + normal = R3{-1, 0, 0}; + break; + } + case 23: { + normal = R3{1, 0, 0}; + break; + } + case 24: { + normal = R3{0, 0, 1}; + break; + } + case 25: { + normal = R3{0, 0, -1}; + break; + } + case 26: { + normal = R3{0, 1, 0}; + break; + } + case 27: { + normal = R3{0, -1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R3{0, -1, 0}; + } else if (name == "YMAX") { + normal = R3{0, 1, 0}; + } else if (name == "ZMIN") { + normal = R3{0, 0, -1}; + } else if (name == "ZMAX") { + normal = R3{0, 0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + } + + SECTION("rotated axis") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + const double theta = 0.3; + const TinyMatrix<2> R{std::cos(theta), -std::sin(theta), // + std::sin(theta), std::cos(theta)}; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R2> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 normal = zero; + + switch (tag) { + case 0: { + normal = R * R2{-1, 0}; + break; + } + case 1: { + normal = R * R2{1, 0}; + break; + } + case 2: { + normal = R * R2{0, -1}; + break; + } + case 3: { + normal = R * R2{0, 1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 normal = zero; + + if (name == "XMIN") { + normal = R * R2{-1, 0}; + } else if (name == "XMAX") { + normal = R * R2{1, 0}; + } else if (name == "YMIN") { + normal = R * R2{0, -1}; + } else if (name == "YMAX") { + normal = R * R2{0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R2> rotated_xr{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 normal = zero; + + switch (tag) { + case 1: { + normal = R * R2{-1, 0}; + break; + } + case 2: { + normal = R * R2{1, 0}; + break; + } + case 3: { + normal = R * R2{0, 1}; + break; + } + case 4: { + normal = R * R2{0, -1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + R2 normal = zero; + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + if (name == "XMIN") { + normal = R * R2{-1, 0}; + } else if (name == "XMAX") { + normal = R * R2{1, 0}; + } else if (name == "YMIN") { + normal = R * R2{0, -1}; + } else if (name == "YMAX") { + normal = R * R2{0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + const double theta = 0.3; + const double phi = 0.4; + const TinyMatrix<3> R = + TinyMatrix<3>{std::cos(theta), -std::sin(theta), 0, std::sin(theta), std::cos(theta), 0, 0, 0, 1} * + TinyMatrix<3>{0, std::cos(phi), -std::sin(phi), 0, std::sin(phi), std::cos(phi), 1, 0, 0}; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R3> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {0, 1, 2, 3, 4, 5}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R3 normal = zero; + + switch (tag) { + case 0: { + normal = R * R3{-1, 0, 0}; + break; + } + case 1: { + normal = R * R3{1, 0, 0}; + break; + } + case 2: { + normal = R * R3{0, -1, 0}; + break; + } + case 3: { + normal = R * R3{0, 1, 0}; + break; + } + case 4: { + normal = R * R3{0, 0, -1}; + break; + } + case 5: { + normal = R * R3{0, 0, 1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R * R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R * R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R * R3{0, -1, 0}; + } else if (name == "YMAX") { + normal = R * R3{0, 1, 0}; + } else if (name == "ZMIN") { + normal = R * R3{0, 0, -1}; + } else if (name == "ZMAX") { + normal = R * R3{0, 0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R3> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {22, 23, 24, 25, 26, 27}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R3 normal = zero; + + switch (tag) { + case 22: { + normal = R * R3{-1, 0, 0}; + break; + } + case 23: { + normal = R * R3{1, 0, 0}; + break; + } + case 24: { + normal = R * R3{0, 0, 1}; + break; + } + case 25: { + normal = R * R3{0, 0, -1}; + break; + } + case 26: { + normal = R * R3{0, 1, 0}; + break; + } + case 27: { + normal = R * R3{0, -1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R * R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R * R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R * R3{0, -1, 0}; + } else if (name == "YMAX") { + normal = R * R3{0, 1, 0}; + } else if (name == "ZMIN") { + normal = R * R3{0, 0, -1}; + } else if (name == "ZMAX") { + normal = R * R3{0, 0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + } + + SECTION("curved mesh") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + auto curve = [](const R2& X) -> R2 { return R2{X[0], (1 + X[0] * X[0]) * X[1]}; }; + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<TinyVector<2>> curved_xr{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {1, 2, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 normal = zero; + + switch (tag) { + case 1: { + normal = R2{-1, 0}; + break; + } + case 2: { + normal = R2{1, 0}; + break; + } + case 4: { + normal = R2{0, -1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(3); + REQUIRE_THROWS_WITH(getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not flat!"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + R2 normal = zero; + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + if (name == "XMIN") { + normal = R2{-1, 0}; + } else if (name == "XMAX") { + normal = R2{1, 0}; + } else if (name == "YMIN") { + normal = R2{0, -1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("YMAX"); + REQUIRE_THROWS_WITH(getMeshFlatFaceBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not flat!"); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + auto curve = [](const R3& X) -> R3 { + return R3{X[0], (1 + X[0] * X[0]) * (X[1] + 1), (1 - 0.2 * X[0] * X[0]) * X[2]}; + }; + + NodeValue<R3> curved_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {0, 1, 2, 4}; + + for (auto tag : tag_set) { + auto face_list = get_face_list_from_tag(tag, connectivity); + + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor); + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R3 normal = zero; + + switch (tag) { + case 0: { + normal = R3{-1, 0, 0}; + break; + } + case 1: { + normal = R3{1, 0, 0}; + break; + } + case 2: { + normal = R3{0, -1, 0}; + break; + } + case 4: { + normal = R3{0, 0, -1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(3); + REQUIRE_THROWS_WITH(getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not flat!"); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(5); + REQUIRE_THROWS_WITH(getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"ZMAX(5)\": boundary is not flat!"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "ZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R3{0, -1, 0}; + } else if (name == "ZMIN") { + normal = R3{0, 0, -1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("YMAX"); + REQUIRE_THROWS_WITH(getMeshFlatFaceBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not flat!"); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("ZMAX"); + REQUIRE_THROWS_WITH(getMeshFlatFaceBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"ZMAX(5)\": boundary is not flat!"); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + auto curve = [](const R3& X) -> R3 { + return R3{X[0], (1 + X[0] * X[0]) * X[1], (1 - 0.2 * X[0] * X[0]) * X[2]}; + }; + + NodeValue<R3> curved_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {22, 23, 25, 27}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R3 normal = zero; + + switch (tag) { + case 22: { + normal = R3{-1, 0, 0}; + break; + } + case 23: { + normal = R3{1, 0, 0}; + break; + } + case 25: { + normal = R3{0, 0, -1}; + break; + } + case 27: { + normal = R3{0, -1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(24); + REQUIRE_THROWS_WITH(getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"ZMAX(24)\": boundary is not flat!"); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(26); + REQUIRE_THROWS_WITH(getMeshFlatFaceBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"YMAX(26)\": boundary is not flat!"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "ZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshFlatFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R3{0, -1, 0}; + } else if (name == "ZMIN") { + normal = R3{0, 0, -1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("YMAX"); + REQUIRE_THROWS_WITH(getMeshFlatFaceBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"YMAX(26)\": boundary is not flat!"); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("ZMAX"); + REQUIRE_THROWS_WITH(getMeshFlatFaceBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"ZMAX(24)\": boundary is not flat!"); + } + } + } + } + } +} diff --git a/tests/test_MeshFlatNodeBoundary.cpp b/tests/test_MeshFlatNodeBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..29b0110569a14a6612a8b50cc76bc892c476d1ad --- /dev/null +++ b/tests/test_MeshFlatNodeBoundary.cpp @@ -0,0 +1,1471 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshFlatNodeBoundary.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/NumberedBoundaryDescriptor.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("MeshFlatNodeBoundary", "[mesh]") +{ + auto is_same = [](const auto& a, const auto& b) -> bool { + if (a.size() > 0 and b.size() > 0) { + return (a[0] == b[0]); + } else { + return (a.size() == b.size()); + } + }; + + auto get_node_list_from_tag = [](const size_t tag, const auto& connectivity) -> Array<const NodeId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::node>(); ++i) { + const auto& ref_node_list = connectivity.template refItemList<ItemType::node>(i); + const RefId ref_id = ref_node_list.refId(); + if (ref_id.tagNumber() == tag) { + return ref_node_list.list(); + } + } + return {}; + }; + + auto get_node_list_from_name = [](const std::string& name, const auto& connectivity) -> Array<const NodeId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::node>(); ++i) { + const auto& ref_node_list = connectivity.template refItemList<ItemType::node>(i); + const RefId ref_id = ref_node_list.refId(); + if (ref_id.tagName() == name) { + return ref_node_list.list(); + } + } + return {}; + }; + + SECTION("aligned axis") + { + SECTION("1D") + { + static constexpr size_t Dimension = 1; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R1 = TinyVector<1>; + + SECTION("cartesian 1d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian1DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R1 normal = zero; + + switch (tag) { + case 0: { + normal = R1{-1}; + break; + } + case 1: { + normal = R1{1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R1 normal = zero; + + if (name == "XMIN") { + normal = R1{-1}; + } else if (name == "XMAX") { + normal = R1{1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("unordered 1d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R1 normal = zero; + + switch (tag) { + case 1: { + normal = R1{-1}; + break; + } + case 2: { + normal = R1{1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R1 normal = zero; + + if (name == "XMIN") { + normal = R1{-1}; + } else if (name == "XMAX") { + normal = R1{1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 normal = zero; + + switch (tag) { + case 0: { + normal = R2{-1, 0}; + break; + } + case 1: { + normal = R2{1, 0}; + break; + } + case 2: { + normal = R2{0, -1}; + break; + } + case 3: { + normal = R2{0, 1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: invalid boundary \"XMINYMIN(10)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: invalid boundary \"XMAXYMIN(11)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(12)), + "error: invalid boundary \"XMAXYMAX(12)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(13)), + "error: invalid boundary \"XMINYMAX(13)\": unable to compute normal"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 normal = zero; + + if (name == "XMIN") { + normal = R2{-1, 0}; + } else if (name == "XMAX") { + normal = R2{1, 0}; + } else if (name == "YMIN") { + normal = R2{0, -1}; + } else if (name == "YMAX") { + normal = R2{0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: invalid boundary \"XMINYMIN(10)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: invalid boundary \"XMINYMAX(13)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMIN")), + "error: invalid boundary \"XMAXYMIN(11)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: invalid boundary \"XMAXYMAX(12)\": unable to compute normal"); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 normal = zero; + + switch (tag) { + case 1: { + normal = R2{-1, 0}; + break; + } + case 2: { + normal = R2{1, 0}; + break; + } + case 3: { + normal = R2{0, 1}; + break; + } + case 4: { + normal = R2{0, -1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(8)), + "error: invalid boundary \"XMINYMIN(8)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(9)), + "error: invalid boundary \"XMINYMAX(9)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: invalid boundary \"XMAXYMIN(10)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: invalid boundary \"XMAXYMAX(11)\": unable to compute normal"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + R2 normal = zero; + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + if (name == "XMIN") { + normal = R2{-1, 0}; + } else if (name == "XMAX") { + normal = R2{1, 0}; + } else if (name == "YMIN") { + normal = R2{0, -1}; + } else if (name == "YMAX") { + normal = R2{0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: invalid boundary \"XMINYMIN(8)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: invalid boundary \"XMINYMAX(9)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMIN")), + "error: invalid boundary \"XMAXYMIN(10)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: invalid boundary \"XMAXYMAX(11)\": unable to compute normal"); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3, 4, 5}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 normal = zero; + + switch (tag) { + case 0: { + normal = R3{-1, 0, 0}; + break; + } + case 1: { + normal = R3{1, 0, 0}; + break; + } + case 2: { + normal = R3{0, -1, 0}; + break; + } + case 3: { + normal = R3{0, 1, 0}; + break; + } + case 4: { + normal = R3{0, 0, -1}; + break; + } + case 5: { + normal = R3{0, 0, 1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(20)), + "error: cannot find surface with name \"20\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(21)), + "error: cannot find surface with name \"21\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(22)), + "error: cannot find surface with name \"22\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(23)), + "error: cannot find surface with name \"23\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(24)), + "error: cannot find surface with name \"24\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(25)), + "error: cannot find surface with name \"25\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(26)), + "error: cannot find surface with name \"26\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(27)), + "error: cannot find surface with name \"27\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(28)), + "error: cannot find surface with name \"28\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(29)), + "error: cannot find surface with name \"29\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(30)), + "error: cannot find surface with name \"30\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(31)), + "error: cannot find surface with name \"31\""); + + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: invalid boundary \"XMINYMINZMIN(10)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: invalid boundary \"XMAXYMINZMIN(11)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(12)), + "error: invalid boundary \"XMAXYMAXZMIN(12)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(13)), + "error: invalid boundary \"XMINYMAXZMIN(13)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(14)), + "error: invalid boundary \"XMINYMINZMAX(14)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(15)), + "error: invalid boundary \"XMAXYMINZMAX(15)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(16)), + "error: invalid boundary \"XMAXYMAXZMAX(16)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(17)), + "error: invalid boundary \"XMINYMAXZMAX(17)\": unable to compute normal"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R3{0, -1, 0}; + } else if (name == "YMAX") { + normal = R3{0, 1, 0}; + } else if (name == "ZMIN") { + normal = R3{0, 0, -1}; + } else if (name == "ZMAX") { + normal = R3{0, 0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: cannot find surface with name \"XMINYMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find surface with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: cannot find surface with name \"XMAXYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find surface with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINZMIN")), + "error: cannot find surface with name \"XMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINZMAX")), + "error: cannot find surface with name \"XMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXZMAX")), + "error: cannot find surface with name \"XMAXZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINZMAX")), + "error: cannot find surface with name \"XMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("YMINZMIN")), + "error: cannot find surface with name \"YMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("YMINZMAX")), + "error: cannot find surface with name \"YMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("YMAXZMAX")), + "error: cannot find surface with name \"YMAXZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("YMINZMAX")), + "error: cannot find surface with name \"YMINZMAX\""); + + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMIN")), + "error: invalid boundary \"XMINYMINZMIN(10)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMIN")), + "error: invalid boundary \"XMAXYMINZMIN(11)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMIN")), + "error: invalid boundary \"XMAXYMAXZMIN(12)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMIN")), + "error: invalid boundary \"XMINYMAXZMIN(13)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMAX")), + "error: invalid boundary \"XMINYMINZMAX(14)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMAX")), + "error: invalid boundary \"XMAXYMINZMAX(15)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMAX")), + "error: invalid boundary \"XMAXYMAXZMAX(16)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMAX")), + "error: invalid boundary \"XMINYMAXZMAX(17)\": unable to compute normal"); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {22, 23, 24, 25, 26, 27}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 normal = zero; + + switch (tag) { + case 22: { + normal = R3{-1, 0, 0}; + break; + } + case 23: { + normal = R3{1, 0, 0}; + break; + } + case 24: { + normal = R3{0, 0, 1}; + break; + } + case 25: { + normal = R3{0, 0, -1}; + break; + } + case 26: { + normal = R3{0, 1, 0}; + break; + } + case 27: { + normal = R3{0, -1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(28)), + "error: cannot find surface with name \"28\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(29)), + "error: cannot find surface with name \"29\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(30)), + "error: cannot find surface with name \"30\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(31)), + "error: cannot find surface with name \"31\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(32)), + "error: cannot find surface with name \"32\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(33)), + "error: cannot find surface with name \"33\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(34)), + "error: cannot find surface with name \"34\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(35)), + "error: cannot find surface with name \"35\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(36)), + "error: cannot find surface with name \"36\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(37)), + "error: cannot find surface with name \"37\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(38)), + "error: cannot find surface with name \"38\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(39)), + "error: cannot find surface with name \"39\""); + + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(40)), + "error: invalid boundary \"XMINYMINZMIN(40)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(41)), + "error: invalid boundary \"XMAXYMINZMIN(41)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(42)), + "error: invalid boundary \"XMINYMAXZMIN(42)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(43)), + "error: invalid boundary \"XMINYMAXZMAX(43)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(44)), + "error: invalid boundary \"XMINYMINZMAX(44)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(45)), + "error: invalid boundary \"XMAXYMINZMAX(45)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(47)), + "error: invalid boundary \"XMAXYMAXZMAX(47)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NumberedBoundaryDescriptor(51)), + "error: invalid boundary \"XMAXYMAXZMIN(51)\": unable to compute normal"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R3{0, -1, 0}; + } else if (name == "YMAX") { + normal = R3{0, 1, 0}; + } else if (name == "ZMIN") { + normal = R3{0, 0, -1}; + } else if (name == "ZMAX") { + normal = R3{0, 0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: cannot find surface with name \"XMINYMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find surface with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: cannot find surface with name \"XMAXYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find surface with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINZMIN")), + "error: cannot find surface with name \"XMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINZMAX")), + "error: cannot find surface with name \"XMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXZMAX")), + "error: cannot find surface with name \"XMAXZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINZMAX")), + "error: cannot find surface with name \"XMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("YMINZMIN")), + "error: cannot find surface with name \"YMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("YMINZMAX")), + "error: cannot find surface with name \"YMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("YMAXZMAX")), + "error: cannot find surface with name \"YMAXZMAX\""); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("YMINZMAX")), + "error: cannot find surface with name \"YMINZMAX\""); + + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMIN")), + "error: invalid boundary \"XMINYMINZMIN(40)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMIN")), + "error: invalid boundary \"XMAXYMINZMIN(41)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMIN")), + "error: invalid boundary \"XMAXYMAXZMIN(51)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMIN")), + "error: invalid boundary \"XMINYMAXZMIN(42)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMAX")), + "error: invalid boundary \"XMINYMINZMAX(44)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMAX")), + "error: invalid boundary \"XMAXYMINZMAX(45)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMAX")), + "error: invalid boundary \"XMAXYMAXZMAX(47)\": unable to compute normal"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMAX")), + "error: invalid boundary \"XMINYMAXZMAX(43)\": unable to compute normal"); + } + } + } + } + } + + SECTION("rotated axis") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + const double theta = 0.3; + const TinyMatrix<2> R{std::cos(theta), -std::sin(theta), // + std::sin(theta), std::cos(theta)}; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R2> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 normal = zero; + + switch (tag) { + case 0: { + normal = R * R2{-1, 0}; + break; + } + case 1: { + normal = R * R2{1, 0}; + break; + } + case 2: { + normal = R * R2{0, -1}; + break; + } + case 3: { + normal = R * R2{0, 1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 normal = zero; + + if (name == "XMIN") { + normal = R * R2{-1, 0}; + } else if (name == "XMAX") { + normal = R * R2{1, 0}; + } else if (name == "YMIN") { + normal = R * R2{0, -1}; + } else if (name == "YMAX") { + normal = R * R2{0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R2> rotated_xr{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 normal = zero; + + switch (tag) { + case 1: { + normal = R * R2{-1, 0}; + break; + } + case 2: { + normal = R * R2{1, 0}; + break; + } + case 3: { + normal = R * R2{0, 1}; + break; + } + case 4: { + normal = R * R2{0, -1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + R2 normal = zero; + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + if (name == "XMIN") { + normal = R * R2{-1, 0}; + } else if (name == "XMAX") { + normal = R * R2{1, 0}; + } else if (name == "YMIN") { + normal = R * R2{0, -1}; + } else if (name == "YMAX") { + normal = R * R2{0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + const double theta = 0.3; + const double phi = 0.4; + const TinyMatrix<3> R = + TinyMatrix<3>{std::cos(theta), -std::sin(theta), 0, std::sin(theta), std::cos(theta), 0, 0, 0, 1} * + TinyMatrix<3>{0, std::cos(phi), -std::sin(phi), 0, std::sin(phi), std::cos(phi), 1, 0, 0}; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R3> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {0, 1, 2, 3, 4, 5}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 normal = zero; + + switch (tag) { + case 0: { + normal = R * R3{-1, 0, 0}; + break; + } + case 1: { + normal = R * R3{1, 0, 0}; + break; + } + case 2: { + normal = R * R3{0, -1, 0}; + break; + } + case 3: { + normal = R * R3{0, 1, 0}; + break; + } + case 4: { + normal = R * R3{0, 0, -1}; + break; + } + case 5: { + normal = R * R3{0, 0, 1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R * R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R * R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R * R3{0, -1, 0}; + } else if (name == "YMAX") { + normal = R * R3{0, 1, 0}; + } else if (name == "ZMIN") { + normal = R * R3{0, 0, -1}; + } else if (name == "ZMAX") { + normal = R * R3{0, 0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R3> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {22, 23, 24, 25, 26, 27}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 normal = zero; + + switch (tag) { + case 22: { + normal = R * R3{-1, 0, 0}; + break; + } + case 23: { + normal = R * R3{1, 0, 0}; + break; + } + case 24: { + normal = R * R3{0, 0, 1}; + break; + } + case 25: { + normal = R * R3{0, 0, -1}; + break; + } + case 26: { + normal = R * R3{0, 1, 0}; + break; + } + case 27: { + normal = R * R3{0, -1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R * R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R * R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R * R3{0, -1, 0}; + } else if (name == "YMAX") { + normal = R * R3{0, 1, 0}; + } else if (name == "ZMIN") { + normal = R * R3{0, 0, -1}; + } else if (name == "ZMAX") { + normal = R * R3{0, 0, 1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + } + + SECTION("curved mesh") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + auto curve = [](const R2& X) -> R2 { return R2{X[0], (1 + X[0] * X[0]) * X[1]}; }; + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<TinyVector<2>> curved_xr{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {1, 2, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 normal = zero; + + switch (tag) { + case 1: { + normal = R2{-1, 0}; + break; + } + case 2: { + normal = R2{1, 0}; + break; + } + case 4: { + normal = R2{0, -1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(3); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not flat!"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + R2 normal = zero; + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + if (name == "XMIN") { + normal = R2{-1, 0}; + } else if (name == "XMAX") { + normal = R2{1, 0}; + } else if (name == "YMIN") { + normal = R2{0, -1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("YMAX"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not flat!"); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + auto curve = [](const R3& X) -> R3 { + return R3{X[0], (1 + X[0] * X[0]) * (X[1] + 1), (1 - 0.2 * X[0] * X[0]) * X[2]}; + }; + + NodeValue<R3> curved_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {0, 1, 2, 4}; + + for (auto tag : tag_set) { + auto node_list = get_node_list_from_tag(tag, connectivity); + + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 normal = zero; + + switch (tag) { + case 0: { + normal = R3{-1, 0, 0}; + break; + } + case 1: { + normal = R3{1, 0, 0}; + break; + } + case 2: { + normal = R3{0, -1, 0}; + break; + } + case 4: { + normal = R3{0, 0, -1}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(3); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not flat!"); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(5); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"ZMAX(5)\": boundary is not flat!"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "ZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R3{0, -1, 0}; + } else if (name == "ZMIN") { + normal = R3{0, 0, -1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("YMAX"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not flat!"); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("ZMAX"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"ZMAX(5)\": boundary is not flat!"); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + auto curve = [](const R3& X) -> R3 { + return R3{X[0], (1 + X[0] * X[0]) * X[1], (1 - 0.2 * X[0] * X[0]) * X[2]}; + }; + + NodeValue<R3> curved_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {22, 23, 25, 27}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 normal = zero; + + switch (tag) { + case 22: { + normal = R3{-1, 0, 0}; + break; + } + case 23: { + normal = R3{1, 0, 0}; + break; + } + case 25: { + normal = R3{0, 0, -1}; + break; + } + case 27: { + normal = R3{0, -1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(24); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"ZMAX(24)\": boundary is not flat!"); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(26); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"YMAX(26)\": boundary is not flat!"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "ZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshFlatNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 normal = zero; + + if (name == "XMIN") { + normal = R3{-1, 0, 0}; + } else if (name == "XMAX") { + normal = R3{1, 0, 0}; + } else if (name == "YMIN") { + normal = R3{0, -1, 0}; + } else if (name == "ZMIN") { + normal = R3{0, 0, -1}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.outgoingNormal() - normal) == Catch::Approx(0).margin(1E-13)); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("YMAX"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"YMAX(26)\": boundary is not flat!"); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("ZMAX"); + REQUIRE_THROWS_WITH(getMeshFlatNodeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"ZMAX(24)\": boundary is not flat!"); + } + } + } + } + } +} diff --git a/tests/test_MeshLineEdgeBoundary.cpp b/tests/test_MeshLineEdgeBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b4b21d0dc7e692216f80d0119f97d423518eb884 --- /dev/null +++ b/tests/test_MeshLineEdgeBoundary.cpp @@ -0,0 +1,1258 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshLineEdgeBoundary.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/NumberedBoundaryDescriptor.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("MeshLineEdgeBoundary", "[mesh]") +{ + auto is_same = [](const auto& a, const auto& b) -> bool { + if (a.size() > 0 and b.size() > 0) { + return (a[0] == b[0]); + } else { + return (a.size() == b.size()); + } + }; + + auto get_edge_list_from_tag = [](const size_t tag, const auto& connectivity) -> Array<const EdgeId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::edge>(); ++i) { + const auto& ref_edge_list = connectivity.template refItemList<ItemType::edge>(i); + const RefId ref_id = ref_edge_list.refId(); + if (ref_id.tagNumber() == tag) { + return ref_edge_list.list(); + } + } + return {}; + }; + + auto get_edge_list_from_name = [](const std::string& name, const auto& connectivity) -> Array<const EdgeId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::edge>(); ++i) { + const auto& ref_edge_list = connectivity.template refItemList<ItemType::edge>(i); + const RefId ref_id = ref_edge_list.refId(); + if (ref_id.tagName() == name) { + return ref_edge_list.list(); + } + } + return {}; + }; + + SECTION("aligned axis") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 direction = zero; + + switch (tag) { + case 0: { + direction = R2{0, 1}; + break; + } + case 1: { + direction = R2{0, 1}; + break; + } + case 2: { + direction = R2{1, 0}; + break; + } + case 3: { + direction = R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: cannot find edge list with name \"10\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: cannot find edge list with name \"11\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(12)), + "error: cannot find edge list with name \"12\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(13)), + "error: cannot find edge list with name \"13\""); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 direction = zero; + + if (name == "XMIN") { + direction = R2{0, 1}; + } else if (name == "XMAX") { + direction = R2{0, 1}; + } else if (name == "YMIN") { + direction = R2{1, 0}; + } else if (name == "YMAX") { + direction = R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: cannot find edge list with name \"XMINYMIN\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find edge list with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMIN")), + "error: cannot find edge list with name \"XMAXYMIN\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: cannot find edge list with name \"XMAXYMAX\""); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 direction = zero; + + switch (tag) { + case 1: { + direction = R2{0, 1}; + break; + } + case 2: { + direction = R2{0, 1}; + break; + } + case 3: { + direction = R2{1, 0}; + break; + } + case 4: { + direction = R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(8)), + "error: cannot find edge list with name \"8\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(9)), + "error: cannot find edge list with name \"9\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: cannot find edge list with name \"10\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: cannot find edge list with name \"11\""); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + R2 direction = zero; + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + if (name == "XMIN") { + direction = R2{0, 1}; + } else if (name == "XMAX") { + direction = R2{0, 1}; + } else if (name == "YMIN") { + direction = R2{1, 0}; + } else if (name == "YMAX") { + direction = R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: cannot find edge list with name \"XMINYMIN\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find edge list with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMIN")), + "error: cannot find edge list with name \"XMAXYMIN\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: cannot find edge list with name \"XMAXYMAX\""); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 direction = zero; + + switch (tag) { + case 20: + case 21: + case 22: + case 23: { + direction = R3{0, 0, -1}; + break; + } + case 24: + case 25: + case 26: + case 27: { + direction = R3{0, -1, 0}; + break; + } + case 28: + case 29: + case 30: + case 31: { + direction = R3{1, 0, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(0)), + "error: invalid boundary \"XMIN(0)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(1)), + "error: invalid boundary \"XMAX(1)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(2)), + "error: invalid boundary \"YMIN(2)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(3)), + "error: invalid boundary \"YMAX(3)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(4)), + "error: invalid boundary \"ZMIN(4)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(5)), + "error: invalid boundary \"ZMAX(5)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: cannot find edge list with name \"10\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: cannot find edge list with name \"11\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(12)), + "error: cannot find edge list with name \"12\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(13)), + "error: cannot find edge list with name \"13\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(14)), + "error: cannot find edge list with name \"14\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(15)), + "error: cannot find edge list with name \"15\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(16)), + "error: cannot find edge list with name \"16\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(17)), + "error: cannot find edge list with name \"17\""); + } + + { + const std::set<std::string> name_set = {"XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX", + "XMINZMIN", "XMINZMAX", "XMAXZMAX", "XMINZMAX", + "YMINZMIN", "YMINZMAX", "YMAXZMAX", "YMAXZMIN"}; + + for (const auto& name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 direction = zero; + + if ((name == "XMINYMIN") or (name == "XMINYMAX") or (name == "XMAXYMIN") or (name == "XMAXYMAX")) { + direction = R3{0, 0, -1}; + } else if ((name == "XMINZMIN") or (name == "XMINZMAX") or (name == "XMAXZMIN") or (name == "XMAXZMAX")) { + direction = R3{0, -1, 0}; + } else if ((name == "YMINZMIN") or (name == "YMINZMAX") or (name == "YMAXZMIN") or (name == "YMAXZMAX")) { + direction = R3{1, 0, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMIN")), + "error: invalid boundary \"XMIN(0)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAX")), + "error: invalid boundary \"XMAX(1)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("YMIN")), + "error: invalid boundary \"YMIN(2)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("YMAX")), + "error: invalid boundary \"YMAX(3)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("ZMIN")), + "error: invalid boundary \"ZMIN(4)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("ZMAX")), + "error: invalid boundary \"ZMAX(5)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMIN")), + "error: cannot find edge list with name \"XMINYMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMIN")), + "error: cannot find edge list with name \"XMAXYMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMIN")), + "error: cannot find edge list with name \"XMAXYMAXZMIN\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMIN")), + "error: cannot find edge list with name \"XMINYMAXZMIN\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMAX")), + "error: cannot find edge list with name \"XMINYMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMAX")), + "error: cannot find edge list with name \"XMAXYMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMAX")), + "error: cannot find edge list with name \"XMAXYMAXZMAX\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMAX")), + "error: cannot find edge list with name \"XMINYMAXZMAX\""); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 direction = zero; + + switch (tag) { + case 30: + case 31: + case 34: + case 35: { + direction = R3{0, 0, -1}; + break; + } + case 28: + case 29: + case 32: + case 33: { + direction = R3{0, -1, 0}; + break; + } + case 36: + case 37: + case 38: + case 39: { + direction = R3{1, 0, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(22)), + "error: invalid boundary \"XMIN(22)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(23)), + "error: invalid boundary \"XMAX(23)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(24)), + "error: invalid boundary \"ZMAX(24)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(25)), + "error: invalid boundary \"ZMIN(25)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(26)), + "error: invalid boundary \"YMAX(26)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(27)), + "error: invalid boundary \"YMIN(27)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(40)), + "error: cannot find edge list with name \"40\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(41)), + "error: cannot find edge list with name \"41\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(42)), + "error: cannot find edge list with name \"42\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(43)), + "error: cannot find edge list with name \"43\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(44)), + "error: cannot find edge list with name \"44\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(45)), + "error: cannot find edge list with name \"45\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(47)), + "error: cannot find edge list with name \"47\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(51)), + "error: cannot find edge list with name \"51\""); + } + } + + { + const std::set<std::string> name_set = {"XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX", + "XMINZMIN", "XMINZMAX", "XMAXZMAX", "XMINZMAX", + "YMINZMIN", "YMINZMAX", "YMAXZMAX", "YMAXZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 direction = zero; + + if ((name == "XMINYMIN") or (name == "XMINYMAX") or (name == "XMAXYMIN") or (name == "XMAXYMAX")) { + direction = R3{0, 0, -1}; + } else if ((name == "XMINZMIN") or (name == "XMINZMAX") or (name == "XMAXZMIN") or (name == "XMAXZMAX")) { + direction = R3{0, -1, 0}; + } else if ((name == "YMINZMIN") or (name == "YMINZMAX") or (name == "YMAXZMIN") or (name == "YMAXZMAX")) { + direction = R3{1, 0, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMIN")), + "error: invalid boundary \"XMIN(22)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAX")), + "error: invalid boundary \"XMAX(23)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("YMIN")), + "error: invalid boundary \"YMIN(27)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("YMAX")), + "error: invalid boundary \"YMAX(26)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("ZMIN")), + "error: invalid boundary \"ZMIN(25)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("ZMAX")), + "error: invalid boundary \"ZMAX(24)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMIN")), + "error: cannot find edge list with name \"XMINYMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMIN")), + "error: cannot find edge list with name \"XMAXYMINZMIN\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMIN")), + "error: cannot find edge list with name \"XMAXYMAXZMIN\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMIN")), + "error: cannot find edge list with name \"XMINYMAXZMIN\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMAX")), + "error: cannot find edge list with name \"XMINYMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMAX")), + "error: cannot find edge list with name \"XMAXYMINZMAX\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMAX")), + "error: cannot find edge list with name \"XMAXYMAXZMAX\""); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMAX")), + "error: cannot find edge list with name \"XMINYMAXZMAX\""); + } + } + } + } + } + + SECTION("rotated axis") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + const double theta = 0.3; + const TinyMatrix<2> R{std::cos(theta), -std::sin(theta), // + std::sin(theta), std::cos(theta)}; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R2> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 direction = zero; + + switch (tag) { + case 0: { + direction = R * R2{0, -1}; + break; + } + case 1: { + direction = R * R2{0, -1}; + break; + } + case 2: { + direction = R * R2{1, 0}; + break; + } + case 3: { + direction = R * R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 direction = zero; + + if (name == "XMIN") { + direction = R * R2{0, -1}; + } else if (name == "XMAX") { + direction = R * R2{0, -1}; + } else if (name == "YMIN") { + direction = R * R2{1, 0}; + } else if (name == "YMAX") { + direction = R * R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R2> rotated_xr{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 direction = zero; + + switch (tag) { + case 1: { + direction = R * R2{0, -1}; + break; + } + case 2: { + direction = R * R2{0, -1}; + break; + } + case 3: { + direction = R * R2{1, 0}; + break; + } + case 4: { + direction = R * R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + R2 direction = zero; + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + if (name == "XMIN") { + direction = R * R2{0, -1}; + } else if (name == "XMAX") { + direction = R * R2{0, -1}; + } else if (name == "YMIN") { + direction = R * R2{1, 0}; + } else if (name == "YMAX") { + direction = R * R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + const double theta = 0.3; + const double phi = 0.4; + const TinyMatrix<3> R = + TinyMatrix<3>{std::cos(theta), -std::sin(theta), 0, std::sin(theta), std::cos(theta), 0, 0, 0, 1} * + TinyMatrix<3>{0, std::cos(phi), -std::sin(phi), 0, std::sin(phi), std::cos(phi), 1, 0, 0}; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R3> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 direction = zero; + + switch (tag) { + case 20: + case 21: + case 22: + case 23: { + direction = R * R3{0, 0, -1}; + break; + } + case 24: + case 25: + case 26: + case 27: { + direction = R * R3{0, 1, 0}; + break; + } + case 28: + case 29: + case 30: + case 31: { + direction = R * R3{-1, 0, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX", + "XMINZMIN", "XMINZMAX", "XMAXZMAX", "XMINZMAX", + "YMINZMIN", "YMINZMAX", "YMAXZMAX", "YMAXZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 direction = zero; + + if ((name == "XMINYMIN") or (name == "XMINYMAX") or (name == "XMAXYMIN") or (name == "XMAXYMAX")) { + direction = R * R3{0, 0, -1}; + } else if ((name == "XMINZMIN") or (name == "XMINZMAX") or (name == "XMAXZMIN") or (name == "XMAXZMAX")) { + direction = R * R3{0, 1, 0}; + } else if ((name == "YMINZMIN") or (name == "YMINZMAX") or (name == "YMAXZMIN") or (name == "YMAXZMAX")) { + direction = R * R3{-1, 0, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R3> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 direction = zero; + + switch (tag) { + case 30: + case 31: + case 34: + case 35: { + direction = R * R3{0, 0, -1}; + break; + } + case 28: + case 29: + case 32: + case 33: { + direction = R * R3{0, 1, 0}; + break; + } + case 36: + case 37: + case 38: + case 39: { + direction = R * R3{-1, 0, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX", + "XMINZMIN", "XMINZMAX", "XMAXZMAX", "XMINZMAX", + "YMINZMIN", "YMINZMAX", "YMAXZMAX", "YMAXZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 direction = zero; + + if ((name == "XMINYMIN") or (name == "XMINYMAX") or (name == "XMAXYMIN") or (name == "XMAXYMAX")) { + direction = R * R3{0, 0, -1}; + } else if ((name == "XMINZMIN") or (name == "XMINZMAX") or (name == "XMAXZMIN") or (name == "XMAXZMAX")) { + direction = R * R3{0, 1, 0}; + } else if ((name == "YMINZMIN") or (name == "YMINZMAX") or (name == "YMAXZMIN") or (name == "YMAXZMAX")) { + direction = R * R3{-1, 0, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + } + + SECTION("curved mesh") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + auto curve = [](const R2& X) -> R2 { return R2{X[0], (1 + X[0] * X[0]) * X[1]}; }; + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<TinyVector<2>> curved_xr{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {1, 2, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R2 direction = zero; + + switch (tag) { + case 1: { + direction = R2{0, 1}; + break; + } + case 2: { + direction = R2{0, 1}; + break; + } + case 4: { + direction = R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(3); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not a line!"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + R2 direction = zero; + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + if (name == "XMIN") { + direction = R2{0, 1}; + } else if (name == "XMAX") { + direction = R2{0, 1}; + } else if (name == "YMIN") { + direction = R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("YMAX"); + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not a line!"); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + auto curve = [](const R3& X) -> R3 { + return R3{X[0], (1 + X[0] * X[0]) * (X[1] + 1), (1 - 0.2 * X[0] * X[0]) * X[2]}; + }; + + NodeValue<R3> curved_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {20, 21, 22, 23, 24, 25, 26, 27, 28}; + + for (auto tag : tag_set) { + auto edge_list = get_edge_list_from_tag(tag, connectivity); + + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, numbered_boundary_descriptor); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 direction = zero; + + switch (tag) { + case 20: + case 21: + case 22: + case 23: { + direction = R3{0, 0, -1}; + break; + } + case 24: + case 25: + case 26: + case 27: { + direction = R3{0, -1, 0}; + break; + } + case 28: { + direction = R3{1, 0, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(29)), + "error: invalid boundary \"YMINZMAX(29)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(30)), + "error: invalid boundary \"YMAXZMAX(30)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(31)), + "error: invalid boundary \"YMAXZMIN(31)\": boundary is not a line!"); + } + } + + { + const std::set<std::string> name_set = {"XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX", "XMINZMIN", + "XMINZMAX", "XMAXZMAX", "XMINZMAX", "YMINZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 direction = zero; + + if ((name == "XMINYMIN") or (name == "XMINYMAX") or (name == "XMAXYMIN") or (name == "XMAXYMAX")) { + direction = R3{0, 0, -1}; + } else if ((name == "XMINZMIN") or (name == "XMINZMAX") or (name == "XMAXZMIN") or (name == "XMAXZMAX")) { + direction = R3{0, -1, 0}; + } else if ((name == "YMINZMIN")) { + direction = R3{1, 0, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("YMAXZMAX")), + "error: invalid boundary \"YMAXZMAX(30)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("YMINZMAX")), + "error: invalid boundary \"YMINZMAX(29)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("YMAXZMIN")), + "error: invalid boundary \"YMAXZMIN(31)\": boundary is not a line!"); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + auto curve = [](const R3& X) -> R3 { + return R3{X[0], (1 + X[0] * X[0]) * X[1], (1 - 0.2 * X[0] * X[0]) * X[2]}; + }; + + NodeValue<R3> curved_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {28, 29, 30, 31, 32, 33, 34, 35, 36}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, numbered_boundary_descriptor); + + auto edge_list = get_edge_list_from_tag(tag, connectivity); + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 direction = zero; + + switch (tag) { + case 30: + case 31: + case 34: + case 35: { + direction = R3{0, 0, -1}; + break; + } + case 28: + case 29: + case 32: + case 33: { + direction = R3{0, -1, 0}; + break; + } + case 36: + // case 37: + // case 38: + case 39: { + direction = R3{1, 0, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(37)), + "error: invalid boundary \"YMINZMAX(37)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(38)), + "error: invalid boundary \"YMAXZMIN(38)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NumberedBoundaryDescriptor(39)), + "error: invalid boundary \"YMAXZMAX(39)\": boundary is not a line!"); + } + } + + { + const std::set<std::string> name_set = {"XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX", "XMINZMIN", + "XMINZMAX", "XMAXZMAX", "XMINZMAX", "YMINZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& edge_boundary = getMeshLineEdgeBoundary(mesh, named_boundary_descriptor); + + auto edge_list = get_edge_list_from_name(name, connectivity); + + REQUIRE(is_same(edge_boundary.edgeList(), edge_list)); + + R3 direction = zero; + + if ((name == "XMINYMIN") or (name == "XMINYMAX") or (name == "XMAXYMIN") or (name == "XMAXYMAX")) { + direction = R3{0, 0, -1}; + } else if ((name == "XMINZMIN") or (name == "XMINZMAX") or (name == "XMAXZMIN") or (name == "XMAXZMAX")) { + direction = R3{0, -1, 0}; + } else if ((name == "YMINZMIN")) { + direction = R3{1, 0, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(edge_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("YMAXZMAX")), + "error: invalid boundary \"YMAXZMAX(39)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("YMINZMAX")), + "error: invalid boundary \"YMINZMAX(37)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineEdgeBoundary(mesh, NamedBoundaryDescriptor("YMAXZMIN")), + "error: invalid boundary \"YMAXZMIN(38)\": boundary is not a line!"); + } + } + } + } + } +} diff --git a/tests/test_MeshLineFaceBoundary.cpp b/tests/test_MeshLineFaceBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f5cb9eb18714f668d63a0a4c2fd40a04b7cbeee9 --- /dev/null +++ b/tests/test_MeshLineFaceBoundary.cpp @@ -0,0 +1,536 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshLineFaceBoundary.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/NumberedBoundaryDescriptor.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("MeshLineFaceBoundary", "[mesh]") +{ + auto is_same = [](const auto& a, const auto& b) -> bool { + if (a.size() > 0 and b.size() > 0) { + return (a[0] == b[0]); + } else { + return (a.size() == b.size()); + } + }; + + auto get_face_list_from_tag = [](const size_t tag, const auto& connectivity) -> Array<const FaceId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::face>(); ++i) { + const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i); + const RefId ref_id = ref_face_list.refId(); + if (ref_id.tagNumber() == tag) { + return ref_face_list.list(); + } + } + return {}; + }; + + auto get_face_list_from_name = [](const std::string& name, const auto& connectivity) -> Array<const FaceId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::face>(); ++i) { + const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i); + const RefId ref_id = ref_face_list.refId(); + if (ref_id.tagName() == name) { + return ref_face_list.list(); + } + } + return {}; + }; + + SECTION("aligned axis") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshLineFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 direction = zero; + + switch (tag) { + case 0: { + direction = R2{0, 1}; + break; + } + case 1: { + direction = R2{0, 1}; + break; + } + case 2: { + direction = R2{1, 0}; + break; + } + case 3: { + direction = R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: cannot find face list with name \"10\""); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: cannot find face list with name \"11\""); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NumberedBoundaryDescriptor(12)), + "error: cannot find face list with name \"12\""); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NumberedBoundaryDescriptor(13)), + "error: cannot find face list with name \"13\""); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshLineFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 direction = zero; + + if (name == "XMIN") { + direction = R2{0, 1}; + } else if (name == "XMAX") { + direction = R2{0, 1}; + } else if (name == "YMIN") { + direction = R2{1, 0}; + } else if (name == "YMAX") { + direction = R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: cannot find face list with name \"XMINYMIN\""); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find face list with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NamedBoundaryDescriptor("XMAXYMIN")), + "error: cannot find face list with name \"XMAXYMIN\""); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: cannot find face list with name \"XMAXYMAX\""); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshLineFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 direction = zero; + + switch (tag) { + case 1: { + direction = R2{0, 1}; + break; + } + case 2: { + direction = R2{0, 1}; + break; + } + case 3: { + direction = R2{1, 0}; + break; + } + case 4: { + direction = R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NumberedBoundaryDescriptor(8)), + "error: cannot find face list with name \"8\""); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NumberedBoundaryDescriptor(9)), + "error: cannot find face list with name \"9\""); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: cannot find face list with name \"10\""); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: cannot find face list with name \"11\""); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshLineFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + R2 direction = zero; + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + if (name == "XMIN") { + direction = R2{0, 1}; + } else if (name == "XMAX") { + direction = R2{0, 1}; + } else if (name == "YMIN") { + direction = R2{1, 0}; + } else if (name == "YMAX") { + direction = R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: cannot find face list with name \"XMINYMIN\""); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: cannot find face list with name \"XMINYMAX\""); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NamedBoundaryDescriptor("XMAXYMIN")), + "error: cannot find face list with name \"XMAXYMIN\""); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: cannot find face list with name \"XMAXYMAX\""); + } + } + } + } + } + + SECTION("rotated axis") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + const double theta = 0.3; + const TinyMatrix<2> R{std::cos(theta), -std::sin(theta), // + std::sin(theta), std::cos(theta)}; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R2> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshLineFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 direction = zero; + + switch (tag) { + case 0: { + direction = R * R2{0, -1}; + break; + } + case 1: { + direction = R * R2{0, -1}; + break; + } + case 2: { + direction = R * R2{1, 0}; + break; + } + case 3: { + direction = R * R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshLineFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 direction = zero; + + if (name == "XMIN") { + direction = R * R2{0, -1}; + } else if (name == "XMAX") { + direction = R * R2{0, -1}; + } else if (name == "YMIN") { + direction = R * R2{1, 0}; + } else if (name == "YMAX") { + direction = R * R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R2> rotated_xr{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshLineFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 direction = zero; + + switch (tag) { + case 1: { + direction = R * R2{0, -1}; + break; + } + case 2: { + direction = R * R2{0, -1}; + break; + } + case 3: { + direction = R * R2{1, 0}; + break; + } + case 4: { + direction = R * R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshLineFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + R2 direction = zero; + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + if (name == "XMIN") { + direction = R * R2{0, -1}; + } else if (name == "XMAX") { + direction = R * R2{0, -1}; + } else if (name == "YMIN") { + direction = R * R2{1, 0}; + } else if (name == "YMAX") { + direction = R * R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + } + + SECTION("curved mesh") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + auto curve = [](const R2& X) -> R2 { return R2{X[0], (1 + X[0] * X[0]) * X[1]}; }; + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<TinyVector<2>> curved_xr{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {1, 2, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& face_boundary = getMeshLineFaceBoundary(mesh, numbered_boundary_descriptor); + + auto face_list = get_face_list_from_tag(tag, connectivity); + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + R2 direction = zero; + + switch (tag) { + case 1: { + direction = R2{0, 1}; + break; + } + case 2: { + direction = R2{0, 1}; + break; + } + case 4: { + direction = R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(face_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(3); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not a line!"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& face_boundary = getMeshLineFaceBoundary(mesh, named_boundary_descriptor); + + auto face_list = get_face_list_from_name(name, connectivity); + + R2 direction = zero; + + REQUIRE(is_same(face_boundary.faceList(), face_list)); + + if (name == "XMIN") { + direction = R2{0, 1}; + } else if (name == "XMAX") { + direction = R2{0, 1}; + } else if (name == "YMIN") { + direction = R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(face_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("YMAX"); + REQUIRE_THROWS_WITH(getMeshLineFaceBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not a line!"); + } + } + } + } + } +} diff --git a/tests/test_MeshLineNodeBoundary.cpp b/tests/test_MeshLineNodeBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..36c151e2d4ddf14e21ad5defed6774a06cf6394e --- /dev/null +++ b/tests/test_MeshLineNodeBoundary.cpp @@ -0,0 +1,1258 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshLineNodeBoundary.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/NumberedBoundaryDescriptor.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("MeshLineNodeBoundary", "[mesh]") +{ + auto is_same = [](const auto& a, const auto& b) -> bool { + if (a.size() > 0 and b.size() > 0) { + return (a[0] == b[0]); + } else { + return (a.size() == b.size()); + } + }; + + auto get_node_list_from_tag = [](const size_t tag, const auto& connectivity) -> Array<const NodeId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::node>(); ++i) { + const auto& ref_node_list = connectivity.template refItemList<ItemType::node>(i); + const RefId ref_id = ref_node_list.refId(); + if (ref_id.tagNumber() == tag) { + return ref_node_list.list(); + } + } + return {}; + }; + + auto get_node_list_from_name = [](const std::string& name, const auto& connectivity) -> Array<const NodeId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::node>(); ++i) { + const auto& ref_node_list = connectivity.template refItemList<ItemType::node>(i); + const RefId ref_id = ref_node_list.refId(); + if (ref_id.tagName() == name) { + return ref_node_list.list(); + } + } + return {}; + }; + + SECTION("aligned axis") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 direction = zero; + + switch (tag) { + case 0: { + direction = R2{0, 1}; + break; + } + case 1: { + direction = R2{0, 1}; + break; + } + case 2: { + direction = R2{1, 0}; + break; + } + case 3: { + direction = R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: invalid boundary \"XMINYMIN(10)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: invalid boundary \"XMAXYMIN(11)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(12)), + "error: invalid boundary \"XMAXYMAX(12)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(13)), + "error: invalid boundary \"XMINYMAX(13)\": unable to compute direction"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 direction = zero; + + if (name == "XMIN") { + direction = R2{0, 1}; + } else if (name == "XMAX") { + direction = R2{0, 1}; + } else if (name == "YMIN") { + direction = R2{1, 0}; + } else if (name == "YMAX") { + direction = R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: invalid boundary \"XMINYMIN(10)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: invalid boundary \"XMINYMAX(13)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMIN")), + "error: invalid boundary \"XMAXYMIN(11)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: invalid boundary \"XMAXYMAX(12)\": unable to compute direction"); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 direction = zero; + + switch (tag) { + case 1: { + direction = R2{0, 1}; + break; + } + case 2: { + direction = R2{0, 1}; + break; + } + case 3: { + direction = R2{1, 0}; + break; + } + case 4: { + direction = R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(8)), + "error: invalid boundary \"XMINYMIN(8)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(9)), + "error: invalid boundary \"XMINYMAX(9)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: invalid boundary \"XMAXYMIN(10)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: invalid boundary \"XMAXYMAX(11)\": unable to compute direction"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + R2 direction = zero; + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + if (name == "XMIN") { + direction = R2{0, 1}; + } else if (name == "XMAX") { + direction = R2{0, 1}; + } else if (name == "YMIN") { + direction = R2{1, 0}; + } else if (name == "YMAX") { + direction = R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMIN")), + "error: invalid boundary \"XMINYMIN(8)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAX")), + "error: invalid boundary \"XMINYMAX(9)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMIN")), + "error: invalid boundary \"XMAXYMIN(10)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAX")), + "error: invalid boundary \"XMAXYMAX(11)\": unable to compute direction"); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 direction = zero; + + switch (tag) { + case 20: + case 21: + case 22: + case 23: { + direction = R3{0, 0, -1}; + break; + } + case 24: + case 25: + case 26: + case 27: { + direction = R3{0, -1, 0}; + break; + } + case 28: + case 29: + case 30: + case 31: { + direction = R3{1, 0, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(0)), + "error: invalid boundary \"XMIN(0)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(1)), + "error: invalid boundary \"XMAX(1)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(2)), + "error: invalid boundary \"YMIN(2)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(3)), + "error: invalid boundary \"YMAX(3)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(4)), + "error: invalid boundary \"ZMIN(4)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(5)), + "error: invalid boundary \"ZMAX(5)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(10)), + "error: invalid boundary \"XMINYMINZMIN(10)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(11)), + "error: invalid boundary \"XMAXYMINZMIN(11)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(12)), + "error: invalid boundary \"XMAXYMAXZMIN(12)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(13)), + "error: invalid boundary \"XMINYMAXZMIN(13)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(14)), + "error: invalid boundary \"XMINYMINZMAX(14)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(15)), + "error: invalid boundary \"XMAXYMINZMAX(15)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(16)), + "error: invalid boundary \"XMAXYMAXZMAX(16)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(17)), + "error: invalid boundary \"XMINYMAXZMAX(17)\": unable to compute direction"); + } + + { + const std::set<std::string> name_set = {"XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX", + "XMINZMIN", "XMINZMAX", "XMAXZMAX", "XMINZMAX", + "YMINZMIN", "YMINZMAX", "YMAXZMAX", "YMAXZMIN"}; + + for (const auto& name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 direction = zero; + + if ((name == "XMINYMIN") or (name == "XMINYMAX") or (name == "XMAXYMIN") or (name == "XMAXYMAX")) { + direction = R3{0, 0, -1}; + } else if ((name == "XMINZMIN") or (name == "XMINZMAX") or (name == "XMAXZMIN") or (name == "XMAXZMAX")) { + direction = R3{0, -1, 0}; + } else if ((name == "YMINZMIN") or (name == "YMINZMAX") or (name == "YMAXZMIN") or (name == "YMAXZMAX")) { + direction = R3{1, 0, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMIN")), + "error: invalid boundary \"XMIN(0)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAX")), + "error: invalid boundary \"XMAX(1)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("YMIN")), + "error: invalid boundary \"YMIN(2)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("YMAX")), + "error: invalid boundary \"YMAX(3)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("ZMIN")), + "error: invalid boundary \"ZMIN(4)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("ZMAX")), + "error: invalid boundary \"ZMAX(5)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMIN")), + "error: invalid boundary \"XMINYMINZMIN(10)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMIN")), + "error: invalid boundary \"XMAXYMINZMIN(11)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMIN")), + "error: invalid boundary \"XMAXYMAXZMIN(12)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMIN")), + "error: invalid boundary \"XMINYMAXZMIN(13)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMAX")), + "error: invalid boundary \"XMINYMINZMAX(14)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMAX")), + "error: invalid boundary \"XMAXYMINZMAX(15)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMAX")), + "error: invalid boundary \"XMAXYMAXZMAX(16)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMAX")), + "error: invalid boundary \"XMINYMAXZMAX(17)\": unable to compute direction"); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 direction = zero; + + switch (tag) { + case 30: + case 31: + case 34: + case 35: { + direction = R3{0, 0, -1}; + break; + } + case 28: + case 29: + case 32: + case 33: { + direction = R3{0, -1, 0}; + break; + } + case 36: + case 37: + case 38: + case 39: { + direction = R3{1, 0, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(22)), + "error: invalid boundary \"XMIN(22)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(23)), + "error: invalid boundary \"XMAX(23)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(24)), + "error: invalid boundary \"ZMAX(24)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(25)), + "error: invalid boundary \"ZMIN(25)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(26)), + "error: invalid boundary \"YMAX(26)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(27)), + "error: invalid boundary \"YMIN(27)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(40)), + "error: invalid boundary \"XMINYMINZMIN(40)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(41)), + "error: invalid boundary \"XMAXYMINZMIN(41)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(42)), + "error: invalid boundary \"XMINYMAXZMIN(42)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(43)), + "error: invalid boundary \"XMINYMAXZMAX(43)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(44)), + "error: invalid boundary \"XMINYMINZMAX(44)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(45)), + "error: invalid boundary \"XMAXYMINZMAX(45)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(47)), + "error: invalid boundary \"XMAXYMAXZMAX(47)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(51)), + "error: invalid boundary \"XMAXYMAXZMIN(51)\": unable to compute direction"); + } + } + + { + const std::set<std::string> name_set = {"XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX", + "XMINZMIN", "XMINZMAX", "XMAXZMAX", "XMINZMAX", + "YMINZMIN", "YMINZMAX", "YMAXZMAX", "YMAXZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 direction = zero; + + if ((name == "XMINYMIN") or (name == "XMINYMAX") or (name == "XMAXYMIN") or (name == "XMAXYMAX")) { + direction = R3{0, 0, -1}; + } else if ((name == "XMINZMIN") or (name == "XMINZMAX") or (name == "XMAXZMIN") or (name == "XMAXZMAX")) { + direction = R3{0, -1, 0}; + } else if ((name == "YMINZMIN") or (name == "YMINZMAX") or (name == "YMAXZMIN") or (name == "YMAXZMAX")) { + direction = R3{1, 0, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMIN")), + "error: invalid boundary \"XMIN(22)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAX")), + "error: invalid boundary \"XMAX(23)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("YMIN")), + "error: invalid boundary \"YMIN(27)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("YMAX")), + "error: invalid boundary \"YMAX(26)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("ZMIN")), + "error: invalid boundary \"ZMIN(25)\": boundary is not a line!"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("ZMAX")), + "error: invalid boundary \"ZMAX(24)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMIN")), + "error: invalid boundary \"XMINYMINZMIN(40)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMIN")), + "error: invalid boundary \"XMAXYMINZMIN(41)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMIN")), + "error: invalid boundary \"XMAXYMAXZMIN(51)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMIN")), + "error: invalid boundary \"XMINYMAXZMIN(42)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMINZMAX")), + "error: invalid boundary \"XMINYMINZMAX(44)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMINZMAX")), + "error: invalid boundary \"XMAXYMINZMAX(45)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMAXYMAXZMAX")), + "error: invalid boundary \"XMAXYMAXZMAX(47)\": unable to compute direction"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("XMINYMAXZMAX")), + "error: invalid boundary \"XMINYMAXZMAX(43)\": unable to compute direction"); + } + } + } + } + } + + SECTION("rotated axis") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + const double theta = 0.3; + const TinyMatrix<2> R{std::cos(theta), -std::sin(theta), // + std::sin(theta), std::cos(theta)}; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R2> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {0, 1, 2, 3}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 direction = zero; + + switch (tag) { + case 0: { + direction = R * R2{0, -1}; + break; + } + case 1: { + direction = R * R2{0, -1}; + break; + } + case 2: { + direction = R * R2{1, 0}; + break; + } + case 3: { + direction = R * R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 direction = zero; + + if (name == "XMIN") { + direction = R * R2{0, -1}; + } else if (name == "XMAX") { + direction = R * R2{0, -1}; + } else if (name == "YMIN") { + direction = R * R2{1, 0}; + } else if (name == "YMAX") { + direction = R * R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R2> rotated_xr{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {1, 2, 3, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 direction = zero; + + switch (tag) { + case 1: { + direction = R * R2{0, -1}; + break; + } + case 2: { + direction = R * R2{0, -1}; + break; + } + case 3: { + direction = R * R2{1, 0}; + break; + } + case 4: { + direction = R * R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + R2 direction = zero; + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + if (name == "XMIN") { + direction = R * R2{0, -1}; + } else if (name == "XMAX") { + direction = R * R2{0, -1}; + } else if (name == "YMIN") { + direction = R * R2{1, 0}; + } else if (name == "YMAX") { + direction = R * R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + const double theta = 0.3; + const double phi = 0.4; + const TinyMatrix<3> R = + TinyMatrix<3>{std::cos(theta), -std::sin(theta), 0, std::sin(theta), std::cos(theta), 0, 0, 0, 1} * + TinyMatrix<3>{0, std::cos(phi), -std::sin(phi), 0, std::sin(phi), std::cos(phi), 1, 0, 0}; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R3> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 direction = zero; + + switch (tag) { + case 20: + case 21: + case 22: + case 23: { + direction = R * R3{0, 0, -1}; + break; + } + case 24: + case 25: + case 26: + case 27: { + direction = R * R3{0, 1, 0}; + break; + } + case 28: + case 29: + case 30: + case 31: { + direction = R * R3{-1, 0, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX", + "XMINZMIN", "XMINZMAX", "XMAXZMAX", "XMINZMAX", + "YMINZMIN", "YMINZMAX", "YMAXZMAX", "YMAXZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 direction = zero; + + if ((name == "XMINYMIN") or (name == "XMINYMAX") or (name == "XMAXYMIN") or (name == "XMAXYMAX")) { + direction = R * R3{0, 0, -1}; + } else if ((name == "XMINZMIN") or (name == "XMINZMAX") or (name == "XMAXZMIN") or (name == "XMAXZMAX")) { + direction = R * R3{0, 1, 0}; + } else if ((name == "YMINZMIN") or (name == "YMINZMAX") or (name == "YMAXZMIN") or (name == "YMAXZMAX")) { + direction = R * R3{-1, 0, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<R3> rotated_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { rotated_xr[node_id] = R * xr[node_id]; }); + + MeshType mesh{p_mesh->shared_connectivity(), rotated_xr}; + + { + const std::set<size_t> tag_set = {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 direction = zero; + + switch (tag) { + case 30: + case 31: + case 34: + case 35: { + direction = R * R3{0, 0, -1}; + break; + } + case 28: + case 29: + case 32: + case 33: { + direction = R * R3{0, 1, 0}; + break; + } + case 36: + case 37: + case 38: + case 39: { + direction = R * R3{-1, 0, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + + { + const std::set<std::string> name_set = {"XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX", + "XMINZMIN", "XMINZMAX", "XMAXZMAX", "XMINZMAX", + "YMINZMIN", "YMINZMAX", "YMAXZMAX", "YMAXZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 direction = zero; + + if ((name == "XMINYMIN") or (name == "XMINYMAX") or (name == "XMAXYMIN") or (name == "XMAXYMAX")) { + direction = R * R3{0, 0, -1}; + } else if ((name == "XMINZMIN") or (name == "XMINZMAX") or (name == "XMAXZMIN") or (name == "XMAXZMAX")) { + direction = R * R3{0, 1, 0}; + } else if ((name == "YMINZMIN") or (name == "YMINZMAX") or (name == "YMAXZMIN") or (name == "YMAXZMAX")) { + direction = R * R3{-1, 0, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + } + + SECTION("curved mesh") + { + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R2 = TinyVector<2>; + + auto curve = [](const R2& X) -> R2 { return R2{X[0], (1 + X[0] * X[0]) * X[1]}; }; + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + NodeValue<TinyVector<2>> curved_xr{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {1, 2, 4}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R2 direction = zero; + + switch (tag) { + case 1: { + direction = R2{0, 1}; + break; + } + case 2: { + direction = R2{0, 1}; + break; + } + case 4: { + direction = R2{1, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + NumberedBoundaryDescriptor numbered_boundary_descriptor(3); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, numbered_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not a line!"); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + R2 direction = zero; + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + if (name == "XMIN") { + direction = R2{0, 1}; + } else if (name == "XMAX") { + direction = R2{0, 1}; + } else if (name == "YMIN") { + direction = R2{1, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + NamedBoundaryDescriptor named_boundary_descriptor("YMAX"); + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"YMAX(3)\": boundary is not a line!"); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + using R3 = TinyVector<3>; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + auto curve = [](const R3& X) -> R3 { + return R3{X[0], (1 + X[0] * X[0]) * (X[1] + 1), (1 - 0.2 * X[0] * X[0]) * X[2]}; + }; + + NodeValue<R3> curved_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {20, 21, 22, 23, 24, 25, 26, 27, 28}; + + for (auto tag : tag_set) { + auto node_list = get_node_list_from_tag(tag, connectivity); + + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, numbered_boundary_descriptor); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 direction = zero; + + switch (tag) { + case 20: + case 21: + case 22: + case 23: { + direction = R3{0, 0, -1}; + break; + } + case 24: + case 25: + case 26: + case 27: { + direction = R3{0, -1, 0}; + break; + } + case 28: { + direction = R3{1, 0, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(29)), + "error: invalid boundary \"YMINZMAX(29)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(30)), + "error: invalid boundary \"YMAXZMAX(30)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(31)), + "error: invalid boundary \"YMAXZMIN(31)\": boundary is not a line!"); + } + } + + { + const std::set<std::string> name_set = {"XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX", "XMINZMIN", + "XMINZMAX", "XMAXZMAX", "XMINZMAX", "YMINZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 direction = zero; + + if ((name == "XMINYMIN") or (name == "XMINYMAX") or (name == "XMAXYMIN") or (name == "XMAXYMAX")) { + direction = R3{0, 0, -1}; + } else if ((name == "XMINZMIN") or (name == "XMINZMAX") or (name == "XMAXZMIN") or (name == "XMAXZMAX")) { + direction = R3{0, -1, 0}; + } else if ((name == "YMINZMIN")) { + direction = R3{1, 0, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("YMAXZMAX")), + "error: invalid boundary \"YMAXZMAX(30)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("YMINZMAX")), + "error: invalid boundary \"YMINZMAX(29)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("YMAXZMIN")), + "error: invalid boundary \"YMAXZMIN(31)\": boundary is not a line!"); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + + const ConnectivityType& connectivity = p_mesh->connectivity(); + + auto xr = p_mesh->xr(); + + auto curve = [](const R3& X) -> R3 { + return R3{X[0], (1 + X[0] * X[0]) * X[1], (1 - 0.2 * X[0] * X[0]) * X[2]}; + }; + + NodeValue<R3> curved_xr{connectivity}; + + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { curved_xr[node_id] = curve(xr[node_id]); }); + + MeshType mesh{p_mesh->shared_connectivity(), curved_xr}; + + { + const std::set<size_t> tag_set = {28, 29, 30, 31, 32, 33, 34, 35, 36}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 direction = zero; + + switch (tag) { + case 30: + case 31: + case 34: + case 35: { + direction = R3{0, 0, -1}; + break; + } + case 28: + case 29: + case 32: + case 33: { + direction = R3{0, -1, 0}; + break; + } + case 36: + // case 37: + // case 38: + case 39: { + direction = R3{1, 0, 0}; + break; + } + default: { + FAIL("unexpected tag number"); + } + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(37)), + "error: invalid boundary \"YMINZMAX(37)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(38)), + "error: invalid boundary \"YMAXZMIN(38)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NumberedBoundaryDescriptor(39)), + "error: invalid boundary \"YMAXZMAX(39)\": boundary is not a line!"); + } + } + + { + const std::set<std::string> name_set = {"XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX", "XMINZMIN", + "XMINZMAX", "XMAXZMAX", "XMINZMAX", "YMINZMIN"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshLineNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + + R3 direction = zero; + + if ((name == "XMINYMIN") or (name == "XMINYMAX") or (name == "XMAXYMIN") or (name == "XMAXYMAX")) { + direction = R3{0, 0, -1}; + } else if ((name == "XMINZMIN") or (name == "XMINZMAX") or (name == "XMAXZMIN") or (name == "XMAXZMAX")) { + direction = R3{0, -1, 0}; + } else if ((name == "YMINZMIN")) { + direction = R3{1, 0, 0}; + } else { + FAIL("unexpected name: " + name); + } + + REQUIRE(l2Norm(node_boundary.direction() - direction) == Catch::Approx(0).margin(1E-13)); + } + + { + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("YMAXZMAX")), + "error: invalid boundary \"YMAXZMAX(39)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("YMINZMAX")), + "error: invalid boundary \"YMINZMAX(37)\": boundary is not a line!"); + + REQUIRE_THROWS_WITH(getMeshLineNodeBoundary(mesh, NamedBoundaryDescriptor("YMAXZMIN")), + "error: invalid boundary \"YMAXZMIN(38)\": boundary is not a line!"); + } + } + } + } + } +} diff --git a/tests/test_MeshNodeBoundary.cpp b/tests/test_MeshNodeBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2639e6370f9681024f0dab87367468a1df2a5002 --- /dev/null +++ b/tests/test_MeshNodeBoundary.cpp @@ -0,0 +1,353 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> + +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshNodeBoundary.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/NumberedBoundaryDescriptor.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("MeshNodeBoundary", "[mesh]") +{ + auto is_same = [](const auto& a, const auto& b) -> bool { + if (a.size() > 0 and b.size() > 0) { + return (a.size() == b.size()) and (&(a[0]) == &(b[0])); + } else { + return (a.size() == b.size()); + } + }; + + auto get_node_list_from_tag = [](const size_t tag, const auto& connectivity) -> Array<const NodeId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::node>(); ++i) { + const auto& ref_node_list = connectivity.template refItemList<ItemType::node>(i); + const RefId ref_id = ref_node_list.refId(); + if (ref_id.tagNumber() == tag) { + return ref_node_list.list(); + } + } + return {}; + }; + + auto get_node_list_from_name = [](const std::string& name, const auto& connectivity) -> Array<const NodeId> { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::node>(); ++i) { + const auto& ref_node_list = connectivity.template refItemList<ItemType::node>(i); + const RefId ref_id = ref_node_list.refId(); + if (ref_id.tagName() == name) { + return ref_node_list.list(); + } + } + return {}; + }; + + SECTION("1D") + { + static constexpr size_t Dimension = 1; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + SECTION("cartesian 1d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian1DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + } + } + } + + SECTION("unordered 1d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor named_boundary_descriptor(name); + const auto& node_boundary = getMeshNodeBoundary(mesh, named_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + } + } + } + } + + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + SECTION("cartesian 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3, 10, 11, 12, 13}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "XMAX", "YMIN", "YMAX", + "XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor numbered_boundary_descriptor(name); + const auto& node_boundary = getMeshNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + } + } + } + + SECTION("hybrid 2d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {1, 2, 3, 4, 8, 9, 10, 11}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + } + } + + { + const std::set<std::string> name_set = {"XMIN", "YMIN", "XMAX", "YMIN", + "XMINYMIN", "XMINYMAX", "XMAXYMIN", "XMAXYMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor numbered_boundary_descriptor(name); + const auto& node_boundary = getMeshNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + } + } + } + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + SECTION("cartesian 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {0, 1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15, 16, + 17, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + } + } + + { + const std::set<std::string> name_set = {// faces + "XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX", + // ridges + "XMINYMIN", "XMINYMAX", "XMINZMIN", "XMINZMAX", "XMAXYMIN", "XMAXYMAX", + "XMAXZMIN", "XMAXZMAX", "YMINZMIN", "YMINZMAX", "YMAXZMIN", "YMAXZMAX", + // corners + "XMINYMINZMIN", "XMINYMINZMAX", "XMINYMAXZMIN", "XMINYMAXZMAX", + "XMAXYMINZMIN", "XMAXYMINZMAX", "XMAXYMAXZMIN", "XMAXYMAXZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor numbered_boundary_descriptor(name); + const auto& node_boundary = getMeshNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + } + } + } + + SECTION("hybrid 3d") + { + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + const ConnectivityType& connectivity = mesh.connectivity(); + + { + const std::set<size_t> tag_set = {22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 47, 51}; + + for (auto tag : tag_set) { + NumberedBoundaryDescriptor numbered_boundary_descriptor(tag); + const auto& node_boundary = getMeshNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_tag(tag, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + } + } + + { + const std::set<std::string> name_set = {// faces + "XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX", + // ridges + "XMINYMIN", "XMINYMAX", "XMINZMIN", "XMINZMAX", "XMAXYMIN", "XMAXYMAX", + "XMAXZMIN", "XMAXZMAX", "YMINZMIN", "YMINZMAX", "YMAXZMIN", "YMAXZMAX", + // corners + "XMINYMINZMIN", "XMINYMINZMAX", "XMINYMAXZMIN", "XMINYMAXZMAX", + "XMAXYMINZMIN", "XMAXYMINZMAX", "XMAXYMAXZMIN", "XMAXYMAXZMAX"}; + + for (auto name : name_set) { + NamedBoundaryDescriptor numbered_boundary_descriptor(name); + const auto& node_boundary = getMeshNodeBoundary(mesh, numbered_boundary_descriptor); + + auto node_list = get_node_list_from_name(name, connectivity); + REQUIRE(is_same(node_boundary.nodeList(), node_list)); + } + } + } + } + + SECTION("errors") + { + SECTION("cannot find boundary") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + NamedBoundaryDescriptor named_boundary_descriptor("invalid_boundary"); + + REQUIRE_THROWS_WITH(getMeshNodeBoundary(mesh, named_boundary_descriptor), + "error: cannot find node list with name \"invalid_boundary\""); + } + + SECTION("surface is inside") + { + SECTION("1D") + { + static constexpr size_t Dimension = 1; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh(); + const MeshType& mesh = *p_mesh; + + NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE"); + + REQUIRE_THROWS_WITH(getMeshNodeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"INTERFACE(3)\": inner nodes cannot be used to define mesh " + "boundaries"); + } + + SECTION("2D") + { + static constexpr size_t Dimension = 2; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh(); + const MeshType& mesh = *p_mesh; + + NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE"); + + REQUIRE_THROWS_WITH(getMeshNodeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"INTERFACE(5)\": inner edges cannot be used to define mesh " + "boundaries"); + } + + SECTION("3D") + { + static constexpr size_t Dimension = 3; + + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh(); + const MeshType& mesh = *p_mesh; + + NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE1"); + + REQUIRE_THROWS_WITH(getMeshNodeBoundary(mesh, named_boundary_descriptor), + "error: invalid boundary \"INTERFACE1(55)\": inner faces cannot be used to define mesh " + "boundaries"); + } + } + } +} diff --git a/tests/test_RefItemList.cpp b/tests/test_RefItemList.cpp index 4a9a7b7b2dc3a7aad46fd29703769d4550fc67dd..147d6543579bfeafb2ff12904dcc1935c7fbd451 100644 --- a/tests/test_RefItemList.cpp +++ b/tests/test_RefItemList.cpp @@ -12,16 +12,18 @@ TEST_CASE("RefItemList", "[mesh]") const Array<NodeId> node_id_array = convert_to_array(std::vector<NodeId>{1, 3, 7, 2, 4, 11}); const RefId ref_id{3, "my_reference"}; - RefItemList<ItemType::node> ref_node_list{ref_id, node_id_array}; + RefItemList<ItemType::node> ref_node_list{ref_id, node_id_array, true}; REQUIRE(ref_node_list.refId() == ref_id); REQUIRE(ref_node_list.list().size() == node_id_array.size()); REQUIRE(&(ref_node_list.list()[0]) == &(node_id_array[0])); + REQUIRE(ref_node_list.isBoundary() == true); { RefItemList copy_ref_node_list{ref_node_list}; REQUIRE(copy_ref_node_list.refId() == ref_id); REQUIRE(copy_ref_node_list.list().size() == node_id_array.size()); REQUIRE(&(copy_ref_node_list.list()[0]) == &(node_id_array[0])); + REQUIRE(copy_ref_node_list.isBoundary() == true); } { @@ -30,12 +32,14 @@ TEST_CASE("RefItemList", "[mesh]") REQUIRE(affect_ref_node_list.refId() == ref_id); REQUIRE(affect_ref_node_list.list().size() == node_id_array.size()); REQUIRE(&(affect_ref_node_list.list()[0]) == &(node_id_array[0])); + REQUIRE(affect_ref_node_list.isBoundary() == true); RefItemList<ItemType::node> move_ref_node_list; move_ref_node_list = std::move(affect_ref_node_list); REQUIRE(move_ref_node_list.refId() == ref_id); REQUIRE(move_ref_node_list.list().size() == node_id_array.size()); REQUIRE(&(move_ref_node_list.list()[0]) == &(node_id_array[0])); + REQUIRE(move_ref_node_list.isBoundary() == true); } } @@ -44,16 +48,18 @@ TEST_CASE("RefItemList", "[mesh]") const Array<EdgeId> edge_id_array = convert_to_array(std::vector<EdgeId>{1, 3, 7, 2, 4, 11}); const RefId ref_id{3, "my_reference"}; - RefItemList<ItemType::edge> ref_edge_list{ref_id, edge_id_array}; + RefItemList<ItemType::edge> ref_edge_list{ref_id, edge_id_array, false}; REQUIRE(ref_edge_list.refId() == ref_id); REQUIRE(ref_edge_list.list().size() == edge_id_array.size()); REQUIRE(&(ref_edge_list.list()[0]) == &(edge_id_array[0])); + REQUIRE(ref_edge_list.isBoundary() == false); { RefItemList copy_ref_edge_list{ref_edge_list}; REQUIRE(copy_ref_edge_list.refId() == ref_id); REQUIRE(copy_ref_edge_list.list().size() == edge_id_array.size()); REQUIRE(&(copy_ref_edge_list.list()[0]) == &(edge_id_array[0])); + REQUIRE(copy_ref_edge_list.isBoundary() == false); } { @@ -62,12 +68,14 @@ TEST_CASE("RefItemList", "[mesh]") REQUIRE(affect_ref_edge_list.refId() == ref_id); REQUIRE(affect_ref_edge_list.list().size() == edge_id_array.size()); REQUIRE(&(affect_ref_edge_list.list()[0]) == &(edge_id_array[0])); + REQUIRE(affect_ref_edge_list.isBoundary() == false); RefItemList<ItemType::edge> move_ref_edge_list; move_ref_edge_list = std::move(affect_ref_edge_list); REQUIRE(move_ref_edge_list.refId() == ref_id); REQUIRE(move_ref_edge_list.list().size() == edge_id_array.size()); REQUIRE(&(move_ref_edge_list.list()[0]) == &(edge_id_array[0])); + REQUIRE(move_ref_edge_list.isBoundary() == false); } } @@ -76,16 +84,18 @@ TEST_CASE("RefItemList", "[mesh]") const Array<FaceId> face_id_array = convert_to_array(std::vector<FaceId>{1, 3, 7, 2, 4, 11}); const RefId ref_id{3, "my_reference"}; - RefItemList<ItemType::face> ref_face_list{ref_id, face_id_array}; + RefItemList<ItemType::face> ref_face_list{ref_id, face_id_array, true}; REQUIRE(ref_face_list.refId() == ref_id); REQUIRE(ref_face_list.list().size() == face_id_array.size()); REQUIRE(&(ref_face_list.list()[0]) == &(face_id_array[0])); + REQUIRE(ref_face_list.isBoundary() == true); { RefItemList copy_ref_face_list{ref_face_list}; REQUIRE(copy_ref_face_list.refId() == ref_id); REQUIRE(copy_ref_face_list.list().size() == face_id_array.size()); REQUIRE(&(copy_ref_face_list.list()[0]) == &(face_id_array[0])); + REQUIRE(copy_ref_face_list.isBoundary() == true); } { @@ -94,12 +104,14 @@ TEST_CASE("RefItemList", "[mesh]") REQUIRE(affect_ref_face_list.refId() == ref_id); REQUIRE(affect_ref_face_list.list().size() == face_id_array.size()); REQUIRE(&(affect_ref_face_list.list()[0]) == &(face_id_array[0])); + REQUIRE(affect_ref_face_list.isBoundary() == true); RefItemList<ItemType::face> move_ref_face_list; move_ref_face_list = std::move(affect_ref_face_list); REQUIRE(move_ref_face_list.refId() == ref_id); REQUIRE(move_ref_face_list.list().size() == face_id_array.size()); REQUIRE(&(move_ref_face_list.list()[0]) == &(face_id_array[0])); + REQUIRE(move_ref_face_list.isBoundary() == true); } } @@ -108,16 +120,18 @@ TEST_CASE("RefItemList", "[mesh]") const Array<CellId> cell_id_array = convert_to_array(std::vector<CellId>{1, 3, 7, 2, 4, 11}); const RefId ref_id{3, "my_reference"}; - RefItemList<ItemType::cell> ref_cell_list{ref_id, cell_id_array}; + RefItemList<ItemType::cell> ref_cell_list{ref_id, cell_id_array, false}; REQUIRE(ref_cell_list.refId() == ref_id); REQUIRE(ref_cell_list.list().size() == cell_id_array.size()); REQUIRE(&(ref_cell_list.list()[0]) == &(cell_id_array[0])); + REQUIRE(ref_cell_list.isBoundary() == false); { RefItemList copy_ref_cell_list{ref_cell_list}; REQUIRE(copy_ref_cell_list.refId() == ref_id); REQUIRE(copy_ref_cell_list.list().size() == cell_id_array.size()); REQUIRE(&(copy_ref_cell_list.list()[0]) == &(cell_id_array[0])); + REQUIRE(copy_ref_cell_list.isBoundary() == false); } { @@ -126,12 +140,14 @@ TEST_CASE("RefItemList", "[mesh]") REQUIRE(affect_ref_cell_list.refId() == ref_id); REQUIRE(affect_ref_cell_list.list().size() == cell_id_array.size()); REQUIRE(&(affect_ref_cell_list.list()[0]) == &(cell_id_array[0])); + REQUIRE(affect_ref_cell_list.isBoundary() == false); RefItemList<ItemType::cell> move_ref_cell_list; move_ref_cell_list = std::move(affect_ref_cell_list); REQUIRE(move_ref_cell_list.refId() == ref_id); REQUIRE(move_ref_cell_list.list().size() == cell_id_array.size()); REQUIRE(&(move_ref_cell_list.list()[0]) == &(cell_id_array[0])); + REQUIRE(move_ref_cell_list.isBoundary() == false); } } }