diff --git a/src/scheme/DiscreteFunctionInterpoler.cpp b/src/scheme/DiscreteFunctionInterpoler.cpp
index 55348a4084f9267d78a8ee2e36a4abb70965e220..07e113b986b4dc8bda4cb1a902d27e7b0a938e5c 100644
--- a/src/scheme/DiscreteFunctionInterpoler.cpp
+++ b/src/scheme/DiscreteFunctionInterpoler.cpp
@@ -7,49 +7,61 @@
 #include <mesh/MeshDataManager.hpp>
 #include <utils/Exceptions.hpp>
 
-template <typename DataType>
+template <size_t Dimension, typename DataType>
 class DiscreteFunctionP0 : public IDiscreteFunction
 {
  private:
-  CellValue<DataType> m_cell_value;
+  using MeshType = Mesh<Connectivity<Dimension>>;
+
+  std::shared_ptr<const MeshType> m_mesh;
+  CellValue<DataType> m_cell_values;
 
  public:
-  DiscreteFunctionP0(const CellValue<DataType>& cell_value) : m_cell_value{cell_value} {}
-  DiscreteFunctionP0(CellValue<DataType>&& cell_value) : m_cell_value{std::move(cell_value)} {}
-  ~DiscreteFunctionP0() = default;
-};
+  PUGS_FORCEINLINE
+  DataType&
+  operator[](const CellId& cell_id) const noexcept(NO_ASSERT)
+  {
+    return m_cell_values[cell_id];
+  }
 
-template <size_t Dimension>
-std::shared_ptr<IDiscreteFunction>
-DiscreteFunctionInterpoler::_interpolate() const
-{
-  using MeshType       = Mesh<Connectivity<Dimension>>;
-  const MeshType& mesh = dynamic_cast<const MeshType&>(*m_mesh);
+  DiscreteFunctionP0(const std::shared_ptr<const MeshType>& mesh, const FunctionSymbolId& function_id) : m_mesh(mesh)
+  {
+    using MeshDataType      = MeshData<Dimension>;
+    MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh);
+    m_cell_values =
+      InterpolateItemValue<DataType(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(function_id,
+                                                                                                  mesh_data.xj());
+  }
 
-  using MeshDataType      = MeshData<Dimension>;
-  MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh);
+  DiscreteFunctionP0(const DiscreteFunctionP0&) noexcept = default;
+  DiscreteFunctionP0(DiscreteFunctionP0&&) noexcept      = default;
 
-  return std::make_shared<DiscreteFunctionP0<double>>(
-    InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(m_function_id,
-                                                                                              mesh_data.xj()));
-}
+  ~DiscreteFunctionP0() = default;
+};
 
 std::shared_ptr<IDiscreteFunction>
 DiscreteFunctionInterpoler::interpolate() const
 {
+  std::shared_ptr<IDiscreteFunction> discrete_function;
   switch (m_mesh->dimension()) {
   case 1: {
-    return this->_interpolate<1>();
+    std::shared_ptr mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(m_mesh);
+    discrete_function    = std::make_shared<DiscreteFunctionP0<1, double>>(mesh, m_function_id);
+    break;
   }
   case 2: {
-    return this->_interpolate<2>();
+    std::shared_ptr mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<2>>>(m_mesh);
+    discrete_function    = std::make_shared<DiscreteFunctionP0<2, double>>(mesh, m_function_id);
+    break;
   }
   case 3: {
-    return this->_interpolate<3>();
+    std::shared_ptr mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<3>>>(m_mesh);
+    discrete_function    = std::make_shared<DiscreteFunctionP0<3, double>>(mesh, m_function_id);
+    break;
   }
   default: {
     throw UnexpectedError("invalid dimension");
   }
   }
-  return nullptr;
+  return discrete_function;
 }