From 040fbf1c56d0b176b969a4727634c38afb0a35e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com>
Date: Wed, 13 Apr 2022 16:17:42 +0200
Subject: [PATCH] Add a "is boundary" attribute to ref item lists

---
 src/mesh/Connectivity.cpp                   |   6 +-
 src/mesh/ConnectivityDispatcher.cpp         |  14 ++-
 src/mesh/DiamondDualConnectivityBuilder.cpp |   9 +-
 src/mesh/Dual1DConnectivityBuilder.cpp      |   3 +-
 src/mesh/GmshReader.cpp                     | 132 ++++++++++++++++++--
 src/mesh/LogicalConnectivityBuilder.cpp     |  48 +++----
 src/mesh/MedianDualConnectivityBuilder.cpp  |   8 +-
 src/mesh/MeshFaceBoundary.cpp               |  15 +--
 src/mesh/RefItemList.hpp                    |  11 +-
 tests/test_RefItemList.cpp                  |  24 +++-
 10 files changed, 206 insertions(+), 64 deletions(-)

diff --git a/src/mesh/Connectivity.cpp b/src/mesh/Connectivity.cpp
index 005dc1f65..fe84284a2 100644
--- a/src/mesh/Connectivity.cpp
+++ b/src/mesh/Connectivity.cpp
@@ -109,8 +109,8 @@ Connectivity<Dimension>::_buildFrom(const ConnectivityDescriptor& descriptor)
         face_list[i] = FaceId::base_type{node_list[i]};
       }
 
-      m_ref_edge_list_vector.emplace_back(RefItemList<ItemType::edge>(ref_id, edge_list));
-      m_ref_face_list_vector.emplace_back(RefItemList<ItemType::face>(ref_id, face_list));
+      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 {
@@ -162,7 +162,7 @@ Connectivity<Dimension>::_buildFrom(const ConnectivityDescriptor& descriptor)
           edge_list[i] = EdgeId::base_type{face_list[i]};
         }
 
-        m_ref_edge_list_vector.emplace_back(RefItemList<ItemType::edge>(ref_id, edge_list));
+        m_ref_edge_list_vector.emplace_back(RefItemList<ItemType::edge>(ref_id, edge_list, ref_face_list.isBoundary()));
       }
 
     } else {
diff --git a/src/mesh/ConnectivityDispatcher.cpp b/src/mesh/ConnectivityDispatcher.cpp
index 8c10a5a8f..fd5b58c6d 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 38e62b737..1afa7d404 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 39dea9ddb..ec0389d33 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 15b34e161..f12e6ab3c 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 db14ff1df..41092faaf 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 4ed951bc3..8ba8046c0 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/MeshFaceBoundary.cpp b/src/mesh/MeshFaceBoundary.cpp
index 4e718f251..5423ad639 100644
--- a/src/mesh/MeshFaceBoundary.cpp
+++ b/src/mesh/MeshFaceBoundary.cpp
@@ -21,19 +21,10 @@ 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();
-      auto face_to_cell_matrix = mesh.connectivity().faceToCellMatrix();
-
-      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 (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";
diff --git a/src/mesh/RefItemList.hpp b/src/mesh/RefItemList.hpp
index 594b5ad25..326e3278c 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/test_RefItemList.cpp b/tests/test_RefItemList.cpp
index 4a9a7b7b2..147d65435 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);
     }
   }
 }
-- 
GitLab