diff --git a/src/language/modules/MeshModule.cpp b/src/language/modules/MeshModule.cpp
index 153c1be1154ac1a5432ae00ba66b56e7b44937c2..192f3aa568fea8c7dc7d5a7ce7e526f2aef304fe 100644
--- a/src/language/modules/MeshModule.cpp
+++ b/src/language/modules/MeshModule.cpp
@@ -10,9 +10,15 @@
 #include <mesh/Connectivity.hpp>
 #include <mesh/DualMeshManager.hpp>
 #include <mesh/GmshReader.hpp>
+#include <mesh/IBoundaryDescriptor.hpp>
+#include <mesh/IZoneDescriptor.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshRelaxer.hpp>
 #include <mesh/MeshTransformer.hpp>
+#include <mesh/NamedBoundaryDescriptor.hpp>
+#include <mesh/NamedZoneDescriptor.hpp>
+#include <mesh/NumberedBoundaryDescriptor.hpp>
+#include <mesh/NumberedZoneDescriptor.hpp>
 #include <utils/Exceptions.hpp>
 
 #include <Kokkos_Core.hpp>
@@ -31,6 +37,45 @@ MeshModule::MeshModule()
 
                               ));
 
+  this->_addBuiltinFunction("boundaryName",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<std::shared_ptr<const IBoundaryDescriptor>(const std::string&)>>(
+
+                              [](const std::string& boundary_name) -> std::shared_ptr<const IBoundaryDescriptor> {
+                                return std::make_shared<NamedBoundaryDescriptor>(boundary_name);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("zoneTag",
+                            std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const IZoneDescriptor>(int64_t)>>(
+
+                              [](int64_t zone_tag) -> std::shared_ptr<const IZoneDescriptor> {
+                                return std::make_shared<NumberedZoneDescriptor>(zone_tag);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("zoneName",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<std::shared_ptr<const IZoneDescriptor>(const std::string&)>>(
+
+                              [](const std::string& zone_name) -> std::shared_ptr<const IZoneDescriptor> {
+                                return std::make_shared<NamedZoneDescriptor>(zone_name);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("boundaryTag",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<std::shared_ptr<const IBoundaryDescriptor>(int64_t)>>(
+
+                              [](int64_t boundary_tag) -> std::shared_ptr<const IBoundaryDescriptor> {
+                                return std::make_shared<NumberedBoundaryDescriptor>(boundary_tag);
+                              }
+
+                              ));
+
   this->_addBuiltinFunction("transform",
                             std::make_shared<BuiltinFunctionEmbedder<
                               std::shared_ptr<const IMesh>(std::shared_ptr<const IMesh>, const FunctionSymbolId&)>>(
diff --git a/src/language/modules/MeshModule.hpp b/src/language/modules/MeshModule.hpp
index 3ef6f2856d052e7b7c5ab63393843145eec6f230..9047412586f24200789ebea289e9b8c4f0fbd27d 100644
--- a/src/language/modules/MeshModule.hpp
+++ b/src/language/modules/MeshModule.hpp
@@ -6,11 +6,20 @@
 #include <utils/PugsMacros.hpp>
 
 class IMesh;
-
 template <>
 inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const IMesh>> =
   ASTNodeDataType::build<ASTNodeDataType::type_id_t>("mesh");
 
+class IBoundaryDescriptor;
+template <>
+inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const IBoundaryDescriptor>> =
+  ASTNodeDataType::build<ASTNodeDataType::type_id_t>("boundary");
+
+class IZoneDescriptor;
+template <>
+inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const IZoneDescriptor>> =
+  ASTNodeDataType::build<ASTNodeDataType::type_id_t>("zone");
+
 class MeshModule : public BuiltinModule
 {
  public:
diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index f80201c7e424728db5663045e5b663c5e306e4d4..ec5329bc9682125005826d4fbfafe07febebed9d 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -33,9 +33,7 @@
 #include <scheme/IBoundaryConditionDescriptor.hpp>
 #include <scheme/IDiscreteFunction.hpp>
 #include <scheme/IDiscreteFunctionDescriptor.hpp>
-#include <scheme/NamedBoundaryDescriptor.hpp>
 #include <scheme/NeumannBoundaryConditionDescriptor.hpp>
-#include <scheme/NumberedBoundaryDescriptor.hpp>
 #include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
 #include <utils/Socket.hpp>
 
@@ -191,26 +189,6 @@ SchemeModule::SchemeModule()
 
                               ));
 
-  this->_addBuiltinFunction("boundaryName",
-                            std::make_shared<
-                              BuiltinFunctionEmbedder<std::shared_ptr<const IBoundaryDescriptor>(const std::string&)>>(
-
-                              [](const std::string& boundary_name) -> std::shared_ptr<const IBoundaryDescriptor> {
-                                return std::make_shared<NamedBoundaryDescriptor>(boundary_name);
-                              }
-
-                              ));
-
-  this->_addBuiltinFunction("boundaryTag",
-                            std::make_shared<
-                              BuiltinFunctionEmbedder<std::shared_ptr<const IBoundaryDescriptor>(int64_t)>>(
-
-                              [](int64_t boundary_tag) -> std::shared_ptr<const IBoundaryDescriptor> {
-                                return std::make_shared<NumberedBoundaryDescriptor>(boundary_tag);
-                              }
-
-                              ));
-
   this
     ->_addBuiltinFunction("fixed",
                           std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const IBoundaryConditionDescriptor>(
diff --git a/src/language/modules/SchemeModule.hpp b/src/language/modules/SchemeModule.hpp
index 52fc163ee5b6bfcec2cbeceb6d618e9ce3d9c213..6c1f0324aa2858437ec8d7f49434c2d8ac718a45 100644
--- a/src/language/modules/SchemeModule.hpp
+++ b/src/language/modules/SchemeModule.hpp
@@ -5,11 +5,6 @@
 #include <language/utils/ASTNodeDataTypeTraits.hpp>
 #include <utils/PugsMacros.hpp>
 
-class IBoundaryDescriptor;
-template <>
-inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const IBoundaryDescriptor>> =
-  ASTNodeDataType::build<ASTNodeDataType::type_id_t>("boundary");
-
 class IBoundaryConditionDescriptor;
 template <>
 inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const IBoundaryConditionDescriptor>> =
diff --git a/src/mesh/IBoundaryDescriptor.hpp b/src/mesh/IBoundaryDescriptor.hpp
index 4e016c35b6e13204fee2cbc23cf9bedf1d6ca4c7..6d9c003c7f2915d34754e4bed79904815eab3363 100644
--- a/src/mesh/IBoundaryDescriptor.hpp
+++ b/src/mesh/IBoundaryDescriptor.hpp
@@ -19,18 +19,20 @@ class IBoundaryDescriptor
 
  public:
   friend std::ostream&
-  operator<<(std::ostream& os, const IBoundaryDescriptor& bd)
+  operator<<(std::ostream& os, const IBoundaryDescriptor& boundary_descriptor)
   {
-    return bd._write(os);
+    return boundary_descriptor._write(os);
   }
 
-  virtual bool operator==(const RefId& ref_id) const = 0;
-  friend bool
-  operator==(const RefId& ref_id, const IBoundaryDescriptor& bcd)
+  [[nodiscard]] virtual bool operator==(const RefId& ref_id) const = 0;
+
+  [[nodiscard]] friend bool
+  operator==(const RefId& ref_id, const IBoundaryDescriptor& boundary_descriptor)
   {
-    return bcd == ref_id;
+    return boundary_descriptor == ref_id;
   }
-  virtual Type type() const = 0;
+
+  [[nodiscard]] virtual Type type() const = 0;
 
   IBoundaryDescriptor(const IBoundaryDescriptor&) = delete;
   IBoundaryDescriptor(IBoundaryDescriptor&&)      = delete;
@@ -38,4 +40,5 @@ class IBoundaryDescriptor
 
   virtual ~IBoundaryDescriptor() = default;
 };
+
 #endif   // I_BOUNDARY_DESCRIPTOR_HPP
diff --git a/src/mesh/IZoneDescriptor.hpp b/src/mesh/IZoneDescriptor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1fed7c8df7c96539cdf08693e8fb0afc35411912
--- /dev/null
+++ b/src/mesh/IZoneDescriptor.hpp
@@ -0,0 +1,44 @@
+#ifndef I_ZONE_DESCRIPTOR_HPP
+#define I_ZONE_DESCRIPTOR_HPP
+
+#include <mesh/RefId.hpp>
+
+#include <iostream>
+
+class IZoneDescriptor
+{
+ public:
+  enum class Type
+  {
+    named,
+    numbered
+  };
+
+ protected:
+  virtual std::ostream& _write(std::ostream& os) const = 0;
+
+ public:
+  friend std::ostream&
+  operator<<(std::ostream& os, const IZoneDescriptor& zone_descriptor)
+  {
+    return zone_descriptor._write(os);
+  }
+
+  [[nodiscard]] virtual bool operator==(const RefId& ref_id) const = 0;
+
+  [[nodiscard]] friend bool
+  operator==(const RefId& ref_id, const IZoneDescriptor& zone_descriptor)
+  {
+    return zone_descriptor == ref_id;
+  }
+
+  [[nodiscard]] virtual Type type() const = 0;
+
+  IZoneDescriptor(const IZoneDescriptor&) = delete;
+  IZoneDescriptor(IZoneDescriptor&&)      = delete;
+  IZoneDescriptor()                       = default;
+
+  virtual ~IZoneDescriptor() = default;
+};
+
+#endif   // I_ZONE_DESCRIPTOR_HPP
diff --git a/src/mesh/MeshCellZone.cpp b/src/mesh/MeshCellZone.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..36418e2b510dae6a15d4f30229c5330ff09af2c9
--- /dev/null
+++ b/src/mesh/MeshCellZone.cpp
@@ -0,0 +1,36 @@
+#include <mesh/MeshCellZone.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+#include <utils/Messenger.hpp>
+
+template <size_t Dimension>
+MeshCellZone<Dimension>::MeshCellZone(const Mesh<Connectivity<Dimension>>&, const RefCellList& ref_cell_list)
+  : 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)
+{
+  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 (ref == zone_descriptor) {
+      return MeshCellZone<Dimension>{mesh, ref_cell_list};
+    }
+  }
+
+  std::ostringstream ost;
+  ost << "cannot find zone with name " << rang::fgB::red << zone_descriptor << rang::style::reset;
+
+  throw NormalError(ost.str());
+}
+
+template MeshCellZone<1> getMeshCellZone(const Mesh<Connectivity<1>>&, const IZoneDescriptor&);
+template MeshCellZone<2> getMeshCellZone(const Mesh<Connectivity<2>>&, const IZoneDescriptor&);
+template MeshCellZone<3> getMeshCellZone(const Mesh<Connectivity<3>>&, const IZoneDescriptor&);
diff --git a/src/mesh/MeshCellZone.hpp b/src/mesh/MeshCellZone.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..de4903689b328bddbac3f0bacf363497374f8fe4
--- /dev/null
+++ b/src/mesh/MeshCellZone.hpp
@@ -0,0 +1,49 @@
+#ifndef MESH_CELL_ZONE_HPP
+#define MESH_CELL_ZONE_HPP
+
+#include <mesh/IZoneDescriptor.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]] MeshCellZone   // clazy:exclude=copyable-polymorphic
+{
+ protected:
+  Array<const CellId> m_cell_list;
+  std::string m_zone_name;
+
+ public:
+  template <size_t MeshDimension>
+  friend MeshCellZone<MeshDimension> getMeshCellZone(const Mesh<Connectivity<MeshDimension>>& mesh,
+                                                     const IZoneDescriptor& zone_descriptor);
+
+  MeshCellZone& operator=(const MeshCellZone&) = default;
+  MeshCellZone& operator=(MeshCellZone&&) = default;
+
+  const Array<const CellId>& cellList() const
+  {
+    return m_cell_list;
+  }
+
+ protected:
+  MeshCellZone(const Mesh<Connectivity<Dimension>>& mesh, const RefCellList& ref_cell_list);
+
+ public:
+  MeshCellZone(const MeshCellZone&) = default;
+  MeshCellZone(MeshCellZone &&)     = default;
+
+  MeshCellZone()          = default;
+  virtual ~MeshCellZone() = default;
+};
+
+template <size_t Dimension>
+MeshCellZone<Dimension> getMeshCellZone(const Mesh<Connectivity<Dimension>>& mesh,
+                                        const IZoneDescriptor& zone_descriptor);
+
+#endif   // MESH_CELL_ZONE_HPP
diff --git a/src/mesh/MeshFaceBoundary.hpp b/src/mesh/MeshFaceBoundary.hpp
index afd409d939b038009944a98ce57bfd38b89fda4d..a45108aa5893c798230f41e14a8cacb071c60c0b 100644
--- a/src/mesh/MeshFaceBoundary.hpp
+++ b/src/mesh/MeshFaceBoundary.hpp
@@ -3,7 +3,6 @@
 
 #include <algebra/TinyVector.hpp>
 #include <mesh/IBoundaryDescriptor.hpp>
-#include <mesh/ItemValue.hpp>
 #include <mesh/RefItemList.hpp>
 #include <utils/Array.hpp>
 
@@ -14,14 +13,14 @@ template <typename ConnectivityType>
 class Mesh;
 
 template <size_t Dimension>
-class MeshFaceBoundary   // clazy:exclude=copyable-polymorphic
+class [[nodiscard]] MeshFaceBoundary   // clazy:exclude=copyable-polymorphic
 {
  protected:
   Array<const FaceId> m_face_list;
   std::string m_boundary_name;
 
-  std::array<TinyVector<Dimension>, Dimension*(Dimension - 1)> _getBounds(
-    const Mesh<Connectivity<Dimension>>& mesh) const;
+  std::array<TinyVector<Dimension>, Dimension*(Dimension - 1)> _getBounds(const Mesh<Connectivity<Dimension>>& mesh)
+    const;
 
  public:
   template <size_t MeshDimension>
@@ -31,8 +30,7 @@ class MeshFaceBoundary   // clazy:exclude=copyable-polymorphic
   MeshFaceBoundary& operator=(const MeshFaceBoundary&) = default;
   MeshFaceBoundary& operator=(MeshFaceBoundary&&) = default;
 
-  const Array<const FaceId>&
-  faceList() const
+  const Array<const FaceId>& faceList() const
   {
     return m_face_list;
   }
@@ -42,7 +40,7 @@ class MeshFaceBoundary   // clazy:exclude=copyable-polymorphic
 
  public:
   MeshFaceBoundary(const MeshFaceBoundary&) = default;
-  MeshFaceBoundary(MeshFaceBoundary&&)      = default;
+  MeshFaceBoundary(MeshFaceBoundary &&)     = default;
 
   MeshFaceBoundary()          = default;
   virtual ~MeshFaceBoundary() = default;
diff --git a/src/mesh/MeshFlatNodeBoundary.hpp b/src/mesh/MeshFlatNodeBoundary.hpp
index 334cf03aac844670c084b8385ebbb00d1cc9507d..8486f02c7377fe2987560d29f2ee7cc45c1b44cf 100644
--- a/src/mesh/MeshFlatNodeBoundary.hpp
+++ b/src/mesh/MeshFlatNodeBoundary.hpp
@@ -4,7 +4,8 @@
 #include <mesh/MeshNodeBoundary.hpp>
 
 template <size_t Dimension>
-class MeshFlatNodeBoundary final : public MeshNodeBoundary<Dimension>   // clazy:exclude=copyable-polymorphic
+class [[nodiscard]] MeshFlatNodeBoundary final
+  : public MeshNodeBoundary<Dimension>   // clazy:exclude=copyable-polymorphic
 {
  public:
   using Rd = TinyVector<Dimension, double>;
@@ -14,16 +15,13 @@ class MeshFlatNodeBoundary final : public MeshNodeBoundary<Dimension>   // clazy
 
   Rd _getNormal(const Mesh<Connectivity<Dimension>>& mesh);
 
-  void _checkBoundaryIsFlat(const TinyVector<Dimension, double>& normal,
-                            const TinyVector<Dimension, double>& origin,
-                            const double length,
-                            const Mesh<Connectivity<Dimension>>& mesh) const;
+  void _checkBoundaryIsFlat(const TinyVector<Dimension, double>& normal, const TinyVector<Dimension, double>& origin,
+                            const double length, const Mesh<Connectivity<Dimension>>& mesh) const;
 
   Rd _getOutgoingNormal(const Mesh<Connectivity<Dimension>>& mesh);
 
  public:
-  const Rd&
-  outgoingNormal() const
+  const Rd& outgoingNormal() const
   {
     return m_outgoing_normal;
   }
@@ -49,7 +47,7 @@ class MeshFlatNodeBoundary final : public MeshNodeBoundary<Dimension>   // clazy
  public:
   MeshFlatNodeBoundary()                            = default;
   MeshFlatNodeBoundary(const MeshFlatNodeBoundary&) = default;
-  MeshFlatNodeBoundary(MeshFlatNodeBoundary&&)      = default;
+  MeshFlatNodeBoundary(MeshFlatNodeBoundary &&)     = default;
   ~MeshFlatNodeBoundary()                           = default;
 };
 
diff --git a/src/mesh/MeshLineNodeBoundary.hpp b/src/mesh/MeshLineNodeBoundary.hpp
index 0ae35d058d3b749453463bd347edb3f69c2a109e..8694dd257247eb5aeaf2224d113a34c9c4c8fa32 100644
--- a/src/mesh/MeshLineNodeBoundary.hpp
+++ b/src/mesh/MeshLineNodeBoundary.hpp
@@ -5,7 +5,8 @@
 #include <mesh/MeshNodeBoundary.hpp>
 
 template <size_t Dimension>
-class MeshLineNodeBoundary final : public MeshNodeBoundary<Dimension>   // clazy:exclude=copyable-polymorphic
+class [[nodiscard]] MeshLineNodeBoundary final
+  : public MeshNodeBoundary<Dimension>   // clazy:exclude=copyable-polymorphic
 {
  public:
   using Rd = TinyVector<Dimension, double>;
@@ -16,10 +17,8 @@ class MeshLineNodeBoundary final : public MeshNodeBoundary<Dimension>   // clazy
   template <size_t MeshDimension>
   TinyVector<MeshDimension> _getDirection(const Mesh<Connectivity<MeshDimension>>&);
 
-  void _checkBoundaryIsLine(const TinyVector<Dimension, double>& direction,
-                            const TinyVector<Dimension, double>& origin,
-                            const double length,
-                            const Mesh<Connectivity<Dimension>>& mesh) const;
+  void _checkBoundaryIsLine(const TinyVector<Dimension, double>& direction, const TinyVector<Dimension, double>& origin,
+                            const double length, const Mesh<Connectivity<Dimension>>& mesh) const;
 
  public:
   template <size_t MeshDimension>
@@ -27,8 +26,7 @@ class MeshLineNodeBoundary final : public MeshNodeBoundary<Dimension>   // clazy
                                                                      const IBoundaryDescriptor& boundary_descriptor);
 
   PUGS_INLINE
-  const Rd&
-  direction() const
+  const Rd& direction() const
   {
     return m_direction;
   }
@@ -39,9 +37,7 @@ class MeshLineNodeBoundary final : public MeshNodeBoundary<Dimension>   // clazy
  private:
   MeshLineNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefEdgeList& ref_edge_list)
     : MeshNodeBoundary<Dimension>(mesh, ref_edge_list), m_direction(_getDirection(mesh))
-  {
-    ;
-  }
+  {}
 
   MeshLineNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefFaceList& ref_face_list)
     : MeshNodeBoundary<Dimension>(mesh, ref_face_list), m_direction(_getDirection(mesh))
@@ -54,7 +50,7 @@ class MeshLineNodeBoundary final : public MeshNodeBoundary<Dimension>   // clazy
  public:
   MeshLineNodeBoundary()                            = default;
   MeshLineNodeBoundary(const MeshLineNodeBoundary&) = default;
-  MeshLineNodeBoundary(MeshLineNodeBoundary&&)      = default;
+  MeshLineNodeBoundary(MeshLineNodeBoundary &&)     = default;
   ~MeshLineNodeBoundary()                           = default;
 };
 
diff --git a/src/mesh/MeshNodeBoundary.hpp b/src/mesh/MeshNodeBoundary.hpp
index 883a9d975682b55e0cad75c191e7f2be14e59190..0422118ffc14abbc85030d4d56ca2f2da29eadb9 100644
--- a/src/mesh/MeshNodeBoundary.hpp
+++ b/src/mesh/MeshNodeBoundary.hpp
@@ -3,7 +3,6 @@
 
 #include <algebra/TinyVector.hpp>
 #include <mesh/IBoundaryDescriptor.hpp>
-#include <mesh/ItemValue.hpp>
 #include <mesh/RefItemList.hpp>
 #include <utils/Array.hpp>
 
@@ -14,14 +13,14 @@ template <typename ConnectivityType>
 class Mesh;
 
 template <size_t Dimension>
-class MeshNodeBoundary   // clazy:exclude=copyable-polymorphic
+class [[nodiscard]] MeshNodeBoundary   // clazy:exclude=copyable-polymorphic
 {
  protected:
   Array<const NodeId> m_node_list;
   std::string m_boundary_name;
 
-  std::array<TinyVector<Dimension>, Dimension*(Dimension - 1)> _getBounds(
-    const Mesh<Connectivity<Dimension>>& mesh) const;
+  std::array<TinyVector<Dimension>, Dimension*(Dimension - 1)> _getBounds(const Mesh<Connectivity<Dimension>>& mesh)
+    const;
 
  public:
   template <size_t MeshDimension>
@@ -31,8 +30,7 @@ class MeshNodeBoundary   // clazy:exclude=copyable-polymorphic
   MeshNodeBoundary& operator=(const MeshNodeBoundary&) = default;
   MeshNodeBoundary& operator=(MeshNodeBoundary&&) = default;
 
-  const Array<const NodeId>&
-  nodeList() const
+  const Array<const NodeId>& nodeList() const
   {
     return m_node_list;
   }
@@ -44,7 +42,7 @@ class MeshNodeBoundary   // clazy:exclude=copyable-polymorphic
 
  public:
   MeshNodeBoundary(const MeshNodeBoundary&) = default;
-  MeshNodeBoundary(MeshNodeBoundary&&)      = default;
+  MeshNodeBoundary(MeshNodeBoundary &&)     = default;
 
   MeshNodeBoundary()          = default;
   virtual ~MeshNodeBoundary() = default;
diff --git a/src/scheme/NamedBoundaryDescriptor.hpp b/src/mesh/NamedBoundaryDescriptor.hpp
similarity index 87%
rename from src/scheme/NamedBoundaryDescriptor.hpp
rename to src/mesh/NamedBoundaryDescriptor.hpp
index 5d8959261ef37459a7ab449d3834c4f2e2050c79..7ee732c59df7814da24b684df9bed60f4676742c 100644
--- a/src/scheme/NamedBoundaryDescriptor.hpp
+++ b/src/mesh/NamedBoundaryDescriptor.hpp
@@ -6,7 +6,7 @@
 #include <iostream>
 #include <string>
 
-class NamedBoundaryDescriptor : public IBoundaryDescriptor
+class NamedBoundaryDescriptor final : public IBoundaryDescriptor
 {
  private:
   std::string m_name;
@@ -19,13 +19,13 @@ class NamedBoundaryDescriptor : public IBoundaryDescriptor
   }
 
  public:
-  bool
+  [[nodiscard]] bool
   operator==(const RefId& ref_id) const final
   {
     return m_name == ref_id.tagName();
   }
 
-  Type
+  [[nodiscard]] Type
   type() const final
   {
     return Type::named;
@@ -33,10 +33,7 @@ class NamedBoundaryDescriptor : public IBoundaryDescriptor
 
   NamedBoundaryDescriptor(const NamedBoundaryDescriptor&) = delete;
   NamedBoundaryDescriptor(NamedBoundaryDescriptor&&)      = delete;
-  NamedBoundaryDescriptor(const std::string& name) : m_name(name)
-  {
-    ;
-  }
+  NamedBoundaryDescriptor(const std::string& name) : m_name(name) {}
   virtual ~NamedBoundaryDescriptor() = default;
 };
 
diff --git a/src/mesh/NamedZoneDescriptor.hpp b/src/mesh/NamedZoneDescriptor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0fbe101421f7f5048019f945622ad4263e8d3e39
--- /dev/null
+++ b/src/mesh/NamedZoneDescriptor.hpp
@@ -0,0 +1,43 @@
+#ifndef NAMED_ZONE_DESCRIPTOR_HPP
+#define NAMED_ZONE_DESCRIPTOR_HPP
+
+#include <mesh/IZoneDescriptor.hpp>
+
+#include <iostream>
+#include <string>
+
+class NamedZoneDescriptor final : public IZoneDescriptor
+{
+ private:
+  std::string m_name;
+
+  std::ostream&
+  _write(std::ostream& os) const final
+  {
+    os << '"' << m_name << '"';
+    return os;
+  }
+
+ public:
+  bool
+  operator==(const RefId& ref_id) const final
+  {
+    return m_name == ref_id.tagName();
+  }
+
+  Type
+  type() const final
+  {
+    return Type::named;
+  }
+
+  NamedZoneDescriptor(const NamedZoneDescriptor&) = delete;
+  NamedZoneDescriptor(NamedZoneDescriptor&&)      = delete;
+  NamedZoneDescriptor(const std::string& name) : m_name(name)
+  {
+    ;
+  }
+  virtual ~NamedZoneDescriptor() = default;
+};
+
+#endif   // NAMED_ZONE_DESCRIPTOR_HPP
diff --git a/src/scheme/NumberedBoundaryDescriptor.hpp b/src/mesh/NumberedBoundaryDescriptor.hpp
similarity index 87%
rename from src/scheme/NumberedBoundaryDescriptor.hpp
rename to src/mesh/NumberedBoundaryDescriptor.hpp
index 47aa7fa537f6f1070e48077cdee62aa28868782b..2abab93956f9fd2dc98a7d3967484444cba4a0fb 100644
--- a/src/scheme/NumberedBoundaryDescriptor.hpp
+++ b/src/mesh/NumberedBoundaryDescriptor.hpp
@@ -5,7 +5,7 @@
 
 #include <iostream>
 
-class NumberedBoundaryDescriptor : public IBoundaryDescriptor
+class NumberedBoundaryDescriptor final : public IBoundaryDescriptor
 {
  private:
   unsigned int m_number;
@@ -17,14 +17,14 @@ class NumberedBoundaryDescriptor : public IBoundaryDescriptor
     return os;
   }
 
-  bool
+  [[nodiscard]] bool
   operator==(const RefId& ref_id) const final
   {
     return m_number == ref_id.tagNumber();
   }
 
  public:
-  Type
+  [[nodiscard]] Type
   type() const final
   {
     return Type::numbered;
diff --git a/src/mesh/NumberedZoneDescriptor.hpp b/src/mesh/NumberedZoneDescriptor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7e72521d2c69346c4fc46edc82dd0bbedd99e20a
--- /dev/null
+++ b/src/mesh/NumberedZoneDescriptor.hpp
@@ -0,0 +1,39 @@
+#ifndef NUMBERED_ZONE_DESCRIPTOR_HPP
+#define NUMBERED_ZONE_DESCRIPTOR_HPP
+
+#include <mesh/IZoneDescriptor.hpp>
+
+#include <iostream>
+
+class NumberedZoneDescriptor final : public IZoneDescriptor
+{
+ private:
+  unsigned int m_number;
+
+  std::ostream&
+  _write(std::ostream& os) const final
+  {
+    os << '"' << m_number << '"';
+    return os;
+  }
+
+  bool
+  operator==(const RefId& ref_id) const final
+  {
+    return m_number == ref_id.tagNumber();
+  }
+
+ public:
+  Type
+  type() const final
+  {
+    return Type::numbered;
+  }
+
+  NumberedZoneDescriptor(const NumberedZoneDescriptor&) = delete;
+  NumberedZoneDescriptor(NumberedZoneDescriptor&&)      = delete;
+  NumberedZoneDescriptor(unsigned int number) : m_number(number) {}
+  virtual ~NumberedZoneDescriptor() = default;
+};
+
+#endif   // NUMBERED_ZONE_DESCRIPTOR_HPP