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