diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index 913789852ec8b7f12b02a1dc0e1f138abe7dfc6e..bdae18265ed7ffc231c5c1feebf8de9d5402325d 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -33,7 +33,6 @@
 #include <scheme/FourierBoundaryConditionDescriptor.hpp>
 #include <scheme/FreeBoundaryConditionDescriptor.hpp>
 #include <scheme/IBoundaryConditionDescriptor.hpp>
-#include <scheme/IDiscreteFunction.hpp>
 #include <scheme/IDiscreteFunctionDescriptor.hpp>
 #include <scheme/NeumannBoundaryConditionDescriptor.hpp>
 #include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
@@ -44,8 +43,6 @@
 SchemeModule::SchemeModule()
 {
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const DiscreteFunctionVariant>>);
-#warning remove after reworked
-  this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IDiscreteFunction>>);
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IDiscreteFunctionDescriptor>>);
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IQuadratureDescriptor>>);
 
diff --git a/src/language/modules/SchemeModule.hpp b/src/language/modules/SchemeModule.hpp
index 868c681af6468ac667456d977c5f62814f1ba93e..d56a5ec74069bd23d8169f25ff8a829c7c4a53ff 100644
--- a/src/language/modules/SchemeModule.hpp
+++ b/src/language/modules/SchemeModule.hpp
@@ -15,12 +15,6 @@ template <>
 inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const DiscreteFunctionVariant>> =
   ASTNodeDataType::build<ASTNodeDataType::type_id_t>("Vh");
 
-#warning remove after reworked
-class IDiscreteFunction;
-template <>
-inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const IDiscreteFunction>> =
-  ASTNodeDataType::build<ASTNodeDataType::type_id_t>("Vh_legacy");
-
 class IDiscreteFunctionDescriptor;
 template <>
 inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const IDiscreteFunctionDescriptor>> =
diff --git a/src/output/WriterBase.cpp b/src/output/WriterBase.cpp
index e49e69025b7dfccd84c7c9779cedca8d476a3c38..83d890c2ab8bf31e60d7ed09e4e2da7770ebbfb8 100644
--- a/src/output/WriterBase.cpp
+++ b/src/output/WriterBase.cpp
@@ -17,9 +17,10 @@ WriterBase::_registerDiscreteFunction(const std::string& name,
                                       const DiscreteFunctionType& discrete_function,
                                       OutputNamedItemDataSet& named_item_data_set)
 {
-  if constexpr (DiscreteFunctionType::handled_data_type == IDiscreteFunction::HandledItemDataType::value) {
+  if constexpr (is_discrete_function_P0_v<DiscreteFunctionType>) {
     named_item_data_set.add(NamedItemData{name, discrete_function.cellValues()});
   } else {
+    static_assert(is_discrete_function_P0_vector_v<DiscreteFunctionType>, "unexpected discrete function type");
     named_item_data_set.add(NamedItemData{name, discrete_function.cellArrays()});
   }
 }
diff --git a/src/scheme/DiscreteFunctionIntegrator.cpp b/src/scheme/DiscreteFunctionIntegrator.cpp
index f5aa2829a88c17ea5e2a941de4815ee5db27d979..c77f7a2c87ece74da4f48902855e9f3a5f6742bc 100644
--- a/src/scheme/DiscreteFunctionIntegrator.cpp
+++ b/src/scheme/DiscreteFunctionIntegrator.cpp
@@ -171,7 +171,6 @@ DiscreteFunctionIntegrator::_integrate() const
 DiscreteFunctionVariant
 DiscreteFunctionIntegrator::integrate() const
 {
-  std::shared_ptr<IDiscreteFunction> discrete_function;
   switch (m_mesh->dimension()) {
   case 1: {
     return this->_integrate<1>();
diff --git a/src/scheme/DiscreteFunctionP0.hpp b/src/scheme/DiscreteFunctionP0.hpp
index 762330d77080f8fe82c1d7923fbfe89db8156654..1fb2ce14cee3f42e6c27c262e3ba6deefb5b555d 100644
--- a/src/scheme/DiscreteFunctionP0.hpp
+++ b/src/scheme/DiscreteFunctionP0.hpp
@@ -1,7 +1,7 @@
 #ifndef DISCRETE_FUNCTION_P0_HPP
 #define DISCRETE_FUNCTION_P0_HPP
 
-#include <scheme/IDiscreteFunction.hpp>
+#include <language/utils/ASTNodeDataTypeTraits.hpp>
 
 #include <mesh/Connectivity.hpp>
 #include <mesh/ItemValueUtils.hpp>
@@ -11,14 +11,12 @@
 #include <scheme/DiscreteFunctionDescriptorP0.hpp>
 
 template <size_t Dimension, typename DataType>
-class DiscreteFunctionP0 : public IDiscreteFunction
+class DiscreteFunctionP0
 {
  public:
   using data_type = DataType;
   using MeshType  = Mesh<Connectivity<Dimension>>;
 
-  static constexpr HandledItemDataType handled_data_type = HandledItemDataType::value;
-
   friend class DiscreteFunctionP0<Dimension, std::add_const_t<DataType>>;
   friend class DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>;
 
@@ -31,7 +29,7 @@ class DiscreteFunctionP0 : public IDiscreteFunction
  public:
   PUGS_INLINE
   ASTNodeDataType
-  dataType() const final
+  dataType() const
   {
     return ast_node_data_type_from<std::remove_const_t<DataType>>;
   }
@@ -52,7 +50,7 @@ class DiscreteFunctionP0 : public IDiscreteFunction
 
   PUGS_INLINE
   const IDiscreteFunctionDescriptor&
-  descriptor() const final
+  descriptor() const
   {
     return m_discrete_function_descriptor;
   }
diff --git a/src/scheme/DiscreteFunctionP0Vector.hpp b/src/scheme/DiscreteFunctionP0Vector.hpp
index 351b6e4ec6e8249791c15524cef883be67cec547..7bd833f344ffd29f6f47f6fa0d0bff13a2407339 100644
--- a/src/scheme/DiscreteFunctionP0Vector.hpp
+++ b/src/scheme/DiscreteFunctionP0Vector.hpp
@@ -1,8 +1,6 @@
 #ifndef DISCRETE_FUNCTION_P0_VECTOR_HPP
 #define DISCRETE_FUNCTION_P0_VECTOR_HPP
 
-#include <scheme/IDiscreteFunction.hpp>
-
 #include <algebra/Vector.hpp>
 #include <mesh/Connectivity.hpp>
 #include <mesh/ItemArray.hpp>
@@ -14,14 +12,12 @@
 #include <utils/Exceptions.hpp>
 
 template <size_t Dimension, typename DataType>
-class DiscreteFunctionP0Vector : public IDiscreteFunction
+class DiscreteFunctionP0Vector
 {
  public:
   using data_type = DataType;
   using MeshType  = Mesh<Connectivity<Dimension>>;
 
-  static constexpr HandledItemDataType handled_data_type = HandledItemDataType::vector;
-
   friend class DiscreteFunctionP0Vector<Dimension, std::add_const_t<DataType>>;
   friend class DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>>;
 
@@ -36,7 +32,7 @@ class DiscreteFunctionP0Vector : public IDiscreteFunction
  public:
   PUGS_INLINE
   ASTNodeDataType
-  dataType() const final
+  dataType() const
   {
     return ast_node_data_type_from<std::remove_const_t<data_type>>;
   }
@@ -64,7 +60,7 @@ class DiscreteFunctionP0Vector : public IDiscreteFunction
 
   PUGS_INLINE
   const IDiscreteFunctionDescriptor&
-  descriptor() const final
+  descriptor() const
   {
     return m_discrete_function_descriptor;
   }
diff --git a/src/scheme/DiscreteFunctionUtils.cpp b/src/scheme/DiscreteFunctionUtils.cpp
index 225cdaf15e2ca1d87f78093b8707a26ca564126f..ba2416a9ab08e03e75bb0824f32930c105cd366c 100644
--- a/src/scheme/DiscreteFunctionUtils.cpp
+++ b/src/scheme/DiscreteFunctionUtils.cpp
@@ -53,18 +53,6 @@ hasSameMesh(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& d
   return is_same_mesh;
 }
 
-template <size_t Dimension, typename DataType>
-std::shared_ptr<const IDiscreteFunction>
-shallowCopy(const std::shared_ptr<const Mesh<Connectivity<Dimension>>>& mesh,
-            const std::shared_ptr<const DiscreteFunctionP0<Dimension, DataType>>& discrete_function)
-{
-  Assert(mesh->shared_connectivity() ==
-           dynamic_cast<const Mesh<Connectivity<Dimension>>&>(*discrete_function->mesh()).shared_connectivity(),
-         "connectivities should be the same");
-
-  return std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh, discrete_function->cellValues());
-}
-
 template <typename MeshType, typename DiscreteFunctionT>
 std::shared_ptr<const DiscreteFunctionVariant>
 shallowCopy(const std::shared_ptr<const MeshType>& mesh, const DiscreteFunctionT& f)
diff --git a/src/scheme/DiscreteFunctionUtils.hpp b/src/scheme/DiscreteFunctionUtils.hpp
index a75394cef80e862a134a68c38c45be96859369ce..91acbccb7c1444e2adfbc55333b1ee2a0e89d3b9 100644
--- a/src/scheme/DiscreteFunctionUtils.hpp
+++ b/src/scheme/DiscreteFunctionUtils.hpp
@@ -3,7 +3,6 @@
 
 #include <scheme/DiscreteFunctionType.hpp>
 #include <scheme/DiscreteFunctionVariant.hpp>
-#include <scheme/IDiscreteFunction.hpp>
 #include <scheme/IDiscreteFunctionDescriptor.hpp>
 
 #include <vector>
@@ -22,23 +21,6 @@ checkDiscretizationType(const std::vector<std::shared_ptr<const DiscreteFunction
   return true;
 }
 
-PUGS_INLINE
-std::shared_ptr<const IMesh>
-getCommonMesh(const std::vector<std::shared_ptr<const IDiscreteFunction>>& discrete_function_list)
-{
-  std::shared_ptr<const IMesh> i_mesh;
-  for (const auto& discrete_function : discrete_function_list) {
-    if (not i_mesh.use_count()) {
-      i_mesh = discrete_function->mesh();
-    } else {
-      if (i_mesh != discrete_function->mesh()) {
-        return {};
-      }
-    }
-  }
-  return i_mesh;
-}
-
 std::shared_ptr<const IMesh> getCommonMesh(
   const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list);
 
diff --git a/src/scheme/IDiscreteFunction.hpp b/src/scheme/IDiscreteFunction.hpp
deleted file mode 100644
index be6ca66278de95265e9a301ebc8f2fe0fa7ee5db..0000000000000000000000000000000000000000
--- a/src/scheme/IDiscreteFunction.hpp
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef I_DISCRETE_FUNCTION_HPP
-#define I_DISCRETE_FUNCTION_HPP
-
-class IMesh;
-class IDiscreteFunctionDescriptor;
-
-#include <language/utils/ASTNodeDataTypeTraits.hpp>
-#include <memory>
-
-class IDiscreteFunction
-{
- public:
-  enum class HandledItemDataType
-  {
-    value,
-    vector,
-  };
-
-  virtual std::shared_ptr<const IMesh> mesh() const             = 0;
-  virtual const IDiscreteFunctionDescriptor& descriptor() const = 0;
-  virtual ASTNodeDataType dataType() const                      = 0;
-
-  IDiscreteFunction() = default;
-
-  IDiscreteFunction(const IDiscreteFunction&) = default;
-
-  IDiscreteFunction(IDiscreteFunction&&) noexcept = default;
-
-  virtual ~IDiscreteFunction() noexcept = default;
-};
-
-#endif   // I_DISCRETE_FUNCTION_HPP
diff --git a/tests/test_DiscreteFunctionUtils.cpp b/tests/test_DiscreteFunctionUtils.cpp
index b206c64062be67e450bfd4df1a06ea779acb9bc1..85c916c4cf559ca4c2ceef612d1da03db779715e 100644
--- a/tests/test_DiscreteFunctionUtils.cpp
+++ b/tests/test_DiscreteFunctionUtils.cpp
@@ -28,14 +28,19 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         SECTION("common mesh")
         {
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-          std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh);
+          DiscreteFunctionP0<Dimension, double> uh(mesh);
+          DiscreteFunctionP0<Dimension, double> vh(mesh);
+          DiscreteFunctionP0<Dimension, TinyVector<2>> wh(mesh);
+
+          DiscreteFunctionP0<Dimension, double> qh(mesh_copy);
 
-          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
+          std::shared_ptr uh_v = std::make_shared<DiscreteFunctionVariant>(uh);
+          std::shared_ptr vh_v = std::make_shared<DiscreteFunctionVariant>(vh);
+          std::shared_ptr wh_v = std::make_shared<DiscreteFunctionVariant>(wh);
+          std::shared_ptr qh_v = std::make_shared<DiscreteFunctionVariant>(qh);
 
-          REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get());
-          REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0);
+          REQUIRE(getCommonMesh({uh_v, vh_v, wh_v}).get() == mesh.get());
+          REQUIRE(getCommonMesh({uh_v, vh_v, wh_v, qh_v}).use_count() == 0);
         }
 
         SECTION("check discretization type")
@@ -209,14 +214,19 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         SECTION("common mesh")
         {
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-          std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh);
+          DiscreteFunctionP0<Dimension, double> uh(mesh);
+          DiscreteFunctionP0<Dimension, double> vh(mesh);
+          DiscreteFunctionP0<Dimension, TinyVector<2>> wh(mesh);
 
-          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
+          DiscreteFunctionP0<Dimension, double> qh(mesh_copy);
+
+          std::shared_ptr uh_v = std::make_shared<DiscreteFunctionVariant>(uh);
+          std::shared_ptr vh_v = std::make_shared<DiscreteFunctionVariant>(vh);
+          std::shared_ptr wh_v = std::make_shared<DiscreteFunctionVariant>(wh);
+          std::shared_ptr qh_v = std::make_shared<DiscreteFunctionVariant>(qh);
 
-          REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get());
-          REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0);
+          REQUIRE(getCommonMesh({uh_v, vh_v, wh_v}).get() == mesh.get());
+          REQUIRE(getCommonMesh({uh_v, vh_v, wh_v, qh_v}).use_count() == 0);
         }
 
         SECTION("check discretization type")
@@ -390,14 +400,19 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         SECTION("common mesh")
         {
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-          std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh);
+          DiscreteFunctionP0<Dimension, double> uh(mesh);
+          DiscreteFunctionP0<Dimension, double> vh(mesh);
+          DiscreteFunctionP0<Dimension, TinyVector<2>> wh(mesh);
+
+          DiscreteFunctionP0<Dimension, double> qh(mesh_copy);
 
-          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
+          std::shared_ptr uh_v = std::make_shared<DiscreteFunctionVariant>(uh);
+          std::shared_ptr vh_v = std::make_shared<DiscreteFunctionVariant>(vh);
+          std::shared_ptr wh_v = std::make_shared<DiscreteFunctionVariant>(wh);
+          std::shared_ptr qh_v = std::make_shared<DiscreteFunctionVariant>(qh);
 
-          REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get());
-          REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0);
+          REQUIRE(getCommonMesh({uh_v, vh_v, wh_v}).get() == mesh.get());
+          REQUIRE(getCommonMesh({uh_v, vh_v, wh_v, qh_v}).use_count() == 0);
         }
 
         SECTION("check discretization type")