diff --git a/cmake/modules/DuneXtMacros.cmake b/cmake/modules/DuneXtMacros.cmake
index 3399d2aa8fdb57f907fd79294a30bdc740edaf24..b52731c495b2c328e03eadbd96861621b1abd7b4 100644
--- a/cmake/modules/DuneXtMacros.cmake
+++ b/cmake/modules/DuneXtMacros.cmake
@@ -100,7 +100,7 @@ if(HAVE_MPI)
     # this only works in dependent modules
     dune_register_package_flags(INCLUDE_DIRS "${MPI4PY_INCLUDE_DIR}")
     # this only works in dune-xt itself
-    include_directories("${MPI4PY_INCLUDE_DIR}" ${PYTHON_INCLUDE_DIRS})
+    include_directories(SYSTEM "${MPI4PY_INCLUDE_DIR}" ${PYTHON_INCLUDE_DIRS})
   else()
     message(FATAL_ERROR kaput)
   endif()
diff --git a/cmake/modules/FindClangTidy.cmake b/cmake/modules/FindClangTidy.cmake
index 9bde293f4914bc03741d0945a264ddfd94aa5692..cf595db5debf543931f784e8bfa25d31b55b6663 100644
--- a/cmake/modules/FindClangTidy.cmake
+++ b/cmake/modules/FindClangTidy.cmake
@@ -33,7 +33,7 @@ find_program(ClangTidy_EXECUTABLE
 if(EXISTS ${ClangTidy_EXECUTABLE})
   execute_process(COMMAND ${ClangTidy_EXECUTABLE} -version OUTPUT_VARIABLE clang_out)
   string(REGEX
-         REPLACE ".*LLVM version ([0-9]+\\.[0-9]+).*"
+         REPLACE ".*LLVM version ([0-9]+)\.[0-9]+\.[0-9]*.*"
                  "\\1"
                  ClangTidy_VERSION
                  ${clang_out})
diff --git a/cmake/modules/XtCompilerSupport.cmake b/cmake/modules/XtCompilerSupport.cmake
index eff7904c94b1e944bcf06ff9f66a26fa606d32cd..b649a881b771dba4d57f381395335995cf66d0ec 100644
--- a/cmake/modules/XtCompilerSupport.cmake
+++ b/cmake/modules/XtCompilerSupport.cmake
@@ -25,13 +25,8 @@ endmacro(ADD_IF_SUPPORTED)
 macro(INCLUDE_SYS_DIR)
   foreach(ARG ${ARGN})
     if(IS_DIRECTORY ${ARG})
-      include_directories(${ARG}) # due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70129  we have to filter what
-                                  # to sys-include includes
-      if(${ARG} MATCHES "/usr/include")
-        message(AUTHOR_WARNING "-isystem not supported for ${ARG}")
-      else()
-        add_definitions("-isystem ${ARG}")
-      endif()
+      include_directories(SYSTEM ${ARG}) # due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70129  we have to filter
+                                         # what to sys-include includes
     else(IS_DIRECTORY ${ARG})
       message(STATUS "Include directory ${ARG} does not exist")
     endif(IS_DIRECTORY ${ARG})
diff --git a/cmake/modules/XtTooling.cmake b/cmake/modules/XtTooling.cmake
index 1347da95a2de9a6396b9d25ef6177cfcf6105603..74b1b6c13703c8a18846119aaa935e95428f5bdc 100644
--- a/cmake/modules/XtTooling.cmake
+++ b/cmake/modules/XtTooling.cmake
@@ -94,20 +94,56 @@ macro(add_format glob_dir)
 endmacro(add_format)
 
 find_package(ClangTidy 8)
-macro(add_tidy glob_dir)
+macro(add_tidy)
   if(ClangTidy_FOUND)
     dune_symlink_to_source_files(FILES .clang-tidy)
     message(STATUS "adding tidy target")
-    set(TIDY_ARGS -config= -style=file -p=${CMAKE_CURRENT_BINARY_DIR} -j ${DXT_TEST_PROCS})
-    add_custom_target("tidy"
-                      ${RunTidy_EXECUTABLE}
-                      ${TIDY_ARGS}
-                      -export-fixes=${CMAKE_CURRENT_BINARY_DIR}/clang-tidy.fixes)
-    add_custom_target("fix_tidy" ${RunTidy_EXECUTABLE} ${TIDY_ARGS} -fix)
+    add_custom_target(tidy)
+    add_custom_target(fix_tidy)
+    add_tidy_subdir(common)
+    add_tidy_subdir(grid)
+    add_tidy_subdir(la)
+    add_tidy_subdir(functions)
   else()
     message(WARNING "not adding tidy target because clang-tidy is missing or"
                     "wrong version: ${ClangTidy_EXECUTABLE} ${ClangTidy_VERSION}")
   endif(ClangTidy_FOUND)
+endmacro()
+
+macro(add_tidy_subdir _dxt_subdir)
+  set(BASE ${PROJECT_SOURCE_DIR}/dune/xt/${_dxt_subdir})
+  # including the headercheck sources works around our headers needing included config.h to function adding "-extra-
+  # arg-before='-include config.h'" to TIDY_ARGS results in a tidy error despite the arg actually being applied
+  # correctly
+  file(GLOB_RECURSE _files
+                    "${PROJECT_BINARY_DIR}/headercheck/dune/xt/${_dxt_subdir}/*.cc"
+                    "${BASE}/*.cc"
+                    "${BASE}/*.cxx"
+                    "${BASE}/*.cpp"
+                    "${BASE}/*.c")
+  set(BASE ${PROJECT_SOURCE_DIR}/python/dune/xt/${_dxt_subdir})
+  file(GLOB_RECURSE _pyfiles
+                    "${PROJECT_BINARY_DIR}/headercheck/python/dune/xt/${_dxt_subdir}/*.cc"
+                    "${BASE}/*.cc"
+                    "${BASE}/*.cxx"
+                    "${BASE}/*.cpp"
+                    "${BASE}/*.c")
+  list(APPEND _files ${_pyfiles})
+  list(REMOVE_DUPLICATES _files)
+  set(TIDY_ARGS
+      -config=
+      -format-style=file
+      -p=${CMAKE_CURRENT_BINARY_DIR}
+      -header-filter=\".*/dune/xt/${_dxt_subdir}.*\")
+  add_custom_target(tidy_${_dxt_subdir}
+                    COMMAND ${ClangTidy_EXECUTABLE} ${TIDY_ARGS}
+                            -export-fixes=${CMAKE_CURRENT_BINARY_DIR}/clang-tidy.fixes ${_files}
+                    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
+  add_custom_target(fix_tidy_${_dxt_subdir}
+                    COMMAND ${ClangTidy_EXECUTABLE} ${TIDY_ARGS} -fix ${_files}
+                    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
+  add_dependencies(tidy tidy_${_dxt_subdir})
+  add_dependencies(fix_tidy fix_tidy_${_dxt_subdir})
 endmacro(add_tidy)
 
 macro(add_forced_doxygen_target)
diff --git a/dune/xt/common/configuration.cc b/dune/xt/common/configuration.cc
index 1c2296dd1149b25fb9fe364709b7ac13627d082e..5254ad12c5dd75a51602a22fc702397e0835225b 100644
--- a/dune/xt/common/configuration.cc
+++ b/dune/xt/common/configuration.cc
@@ -126,7 +126,7 @@ bool Configuration::has_key(const std::string& key) const
   return BaseType::hasKey(key);
 }
 
-Configuration Configuration::sub(const std::string sub_id, bool fail_if_missing, Configuration default_value) const
+Configuration Configuration::sub(const std::string& sub_id, bool fail_if_missing, Configuration default_value) const
 {
   if ((empty() || !has_sub(sub_id)) && !fail_if_missing)
     return default_value;
@@ -146,7 +146,7 @@ Configuration Configuration::sub(const std::string sub_id, bool fail_if_missing,
   return Configuration(BaseType::sub(sub_id));
 } // ... sub(...)
 
-bool Configuration::has_sub(const std::string subTreeName) const
+bool Configuration::has_sub(const std::string& subTreeName) const
 {
   return BaseType::hasSub(subTreeName);
 }
diff --git a/dune/xt/common/configuration.hh b/dune/xt/common/configuration.hh
index b2db3b92e18f14f88cfba7ecd2b4b0766eb5289b..e71dcf44df16c0eb6c504ffa693b1a060f5c2d2f 100644
--- a/dune/xt/common/configuration.hh
+++ b/dune/xt/common/configuration.hh
@@ -124,7 +124,7 @@ some_function_which_expects_a_config({{"type", "custom"}, {"tolerance", "1e-10"}
   bool has_key(const std::string& key) const;
 
   //! check if sub is existing in tree_
-  bool has_sub(const std::string subTreeName) const;
+  bool has_sub(const std::string& subTreeName) const;
 
   /** \brief print the ParameterTree
    *  \param out output stream
@@ -136,7 +136,7 @@ some_function_which_expects_a_config({{"type", "custom"}, {"tolerance", "1e-10"}
    * @attention Please note the difference to Dune::ParameterTree::sub (return: value vs. reference)!
    */
   Configuration
-  sub(const std::string sub_id, bool fail_if_missing = true, Configuration default_value = Configuration()) const;
+  sub(const std::string& sub_id, bool fail_if_missing = true, Configuration default_value = Configuration()) const;
 
   /**
    * \}
diff --git a/dune/xt/common/fmatrix-2.7.hh b/dune/xt/common/fmatrix-2.7.hh
index 145533bfc7bc7398ce7ec29ca67c38b869138842..c8cc7e37eea9f006db03c023aa510f8c4d71c575 100644
--- a/dune/xt/common/fmatrix-2.7.hh
+++ b/dune/xt/common/fmatrix-2.7.hh
@@ -973,6 +973,17 @@ void rightmultiply(Dune::FieldMatrix<K, L_ROWS, R_COLS>& ret,
   }
 }
 
+
+template <class K, int rows, int cols>
+XT::Common::FieldVector<K, rows> operator*(const Dune::FieldMatrix<K, rows, cols>& mat,
+                                           const Dune::FieldVector<K, cols>& vec)
+{
+  XT::Common::FieldVector<K, rows> ret;
+  mat.mv(vec, ret);
+  return ret;
+}
+
+
 // versions that do not allocate matrices on the stack (for large matrices)
 template <class K, int L_ROWS, int L_COLS, int R_COLS>
 std::unique_ptr<Dune::XT::Common::FieldMatrix<K, L_ROWS, R_COLS>>
diff --git a/dune/xt/common/lapacke.cc b/dune/xt/common/lapacke.cc
index 8cbb03340b95f5421ed1909ed8d6dfae4055ffde..11370f27703ceb2553d54b14a6534af8e81f2ebc 100644
--- a/dune/xt/common/lapacke.cc
+++ b/dune/xt/common/lapacke.cc
@@ -275,7 +275,7 @@ int dgesvd(int DXTC_LAPACKE_ONLY(matrix_layout),
 #endif
 }
 
-double dlamch(char cmach)
+double dlamch(char DXTC_LAPACKE_ONLY(cmach))
 {
 #if HAVE_MKL || HAVE_LAPACKE
   return LAPACKE_dlamch(cmach);
diff --git a/dune/xt/common/matrix.hh b/dune/xt/common/matrix.hh
index d931557fde93f92bc9ee9f7512edddcc365f5632..1e8727cd10794b25365359ff7a43bf4d0962bc03 100644
--- a/dune/xt/common/matrix.hh
+++ b/dune/xt/common/matrix.hh
@@ -323,13 +323,15 @@ auto set_matrix_entry(MatrixType& matrix, const size_t ii, const size_t jj, cons
 }
 
 
+// The enable_if has to stay, there is an alternative in vector.hh!
 template <class MatrixType,
           size_t ROWS = MatrixAbstraction<MatrixType>::static_rows,
           size_t COLS = MatrixAbstraction<MatrixType>::static_cols,
           class FieldType = typename MatrixAbstraction<MatrixType>::S,
           class SparsityPatternType = FullPattern>
-typename std::enable_if_t<is_matrix<MatrixType>::value,
-                          typename MatrixAbstraction<MatrixType>::template MatrixTypeTemplate<ROWS, COLS, FieldType>>
+typename std::enable_if<
+    is_matrix<MatrixType>::value,
+    typename MatrixAbstraction<MatrixType>::template MatrixTypeTemplate<ROWS, COLS, FieldType>>::type
 create(const size_t rows,
        const size_t cols,
        const FieldType& val = 0,
@@ -343,8 +345,10 @@ create(const size_t rows,
 }
 
 
+// The enable_if has to stay, there is an alternative in vector.hh!
 template <class TargetMatrixType, class SourceMatrixType>
-typename std::enable_if_t<is_matrix<TargetMatrixType>::value && is_matrix<SourceMatrixType>::value, TargetMatrixType>
+typename std::enable_if<is_matrix<TargetMatrixType>::value && is_matrix<SourceMatrixType>::value,
+                        TargetMatrixType>::type
 zeros_like(const SourceMatrixType& source)
 {
   return create<TargetMatrixType>(
@@ -352,8 +356,9 @@ zeros_like(const SourceMatrixType& source)
 }
 
 
+// The enable_if has to stay, there is an alternative in vector.hh!
 template <class MatrixType>
-typename std::enable_if_t<is_matrix<MatrixType>::value, MatrixType> zeros_like(const MatrixType& source)
+typename std::enable_if<is_matrix<MatrixType>::value, MatrixType>::type zeros_like(const MatrixType& source)
 {
   return zeros_like<MatrixType, MatrixType>(source);
 }
diff --git a/dune/xt/common/parallel/communicator.hh b/dune/xt/common/parallel/communicator.hh
index e883412c51685429f0af05eed46e784776c5891e..3f1e2718a1bb58c3d9919d62190c5ed6f380e650 100644
--- a/dune/xt/common/parallel/communicator.hh
+++ b/dune/xt/common/parallel/communicator.hh
@@ -16,6 +16,9 @@
 
 #if !HAVE_MPI
 
+// defines No_Comm + CollectiveCommunication
+#  include <dune/common/parallel/communication.hh>
+
 namespace Dune {
 
 
diff --git a/dune/xt/common/parallel/helper.hh b/dune/xt/common/parallel/helper.hh
index 6123550df6bdf0c0680c4747336aa662649c802d..30ab7245cb295a0ac6d66dfef1476dd79f1f3643 100644
--- a/dune/xt/common/parallel/helper.hh
+++ b/dune/xt/common/parallel/helper.hh
@@ -22,6 +22,9 @@
 #  include <dune/common/parallel/collectivecommunication.hh>
 #endif
 
+// pinfo does not source deprecated.hh and therefore errors out on tidy/headercheck
+#include <dune/common/deprecated.hh>
+#include <dune/common/parallel/communicator.hh>
 #include <dune/istl/paamg/pinfo.hh>
 
 namespace Dune {
diff --git a/dune/xt/common/print.hh b/dune/xt/common/print.hh
index 740984b0cd7fd6b6e3be377c32082ef37be4fac2..ce349ea101eb52adc5cebf9558b549975e3d3196 100644
--- a/dune/xt/common/print.hh
+++ b/dune/xt/common/print.hh
@@ -40,6 +40,8 @@ namespace internal {
 
 
 /// \note Should be used to derive from (except for vectors and matrices), when specializing Printer.
+/// \todo Drop silenced warnings once all operator<< overloads in dune-xt and dune-gdt which are deprecated due to this
+///       DefaultPrinter are removed!
 /// \sa Printer
 /// \sa VectorPrinter
 template <class T, bool use_repr = false>
@@ -66,7 +68,10 @@ public:
   virtual void repr(std::ostream& out) const
   {
     if constexpr (is_printable<T>::value) {
+      // There are some of our operator<< overloads that are deprecated due to the introduction of this Printer.
+#include <dune/xt/common/disable_warnings.hh>
       out << value;
+#include <dune/xt/common/reenable_warnings.hh>
     } else {
       out << "missing specialization for Printer<T> with T=" << Typename<T>::value();
     }
@@ -157,17 +162,17 @@ public:
     const auto rows = M::rows(this->value);
     const auto cols = M::cols(this->value);
     if (rows * cols > 0) {
-      out << "{";
+      out << "[";
       const std::string delim =
           (std::use_facet<std::numpunct<char>>(std::cout.getloc()).decimal_point() == ',') ? ";" : ",";
       const std::string newline = "\n";
       for (auto&& ii : value_range(rows)) {
-        out << (ii == 0 ? "{" : " ") << "{" << print(M::get_entry(this->value, ii, 0), this->opts);
+        out << (ii == 0 ? "[" : " ") << "[" << print(M::get_entry(this->value, ii, 0), this->opts);
         for (auto&& jj : value_range(decltype(cols)(1), cols))
           out << delim << " " << print(M::get_entry(this->value, ii, jj), this->opts);
-        out << "}" << ((ii == rows - 1) ? "" : ",") << ((ii == rows - 1) ? "" : newline);
+        out << "]" << ((ii == rows - 1) ? "" : ",") << ((ii == rows - 1) ? "" : newline);
       }
-      out << "}";
+      out << "]";
     }
     out << ")";
   } // ... repr(...)
@@ -264,6 +269,41 @@ public:
 }; // class Printer
 
 
+template <bool use_repr, typename anything>
+class Printer<Configuration, use_repr, anything> : public internal::DefaultPrinter<Configuration, use_repr>
+{
+public:
+  Printer(const Configuration& val, const Configuration& param = {{"oneline", "false"}})
+    : internal::DefaultPrinter<Configuration, use_repr>(val, param)
+  {}
+
+  // let repr() default to operator<<, handled in internal::DefaultPrinter
+
+  void str(std::ostream& out) const override
+  {
+    if (!this->opts.get("oneline", false))
+      this->repr(out);
+    else {
+      const auto key_value_map = this->value.flatten();
+      const auto sz = key_value_map.size();
+      if (sz == 0)
+        out << "{}";
+      else {
+        out << "{";
+        size_t counter = 0;
+        for (const auto& key_value_pair : key_value_map) {
+          out << "\"" << key_value_pair.first << "\": \"" << key_value_pair.second << "\"";
+          if (counter < sz - 1)
+            out << ", ";
+          ++counter;
+        }
+        out << "}";
+      }
+    }
+  } // ... str(...)
+}; // class Printer
+
+
 /// \sa Printer
 template <class T, bool use_repr>
 std::ostream& operator<<(std::ostream& out, const Printer<T, use_repr>& printer)
diff --git a/dune/xt/common/timedlogging.cc b/dune/xt/common/timedlogging.cc
index e1245eb3daba394bff8db9ef0d34dc63c4afe65b..1bcf5a23417f49eb7ee39084a2cccafcc740e9c7 100644
--- a/dune/xt/common/timedlogging.cc
+++ b/dune/xt/common/timedlogging.cc
@@ -27,22 +27,23 @@ namespace XT {
 namespace Common {
 
 
-DefaultLogger::DefaultLogger(const std::string& prefix,
+DefaultLogger::DefaultLogger(const std::string& prfx,
                              bool start_disabled,
                              const std::array<std::string, 3>& colors,
                              bool global_timer)
-  : timer_()
-  , prefix_(prefix)
+  : prefix(prfx)
+  , copy_count(0)
+  , timer_()
   , colors_(colors)
   , global_timer_(global_timer)
   , info_(std::make_shared<TimedPrefixedLogStream>(global_timer_ ? SecondsSinceStartup() : timer_,
-                                                   build_prefix(prefix_.empty() ? "info" : prefix_, colors_[0]),
+                                                   build_prefix(prfx.empty() ? "info" : prfx, copy_count, colors_[0]),
                                                    std::cout))
   , debug_(std::make_shared<TimedPrefixedLogStream>(global_timer_ ? SecondsSinceStartup() : timer_,
-                                                    build_prefix(prefix_.empty() ? "debug" : prefix_, colors_[1]),
+                                                    build_prefix(prfx.empty() ? "debug" : prfx, copy_count, colors_[1]),
                                                     std::cout))
   , warn_(std::make_shared<TimedPrefixedLogStream>(global_timer_ ? SecondsSinceStartup() : timer_,
-                                                   build_prefix(prefix_.empty() ? "warn" : prefix_, colors_[2]),
+                                                   build_prefix(prfx.empty() ? "warn" : prfx, copy_count, colors_[2]),
                                                    std::cerr))
   , info_enabled(!start_disabled)
   , debug_enabled(!start_disabled)
@@ -50,19 +51,23 @@ DefaultLogger::DefaultLogger(const std::string& prefix,
 {}
 
 DefaultLogger::DefaultLogger(const DefaultLogger& other)
-  : timer_()
-  , prefix_(other.prefix_)
+  : prefix(other.prefix)
+  , copy_count(other.copy_count + 1)
+  , timer_()
   , colors_(other.colors_)
   , global_timer_(other.global_timer_)
-  , info_(std::make_shared<TimedPrefixedLogStream>(global_timer_ ? SecondsSinceStartup() : timer_,
-                                                   build_prefix(prefix_.empty() ? "info" : prefix_, colors_[0]),
-                                                   std::cout))
-  , debug_(std::make_shared<TimedPrefixedLogStream>(global_timer_ ? SecondsSinceStartup() : timer_,
-                                                    build_prefix(prefix_.empty() ? "debug" : prefix_, colors_[1]),
-                                                    std::cout))
-  , warn_(std::make_shared<TimedPrefixedLogStream>(global_timer_ ? SecondsSinceStartup() : timer_,
-                                                   build_prefix(prefix_.empty() ? "warn" : prefix_, colors_[2]),
-                                                   std::cerr))
+  , info_(
+        std::make_shared<TimedPrefixedLogStream>(global_timer_ ? SecondsSinceStartup() : timer_,
+                                                 build_prefix(prefix.empty() ? "info" : prefix, copy_count, colors_[0]),
+                                                 std::cout))
+  , debug_(std::make_shared<TimedPrefixedLogStream>(
+        global_timer_ ? SecondsSinceStartup() : timer_,
+        build_prefix(prefix.empty() ? "debug" : prefix, copy_count, colors_[1]),
+        std::cout))
+  , warn_(
+        std::make_shared<TimedPrefixedLogStream>(global_timer_ ? SecondsSinceStartup() : timer_,
+                                                 build_prefix(prefix.empty() ? "warn" : prefix, copy_count, colors_[2]),
+                                                 std::cerr))
   , info_enabled(other.info_enabled)
   , debug_enabled(other.debug_enabled)
   , warn_enabled(other.warn_enabled)
@@ -71,19 +76,23 @@ DefaultLogger::DefaultLogger(const DefaultLogger& other)
 DefaultLogger& DefaultLogger::operator=(const DefaultLogger& other)
 {
   if (&other != this) {
+    prefix = other.prefix;
+    copy_count = other.copy_count;
     timer_ = other.timer_;
-    prefix_ = other.prefix_;
     colors_ = other.colors_;
     global_timer_ = other.global_timer_;
-    info_ = std::make_shared<TimedPrefixedLogStream>(global_timer_ ? SecondsSinceStartup() : timer_,
-                                                     build_prefix(prefix_.empty() ? "info" : prefix_, colors_[0]),
-                                                     std::cout);
-    debug_ = std::make_shared<TimedPrefixedLogStream>(global_timer_ ? SecondsSinceStartup() : timer_,
-                                                      build_prefix(prefix_.empty() ? "debug" : prefix_, colors_[1]),
-                                                      std::cout);
-    warn_ = std::make_shared<TimedPrefixedLogStream>(global_timer_ ? SecondsSinceStartup() : timer_,
-                                                     build_prefix(prefix_.empty() ? "warn" : prefix_, colors_[2]),
-                                                     std::cerr);
+    info_ =
+        std::make_shared<TimedPrefixedLogStream>(global_timer_ ? SecondsSinceStartup() : timer_,
+                                                 build_prefix(prefix.empty() ? "info" : prefix, copy_count, colors_[0]),
+                                                 std::cout);
+    debug_ = std::make_shared<TimedPrefixedLogStream>(
+        global_timer_ ? SecondsSinceStartup() : timer_,
+        build_prefix(prefix.empty() ? "debug" : prefix, copy_count, colors_[1]),
+        std::cout);
+    warn_ =
+        std::make_shared<TimedPrefixedLogStream>(global_timer_ ? SecondsSinceStartup() : timer_,
+                                                 build_prefix(prefix.empty() ? "warn" : prefix, copy_count, colors_[2]),
+                                                 std::cerr);
     info_enabled = other.info_enabled;
     debug_enabled = other.debug_enabled;
     warn_enabled = other.warn_enabled;
@@ -91,19 +100,20 @@ DefaultLogger& DefaultLogger::operator=(const DefaultLogger& other)
   return *this;
 } // ... operator=(...)
 
-void DefaultLogger::enable(const std::string& prefix)
+void DefaultLogger::enable(const std::string& prfx)
 {
   info_enabled = true;
   debug_enabled = true;
   warn_enabled = true;
-  if (!prefix.empty()) {
-    prefix_ = prefix;
+  if (!prfx.empty()) {
+    prefix = prfx;
+    copy_count = 0;
     info_ = std::make_shared<TimedPrefixedLogStream>(
-        global_timer_ ? SecondsSinceStartup() : timer_, build_prefix(prefix_, colors_[0]), std::cout);
+        global_timer_ ? SecondsSinceStartup() : timer_, build_prefix(prfx, copy_count, colors_[0]), std::cout);
     debug_ = std::make_shared<TimedPrefixedLogStream>(
-        global_timer_ ? SecondsSinceStartup() : timer_, build_prefix(prefix_, colors_[1]), std::cout);
+        global_timer_ ? SecondsSinceStartup() : timer_, build_prefix(prfx, copy_count, colors_[1]), std::cout);
     warn_ = std::make_shared<TimedPrefixedLogStream>(
-        global_timer_ ? SecondsSinceStartup() : timer_, build_prefix(prefix_, colors_[2]), std::cerr);
+        global_timer_ ? SecondsSinceStartup() : timer_, build_prefix(prfx, copy_count, colors_[2]), std::cerr);
   }
 } // ... enable(...)
 
@@ -206,9 +216,9 @@ void TimedLogging::create(const ssize_t max_info_level,
                           const ssize_t max_debug_level,
                           const bool enable_warnings,
                           const bool enable_colors,
-                          const std::string info_color,
-                          const std::string debug_color,
-                          const std::string warning_color)
+                          const std::string& info_color,
+                          const std::string& debug_color,
+                          const std::string& warning_color)
 {
   DUNE_UNUSED std::lock_guard<std::mutex> guard(mutex_);
   DUNE_THROW_IF(created_, Exceptions::logger_error, "Do not call create() more than once!");
@@ -224,7 +234,7 @@ void TimedLogging::create(const ssize_t max_info_level,
   update_colors();
 } // ... create(...)
 
-TimedLogManager TimedLogging::get(const std::string id)
+TimedLogManager TimedLogging::get(const std::string& id)
 {
   DUNE_UNUSED std::lock_guard<std::mutex> guard(mutex_);
   ++current_level_;
diff --git a/dune/xt/common/timedlogging.hh b/dune/xt/common/timedlogging.hh
index 7804b9efa51cd818b2bf1b9955ce99b222e82ab2..54ebcc75945e0143407e23123073549869d63cdd 100644
--- a/dune/xt/common/timedlogging.hh
+++ b/dune/xt/common/timedlogging.hh
@@ -56,17 +56,26 @@ DUNE_EXPORT inline const Timer& SecondsSinceStartup()
  */
 class DefaultLogger
 {
-  static std::string build_prefix(const std::string& prefix, const std::string& clr)
+  static std::string build_prefix(const std::string& prfx, const size_t cnt, const std::string& clr)
   {
     const std::string actual_color = terminal_supports_color() ? color(clr) : "";
+    std::string copy_count_str = "";
+    if (cnt > 0)
+      copy_count_str += "[" + to_string(cnt) + "]";
+    std::string ret;
     if (actual_color.empty())
-      return prefix + ": ";
+      ret = prfx + copy_count_str + ": ";
     else
-      return actual_color + StreamModifiers::bold + prefix + ": " + StreamModifiers::normal;
-  }
+      ret = actual_color + StreamModifiers::bold + prfx + StreamModifiers::normal + copy_count_str
+            + StreamModifiers::bold + ": " + StreamModifiers::normal;
+    return ret;
+  } // ... build_prefix(...)
 
 public:
-  DefaultLogger(const std::string& prefix = "",
+  std::string prefix;
+  size_t copy_count;
+
+  DefaultLogger(const std::string& prfx = "",
                 bool start_disabled = false,
                 const std::array<std::string, 3>& colors = {"blue", "darkgray", "red"},
                 bool global_timer = true);
@@ -77,7 +86,6 @@ public:
 
 private:
   Timer timer_;
-  std::string prefix_;
   std::array<std::string, 3> colors_;
   bool global_timer_;
   std::shared_ptr<std::ostream> info_;
@@ -89,7 +97,14 @@ public:
   bool debug_enabled;
   bool warn_enabled;
 
-  void enable(const std::string& prefix = "");
+  void enable(const std::string& prfx = "");
+
+  void enable_like(const DefaultLogger& other)
+  {
+    this->info_enabled = this->info_enabled || other.info_enabled;
+    this->debug_enabled = this->debug_enabled || other.debug_enabled;
+    this->warn_enabled = this->warn_enabled || other.warn_enabled;
+  }
 
   void disable();
 
@@ -102,13 +117,21 @@ public:
 
 
 #ifdef LOG_
-#  error Macro LOG_ already defined, open an issue at https://github.com/dune-community/dune-xt/issues/new !
+#  error Macro LOG_ already defined, open an issue at https://zivgitlab.uni-muenster.de/ag-ohlberger/dune-community/dune-xt/-/issues !
 #else
 #  define LOG_(type)                                                                                                   \
     if (this->logger.type##_enabled)                                                                                   \
     this->logger.type()
 #endif
 
+#ifdef LOG__
+#  error Macro LOG_ already defined, open an issue at https://zivgitlab.uni-muenster.de/ag-ohlberger/dune-community/dune-xt/-/issues !
+#else
+#  define LOG__(base, type)                                                                                            \
+    if (base ::logger.type##_enabled)                                                                                  \
+    base ::logger.type()
+#endif
+
 
 template <typename T = void>
 class WithLogger
@@ -118,47 +141,58 @@ class WithLogger
 public:
   mutable DefaultLogger logger;
 
-protected:
-  const std::string logging_id;
+  [[deprecated("Use this.logger.prefix instead (12.08.2020)!")]] static const std::string logging_id;
 
-public:
-  WithLogger(const std::string& prefix, const std::string& id, const bool start_enabled = false)
-    : logger(prefix, start_enabled)
-    , logging_id(id)
+  [[deprecated("Use WithLogger(id, start_enabled) instead (12.08.2020)!")]] WithLogger(const std::string& /*prefix*/,
+                                                                                       const std::string& id,
+                                                                                       const bool start_enabled = false)
+    : logger(id, start_enabled)
   {
-    LOG_(debug) << logging_id << "(this=" << this << ")" << std::endl;
+    LOG_(debug) << "WithLogger(this=" << this << ")" << std::endl;
+  }
+
+  WithLogger(const std::string& id, const bool start_enabled = false)
+    : logger(id, start_enabled)
+  {
+    LOG_(debug) << "WithLogger(this=" << this << ")" << std::endl;
   }
 
   WithLogger(const ThisType& other)
     : logger(other.logger)
-    , logging_id(other.logging_id)
   {
-    LOG_(debug) << logging_id << "(this=" << this << ", other=" << &other << ")" << std::endl;
+    LOG_(debug) << "WithLogger(this=" << this << ", other=" << &other << ")" << std::endl;
   }
 
   WithLogger(ThisType&& source)
     : logger(std::move(source.logger))
-    , logging_id(std::move(source.logging_id))
   {
-    LOG_(debug) << logging_id << "(this=" << this << ", source=" << &source << ")" << std::endl;
+    LOG_(debug) << "WithLogger(this=" << this << ", source=" << &source << ")" << std::endl;
   }
 
   ~WithLogger()
   {
-    LOG_(debug) << "~" << logging_id << "(this=" << this << ")" << std::endl;
+    LOG_(debug) << "~WithLogger(this=" << this << ")" << std::endl;
   }
 
   ThisType& operator=(const ThisType& other)
   {
-    LOG_(debug) << logging_id << "operator=(this=" << this << ", other=" << &other << ")" << std::endl;
+    LOG_(debug) << "WithLogger.operator=(this=" << this << ", other=" << &other << ")" << std::endl;
   }
 
   ThisType& operator=(ThisType&& source)
   {
-    LOG_(debug) << logging_id << "operator=(this=" << this << ", source=" << &source << ")" << std::endl;
+    LOG_(debug) << "WithLogger.operator=(this=" << this << ", source=" << &source << ")" << std::endl;
+  }
+
+  [[deprecated("Use this.logger.enable_like(other.logger) instead (12.08.2020)!")]] void
+  enable_logging_like(const ThisType& other)
+  {
+    this->logger.enable_like(other.logger);
   }
 }; // class WithLogger
 
+template <class T>
+const std::string WithLogger<T>::logging_id = "Use this.logger.prefix instead";
 
 /**
  * \brief A logging manager that provides info, debug and warning streams
@@ -228,7 +262,7 @@ public:
   /**
    * \brief sets the state
    *
-   *        This methos is mainly intended to be used on the global TimedLogger() instance. Before calling this method
+   *        This method is mainly intended to be used on the global TimedLogger() instance. Before calling this method
    *        the state is set according to the defaults default_max_info_level, default_max_debug_level and
    *        default_enable_warnings.
    * \note  Calling this method more than once will throw an Exceptions::you_are_using_this_wrong, following the idea of
@@ -238,11 +272,11 @@ public:
               const ssize_t max_debug_level = default_max_debug_level,
               const bool enable_warnings = default_enable_warnings,
               const bool enable_colors = default_enable_colors,
-              const std::string info_color = default_info_color(),
-              const std::string debug_color = default_debug_color(),
-              const std::string warning_color = default_warning_color());
+              const std::string& info_color = default_info_color(),
+              const std::string& debug_color = default_debug_color(),
+              const std::string& warning_color = default_warning_color());
 
-  TimedLogManager get(const std::string id);
+  TimedLogManager get(const std::string& id);
 
 private:
   void update_colors();
@@ -388,89 +422,81 @@ int main() {
  * The same holds for the move ctor as well as move and assignment operators.
  */
 template <typename T = void>
-class EnableDebugLoggingForCtors
+class NoOpEnableDebugLoggingForCtors
 {
-  typedef EnableDebugLoggingForCtors<T> ThisType;
+  using ThisType = NoOpEnableDebugLoggingForCtors<T>;
 
 public:
-  EnableDebugLoggingForCtors(const std::string&
-#if DUNE_XT_COMMON_TIMEDLOGGING_ENABLE_DEBUG
-                                 prefix
-#endif
-                             ,
-                             const std::string&
-#if DUNE_XT_COMMON_TIMEDLOGGING_ENABLE_DEBUG
-                                 class_id
-#endif
-                             )
-#if DUNE_XT_COMMON_TIMEDLOGGING_ENABLE_DEBUG
+  NoOpEnableDebugLoggingForCtors(const std::string&, const std::string&) {}
+
+  NoOpEnableDebugLoggingForCtors(const ThisType& other) = default;
+
+  NoOpEnableDebugLoggingForCtors(ThisType&& source) = default;
+
+  ~NoOpEnableDebugLoggingForCtors() = default;
+
+  ThisType& operator=(const ThisType& other) = default;
+
+  ThisType& operator=(ThisType&& source) = default;
+
+}; // class NoOpEnableDebugLoggingForCtors
+
+template <typename T = void>
+class ActiveEnableDebugLoggingForCtors
+{
+  using ThisType = ActiveEnableDebugLoggingForCtors<T>;
+
+public:
+  ActiveEnableDebugLoggingForCtors(const std::string& prefix, const std::string& class_id)
     : logger_(TimedLogger().get(prefix))
     , class_id_(class_id)
   {
     logger_.debug() << class_id_ << "(this=" << this << ")" << std::endl;
   }
-#else
-  {}
-#endif
 
-  EnableDebugLoggingForCtors(const ThisType& other)
-#if DUNE_XT_COMMON_TIMEDLOGGING_ENABLE_DEBUG
+  ActiveEnableDebugLoggingForCtors(const ThisType& other)
     : logger_(other.logger_)
     , class_id_(other.class_id_)
   {
     logger_.debug() << class_id_ << "(this=" << this << ", other=" << &other << ")" << std::endl;
   }
-#else
-      = default;
-#endif
 
-  EnableDebugLoggingForCtors(ThisType&& source)
-#if DUNE_XT_COMMON_TIMEDLOGGING_ENABLE_DEBUG
+  ActiveEnableDebugLoggingForCtors(ThisType&& source)
     : logger_(std::move(source.logger_))
     , class_id_(std::move(source.class_id_))
   {
     logger_.debug() << class_id_ << "(this=" << this << ", source=" << &source << ")" << std::endl;
   }
-#else
-      = default;
-#endif
 
-  ~EnableDebugLoggingForCtors()
-#if DUNE_XT_COMMON_TIMEDLOGGING_ENABLE_DEBUG
+  ~ActiveEnableDebugLoggingForCtors()
   {
     logger_.debug() << "~" << class_id_ << "(this=" << this << ")" << std::endl;
   }
-#else
-      = default;
-#endif
 
   ThisType& operator=(const ThisType& other)
-#if DUNE_XT_COMMON_TIMEDLOGGING_ENABLE_DEBUG
   {
     logger_.debug() << class_id_ << "operator=(this=" << this << ", other=" << &other << ")" << std::endl;
   }
-#else
-      = default;
-#endif
 
   ThisType& operator=(ThisType&& source)
-#if DUNE_XT_COMMON_TIMEDLOGGING_ENABLE_DEBUG
   {
     logger_.debug() << class_id_ << "operator=(this=" << this << ", source=" << &source << ")" << std::endl;
   }
-#else
-      = default;
-#endif
 
-#if DUNE_XT_COMMON_TIMEDLOGGING_ENABLE_DEBUG
 protected:
   TimedLogManager logger_;
 
 private:
   const std::string class_id_;
-#endif
-}; // class EnableDebugLoggingForCtors
+}; // class ActiveEnableDebugLoggingForCtors
 
+#if DUNE_XT_COMMON_TIMEDLOGGING_ENABLE_DEBUG
+template <class T>
+using EnableDebugLoggingForCtors = ActiveEnableDebugLoggingForCtors<T>;
+#else
+template <class T>
+using EnableDebugLoggingForCtors = NoOpEnableDebugLoggingForCtors<T>;
+#endif
 
 } // namespace Common
 } // namespace XT
diff --git a/dune/xt/functions/ESV2007.hh b/dune/xt/functions/ESV2007.hh
index 7e78047b7964068bd335d79707110c2ed10a7bac..a03839fb3db79acf26af31bebb96f5ff1c302afd 100644
--- a/dune/xt/functions/ESV2007.hh
+++ b/dune/xt/functions/ESV2007.hh
@@ -26,6 +26,7 @@
 #include <dune/xt/common/configuration.hh>
 #include <dune/xt/la/eigen-solver.hh>
 
+#include <dune/xt/functions/grid-function.hh>
 #include <dune/xt/functions/interfaces/grid-function.hh>
 #include <dune/xt/functions/interfaces/function.hh>
 
@@ -74,14 +75,26 @@ public:
   } // ... defaults(...)
 
   Testcase1Force(const size_t ord = defaults().template get<int>("integration_order"),
-                 const std::string nm = static_id())
+                 const std::string nm = "ESV2007Testcase1Force")
     : order_(static_cast<int>(ord))
     , name_(nm)
   {}
 
-  Testcase1Force(const ThisType& /*other*/) = default;
+  Testcase1Force(const ThisType&) = default;
 
-  ThisType& operator=(const ThisType& /*other*/) = delete;
+  Testcase1Force(ThisType&&) = default;
+
+private:
+  ThisType* copy_as_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_function_impl());
+  }
 
   std::string name() const override final
   {
@@ -160,14 +173,26 @@ public:
   } // ... defaults(...)
 
   Testcase1ExactSolution(const size_t ord = defaults().template get<int>("integration_order"),
-                         const std::string nm = static_id())
+                         const std::string nm = "ESV2007Testcase1ExactSolution")
     : order_(static_cast<int>(ord))
     , name_(nm)
   {}
 
-  Testcase1ExactSolution(const ThisType& /*other*/) = default;
+  Testcase1ExactSolution(const ThisType&) = default;
 
-  ThisType& operator=(const ThisType& /*other*/) = delete;
+  Testcase1ExactSolution(ThisType&&) = default;
+
+private:
+  ThisType* copy_as_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_function_impl());
+  }
 
   std::string name() const override final
   {
@@ -223,7 +248,7 @@ private:
   class LocalCutoffFunction : public ElementFunctionInterface<E, 1, 1, R>
   {
     using BaseType = ElementFunctionInterface<E, 1, 1, R>;
-
+    using DiffusionType = GridFunctionInterface<E, d, d, R>;
 
   public:
     using typename BaseType::DerivativeRangeReturnType;
@@ -234,10 +259,11 @@ private:
     using typename BaseType::RangeReturnType;
 
     LocalCutoffFunction(const DiffusionType& diffusion, const RangeFieldType poincare_constant)
-      : BaseType()
-      , value_(0)
-      , local_diffusion_(diffusion.local_function())
+      : BaseType(diffusion.parameter_type())
+      , diffusion_(diffusion.copy_as_grid_function())
+      , local_diffusion_(diffusion_->local_function())
       , poincare_constant_(poincare_constant)
+      , value_(0)
     {}
 
   protected:
@@ -313,9 +339,10 @@ private:
       }
     };
 
-    RangeFieldType value_;
+    const std::unique_ptr<DiffusionType> diffusion_;
     std::unique_ptr<typename DiffusionType::LocalFunctionType> local_diffusion_;
     const RangeFieldType poincare_constant_;
+    RangeFieldType value_;
   }; // class LocalCutoffFunction
 
 public:
@@ -328,18 +355,36 @@ public:
     return BaseType::static_id() + ".ESV2007.cutoff";
   }
 
-  CutoffFunction(const DiffusionType& diffusion,
+  CutoffFunction(GridFunction<E, d, d, R> diffusion,
                  const RangeFieldType poincare_constant = 1.0 / (M_PI * M_PI),
-                 const std::string nm = static_id())
-    : diffusion_(diffusion)
+                 const std::string nm = "ESV2007CutoffFunction")
+    : BaseType(diffusion.parameter_type())
+    , diffusion_(diffusion.copy_as_grid_function())
     , poincare_constant_(poincare_constant)
     , name_(nm)
   {}
 
-  CutoffFunction(const ThisType& other) = default;
+  CutoffFunction(const ThisType& other)
+    : BaseType(other)
+    , diffusion_(other.diffusion_->copy_as_grid_function())
+    , poincare_constant_(other.poincare_constant_)
+    , name_(other.name_)
+  {}
+
+  CutoffFunction(ThisType&&) = default;
 
-  ThisType& operator=(const ThisType& other) = delete;
 
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
   std::string name() const override final
   {
     return name_;
@@ -347,11 +392,11 @@ public:
 
   std::unique_ptr<LocalFunctionType> local_function() const override final
   {
-    return std::make_unique<LocalCutoffFunction>(diffusion_, poincare_constant_);
+    return std::make_unique<LocalCutoffFunction>(*diffusion_, poincare_constant_);
   }
 
 private:
-  const DiffusionType& diffusion_;
+  std::unique_ptr<GridFunctionInterface<E, d, d, R>> diffusion_;
   const RangeFieldType poincare_constant_;
   std::string name_;
 }; // class Cutoff
diff --git a/dune/xt/functions/base/combined-element-functions.hh b/dune/xt/functions/base/combined-element-functions.hh
index 96639515083029ff0acf8a6c4e0899f0c8cd1202..5e99c103a87eb768da6a9673127d3678501969ef 100644
--- a/dune/xt/functions/base/combined-element-functions.hh
+++ b/dune/xt/functions/base/combined-element-functions.hh
@@ -12,371 +12,36 @@
 #ifndef DUNE_XT_FUNCTIONS_BASE_COMBINED_ELEMENT_FUNCTIONS_HH
 #define DUNE_XT_FUNCTIONS_BASE_COMBINED_ELEMENT_FUNCTIONS_HH
 
-#include <dune/xt/common/memory.hh>
+#include <dune/xt/common/type_traits.hh>
 
+#include <dune/xt/functions/exceptions.hh>
 #include <dune/xt/functions/interfaces/element-functions.hh>
 #include <dune/xt/functions/type_traits.hh>
 
+#include "combined.hh"
+
 namespace Dune {
 namespace XT {
 namespace Functions {
-namespace internal {
 
 
-/**
- * \brief Helper class defining types of combined functions, if available.
- *
- * \note Most likely you do not want to use this class directly, but CombinedConstElementFunction or
- *       CombinedElementFunction.
- */
-template <class LeftType, class RightType, CombinationType comb>
-class CombinedElementFunctionHelper
+template <class LeftType, class RightType, typename comb>
+class CombinedConstElementFunction
+  : public ElementFunctionInterface<typename LeftType::E,
+                                    internal::CombinedHelper<LeftType, RightType, comb>::r,
+                                    internal::CombinedHelper<LeftType, RightType, comb>::rC,
+                                    typename internal::CombinedHelper<LeftType, RightType, comb>::R>
 {
   static_assert(is_element_function<LeftType>::value, "");
   static_assert(is_element_function<RightType>::value, "");
+  static_assert(std::is_same<typename LeftType::E, typename RightType::E>::value, "");
 
-public:
-  using E = typename LeftType::E;
-  using R = typename LeftType::R;
-
-private:
-  using D = typename LeftType::D;
-  static constexpr size_t d = LeftType::d;
-
-private:
-  static_assert(std::is_same<typename RightType::E, E>::value, "");
-  static_assert(std::is_same<typename RightType::D, D>::value, "");
-  static_assert(RightType::d == d, "");
-  static_assert(std::is_same<typename RightType::R, R>::value, "");
-
-  template <class L, class R>
-  class dim_switch
-  {
-    //! last tpl arg cannot be dropped due to gcc bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282
-    template <CombinationType cc = comb,
-              size_t rL = L::r,
-              size_t rCL = L::rC,
-              size_t rR = R::r,
-              size_t rCR = R::rC,
-              bool anything = true>
-    class Dimension
-    {
-    public:
-      static constexpr bool available = false;
-    };
-
-    template <size_t r_, size_t rC_, bool anything>
-    class Dimension<CombinationType::difference, r_, rC_, r_, rC_, anything>
-    {
-    public:
-      static constexpr bool available = true;
-      static constexpr size_t r = r_;
-      static constexpr size_t rC = rC_;
-    };
-
-    template <size_t r_, size_t rC_, bool anything>
-    class Dimension<CombinationType::sum, r_, rC_, r_, rC_, anything>
-    {
-    public:
-      static constexpr bool available = true;
-      static constexpr size_t r = r_;
-      static constexpr size_t rC = rC_;
-    };
-
-    template <bool anything>
-    class Dimension<CombinationType::product, 1, 1, 1, 1, anything>
-    {
-    public:
-      static constexpr bool available = true;
-      static constexpr size_t r = 1;
-      static constexpr size_t rC = 1;
-    };
-
-    template <size_t r_, size_t rC_, bool anything>
-    class Dimension<CombinationType::product, r_, rC_, 1, 1, anything>
-    {
-    public:
-      static constexpr bool available = true;
-      static constexpr size_t r = r_;
-      static constexpr size_t rC = rC_;
-    };
-
-    template <size_t r_, size_t rC_, bool anything>
-    class Dimension<CombinationType::product, 1, 1, r_, rC_, anything>
-    {
-    public:
-      static constexpr bool available = true;
-      static constexpr size_t r = r_;
-      static constexpr size_t rC = rC_;
-    };
-
-    template <size_t rL, size_t c, size_t rR, bool anything>
-    class Dimension<CombinationType::product, rL, c, c, rR, anything>
-    {
-    public:
-      static constexpr bool available = true;
-      static constexpr size_t r = rL;
-      static constexpr size_t rC = rR;
-    };
-
-  public:
-    static constexpr bool available = Dimension<>::available;
-    static constexpr size_t r = Dimension<>::r;
-    static constexpr size_t rC = Dimension<>::rC;
-  }; // class dim_switch
-
-public:
-  static constexpr size_t r = dim_switch<LeftType, RightType>::r;
-  static constexpr size_t rC = dim_switch<LeftType, RightType>::rC;
-  static constexpr bool available = dim_switch<LeftType, RightType>::available;
-
-private:
-  using DomainType = typename ElementFunctionInterface<E, r, rC, R>::DomainType;
-  using RangeReturnType = typename RangeTypeSelector<R, r, rC>::return_type;
-  using DerivativeRangeReturnType = typename DerivativeRangeTypeSelector<d, R, r, rC>::return_type;
-
-  template <CombinationType cc = comb,
-            size_t rL = LeftType::r,
-            size_t rCL = LeftType::rC,
-            size_t rR = RightType::r,
-            size_t rCR = RightType::rC,
-            bool anything = true>
-  class CombinationTypeSwitch
-  {
-    static_assert(!anything, "Nothing available for these CombinationType!");
-  };
-
-  template <size_t r_, size_t rC_, bool anything>
-  class CombinationTypeSwitch<CombinationType::difference, r_, rC_, r_, rC_, anything>
-  {
-  public:
-    static int order(const int left_order, const int right_order)
-    {
-      return std::max(left_order, right_order);
-    }
-
-    static RangeReturnType evaluate(const LeftType& left_local,
-                                    const RightType& right_local,
-                                    const DomainType& point_in_reference_element,
-                                    const Common::Parameter& param)
-    {
-      return left_local.evaluate(point_in_reference_element, param)
-             - right_local.evaluate(point_in_reference_element, param);
-    }
-
-    static DerivativeRangeReturnType jacobian(const LeftType& left_local,
-                                              const RightType& right_local,
-                                              const DomainType& point_in_reference_element,
-                                              const Common::Parameter& param)
-    {
-      return left_local.jacobian(point_in_reference_element, param)
-             - right_local.jacobian(point_in_reference_element, param);
-    }
-  }; // class CombinationTypeSwitch< ..., difference >
-
-  template <size_t r_, size_t rC_, bool anything>
-  class CombinationTypeSwitch<CombinationType::sum, r_, rC_, r_, rC_, anything>
-  {
-  public:
-    static int order(const int left_order, const int right_order)
-    {
-      return std::max(left_order, right_order);
-    }
-
-    static RangeReturnType evaluate(const LeftType& left_local,
-                                    const RightType& right_local,
-                                    const DomainType& point_in_reference_element,
-                                    const Common::Parameter& param)
-    {
-      return left_local.evaluate(point_in_reference_element, param)
-             + right_local.evaluate(point_in_reference_element, param);
-    }
-
-    static DerivativeRangeReturnType jacobian(const LeftType& left_local,
-                                              const RightType& right_local,
-                                              const DomainType& point_in_reference_element,
-                                              const Common::Parameter& param)
-    {
-      return left_local.jacobian(point_in_reference_element, param)
-             + right_local.jacobian(point_in_reference_element, param);
-    }
-  }; // class CombinationTypeSwitch< ..., sum >
-
-  template <bool anything>
-  class CombinationTypeSwitch<CombinationType::product, 1, 1, 1, 1, anything>
-  {
-  public:
-    static int order(const int left_order, const int right_order)
-    {
-      return left_order + right_order;
-    }
-
-    static RangeReturnType evaluate(const LeftType& left_local,
-                                    const RightType& right_local,
-                                    const DomainType& point_in_reference_element,
-                                    const Common::Parameter& param)
-    {
-      return left_local.evaluate(point_in_reference_element, param)
-             * right_local.evaluate(point_in_reference_element, param);
-    }
-
-    static DerivativeRangeReturnType jacobian(const LeftType& /*left_local*/,
-                                              const RightType& /*right_local*/,
-                                              const DomainType& /*point_in_reference_element*/,
-                                              const Common::Parameter& /*param*/)
-    {
-      DUNE_THROW(NotImplemented, "If you need this, implement it!");
-      return DerivativeRangeReturnType();
-    }
-  }; // class CombinationTypeSwitch< ..., product >
-
-  template <size_t r_, size_t rC_, bool anything>
-  class CombinationTypeSwitch<CombinationType::product, r_, rC_, 1, 1, anything>
-  {
-  public:
-    static int order(const int left_order, const int right_order)
-    {
-      return left_order + right_order;
-    }
-
-    static RangeReturnType evaluate(const LeftType& left_local,
-                                    const RightType& right_local,
-                                    const DomainType& point_in_reference_element,
-                                    const Common::Parameter& param)
-    {
-      auto result = left_local.evaluate(point_in_reference_element, param);
-      result *= right_local.evaluate(point_in_reference_element, param);
-      return result;
-    }
-
-    static DerivativeRangeReturnType jacobian(const LeftType& /*left_local*/,
-                                              const RightType& /*right_local*/,
-                                              const DomainType& /*point_in_reference_element*/,
-                                              const Common::Parameter& /*param*/)
-    {
-      DUNE_THROW(NotImplemented, "If you need this, implement it!");
-      return DerivativeRangeReturnType();
-    }
-  }; // class CombinationTypeSwitch< ..., product >
-
-  template <size_t r_, size_t rC_, bool anything>
-  class CombinationTypeSwitch<CombinationType::product, 1, 1, r_, rC_, anything>
-  {
-  public:
-    static int order(const int left_order, const int right_order)
-    {
-      return left_order + right_order;
-    }
-
-    static RangeReturnType evaluate(const LeftType& left_local,
-                                    const RightType& right_local,
-                                    const DomainType& point_in_reference_element,
-                                    const Common::Parameter& param)
-    {
-      auto result = right_local.evaluate(point_in_reference_element, param);
-      result *= left_local.evaluate(point_in_reference_element, param);
-      return result;
-    }
-
-    static DerivativeRangeReturnType jacobian(const LeftType& /*left_local*/,
-                                              const RightType& /*right_local*/,
-                                              const DomainType& /*point_in_reference_element*/,
-                                              const Common::Parameter& /*param*/)
-    {
-      DUNE_THROW(NotImplemented, "If you need this, implement it!");
-      return DerivativeRangeReturnType();
-    }
-  }; // class CombinationTypeSwitch< ..., product >
-
-  template <size_t rL, size_t c_, size_t rR_, bool anything>
-  class CombinationTypeSwitch<CombinationType::product, rL, c_, c_, rR_, anything>
-  {
-  public:
-    static int order(const int left_order, const int right_order)
-    {
-      return left_order + right_order;
-    }
-
-    static RangeReturnType evaluate(const LeftType& left_local,
-                                    const RightType& right_local,
-                                    const DomainType& point_in_reference_element,
-                                    const Common::Parameter& param)
-    {
-      return left_local.evaluate(point_in_reference_element, param)
-             * right_local.evaluate(point_in_reference_element, param);
-    }
-
-    static DerivativeRangeReturnType jacobian(const LeftType& /*left*/,
-                                              const RightType& /*right*/,
-                                              const DomainType& /*xx*/,
-                                              const Common::Parameter& /*param*/)
-    {
-      DUNE_THROW(NotImplemented, "If you need this, implement it!");
-      return DerivativeRangeReturnType();
-    }
-  }; // class CombinationTypeSwitch< ..., product >
-
-public:
-  static int order(const LeftType& left, const RightType& right, const Common::Parameter& param)
-  {
-    return CombinationTypeSwitch<>::order(left.order(param), right.order(param));
-  }
-
-  static RangeReturnType
-  evaluate(const LeftType& left, const RightType& right, const DomainType& xx, const Common::Parameter& param)
-  {
-    return CombinationTypeSwitch<>::evaluate(left, right, xx, param);
-  }
-
-  static DerivativeRangeReturnType
-  jacobian(const LeftType& left, const RightType& right, const DomainType& xx, const Common::Parameter& param)
-  {
-    return CombinationTypeSwitch<>::jacobian(left, right, xx, param);
-  }
-}; // class CombinedElementFunctionHelper
-
-
-template <class LeftType, class RightType>
-struct DualStorageProvider
-{
-  XT::Common::StorageProvider<LeftType> left;
-  XT::Common::StorageProvider<RightType> right;
-
-  DualStorageProvider(LeftType& lft, RightType& rght)
-    : left(lft)
-    , right(rght)
-  {}
-
-  DualStorageProvider(std::shared_ptr<LeftType> lft, std::shared_ptr<RightType> rght)
-    : left(lft)
-    , right(rght)
-  {}
-
-  DualStorageProvider(std::unique_ptr<LeftType>&& lft, std::unique_ptr<RightType>&& rght)
-    : left(std::move(lft))
-    , right(std::move(rght))
-  {}
-}; // struct DualStorageProvider
-
-
-} // namespace internal
-
-
-template <class LeftType, class RightType, CombinationType combination>
-class CombinedConstElementFunction
-  : public ElementFunctionInterface<
-        typename internal::CombinedElementFunctionHelper<LeftType, RightType, combination>::E,
-        internal::CombinedElementFunctionHelper<LeftType, RightType, combination>::r,
-        internal::CombinedElementFunctionHelper<LeftType, RightType, combination>::rC,
-        typename internal::CombinedElementFunctionHelper<LeftType, RightType, combination>::R>
-{
-  using BaseType =
-      ElementFunctionInterface<typename internal::CombinedElementFunctionHelper<LeftType, RightType, combination>::E,
-                               internal::CombinedElementFunctionHelper<LeftType, RightType, combination>::r,
-                               internal::CombinedElementFunctionHelper<LeftType, RightType, combination>::rC,
-                               typename internal::CombinedElementFunctionHelper<LeftType, RightType, combination>::R>;
-
-  using Select = internal::CombinedElementFunctionHelper<LeftType, RightType, combination>;
+  using ThisType = CombinedConstElementFunction;
+  using BaseType = ElementFunctionInterface<typename LeftType::E,
+                                            internal::CombinedHelper<LeftType, RightType, comb>::r,
+                                            internal::CombinedHelper<LeftType, RightType, comb>::rC,
+                                            typename internal::CombinedHelper<LeftType, RightType, comb>::R>;
+  using Helper = internal::CombinedHelper<LeftType, RightType, comb>;
 
 public:
   using typename BaseType::DerivativeRangeReturnType;
@@ -387,7 +52,7 @@ public:
   using typename BaseType::RangeType;
 
   CombinedConstElementFunction(const LeftType& left, const RightType& right)
-    : BaseType()
+    : BaseType(left.parameter_type() + right.parameter_type())
     , left_(left)
     , right_(right)
     , bind_is_temporarily_ok_(false)
@@ -395,8 +60,17 @@ public:
     bind_if_arguments_were_bound();
   }
 
+  CombinedConstElementFunction(const LeftType& left, RightType&& right)
+    : BaseType(left.parameter_type() + right.parameter_type())
+    , left_(left)
+    , right_(std::move(right))
+    , bind_is_temporarily_ok_(false)
+  {
+    bind_if_arguments_were_bound();
+  }
+
   CombinedConstElementFunction(std::shared_ptr<const LeftType> left, std::shared_ptr<const RightType> right)
-    : BaseType()
+    : BaseType(left->parameter_type() + right->parameter_type())
     , left_(left)
     , right_(right)
     , bind_is_temporarily_ok_(false)
@@ -405,7 +79,7 @@ public:
   }
 
   CombinedConstElementFunction(std::unique_ptr<const LeftType>&& left, std::unique_ptr<const RightType>&& right)
-    : BaseType()
+    : BaseType(left->parameter_type() + right->parameter_type())
     , left_(std::move(left))
     , right_(std::move(right))
     , bind_is_temporarily_ok_(false)
@@ -413,6 +87,10 @@ public:
     bind_if_arguments_were_bound();
   }
 
+  CombinedConstElementFunction(const ThisType&) = default;
+
+  CombinedConstElementFunction(ThisType&&) = default;
+
 protected:
   void post_bind(const ElementType& /*element*/) override
   {
@@ -422,19 +100,19 @@ protected:
 public:
   int order(const XT::Common::Parameter& param = {}) const override final
   {
-    return Select::order(left_.access(), right_.access(), param);
+    return Helper::order(left_.access(), right_.access(), param);
   }
 
   RangeReturnType evaluate(const DomainType& point_in_reference_element,
                            const Common::Parameter& param = {}) const override final
   {
-    return Select::evaluate(left_.access(), right_.access(), point_in_reference_element, param);
+    return Helper::evaluate(left_.access(), right_.access(), point_in_reference_element, param);
   }
 
   DerivativeRangeReturnType jacobian(const DomainType& point_in_reference_element,
                                      const Common::Parameter& param = {}) const override final
   {
-    return Select::jacobian(left_.access(), right_.access(), point_in_reference_element, param);
+    return Helper::jacobian(left_.access(), right_.access(), point_in_reference_element, param);
   }
 
 private:
@@ -468,32 +146,42 @@ private:
 }; // class CombinedConstElementFunction
 
 
-template <class LeftType, class RightType, CombinationType combination>
+template <class LeftType, class RightType, class combination>
 class CombinedElementFunction
-  : internal::DualStorageProvider<LeftType, RightType>
+  : internal::CombinedStorageProvider<LeftType, RightType>
   , public CombinedConstElementFunction<LeftType, RightType, combination>
 {
-  using Storage = internal::DualStorageProvider<LeftType, RightType>;
+  using ThisType = CombinedElementFunction;
+  using Storage = internal::CombinedStorageProvider<LeftType, RightType>;
   using BaseType = CombinedConstElementFunction<LeftType, RightType, combination>;
 
 public:
   using typename BaseType::ElementType;
 
-  CombinedElementFunction(LeftType& left, RightType& right)
-    : Storage(left, right)
+  CombinedElementFunction(LeftType& lft, RightType& rght)
+    : Storage(lft, rght)
     , BaseType(Storage::left.access(), Storage::right.access())
   {}
 
-  CombinedElementFunction(std::shared_ptr<LeftType> left, std::shared_ptr<RightType> right)
-    : Storage(left, right)
+  CombinedElementFunction(LeftType& lft, RightType&& rght)
+    : Storage(lft, std::move(rght))
     , BaseType(Storage::left.access(), Storage::right.access())
   {}
 
-  CombinedElementFunction(std::unique_ptr<LeftType>&& left, std::unique_ptr<RightType>&& right)
-    : Storage(std::move(left), std::move(right))
+  CombinedElementFunction(std::shared_ptr<LeftType> lft, std::shared_ptr<RightType> rght)
+    : Storage(lft, rght)
     , BaseType(Storage::left.access(), Storage::right.access())
   {}
 
+  CombinedElementFunction(std::unique_ptr<LeftType>&& lft, std::unique_ptr<RightType>&& rght)
+    : Storage(std::move(lft), std::move(rght))
+    , BaseType(Storage::left.access(), Storage::right.access())
+  {}
+
+  CombinedElementFunction(const ThisType&) = default;
+
+  CombinedElementFunction(ThisType&&) = default;
+
 protected:
   void post_bind(const ElementType& element) override final
   {
@@ -504,7 +192,7 @@ protected:
 
 
 /**
- * \brief Element function representing the difference between two element functions.
+ * \brief Element function representing the difference between two element functions (const variant).
  *
  * \sa CombinedConstElementFunction
  */
@@ -542,7 +230,7 @@ public:
 
 
 /**
- * \brief Element function representing the sum of two element functions.
+ * \brief Element function representing the sum of two element functions (const variant).
  *
  * \sa CombinedConstElementFunction
  */
@@ -578,6 +266,25 @@ public:
 };
 
 
+/**
+ * \brief Element function representing the product of two element functions (const variant).
+ *
+ * \sa CombinedElementFunction
+ */
+template <class LeftFactorType, class RightFactorType>
+class ConstProductElementFunction
+  : public CombinedConstElementFunction<LeftFactorType, RightFactorType, CombinationType::product>
+{
+  using BaseType = CombinedConstElementFunction<LeftFactorType, RightFactorType, CombinationType::product>;
+
+public:
+  template <class... Args>
+  explicit ConstProductElementFunction(Args&&... args)
+    : BaseType(std::forward<Args>(args)...)
+  {}
+};
+
+
 /**
  * \brief Element function representing the product of two element functions.
  *
@@ -596,6 +303,44 @@ public:
 };
 
 
+/**
+ * \brief Element function representing the fraction of two element functions (const variant).
+ *
+ * \sa CombinedElementFunction
+ */
+template <class LeftFactorType, class RightFactorType>
+class ConstFractionElementFunction
+  : public CombinedConstElementFunction<LeftFactorType, RightFactorType, CombinationType::fraction>
+{
+  using BaseType = CombinedConstElementFunction<LeftFactorType, RightFactorType, CombinationType::fraction>;
+
+public:
+  template <class... Args>
+  explicit ConstFractionElementFunction(Args&&... args)
+    : BaseType(std::forward<Args>(args)...)
+  {}
+};
+
+
+/**
+ * \brief Element function representing the fraction of two element functions.
+ *
+ * \sa CombinedElementFunction
+ */
+template <class LeftFactorType, class RightFactorType>
+class FractionElementFunction
+  : public CombinedElementFunction<LeftFactorType, RightFactorType, CombinationType::fraction>
+{
+  using BaseType = CombinedElementFunction<LeftFactorType, RightFactorType, CombinationType::fraction>;
+
+public:
+  template <class... Args>
+  explicit FractionElementFunction(Args&&... args)
+    : BaseType(std::forward<Args>(args)...)
+  {}
+};
+
+
 } // namespace Functions
 } // namespace XT
 } // namespace Dune
diff --git a/dune/xt/functions/base/combined-functions.hh b/dune/xt/functions/base/combined-functions.hh
index d8ffaf9cc513b9940c69ad26ab1cd5182a672ed4..a429159963e5973d467c71cbbf67728d708a22c5 100644
--- a/dune/xt/functions/base/combined-functions.hh
+++ b/dune/xt/functions/base/combined-functions.hh
@@ -13,225 +13,19 @@
 #ifndef DUNE_XT_FUNCTIONS_BASE_COMBINED_FUNCTIONS_HH
 #define DUNE_XT_FUNCTIONS_BASE_COMBINED_FUNCTIONS_HH
 
-#include <dune/xt/common/memory.hh>
-
 #include <dune/xt/functions/interfaces/function.hh>
 #include <dune/xt/functions/type_traits.hh>
 
+#include "combined.hh"
+
 
 namespace Dune {
 namespace XT {
 namespace Functions {
-namespace internal {
 
 
 /**
- * \brief Helper class defining types of combined functions, if available.
- *
- * \note Most likely you do not want to use this class directly, but Combined.
- *
- * \todo Update product handling as in CombinedElementFunctionHelper to allow for more combinations!
- */
-template <class LeftType, class RightType, CombinationType comb>
-class SelectCombined
-{
-
-public:
-  using D = typename LeftType::DomainFieldType;
-  static constexpr size_t d = LeftType::domain_dim;
-  using R = typename LeftType::RangeFieldType;
-
-private:
-  static_assert(std::is_same<typename RightType::DomainFieldType, D>::value, "Types do not match!");
-  static_assert(RightType::domain_dim == d, "Dimensions do not match!");
-  static_assert(std::is_same<typename RightType::RangeFieldType, R>::value, "Types do not match!");
-
-  template <class L, class R>
-  class Choose
-  {
-    //! last tpl arg cannot be dropped due to gcc bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282
-    template <size_t rL, size_t rR, size_t rCL, size_t rcR, CombinationType cc, bool anything = true>
-    class Dimension
-    {
-      static_assert(!anything, "No combination for these dimensions available!");
-    };
-
-    template <size_t r_in, size_t rC_in, bool anything>
-    class Dimension<r_in, r_in, rC_in, rC_in, CombinationType::difference, anything>
-    {
-    public:
-      static constexpr size_t r = r_in;
-      static constexpr size_t rC = rC_in;
-    };
-
-    template <size_t r_in, size_t rC_in, bool anything>
-    class Dimension<r_in, r_in, rC_in, rC_in, CombinationType::sum, anything>
-    {
-    public:
-      static constexpr size_t r = r_in;
-      static constexpr size_t rC = rC_in;
-    };
-
-    template <size_t r_in, size_t rC_in, bool anything>
-    class Dimension<1, r_in, 1, rC_in, CombinationType::product, anything>
-    {
-    public:
-      static constexpr size_t r = r_in;
-      static constexpr size_t rC = rC_in;
-    };
-
-  public:
-    static constexpr size_t r = Dimension<L::range_dim, R::range_dim, L::range_dim_cols, R::range_dim_cols, comb>::r;
-    static constexpr size_t rC = Dimension<L::range_dim, R::range_dim, L::range_dim_cols, R::range_dim_cols, comb>::rC;
-  }; // class Choose
-
-public:
-  static constexpr size_t r = Choose<LeftType, RightType>::r;
-  static constexpr size_t rC = Choose<LeftType, RightType>::rC;
-
-  using DomainType = typename FunctionInterface<d, r, rC, R>::DomainType;
-  using RangeReturnType = typename RightType::RangeReturnType;
-  using ScalarRangeReturnType = typename LeftType::RangeReturnType;
-  using DerivativeRangeReturnType = typename FunctionInterface<d, r, rC, R>::DerivativeRangeReturnType;
-
-private:
-  template <CombinationType cc, bool anything = true>
-  class Call
-  {
-    static_assert(!anything, "Nothing available for these combinations!");
-  }; // class Call
-
-  template <bool anything>
-  class Call<CombinationType::difference, anything>
-  {
-  public:
-    static std::string type()
-    {
-      return "difference";
-    }
-
-    static size_t order(const size_t left_order, const size_t right_order)
-    {
-      return std::max(left_order, right_order);
-    }
-
-    static RangeReturnType evaluate(const LeftType& left_,
-                                    const RightType& right_,
-                                    const DomainType& point_in_global_coordinates,
-                                    const Common::Parameter& param)
-    {
-      return left_.evaluate(point_in_global_coordinates, param) - right_.evaluate(point_in_global_coordinates, param);
-    }
-
-    static DerivativeRangeReturnType jacobian(const LeftType& left_,
-                                              const RightType& right_,
-                                              const DomainType& point_in_global_coordinates,
-                                              const Common::Parameter& param)
-    {
-      return left_.jacobian(point_in_global_coordinates, param) - right_.jacobian(point_in_global_coordinates, param);
-    } // ... jacobian(...)
-  }; // class Call< ..., difference >
-
-  template <bool anything>
-  class Call<CombinationType::sum, anything>
-  {
-  public:
-    static std::string type()
-    {
-      return "sum";
-    }
-
-    static size_t order(const size_t left_order, const size_t right_order)
-    {
-      return std::max(left_order, right_order);
-    }
-
-    static RangeReturnType evaluate(const LeftType& left_,
-                                    const RightType& right_,
-                                    const DomainType& point_in_global_coordinates,
-                                    const Common::Parameter& param)
-    {
-      return left_.evaluate(point_in_global_coordinates, param) + right_.evaluate(point_in_global_coordinates, param);
-    } // ... evaluate(...)
-
-    static DerivativeRangeReturnType jacobian(const LeftType& left_,
-                                              const RightType& right_,
-                                              const DomainType& point_in_global_coordinates,
-                                              const Common::Parameter& param)
-    {
-      return left_.jacobian(point_in_global_coordinates, param) + right_.jacobian(point_in_global_coordinates, param);
-    } // ... jacobian(...)
-  }; // class Call< ..., sum >
-
-  // left only scalar atm
-  template <bool anything>
-  class Call<CombinationType::product, anything>
-  {
-  public:
-    static std::string type()
-    {
-      return "product";
-    }
-
-    static size_t order(const size_t left_order, const size_t right_order)
-    {
-      return left_order + right_order;
-    }
-
-    static RangeReturnType evaluate(const LeftType& left_,
-                                    const RightType& right_,
-                                    const DomainType& point_in_global_coordinates,
-                                    const Common::Parameter& param)
-    {
-      ScalarRangeReturnType left_eval = left_.evaluate(point_in_global_coordinates, param);
-      RangeReturnType right_eval = right_.evaluate(point_in_global_coordinates, param);
-      if (left_eval.size() != 1)
-        DUNE_THROW(NotImplemented, "Only available for scalar left type!");
-      right_eval *= left_eval[0];
-      return right_eval;
-    } // ... evaluate(...)
-
-    static DerivativeRangeReturnType jacobian(const LeftType& /*left_*/,
-                                              const RightType& /*right_*/,
-                                              const DomainType& /*point_in_global_coordinates*/,
-                                              const Common::Parameter& /*param*/)
-    {
-      DUNE_THROW(NotImplemented, "If you need this, implement it!");
-      return DerivativeRangeReturnType();
-    }
-  }; // class Call< ..., product >
-
-public:
-  static std::string type()
-  {
-    return Call<comb>::type();
-  }
-
-  static size_t order(const size_t left_order, const size_t right_order)
-  {
-    return Call<comb>::order(left_order, right_order);
-  }
-
-  static RangeReturnType evaluate(const LeftType& left_,
-                                  const RightType& right_,
-                                  const DomainType& point_in_global_coordinates,
-                                  const Common::Parameter& param)
-  {
-    return Call<comb>::evaluate(left_, right_, point_in_global_coordinates, param);
-  }
-
-  static DerivativeRangeReturnType jacobian(const LeftType& left_,
-                                            const RightType& right_,
-                                            const DomainType& point_in_global_coordinates,
-                                            const Common::Parameter& param)
-  {
-    return Call<comb>::jacobian(left_, right_, point_in_global_coordinates, param);
-  }
-}; // class SelectCombined
-
-
-/**
- * \brief Generic combined function.
+ * \brief Combined function.
  *
  *        This class combines two given functions of type LeftType and RightType
 using the given combination
@@ -282,122 +76,95 @@ Difference< ConstantType, ConstantType > stupid_difference()
  * \note  Most likely you do not want to use this class diretly, but one of
 Difference, Sum or Product.
  */
-template <class LeftType, class RightType, CombinationType comb>
+template <class LeftType, class RightType, class comb>
 class CombinedFunction
-  : public FunctionInterface<LeftType::domain_dim,
-                             SelectCombined<LeftType, RightType, comb>::r,
-                             SelectCombined<LeftType, RightType, comb>::rC,
-                             typename SelectCombined<LeftType, RightType, comb>::R>
+  : public FunctionInterface<LeftType::d,
+                             internal::CombinedHelper<LeftType, RightType, comb>::r,
+                             internal::CombinedHelper<LeftType, RightType, comb>::rC,
+                             typename internal::CombinedHelper<LeftType, RightType, comb>::R>
 {
-  using BaseType = FunctionInterface<LeftType::domain_dim,
-                                     SelectCombined<LeftType, RightType, comb>::r,
-                                     SelectCombined<LeftType, RightType, comb>::rC,
-                                     typename SelectCombined<LeftType, RightType, comb>::R>;
-
+  static_assert(is_function<LeftType>::value, "");
+  static_assert(is_function<RightType>::value, "");
+  static_assert(LeftType::d == RightType::d, "");
+
+  using BaseType = FunctionInterface<LeftType::d,
+                                     internal::CombinedHelper<LeftType, RightType, comb>::r,
+                                     internal::CombinedHelper<LeftType, RightType, comb>::rC,
+                                     typename internal::CombinedHelper<LeftType, RightType, comb>::R>;
   using ThisType = CombinedFunction;
 
-  using Select = SelectCombined<LeftType, RightType, comb>;
-
-  using LeftStorageType = Common::ConstStorageProvider<LeftType>;
-  using RightStorageType = Common::ConstStorageProvider<RightType>;
+  using Helper = internal::CombinedHelper<LeftType, RightType, comb>;
 
 public:
-  CombinedFunction(const LeftType& left, const RightType& right, const std::string nm = "")
-    : left_(std::make_unique<LeftStorageType>(left))
-    , right_(std::make_unique<RightStorageType>(right))
-    , name_(get_name(left, right, nm))
-  {}
-
-  CombinedFunction(const std::shared_ptr<const LeftType> left,
-                   const std::shared_ptr<const RightType> right,
-                   const std::string nm = "")
-    : left_(std::make_unique<LeftStorageType>(left))
-    , right_(std::make_unique<RightStorageType>(right))
-    , name_(get_name(*left, *right, nm))
-  {}
-
-  CombinedFunction(const LeftType& left, const std::shared_ptr<const RightType> right, const std::string nm = "")
-    : left_(std::make_unique<LeftStorageType>(left))
-    , right_(std::make_unique<RightStorageType>(right))
-    , name_(get_name(left, *right, nm))
-  {}
+  using typename BaseType::DerivativeRangeReturnType;
+  using typename BaseType::DomainType;
+  using typename BaseType::RangeReturnType;
 
-  CombinedFunction(const std::shared_ptr<const LeftType> left, const RightType& right, const std::string nm = "")
-    : left_(std::make_unique<LeftStorageType>(left))
-    , right_(std::make_unique<RightStorageType>(right))
-    , name_(get_name(*left, right, nm))
+  CombinedFunction(const LeftType& left, const RightType& right, const std::string nm = "")
+    : left_(std::move(left.copy_as_function()))
+    , right_(std::move(right.copy_as_function()))
+    , name_(nm.empty() ? "(" + left_->name() + " " + GetCombination<comb>::symbol() + " " + right_->name() + ")" : nm)
   {}
 
-  CombinedFunction(LeftType*&& left, RightType*&& right, const std::string nm = "")
-    : left_(std::make_unique<LeftStorageType>(std::move(left)))
-    , right_(std::make_unique<RightStorageType>(std::move(right)))
-    , name_(nm.empty() ? SelectCombined<LeftType, RightType, comb>::type() + " of '" + left_->access().name()
-                             + "' and '" + right_->access().name() + "'"
-                       : nm)
+  CombinedFunction(const ThisType& other)
+    : BaseType(other)
+    , left_(other.left_->copy_as_function())
+    , right_(other.right_->copy_as_function())
+    , name_(other.name_)
   {}
 
   CombinedFunction(ThisType&& source) = default;
 
-  CombinedFunction(const ThisType& other) = delete;
-
-  ThisType& operator=(const ThisType& other) = delete;
+private:
+  ThisType* copy_as_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
 
-  ThisType& operator=(ThisType&& other) = delete;
+public:
+  std::unique_ptr<ThisType> copy_as_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_function_impl());
+  }
 
   std::string name() const override final
   {
     return name_;
   }
 
-  using typename BaseType::DerivativeRangeReturnType;
-  using typename BaseType::DomainType;
-  using typename BaseType::RangeReturnType;
-
   int order(const XT::Common::Parameter& param = {}) const override final
   {
-    auto ret = Select::order(left_->access().order(param), right_->access().order(param));
-    assert(ret < std::numeric_limits<int>::max());
-    return static_cast<int>(ret);
+    return Helper::order(*left_, *right_, param);
   }
 
   RangeReturnType evaluate(const DomainType& point_in_global_coordinates,
                            const Common::Parameter& param = {}) const override final
   {
-    return Select::evaluate(left_->access(), right_->access(), point_in_global_coordinates, param);
+    return Helper::evaluate(*left_, *right_, point_in_global_coordinates, param);
   }
 
   DerivativeRangeReturnType jacobian(const DomainType& point_in_global_coordinates,
                                      const Common::Parameter& param = {}) const override final
   {
-    return Select::jacobian(left_->access(), right_->access(), point_in_global_coordinates, param);
+    return Helper::jacobian(*left_, *right_, point_in_global_coordinates, param);
   }
 
 private:
-  static std::string get_name(const LeftType& left, const RightType& right, const std::string& nm)
-  {
-    return nm.empty() ? SelectCombined<LeftType, RightType, comb>::type() + " of '" + left.name() + "' and '"
-                            + right.name() + "'"
-                      : nm;
-  }
-
-  std::unique_ptr<const LeftStorageType> left_;
-  std::unique_ptr<const RightStorageType> right_;
+  std::unique_ptr<LeftType> left_;
+  std::unique_ptr<RightType> right_;
   const std::string name_;
 }; // class Combined
 
 
-} // namespace internal
-
-
 /**
  * \brief Function representing the difference between two functions.
  *
  * \see internal::Combined
  */
 template <class MinuendType, class SubtrahendType>
-class DifferenceFunction : public internal::CombinedFunction<MinuendType, SubtrahendType, CombinationType::difference>
+class DifferenceFunction : public CombinedFunction<MinuendType, SubtrahendType, CombinationType::difference>
 {
-  using BaseType = internal::CombinedFunction<MinuendType, SubtrahendType, CombinationType::difference>;
+  using BaseType = CombinedFunction<MinuendType, SubtrahendType, CombinationType::difference>;
 
 public:
   template <class... Args>
@@ -413,9 +180,9 @@ public:
  * \see internal::Combined
  */
 template <class LeftSummandType, class RightSummandType>
-class SumFunction : public internal::CombinedFunction<LeftSummandType, RightSummandType, CombinationType::sum>
+class SumFunction : public CombinedFunction<LeftSummandType, RightSummandType, CombinationType::sum>
 {
-  using BaseType = internal::CombinedFunction<LeftSummandType, RightSummandType, CombinationType::sum>;
+  using BaseType = CombinedFunction<LeftSummandType, RightSummandType, CombinationType::sum>;
 
 public:
   template <class... Args>
@@ -426,65 +193,39 @@ public:
 
 
 /**
- * \brief Function representing the product of two functions.
+ * \brief Function representing the fraction of two functions.
  *
- * \see internal::Combined
+ * \see CombinedFunction
  */
-template <class LeftSummandType, class RightSummandType>
-class ProductFunction : public internal::CombinedFunction<LeftSummandType, RightSummandType, CombinationType::product>
+template <class NominatorType, class DenominatorType>
+class FractionFunction : public CombinedFunction<NominatorType, DenominatorType, CombinationType::fraction>
 {
-  using BaseType = internal::CombinedFunction<LeftSummandType, RightSummandType, CombinationType::product>;
+  using BaseType = CombinedFunction<NominatorType, DenominatorType, CombinationType::fraction>;
 
 public:
   template <class... Args>
-  explicit ProductFunction(Args&&... args)
+  explicit FractionFunction(Args&&... args)
     : BaseType(std::forward<Args>(args)...)
   {}
-}; // class ProductFunction
+}; // class FractionFunction
 
 
-template <class T1, class T2, class... Args>
-std::shared_ptr<DifferenceFunction<T1, T2>> make_difference(const T1& left, const T2& right, Args&&... args)
-{
-  return std::make_shared<DifferenceFunction<T1, T2>>(left, right, std::forward<Args>(args)...);
-}
-
-
-template <class T1, class T2, class... Args>
-std::shared_ptr<DifferenceFunction<T1, T2>>
-make_difference(std::shared_ptr<T1> left, std::shared_ptr<T2> right, Args&&... args)
-{
-  return std::make_shared<DifferenceFunction<T1, T2>>(left, right, std::forward<Args>(args)...);
-}
-
-
-template <class T1, class T2, class... Args>
-std::shared_ptr<SumFunction<T1, T2>> make_sum(const T1& left, const T2& right, Args&&... args)
-{
-  return std::make_shared<SumFunction<T1, T2>>(left, right, std::forward<Args>(args)...);
-}
-
-
-template <class T1, class T2, class... Args>
-std::shared_ptr<SumFunction<T1, T2>> make_sum(std::shared_ptr<T1> left, std::shared_ptr<T2> right, Args&&... args)
-{
-  return std::make_shared<SumFunction<T1, T2>>(left, right, std::forward<Args>(args)...);
-}
-
-
-template <class T1, class T2, class... Args>
-std::shared_ptr<ProductFunction<T1, T2>> make_product(const T1& left, const T2& right, Args&&... args)
+/**
+ * \brief Function representing the product of two functions.
+ *
+ * \see internal::Combined
+ */
+template <class LeftFactorType, class RightFactorType>
+class ProductFunction : public CombinedFunction<LeftFactorType, RightFactorType, CombinationType::product>
 {
-  return std::make_shared<ProductFunction<T1, T2>>(left, right, std::forward<Args>(args)...);
-}
+  using BaseType = CombinedFunction<LeftFactorType, RightFactorType, CombinationType::product>;
 
-
-template <class T1, class T2, class... Args>
-std::shared_ptr<ProductFunction<T1, T2>>
-make_product(std::shared_ptr<T1> left, std::shared_ptr<T2> right, Args&&... args)
-{
-  return std::make_shared<ProductFunction<T1, T2>>(left, right, std::forward<Args>(args)...);
-}
+public:
+  template <class... Args>
+  explicit ProductFunction(Args&&... args)
+    : BaseType(std::forward<Args>(args)...)
+  {}
+}; // class ProductFunction
 
 
 } // namespace Functions
diff --git a/dune/xt/functions/base/combined-grid-functions.hh b/dune/xt/functions/base/combined-grid-functions.hh
index e5cd8ef7409c6745f0f0496cc6915632ff9e5f73..bd14f18021959e1642d8f86b59e0ad33d7370bf2 100644
--- a/dune/xt/functions/base/combined-grid-functions.hh
+++ b/dune/xt/functions/base/combined-grid-functions.hh
@@ -14,306 +14,21 @@
 #define DUNE_XT_FUNCTIONS_BASE_COMBINED_GRID_FUNCTIONS_HH
 
 #include <dune/xt/functions/interfaces/grid-function.hh>
-#include <dune/xt/functions/type_traits.hh>
+
+#include "combined.hh"
 
 namespace Dune {
 namespace XT {
 namespace Functions {
-namespace internal {
-
-
-/**
- * \brief Helper class defining types of combined functions, if available.
- *
- * \note Most likely you do not want to use this class directly, but Combined.
- *
- * \todo Update product handling as in CombinedElementFunctionHelper to allow for more combinations!
- */
-template <class LeftType, class RightType, CombinationType comb>
-class SelectCombinedGridFunction
-{
-  static_assert(is_grid_function<LeftType>::value, "");
-  static_assert(is_grid_function<RightType>::value, "");
-
-public:
-  using E = typename LeftType::ElementType;
-  using D = typename LeftType::DomainFieldType;
-  static constexpr size_t d = LeftType::domain_dim;
-  using R = typename LeftType::RangeFieldType;
-
-private:
-  static_assert(std::is_same<typename RightType::ElementType, E>::value, "Types do not match!");
-  static_assert(std::is_same<typename RightType::DomainFieldType, D>::value, "Types do not match!");
-  static_assert(RightType::domain_dim == d, "Dimensions do not match!");
-  static_assert(std::is_same<typename RightType::RangeFieldType, R>::value, "Types do not match!");
-
-  template <class L, class R>
-  class Choose
-  {
-    template <size_t rL, size_t rR, size_t rCL, size_t rcR, CombinationType cc, bool anything = true>
-    class Dimension
-    {
-      static_assert(!anything, "No combination for these dimensions available!");
-    };
-
-    template <size_t r_in, size_t rC_in, bool anything>
-    class Dimension<r_in, r_in, rC_in, rC_in, CombinationType::difference, anything>
-    {
-    public:
-      static constexpr size_t r = r_in;
-      static constexpr size_t rC = rC_in;
-    };
-
-    template <size_t r_in, size_t rC_in, bool anything>
-    class Dimension<r_in, r_in, rC_in, rC_in, CombinationType::sum, anything>
-    {
-    public:
-      static constexpr size_t r = r_in;
-      static constexpr size_t rC = rC_in;
-    };
-
-    template <size_t r_in, size_t rC_in, bool anything>
-    class Dimension<1, r_in, 1, rC_in, CombinationType::product, anything>
-    {
-    public:
-      static constexpr size_t r = r_in;
-      static constexpr size_t rC = rC_in;
-    };
-
-  public:
-    static constexpr size_t r = Dimension<L::range_dim, R::range_dim, L::range_dim_cols, R::range_dim_cols, comb>::r;
-    static constexpr size_t rC = Dimension<L::range_dim, R::range_dim, L::range_dim_cols, R::range_dim_cols, comb>::rC;
-  }; // class Choose
-
-public:
-  static constexpr size_t r = Choose<LeftType, RightType>::r;
-  static constexpr size_t rC = Choose<LeftType, RightType>::rC;
-
-  using LeftLocalFunctionType = typename LeftType::LocalFunctionType;
-  using RightLocalFunctionType = typename RightType::LocalFunctionType;
-  using DomainType = typename ElementFunctionInterface<E, r, rC, R>::DomainType;
-  using RangeType = typename RightType::LocalFunctionType::RangeType;
-  using ScalarRangeType = typename LeftType::LocalFunctionType::RangeType;
-  using DerivativeRangeType = typename ElementFunctionInterface<E, r, rC, R>::DerivativeRangeType;
-  using DerivativeRangeReturnType = typename ElementFunctionInterface<E, r, rC, R>::DerivativeRangeReturnType;
-
-private:
-  template <CombinationType cc, bool anything = true>
-  class Call
-  {
-    static_assert(!anything, "Nothing available for these combinations!");
-  }; // class Call
-
-  template <bool anything>
-  class Call<CombinationType::difference, anything>
-  {
-  public:
-    static std::string type()
-    {
-      return "difference";
-    }
-
-    static size_t order(const size_t left_order, const size_t right_order)
-    {
-      return std::max(left_order, right_order);
-    }
-
-    static RangeType evaluate(const LeftLocalFunctionType& left_local,
-                              const RightLocalFunctionType& right_local,
-                              const DomainType& point_in_reference_element,
-                              const Common::Parameter& param)
-    {
-      return left_local.evaluate(point_in_reference_element, param)
-             - right_local.evaluate(point_in_reference_element, param);
-    }
-
-    static DerivativeRangeReturnType jacobian(const LeftLocalFunctionType& left_local,
-                                              const RightLocalFunctionType& right_local,
-                                              const DomainType& point_in_reference_element,
-                                              const Common::Parameter& param)
-    {
-      return left_local.jacobian(point_in_reference_element, param)
-             - right_local.jacobian(point_in_reference_element, param);
-    } // ... jacobian(...)
-  }; // class Call< ..., difference >
-
-  template <bool anything>
-  class Call<CombinationType::sum, anything>
-  {
-  public:
-    static std::string type()
-    {
-      return "sum";
-    }
-
-    static size_t order(const size_t left_order, const size_t right_order)
-    {
-      return std::max(left_order, right_order);
-    }
-
-    static RangeType evaluate(const LeftLocalFunctionType& left_local,
-                              const RightLocalFunctionType& right_local,
-                              const DomainType& point_in_reference_element,
-                              const Common::Parameter& param)
-    {
-      return left_local.evaluate(point_in_reference_element, param)
-             + right_local.evaluate(point_in_reference_element, param);
-    } // ... evaluate(...)
-
-    static DerivativeRangeReturnType jacobian(const LeftLocalFunctionType& left_local,
-                                              const RightLocalFunctionType& right_local,
-                                              const DomainType& point_in_reference_element,
-                                              const Common::Parameter& param)
-    {
-      return left_local.jacobian(point_in_reference_element, param)
-             + right_local.jacobian(point_in_reference_element, param);
-    } // ... jacobian(...)
-  }; // class Call< ..., sum >
-
-  // left only scalar atm
-  template <bool anything>
-  class Call<CombinationType::product, anything>
-  {
-  public:
-    static std::string type()
-    {
-      return "product";
-    }
-
-    static size_t order(const size_t left_order, const size_t right_order)
-    {
-      return left_order + right_order;
-    }
-
-    static RangeType evaluate(const LeftLocalFunctionType& left_local,
-                              const RightLocalFunctionType& right_local,
-                              const DomainType& point_in_reference_element,
-                              const Common::Parameter& param)
-    {
-      ScalarRangeType left_eval = left_local.evaluate(point_in_reference_element, param);
-      RangeType right_eval = right_local.evaluate(point_in_reference_element, param);
-      if (left_eval.size() != 1)
-        DUNE_THROW(NotImplemented, "Only available for scalar left type!");
-      right_eval *= left_eval[0];
-      return right_eval;
-    } // ... evaluate(...)
-
-    static DerivativeRangeReturnType jacobian(const LeftLocalFunctionType& /*left_local*/,
-                                              const RightLocalFunctionType& /*right_local*/,
-                                              const DomainType& /*point_in_reference_element*/,
-                                              const Common::Parameter& /*param*/)
-    {
-      DUNE_THROW(NotImplemented, "If you need this, implement it!");
-      return DerivativeRangeReturnType();
-    }
-  }; // class Call< ..., product >
-
-public:
-  static std::string type()
-  {
-    return Call<comb>::type();
-  }
-
-  static size_t order(const size_t left_order, const size_t right_order)
-  {
-    return Call<comb>::order(left_order, right_order);
-  }
-
-  static RangeType evaluate(const LeftLocalFunctionType& left_local,
-                            const RightLocalFunctionType& right_local,
-                            const DomainType& point_in_reference_element,
-                            const Common::Parameter& param)
-  {
-    return Call<comb>::evaluate(left_local, right_local, point_in_reference_element, param);
-  }
-
-  static DerivativeRangeReturnType jacobian(const LeftLocalFunctionType& left_local,
-                                            const RightLocalFunctionType& right_local,
-                                            const DomainType& point_in_reference_element,
-                                            const Common::Parameter& param)
-  {
-    return Call<comb>::jacobian(left_local, right_local, point_in_reference_element, param);
-  }
-}; // class SelectCombinedGridFunction
 
 
 /**
- * \brief Generic combined local function.
+ * \brief Base combined grid function.
  *
- * \note Most likely you do not want to use this class directly, but Combined.
- */
-template <class LeftType, class RightType, CombinationType type>
-class CombinedLocalFunction
-  : public ElementFunctionInterface<typename SelectCombinedGridFunction<LeftType, RightType, type>::E,
-                                    SelectCombinedGridFunction<LeftType, RightType, type>::r,
-                                    SelectCombinedGridFunction<LeftType, RightType, type>::rC,
-                                    typename SelectCombinedGridFunction<LeftType, RightType, type>::R>
-{
-  using BaseType = ElementFunctionInterface<typename SelectCombinedGridFunction<LeftType, RightType, type>::E,
-                                            SelectCombinedGridFunction<LeftType, RightType, type>::r,
-                                            SelectCombinedGridFunction<LeftType, RightType, type>::rC,
-                                            typename SelectCombinedGridFunction<LeftType, RightType, type>::R>;
-
-  using Select = SelectCombinedGridFunction<LeftType, RightType, type>;
-
-public:
-  using typename BaseType::DerivativeRangeReturnType;
-  using typename BaseType::DerivativeRangeType;
-  using typename BaseType::DomainType;
-  using typename BaseType::ElementType;
-  using typename BaseType::RangeReturnType;
-  using typename BaseType::RangeType;
-
-  CombinedLocalFunction(const LeftType& left, const RightType& right)
-    : BaseType()
-    , left_local_(left.local_function())
-    , right_local_(right.local_function())
-  {}
-
-protected:
-  void post_bind(const ElementType& element) override final
-  {
-    left_local_->bind(element);
-    right_local_->bind(element);
-  }
-
-public:
-  int order(const XT::Common::Parameter& param = {}) const override final
-  {
-    const auto ret = Select::order(left_local_->order(param), right_local_->order(param));
-    assert(ret < std::numeric_limits<int>::max());
-    return static_cast<int>(ret);
-  }
-
-  RangeReturnType evaluate(const DomainType& point_in_reference_element,
-                           const Common::Parameter& param = {}) const override final
-  {
-    return Select::evaluate(*left_local_, *right_local_, point_in_reference_element, param);
-  }
-
-  DerivativeRangeReturnType jacobian(const DomainType& point_in_reference_element,
-                                     const Common::Parameter& param = {}) const override final
-  {
-    return Select::jacobian(*left_local_, *right_local_, point_in_reference_element, param);
-  }
-
-private:
-  std::unique_ptr<typename LeftType::LocalFunctionType> left_local_;
-  std::unique_ptr<typename RightType::LocalFunctionType> right_local_;
-}; // class CombinedLocalFunction
-
-
-/**
- * \brief Generic combined function.
- *
- *        This class combines two given functions of type LeftType and RightType
-using the given combination
- *        Combination. This class (and any derived class, like Difference, Sum
-or Product) can be used in two ways:
- *        - You can pass references of the left and right operand to this class.
-This is done for instance when calling
- *          operator+, operator- or operator* on any function deriving from
-GridFunctionInterface:
+ *        This class combines two given grid functions of type LeftType and RightType using the given combination
+ *        Combination. This class (and any derived class, like Difference, Sum or Product) can be used in two ways:
+ *        - You can pass references of the left and right operand to this class. This is done for instance when calling
+ *          operator+, operator- or operator* on any function deriving from GridFunctionInterface:
 \code
 using IndicatorType = Functions::IndicatorFunction< ..., double>;
 IndicatorType one( ... );
@@ -323,11 +38,9 @@ auto difference = one - two;
 // is equivalent to
 Difference< IndicatorType, IndicatorType > difference(one, two);
 // and
-internal::Combined< IndicatorType, IndicatorType, CombinationType::difference >
-difference(one, tow);
+internal::Combined< IndicatorType, IndicatorType, CombinationType::difference > difference(one, tow);
 \endcode
- *          In this situation you are responsible to ensure that the arguments
-given are valid throughout the lifetime
+ *          In this situation you are responsible to ensure that the arguments given are valid throughout the lifetime
  *          of this class. The following will lead to a segfault:
 \code
 using IndicatorType = Functions::IndicatorFunction< ..., double >;
@@ -339,89 +52,106 @@ Difference< IndicatorType, IndicatorType > stupid_difference()
   return one - two;
 }
 \endcode
- *        - You can pass shared_ptr of the left and right operands to this
-class. In this case the following is valid:
+ *        - You can pass shared_ptr of the left and right operands to this class. In this case the following is valid:
 \code
 using IndicatorType = Functions::IndicatorFunction< ..., double >;
 
-Difference< IndicatorType, IndicatorType > stupid_difference()
+Difference<IndicatorType, IndicatorType> stupid_difference()
 {
-  auto one = std::make_shared< IndicatorType >(1);
-  auto two = std::make_shared< IndicatorType >(2);
-  return Difference< IndicatorType, IndicatorType >(one, two)
+  auto one = std::make_shared<IndicatorType>(1);
+  auto two = std::make_shared<IndicatorType>(2);
+  return Difference<IndicatorType, IndicatorType>(one, two)
 }
 \endcode
  *
- * \note  Most likely you do not want to use this class diretly, but one of
-Difference, Sum or Product.
+ * \note  Most likely you do not want to use this class diretly, but one of Difference, Fraction, Sum or Product.
+ *
+ * \todo Implement custom local function to hold a copy of this!
  */
-template <class LeftType, class RightType, CombinationType comb>
+template <class LeftType, class RightType, class comb>
 class CombinedGridFunction
-  : public GridFunctionInterface<typename SelectCombinedGridFunction<LeftType, RightType, comb>::E,
-                                 SelectCombinedGridFunction<LeftType, RightType, comb>::r,
-                                 SelectCombinedGridFunction<LeftType, RightType, comb>::rC,
-                                 typename SelectCombinedGridFunction<LeftType, RightType, comb>::R>
+  : public GridFunctionInterface<typename LeftType::E,
+                                 internal::CombinedHelper<LeftType, RightType, comb>::r,
+                                 internal::CombinedHelper<LeftType, RightType, comb>::rC,
+                                 typename internal::CombinedHelper<LeftType, RightType, comb>::R>
 {
-  using BaseType = GridFunctionInterface<typename SelectCombinedGridFunction<LeftType, RightType, comb>::E,
-                                         SelectCombinedGridFunction<LeftType, RightType, comb>::r,
-                                         SelectCombinedGridFunction<LeftType, RightType, comb>::rC,
-                                         typename SelectCombinedGridFunction<LeftType, RightType, comb>::R>;
+  static_assert(is_grid_function<LeftType>::value, "");
+  static_assert(is_grid_function<RightType>::value, "");
+  static_assert(std::is_same<typename LeftType::E, typename RightType::E>::value, "");
 
-  using LeftStorageType = Common::ConstStorageProvider<LeftType>;
-  using RightStorageType = Common::ConstStorageProvider<RightType>;
   using ThisType = CombinedGridFunction;
+  using BaseType = GridFunctionInterface<typename LeftType::E,
+                                         internal::CombinedHelper<LeftType, RightType, comb>::r,
+                                         internal::CombinedHelper<LeftType, RightType, comb>::rC,
+                                         typename internal::CombinedHelper<LeftType, RightType, comb>::R>;
 
 public:
   using ElementType = typename BaseType::ElementType;
   using LocalFunctionType = typename BaseType::LocalFunctionType;
 
-  CombinedGridFunction(const LeftType& left, const RightType& right, const std::string nm = "")
-    : left_(std::make_unique<LeftStorageType>(left))
-    , right_(std::make_unique<RightStorageType>(right))
-    , name_(get_name(left_->access(), right_->access(), nm))
-  {}
-
-  CombinedGridFunction(const std::shared_ptr<const LeftType> left,
-                       const std::shared_ptr<const RightType> right,
-                       const std::string nm = "")
-    : left_(std::make_unique<LeftStorageType>(left))
-    , right_(std::make_unique<RightStorageType>(right))
-    , name_(get_name(left_->access(), right_->access(), nm))
-  {}
-
-  CombinedGridFunction(const LeftType& left, const std::shared_ptr<const RightType> right, const std::string nm = "")
-    : left_(std::make_unique<LeftStorageType>(left))
-    , right_(std::make_unique<RightStorageType>(right))
-    , name_(get_name(left_->access(), right_->access(), nm))
-  {}
+  CombinedGridFunction(const LeftType& left,
+                       const RightType& right,
+                       const std::string nm = "",
+                       const std::string& logging_prefix = "")
+    : BaseType(left.parameter_type() + right.parameter_type(),
+               logging_prefix.empty() ? Common::to_camel_case(get_combination_name(comb{}) + "GridFunction")
+                                      : logging_prefix,
+               logging_prefix.empty())
+    , left_(left.copy_as_grid_function())
+    , right_(right.copy_as_grid_function())
+    , name_(nm.empty() ? "(" + left_->name() + GetCombination<comb>::symbol() + right_->name() + ")" : nm)
+  {
+    LOG_(debug) << Common::to_camel_case(get_combination_name(comb{}) + "GridFunction") << "(left=" << &left
+                << ", right=" << &right << ", nm=\"" << nm << "\")" << std::endl;
+  }
 
-  CombinedGridFunction(const std::shared_ptr<const LeftType> left, const RightType& right, const std::string nm = "")
-    : left_(std::make_unique<LeftStorageType>(left))
-    , right_(std::make_unique<RightStorageType>(right))
-    , name_(get_name(left_->access(), right_->access(), nm))
-  {}
+  CombinedGridFunction(LeftType*&& left,
+                       RightType*&& right,
+                       const std::string nm = "",
+                       const std::string& logging_prefix = "")
+    : BaseType(left->parameter_type() + right->parameter_type(),
+               logging_prefix.empty() ? Common::to_camel_case(get_combination_name(comb{}) + "GridFunction")
+                                      : logging_prefix,
+               logging_prefix.empty())
+    , left_(std::move(left))
+    , right_(std::move(right))
+    , name_(nm.empty() ? "(" + left_->name() + GetCombination<comb>::symbol() + right_->name() + ")" : nm)
+  {
+    LOG_(debug) << Common::to_camel_case(get_combination_name(comb{}) + "GridFunction") << "(left=" << left
+                << ", right=" << right << ", nm=\"" << nm << "\")" << std::endl;
+  }
 
-  CombinedGridFunction(LeftType*&& left, RightType*&& right, const std::string nm = "")
-    : left_(std::make_unique<LeftStorageType>(std::move(left)))
-    , right_(std::make_unique<RightStorageType>(std::move(right)))
-    , name_(get_name(left_->access(), right_->access(), nm))
+  CombinedGridFunction(const ThisType& other)
+    : BaseType(other)
+    , left_(other.left_->copy_as_grid_function())
+    , right_(other.right_->copy_as_grid_function())
+    , name_(other.name_)
   {}
 
   CombinedGridFunction(ThisType&& source) = default;
 
-  CombinedGridFunction(const ThisType& other) = delete;
+  std::unique_ptr<LocalFunctionType> local_function() const override final
+  {
+    LOG_(debug) << Common::to_camel_case(get_combination_name(comb{}) + "GridFunction") + "::local_function()"
+                << std::endl;
+    using LeftLF = typename LeftType::LocalFunctionType;
+    using RightLF = typename RightType::LocalFunctionType;
+    return std::make_unique<CombinedElementFunction<LeftLF, RightLF, comb>>(std::move(left_->local_function()),
+                                                                            std::move(right_->local_function()));
+  } // ... local_function(...)
 
-  ThisType& operator=(const ThisType& other) = delete;
 
-  ThisType& operator=(ThisType&& other) = delete;
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
 
-  std::unique_ptr<LocalFunctionType> local_function() const override final
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
   {
-    using RealLocalFunctionType = CombinedLocalFunction<LeftType, RightType, comb>;
-    assert(left_);
-    assert(right_);
-    return std::make_unique<RealLocalFunctionType>(left_->access(), right_->access());
-  } // ... local_function(...)
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
 
   std::string name() const override final
   {
@@ -429,32 +159,22 @@ public:
   }
 
 private:
-  static std::string get_name(const LeftType& left, const RightType& right, const std::string& nm)
-  {
-    return nm.empty() ? SelectCombinedGridFunction<LeftType, RightType, comb>::type() + " of '" + left.name()
-                            + "' and '" + right.name() + "'"
-                      : nm;
-  }
-
-  std::unique_ptr<const LeftStorageType> left_;
-  std::unique_ptr<const RightStorageType> right_;
+  std::unique_ptr<GridFunctionInterface<typename LeftType::E, LeftType::r, LeftType::rC, typename LeftType::R>> left_;
+  std::unique_ptr<GridFunctionInterface<typename RightType::E, RightType::r, RightType::rC, typename RightType::R>>
+      right_;
   const std::string name_;
 }; // class CombinedGridFunction
 
 
-} // namespace internal
-
-
 /**
  * \brief Function representing the difference between two functions.
  *
- * \see internal::CombinedGridFunction
+ * \see CombinedGridFunction
  */
 template <class MinuendType, class SubtrahendType>
-class DifferenceGridFunction
-  : public internal::CombinedGridFunction<MinuendType, SubtrahendType, CombinationType::difference>
+class DifferenceGridFunction : public CombinedGridFunction<MinuendType, SubtrahendType, CombinationType::difference>
 {
-  using BaseType = internal::CombinedGridFunction<MinuendType, SubtrahendType, CombinationType::difference>;
+  using BaseType = CombinedGridFunction<MinuendType, SubtrahendType, CombinationType::difference>;
 
 public:
   template <class... Args>
@@ -467,12 +187,12 @@ public:
 /**
  * \brief Function representing the sum of two functions.
  *
- * \see internal::CombinedGridFunction
+ * \see CombinedGridFunction
  */
 template <class LeftSummandType, class RightSummandType>
-class SumGridFunction : public internal::CombinedGridFunction<LeftSummandType, RightSummandType, CombinationType::sum>
+class SumGridFunction : public CombinedGridFunction<LeftSummandType, RightSummandType, CombinationType::sum>
 {
-  using BaseType = internal::CombinedGridFunction<LeftSummandType, RightSummandType, CombinationType::sum>;
+  using BaseType = CombinedGridFunction<LeftSummandType, RightSummandType, CombinationType::sum>;
 
 public:
   template <class... Args>
@@ -483,82 +203,39 @@ public:
 
 
 /**
- * \brief Grid function representing the product of two grid functions.
+ * \brief Function representing the fraction of two functions.
  *
- * \see internal::CombinedGridFunction
+ * \see CombinedGridFunction
  */
-template <class LeftSummandType, class RightSummandType>
-class ProductGridFunction
-  : public internal::CombinedGridFunction<LeftSummandType, RightSummandType, CombinationType::product>
+template <class NominatorType, class DenominatorType>
+class FractionGridFunction : public CombinedGridFunction<NominatorType, DenominatorType, CombinationType::fraction>
 {
-  using BaseType = internal::CombinedGridFunction<LeftSummandType, RightSummandType, CombinationType::product>;
+  using BaseType = CombinedGridFunction<NominatorType, DenominatorType, CombinationType::fraction>;
 
 public:
   template <class... Args>
-  explicit ProductGridFunction(Args&&... args)
+  explicit FractionGridFunction(Args&&... args)
     : BaseType(std::forward<Args>(args)...)
   {}
-}; // class ProductGridFunction
+}; // class FractionGridFunction
 
 
-template <class T1, class T2, class... Args>
-std::shared_ptr<DifferenceGridFunction<T1, T2>> make_difference(const T1& left, const T2& right, Args&&... args)
-{
-  return std::make_shared<DifferenceGridFunction<T1, T2>>(left, right, std::forward<Args>(args)...);
-}
-
-template <class T1, class T2, class... Args>
-std::shared_ptr<DifferenceGridFunction<T1, T2>>
-make_difference(std::shared_ptr<T1> left, std::shared_ptr<T2> right, Args&&... args)
-{
-  return std::make_shared<DifferenceGridFunction<T1, T2>>(left, right, std::forward<Args>(args)...);
-}
-
-template <class T1, class T2, class... Args>
-std::shared_ptr<DifferenceGridFunction<T1, T2>> make_difference(T1*&& left, T2*&& right, Args&&... args)
-{
-  return std::make_shared<DifferenceGridFunction<T1, T2>>(
-      std::move(left), std::move(right), std::forward<Args>(args)...);
-}
-
-
-template <class T1, class T2, class... Args>
-std::shared_ptr<SumGridFunction<T1, T2>> make_sum(const T1& left, const T2& right, Args&&... args)
-{
-  return std::make_shared<SumGridFunction<T1, T2>>(left, right, std::forward<Args>(args)...);
-}
-
-template <class T1, class T2, class... Args>
-std::shared_ptr<SumGridFunction<T1, T2>> make_sum(std::shared_ptr<T1> left, std::shared_ptr<T2> right, Args&&... args)
-{
-  return std::make_shared<SumGridFunction<T1, T2>>(left, right, std::forward<Args>(args)...);
-}
-
-template <class T1, class T2, class... Args>
-std::shared_ptr<SumGridFunction<T1, T2>> make_sum(T1*&& left, T2*&& right, Args&&... args)
-{
-  return std::make_shared<SumGridFunction<T1, T2>>(std::move(left), std::move(right), std::forward<Args>(args)...);
-}
-
-
-template <class T1, class T2, class... Args>
-std::shared_ptr<ProductGridFunction<T1, T2>> make_product(const T1& left, const T2& right, Args&&... args)
-{
-  return std::make_shared<ProductGridFunction<T1, T2>>(left, right, std::forward<Args>(args)...);
-}
-
-template <class T1, class T2, class... Args>
-std::shared_ptr<ProductGridFunction<T1, T2>>
-make_product(std::shared_ptr<T1> left, std::shared_ptr<T2> right, Args&&... args)
+/**
+ * \brief Grid function representing the product of two grid functions.
+ *
+ * \see CombinedGridFunction
+ */
+template <class LeftFactorType, class RightFactorType>
+class ProductGridFunction : public CombinedGridFunction<LeftFactorType, RightFactorType, CombinationType::product>
 {
-  return std::make_shared<ProductGridFunction<T1, T2>>(left, right, std::forward<Args>(args)...);
-}
+  using BaseType = CombinedGridFunction<LeftFactorType, RightFactorType, CombinationType::product>;
 
-template <class T1, class T2, class... Args>
-std::shared_ptr<ProductGridFunction<T1, T2>> make_product(T1*&& left, T2*&& right, Args&&... args)
-{
-  return std::make_shared<ProductGridFunction<T1, T2>>(std::move(left), std::move(right), std::forward<Args>(args)...);
-}
+public:
+  template <class... Args>
+  explicit ProductGridFunction(Args&&... args)
+    : BaseType(std::forward<Args>(args)...)
+  {}
+}; // class ProductGridFunction
 
 
 } // namespace Functions
diff --git a/dune/xt/functions/base/combined.hh b/dune/xt/functions/base/combined.hh
new file mode 100644
index 0000000000000000000000000000000000000000..395293e87dc52ae39059e82d4d244fee2a4c22b9
--- /dev/null
+++ b/dune/xt/functions/base/combined.hh
@@ -0,0 +1,340 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+#ifndef DUNE_XT_FUNCTIONS_BASE_COMBINED_HH
+#define DUNE_XT_FUNCTIONS_BASE_COMBINED_HH
+
+#include <dune/xt/common/memory.hh>
+#include <dune/xt/common/parameter.hh>
+#include <dune/xt/common/type_traits.hh>
+#include <dune/xt/functions/exceptions.hh>
+#include <dune/xt/functions/type_traits.hh>
+
+namespace Dune {
+namespace XT {
+namespace Functions {
+namespace internal {
+
+
+template <typename comb, size_t L_r, size_t L_rC, size_t R_r, size_t R_rC>
+struct CombinedDim
+{
+  static constexpr bool available()
+  {
+    return true;
+  };
+  static constexpr size_t r()
+  {
+    return L_r;
+  }
+  static constexpr size_t rC()
+  {
+    return L_rC;
+  }
+};
+
+// general case: non-scalar matrix * non-scalar {matrix or vector}
+template <size_t L_r, size_t L_rC, size_t R_r, size_t R_rC>
+struct CombinedDim<CombinationType::product, L_r, L_rC, R_r, R_rC>
+{
+  static constexpr size_t r()
+  {
+    if constexpr (R_rC == 1 && L_rC == 1 && L_r != 1) {
+      return 1;
+    } else if constexpr (L_r == 1 && L_rC == 1 && R_r * R_rC != 1) {
+      return R_r;
+    } else if constexpr (R_r == 1 && R_rC == 1) {
+      return L_r;
+    } else if constexpr (L_rC * R_rC != 1) {
+      return L_r;
+    }
+    return 0;
+  }
+
+  static constexpr size_t rC()
+  {
+    if constexpr (R_rC == 1 && L_rC == 1 && L_r != 1) {
+      return 1;
+    } else if constexpr (L_r == 1 && L_rC == 1 && R_r * R_rC != 1) {
+      return R_rC;
+    } else if constexpr (R_r == 1 && R_rC == 1) {
+      return L_rC;
+    } else if constexpr (L_rC * R_rC != 1) {
+      return R_rC;
+    }
+    return 0;
+  }
+
+  static constexpr bool available()
+  {
+    return r() != 0 && rC() != 0;
+  }
+};
+
+template <class L, class R>
+static int
+compute_combined_order(const L& left, const R& right, const Common::Parameter& param, CombinationType::difference)
+{
+  return std::max(left.order(param), right.order(param));
+}
+
+template <class L, class R>
+static int
+compute_combined_order(const L& left, const R& right, const Common::Parameter& param, CombinationType::fraction)
+{
+  return left.order(param) + right.order(param);
+}
+template <class L, class R>
+static int
+compute_combined_order(const L& left, const R& right, const Common::Parameter& param, CombinationType::product)
+{
+  return left.order(param) + right.order(param);
+}
+template <class L, class R>
+static int compute_combined_order(const L& left, const R& right, const Common::Parameter& param, CombinationType::sum)
+{
+  return std::max(left.order(param), right.order(param));
+}
+
+template <class Left, class Right, class D>
+static auto compute_combined_eval(
+    const Left& left, const Right& right, const D& point, const Common::Parameter& param, CombinationType::difference)
+{
+  return left.evaluate(point, param) - right.evaluate(point, param);
+}
+
+template <class Left, class Right, class D>
+static auto compute_combined_eval(
+    const Left& left, const Right& right, const D& point, const Common::Parameter& param, CombinationType::fraction)
+{
+  auto left_value = left.evaluate(point, param);
+  const auto right_value = right.evaluate(point, param)[0];
+  left_value /= right_value;
+  return left_value;
+}
+
+template <class Left, class Right, class D>
+static auto compute_combined_eval(
+    const Left& left, const Right& right, const D& point, const Common::Parameter& param, CombinationType::sum)
+{
+  return left.evaluate(point, param) + right.evaluate(point, param);
+}
+
+template <typename comb, class L_R, class R_R, size_t L_r, size_t L_rC, size_t R_r, size_t R_rC>
+struct CombinedEval
+{
+  using R = typename std::conditional<std::is_same<CombinationType::fraction, comb>::value,
+                                      typename Common::multiplication_promotion<L_R, R_R>::type,
+                                      typename Common::plus_promotion<L_R, R_R>::type>::type;
+  static const constexpr size_t r = CombinedDim<comb, L_r, L_rC, R_r, R_rC>::r();
+  static const constexpr size_t rC = CombinedDim<comb, L_r, L_rC, R_r, R_rC>::rC();
+
+  template <class Left, class Right, class D>
+  static auto compute(const Left& left, const Right& right, const D& point, const Common::Parameter& param)
+  {
+    return compute_combined_eval(left, right, point, param, comb{});
+  }
+};
+
+template <class L_R, class R_R, size_t L_r, size_t L_rC, size_t R_r, size_t R_rC>
+struct CombinedEval<CombinationType::product, L_R, R_R, L_r, L_rC, R_r, R_rC>
+{
+  static const constexpr size_t r = CombinedDim<CombinationType::product, L_r, L_rC, R_r, R_rC>::r();
+  static const constexpr size_t rC = CombinedDim<CombinationType::product, L_r, L_rC, R_r, R_rC>::rC();
+  using R = typename Common::multiplication_promotion<L_R, R_R>::type;
+  using RangeReturnType = typename RangeTypeSelector<R, r, rC>::return_type;
+  template <class Left, class Right, class D>
+  static RangeReturnType compute(const Left& left, const Right& right, const D& point, const Common::Parameter& param)
+  {
+    // general case: non-scalar matrix * non-scalar {matrix or vector}
+    if constexpr (R_r == L_rC && L_rC * R_rC != 1) {
+      if constexpr (L_rC == 1) {
+        // need to wrap the left vector into a matrix, for * to do the right thing
+        XT::Common::FieldMatrix<R, 1, L_r> left_value;
+        left_value[0] = left.evaluate(point, param);
+        return left_value.transpose() * right.evaluate(point, param);
+      } else
+        return left.evaluate(point, param) * right.evaluate(point, param);
+    }
+
+    // special case: non-scalar elementwise multiplication by scalar from the left
+    else if constexpr (L_r == 1 && L_rC == 1 && R_r * R_rC != 1) {
+      const auto left_value = left.evaluate(point, param)[0];
+      RangeReturnType right_value = right.evaluate(point, param);
+      right_value *= left_value;
+      return right_value;
+    }
+    // special case: elementwise multiplication by scalar from the right AND all scalar case
+    else if constexpr (std::is_convertible<decltype(left.evaluate(point, param)), RangeReturnType>::value && R_r == 1
+                       && R_rC == 1) {
+      RangeReturnType left_value = left.evaluate(point, param);
+      const auto right_value = right.evaluate(point, param)[0];
+      left_value *= right_value;
+      return left_value;
+    }
+    // special case: non-scalar vectors
+    else if constexpr (L_r == R_r && R_rC == 1 && L_rC == 1 && L_r != 1) {
+      return left.evaluate(point, param) * right.evaluate(point, param);
+    }
+    DUNE_THROW(NotImplemented, "no product compute combination available");
+  }
+};
+
+template <class comb,
+          class L_R,
+          class R_R,
+          size_t d,
+          size_t L_r,
+          size_t L_rC,
+          size_t R_r,
+          size_t R_rC,
+          typename anything = void>
+struct CombinedJac
+{
+  static const constexpr size_t r = CombinedDim<comb, L_r, L_rC, R_r, R_rC>::r();
+  static const constexpr size_t rC = CombinedDim<comb, L_r, L_rC, R_r, R_rC>::rC();
+
+  using R = typename CombinedEval<comb, L_R, R_R, L_r, L_rC, R_r, R_rC>::R;
+  using DerivativeRangeReturnType = typename DerivativeRangeTypeSelector<d, R, r, rC>::return_type;
+
+  template <class Left, class Right, class D>
+  static DerivativeRangeReturnType
+  compute(const Left& /*left*/, const Right& /*right*/, const D& /*point*/, const Common::Parameter& /*param*/)
+  {
+    DUNE_THROW(Exceptions::combined_error,
+               "Not available for a " << get_combination_name(comb{}) << "of " << L_r << "x" << L_rC << " and " << R_r
+                                      << "x" << R_rC << "!");
+    return DerivativeRangeReturnType();
+  }
+};
+
+template <class L_R, class R_R, size_t d, size_t L_r, size_t L_rC, typename a>
+struct CombinedJac<CombinationType::difference, L_R, R_R, d, L_r, L_rC, L_r, L_rC, a>
+{
+  static const constexpr size_t r = CombinedDim<CombinationType::difference, L_r, L_rC, L_r, L_rC>::r();
+  static const constexpr size_t rC = CombinedDim<CombinationType::difference, L_r, L_rC, L_r, L_rC>::rC();
+
+  using R = typename CombinedEval<CombinationType::difference, L_R, R_R, L_r, L_rC, L_r, L_rC>::R;
+  using DerivativeRangeReturnType = typename DerivativeRangeTypeSelector<d, R, r, rC>::return_type;
+
+  template <class Left, class Right, class D>
+  static DerivativeRangeReturnType
+  compute(const Left& left, const Right& right, const D& point, const Common::Parameter& param)
+  {
+    return left.jacobian(point, param) - right.jacobian(point, param);
+  }
+};
+
+template <class L_R, class R_R, size_t d, size_t L_r, size_t L_rC, typename a>
+struct CombinedJac<CombinationType::sum, L_R, R_R, d, L_r, L_rC, L_r, L_rC, a>
+{
+  static const constexpr size_t r = CombinedDim<CombinationType::sum, L_r, L_rC, L_r, L_rC>::r();
+  static const constexpr size_t rC = CombinedDim<CombinationType::sum, L_r, L_rC, L_r, L_rC>::rC();
+
+  using R = typename CombinedEval<CombinationType::sum, L_R, R_R, L_r, L_rC, L_r, L_rC>::R;
+  using DerivativeRangeReturnType = typename DerivativeRangeTypeSelector<d, R, r, rC>::return_type;
+
+  template <class Left, class Right, class D>
+  static DerivativeRangeReturnType
+  compute(const Left& left, const Right& right, const D& point, const Common::Parameter& param)
+  {
+    return left.jacobian(point, param) - right.jacobian(point, param);
+  }
+};
+
+
+/**
+ * \brief Helper class defining types of combined functions, if available.
+ *
+ * \note Most likely you do not want to use this class directly, but CombinedConstElementFunction or
+ *       CombinedElementFunction.
+ */
+template <class Left, class Right, typename comb>
+struct CombinedHelper
+{
+  static_assert(is_element_function<Left>::value || is_function<Left>::value || is_grid_function<Left>::value, "");
+  static_assert(is_element_function<Right>::value || is_function<Right>::value || is_grid_function<Right>::value, "");
+  static_assert(Left::d == Right::d, "");
+
+  static const constexpr size_t d = Left::d;
+  static const constexpr size_t L_r = Left::r;
+  static const constexpr size_t L_rC = Left::rC;
+  static const constexpr size_t R_r = Right::r;
+  static const constexpr size_t R_rC = Right::rC;
+
+  using CombinedDimHelper = CombinedDim<comb, L_r, L_rC, R_r, R_rC>;
+  static const constexpr bool available = CombinedDimHelper::available();
+  static const constexpr size_t r = CombinedDimHelper::r();
+  static const constexpr size_t rC = CombinedDimHelper::rC();
+
+  static int order(const Left& left, const Right& right, const Common::Parameter& param)
+  {
+    return compute_combined_order(left, right, param, comb{});
+  }
+
+  using L_R = typename Left::R;
+  using R_R = typename Right::R;
+  using CombinedEvalHelper = CombinedEval<comb, L_R, R_R, L_r, L_rC, R_r, R_rC>;
+  using R = typename CombinedEvalHelper::R;
+
+  template <class D>
+  static auto evaluate(const Left& left, const Right& right, const D& point, const Common::Parameter& param)
+  {
+    return CombinedEvalHelper::compute(left, right, point, param);
+  }
+
+  using CombinedJacHelper = CombinedJac<comb, L_R, R_R, d, L_r, L_rC, R_r, R_rC>;
+  using DerivativeRangeReturnType = typename CombinedJacHelper::DerivativeRangeReturnType;
+
+  template <class D>
+  static DerivativeRangeReturnType
+  jacobian(const Left& left, const Right& right, const D& point, const Common::Parameter& param)
+  {
+    return CombinedJacHelper::compute(left, right, point, param);
+  }
+}; // struct CombinedHelper
+
+
+template <class LeftType, class RightType>
+struct CombinedStorageProvider
+{
+  using ThisType = CombinedStorageProvider;
+
+  XT::Common::StorageProvider<LeftType> left;
+  XT::Common::StorageProvider<RightType> right;
+
+  CombinedStorageProvider(LeftType& lft, RightType& rght)
+    : left(lft)
+    , right(rght)
+  {}
+
+  CombinedStorageProvider(std::shared_ptr<LeftType> lft, std::shared_ptr<RightType> rght)
+    : left(lft)
+    , right(rght)
+  {}
+
+  CombinedStorageProvider(std::unique_ptr<LeftType>&& lft, std::unique_ptr<RightType>&& rght)
+    : left(std::move(lft))
+    , right(std::move(rght))
+  {}
+
+  CombinedStorageProvider(const ThisType&) = default;
+
+  CombinedStorageProvider(ThisType&) = default;
+
+  CombinedStorageProvider(ThisType&&) = default;
+}; // struct CombinedStorageProvider
+
+
+} // namespace internal
+} // namespace Functions
+} // namespace XT
+} // namespace Dune
+
+#endif // DUNE_XT_FUNCTIONS_BASE_COMBINED_HH
diff --git a/dune/xt/functions/base/composition.hh b/dune/xt/functions/base/composition.hh
index def02415c26b695d027d7cd783ed889e08c92658..e99739a08a846f371f89f7676dcb2c14032ce5ba 100644
--- a/dune/xt/functions/base/composition.hh
+++ b/dune/xt/functions/base/composition.hh
@@ -243,11 +243,20 @@ public:
 
   CompositionFunction(const ThisType& other) = default;
 
-  ThisType& operator=(const ThisType& other) = delete;
 
-  ThisType& operator=(ThisType&& source) = delete;
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
 
-  virtual std::string name() const override
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
+
+  std::string name() const override final
   {
     return name_;
   }
@@ -255,8 +264,7 @@ public:
   std::unique_ptr<LocalFunctionType> local_function() const override final
   {
     return std::make_unique<ElementFunction>(inner_function_, outer_function_, element_search_);
-  } // ... local_function(...)
-
+  }
 
 private:
   const InnerType inner_function_;
diff --git a/dune/xt/functions/base/derivatives-of-element-functions.hh b/dune/xt/functions/base/derivatives-of-element-functions.hh
index ce74eb4f5ceca9a523f607a7aa1801a2f3de878f..9a27933ee8484e42f6560b2b718d87d6a5683d45 100644
--- a/dune/xt/functions/base/derivatives-of-element-functions.hh
+++ b/dune/xt/functions/base/derivatives-of-element-functions.hh
@@ -27,7 +27,7 @@ namespace internal {
 template <class ElementFunctionType, DerivativeType derivative>
 class DerivativeElementFunctionHelper
 {
-  static_assert(is_element_function<ElementFunctionType>::value, "");
+  static_assert(is_element_function<ElementFunctionType>::value || is_grid_function<ElementFunctionType>::value, "");
 
 public:
   using E = typename ElementFunctionType::E;
@@ -201,7 +201,7 @@ public:
   {}
 
 protected:
-  void post_bind(const ElementType& element)
+  void post_bind(const ElementType& element) override final
   {
     if (do_post_bind_)
       func_.access().bind(element);
diff --git a/dune/xt/functions/base/derivatives-of-grid-functions.hh b/dune/xt/functions/base/derivatives-of-grid-functions.hh
new file mode 100644
index 0000000000000000000000000000000000000000..18f4d8d6caf4a6acb03626167b757f95bbb93c97
--- /dev/null
+++ b/dune/xt/functions/base/derivatives-of-grid-functions.hh
@@ -0,0 +1,130 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2019)
+//   René Fritze     (2019)
+//   Tobias Leibner  (2020)
+
+#ifndef DUNE_XT_FUNCTIONS_BASE_DERIVATIVES_OF_GRID_FUNCTIONS_HH
+#define DUNE_XT_FUNCTIONS_BASE_DERIVATIVES_OF_GRID_FUNCTIONS_HH
+
+#include <dune/xt/common/memory.hh>
+
+#include <dune/xt/functions/grid-function.hh>
+#include <dune/xt/functions/interfaces/grid-function.hh>
+#include <dune/xt/functions/type_traits.hh>
+
+#include "derivatives-of-element-functions.hh"
+
+
+namespace Dune {
+namespace XT {
+namespace Functions {
+
+
+template <class GridFunctionType, DerivativeType derivative>
+class DerivativeGridFunction
+  : public GridFunctionInterface<typename internal::DerivativeElementFunctionHelper<GridFunctionType, derivative>::E,
+                                 internal::DerivativeElementFunctionHelper<GridFunctionType, derivative>::r,
+                                 internal::DerivativeElementFunctionHelper<GridFunctionType, derivative>::rC,
+                                 typename internal::DerivativeElementFunctionHelper<GridFunctionType, derivative>::R>
+{
+  static_assert(is_grid_function<GridFunctionType>::value, "");
+
+  using ThisType = DerivativeGridFunction;
+  using BaseType =
+      GridFunctionInterface<typename internal::DerivativeElementFunctionHelper<GridFunctionType, derivative>::E,
+                            internal::DerivativeElementFunctionHelper<GridFunctionType, derivative>::r,
+                            internal::DerivativeElementFunctionHelper<GridFunctionType, derivative>::rC,
+                            typename internal::DerivativeElementFunctionHelper<GridFunctionType, derivative>::R>;
+
+  using Select = internal::DerivativeElementFunctionHelper<GridFunctionType, derivative>;
+
+  static const constexpr size_t r_ = GridFunctionType::r;
+  static const constexpr size_t rC_ = GridFunctionType::rC;
+
+public:
+  using BaseType::r;
+  using BaseType::rC;
+  using typename BaseType::E;
+  using typename BaseType::LocalFunctionType;
+  using typename BaseType::R;
+
+  DerivativeGridFunction(GridFunction<E, r_, rC_, R> grid_function, const std::string& nm = "")
+    : BaseType(grid_function.parameter_type())
+    , grid_function_(grid_function.copy_as_grid_function())
+    , name_(nm.empty() ? "DerivativeGridFunction" : nm)
+  {}
+
+  DerivativeGridFunction(const ThisType& other)
+    : BaseType(other)
+    , grid_function_(other.grid_function_->copy_as_grid_function())
+    , name_(other.name_)
+  {}
+
+  std::unique_ptr<LocalFunctionType> local_function() const override final
+  {
+    return std::make_unique<DerivativeElementFunction<typename GridFunctionType::LocalFunctionType, derivative>>(
+        grid_function_->local_function());
+  }
+
+
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
+
+  std::string name() const override final
+  {
+    return name_;
+  }
+
+private:
+  std::unique_ptr<GridFunctionInterface<E, r_, rC_, R>> grid_function_;
+  const std::string name_;
+}; // class DerivativeGridFunction
+
+
+template <class GridFunctionType>
+class DivergenceGridFunction : public DerivativeGridFunction<GridFunctionType, DerivativeType::divergence>
+{
+  using BaseType = DerivativeGridFunction<GridFunctionType, DerivativeType::divergence>;
+
+public:
+  template <class... Args>
+  explicit DivergenceGridFunction(Args&&... args)
+    : BaseType(std::forward<Args>(args)...)
+  {}
+}; // class DivergenceGridFunction
+
+
+template <class GridFunctionType>
+class GradientGridFunction : public DerivativeGridFunction<GridFunctionType, DerivativeType::gradient>
+{
+  using BaseType = DerivativeGridFunction<GridFunctionType, DerivativeType::gradient>;
+
+public:
+  template <class... Args>
+  explicit GradientGridFunction(Args&&... args)
+    : BaseType(std::forward<Args>(args)...)
+  {}
+}; // class GradientGridFunction
+
+
+} // namespace Functions
+} // namespace XT
+} // namespace Dune
+
+
+#endif // DUNE_XT_FUNCTIONS_DERIVED_HH
diff --git a/dune/xt/functions/base/function-as-grid-function.hh b/dune/xt/functions/base/function-as-grid-function.hh
index 94d1233f5df5f4f5805b571bcd90317c5ea752de..1d626e9e1e7f71db69d01acee4c213cde294df21 100644
--- a/dune/xt/functions/base/function-as-grid-function.hh
+++ b/dune/xt/functions/base/function-as-grid-function.hh
@@ -38,45 +38,50 @@ public:
   using FunctionType = FunctionInterface<d, r, rC, R>;
 
   FunctionAsGridFunctionWrapper(const FunctionType& function)
-    : function_storage_(function)
+    : BaseType(function.parameter_type())
+    , function_(function.copy_as_function())
   {}
 
   FunctionAsGridFunctionWrapper(FunctionType*&& function_ptr)
-    : function_storage_(std::move(function_ptr))
+    : BaseType(function_ptr->parameter_type())
+    , function_(std::move(function_ptr))
   {}
 
   FunctionAsGridFunctionWrapper(std::unique_ptr<FunctionType>&& function_ptr)
-    : function_storage_(std::move(function_ptr))
+    : BaseType(function_ptr->parameter_type())
+    , function_(std::move(function_ptr))
+  {}
+
+  FunctionAsGridFunctionWrapper(const ThisType& other)
+    : BaseType(other)
+    , function_(other.function_->copy_as_function())
   {}
 
-  FunctionAsGridFunctionWrapper(const ThisType&) = default;
   FunctionAsGridFunctionWrapper(ThisType&&) = default;
 
-  /**
-   * \name ´´This method is required by GridFunctionInterface.''
-   * \{
-   **/
 
-  std::unique_ptr<LocalFunctionType> local_function() const override final
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
   {
-    return std::make_unique<LocalFunction>(function_storage_.access());
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
   }
 
-  /**
-   * \}
-   * \name ´´These methods are optionally required by GridFunctionInterface.''
-   * \{
-   **/
+  std::unique_ptr<LocalFunctionType> local_function() const override final
+  {
+    return std::make_unique<LocalFunction>(*function_);
+  }
 
   std::string name() const override final
   {
-    return function_storage_.access().name();
+    return function_->name();
   }
 
-  /**
-   * \}
-   **/
-
 private:
   class LocalFunction : public LocalFunctionType
   {
@@ -90,7 +95,7 @@ private:
 
     LocalFunction(const FunctionType& function)
       : BaseType()
-      , function_(function)
+      , function_(function.copy_as_function())
       , geometry_(nullptr)
     {}
 
@@ -103,8 +108,8 @@ private:
   public:
     int order(const Common::Parameter& param = {}) const override final
     {
-      DUNE_THROW_IF(!(geometry_), Exceptions::not_bound_to_an_element_yet, function_.name());
-      return function_.order(param);
+      DUNE_THROW_IF(!(geometry_), Exceptions::not_bound_to_an_element_yet, function_->name());
+      return function_->order(param);
     }
 
     using BaseType::evaluate;
@@ -112,9 +117,9 @@ private:
     RangeReturnType evaluate(const DomainType& point_in_reference_element,
                              const Common::Parameter& param = {}) const override final
     {
-      DUNE_THROW_IF(!(geometry_), Exceptions::not_bound_to_an_element_yet, function_.name());
+      DUNE_THROW_IF(!(geometry_), Exceptions::not_bound_to_an_element_yet, function_->name());
       this->assert_inside_reference_element(point_in_reference_element);
-      return function_.evaluate(geometry_->global(point_in_reference_element), param);
+      return function_->evaluate(geometry_->global(point_in_reference_element), param);
     }
 
     using BaseType::jacobian;
@@ -122,9 +127,9 @@ private:
     DerivativeRangeReturnType jacobian(const DomainType& point_in_reference_element,
                                        const Common::Parameter& param = {}) const override final
     {
-      DUNE_THROW_IF(!(geometry_), Exceptions::not_bound_to_an_element_yet, function_.name());
+      DUNE_THROW_IF(!(geometry_), Exceptions::not_bound_to_an_element_yet, function_->name());
       this->assert_inside_reference_element(point_in_reference_element);
-      return function_.jacobian(geometry_->global(point_in_reference_element), param);
+      return function_->jacobian(geometry_->global(point_in_reference_element), param);
     }
 
     using BaseType::derivative;
@@ -133,17 +138,17 @@ private:
                                          const DomainType& point_in_reference_element,
                                          const Common::Parameter& param = {}) const override final
     {
-      DUNE_THROW_IF(!(geometry_), Exceptions::not_bound_to_an_element_yet, function_.name());
+      DUNE_THROW_IF(!(geometry_), Exceptions::not_bound_to_an_element_yet, function_->name());
       this->assert_inside_reference_element(point_in_reference_element);
-      return function_.derivative(alpha, geometry_->global(point_in_reference_element), param);
+      return function_->derivative(alpha, geometry_->global(point_in_reference_element), param);
     }
 
   private:
-    const FunctionType& function_;
+    std::unique_ptr<FunctionType> function_;
     std::unique_ptr<GeometryType> geometry_;
   }; // class LocalFunction
 
-  XT::Common::ConstStorageProvider<FunctionType> function_storage_;
+  std::unique_ptr<FunctionType> function_;
 }; // class FunctionAsGridFunctionWrapper
 
 
diff --git a/dune/xt/functions/base/reinterpret.hh b/dune/xt/functions/base/reinterpret.hh
index b2c6c569094668050c13f6832b1c499c1431dda6..00adcf3ceea03ad6275c78b64657e7e9a20db1c5 100644
--- a/dune/xt/functions/base/reinterpret.hh
+++ b/dune/xt/functions/base/reinterpret.hh
@@ -42,7 +42,8 @@ class ReinterpretLocalizableFunction
   : public GridFunctionInterface<TargetElement, range_dim, range_dim_cols, RangeField>
 {
   static_assert(XT::Grid::is_layer<SourceGridView>::value, "");
-  using ThisType = ReinterpretLocalizableFunction<SourceGridView, TargetElement, range_dim, range_dim_cols, RangeField>;
+
+  using ThisType = ReinterpretLocalizableFunction;
   using BaseType = GridFunctionInterface<TargetElement, range_dim, range_dim_cols, RangeField>;
 
 public:
@@ -53,25 +54,41 @@ public:
 
   using SourceType = GridFunctionInterface<XT::Grid::extract_entity_t<SourceGridView>, r, rC, R>;
 
-  static std::string static_id()
-  {
-    return BaseType::static_id() + ".reinterpret";
-  }
-
   ReinterpretLocalizableFunction(const SourceType& source, const SourceGridView& source_grid_view)
     : BaseType(source.parameter_type())
-    , source_(source)
+    , source_(source.copy_as_grid_function())
     , source_grid_view_(source_grid_view)
   {}
 
+  ReinterpretLocalizableFunction(const ThisType& other)
+    : BaseType(other)
+    , source_(other.source_->copy_as_grid_function())
+    , source_grid_view_(other.source_grid_view_)
+  {}
+
+  ReinterpretLocalizableFunction(ThisType&&) = default;
+
+
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
+
   std::unique_ptr<LocalFunctionType> local_function() const override final
   {
-    return std::make_unique<ReinterpretLocalfunction>(source_, source_grid_view_);
+    return std::make_unique<ReinterpretLocalfunction>(*source_, source_grid_view_);
   }
 
   std::string name() const
   {
-    return BaseType::name() + ".reinterpret";
+    return source_->name();
   }
 
 private:
@@ -88,10 +105,10 @@ private:
 
     ReinterpretLocalfunction(const SourceType& source, const SourceGridView& source_grid_view)
       : BaseType(source.parameter_type())
-      , source_(source)
+      , source_(source.copy_as_grid_function())
       , source_grid_view_(source_grid_view)
       , source_element_search_(source_grid_view_)
-      , local_source_(source_.local_function())
+      , local_source_(source_->local_function())
       , source_element_which_contains_complete_target_element_(nullptr)
       , source_element_which_contains_some_point_of_target_element_(nullptr)
       , local_source_valid_for_this_point_(false)
@@ -214,7 +231,7 @@ private:
       }
     } // ... try_to_bind_local_source_for_this_point(...)
 
-    const SourceType& source_;
+    const std::unique_ptr<SourceType> source_;
     const SourceGridView& source_grid_view_;
     mutable XT::Grid::EntityInlevelSearch<SourceGridView> source_element_search_;
     mutable std::unique_ptr<typename SourceType::LocalFunctionType> local_source_;
@@ -227,7 +244,7 @@ private:
     mutable std::vector<DomainType> single_point_;
   }; // class ReinterpretLocalfunction
 
-  const SourceType& source_;
+  std::unique_ptr<SourceType> source_;
   const SourceGridView& source_grid_view_;
 }; // class ReinterpretLocalizableFunction
 
diff --git a/dune/xt/functions/base/sliced.hh b/dune/xt/functions/base/sliced.hh
index efaf20a8a7d6e69ee320fedba6621344ee1e1810..2558782933d25b5072b5bcd614520f5a4f4c70d0 100644
--- a/dune/xt/functions/base/sliced.hh
+++ b/dune/xt/functions/base/sliced.hh
@@ -44,16 +44,18 @@ auto density_times_velocity = XT::Functions::make_sliced_function<d>(u, {1, 2},
 auto energy                 = XT::Functions::make_sliced_function<1>(u, {3},    "energy");
 \endcode
  */
-template <class LF, size_t r>
-class SlicedGridFunction<LF, r, 1> : public XT::Functions::GridFunctionInterface<typename LF::E, r, 1, typename LF::R>
+template <class GF, size_t r>
+class SlicedGridFunction<GF, r, 1> : public XT::Functions::GridFunctionInterface<typename GF::E, r, 1, typename GF::R>
 {
-  static_assert(is_grid_function<LF>::value, "");
-  static_assert(r <= LF::r, "Does not make sense!");
-  using BaseType = XT::Functions::GridFunctionInterface<typename LF::E, r, 1, typename LF::R>;
+  static_assert(is_grid_function<GF>::value, "");
+  static_assert(r <= GF::r, "Does not make sense!");
 
-  class SlicedLocalFunction : public XT::Functions::ElementFunctionInterface<typename LF::E, r, 1, typename LF::R>
+  using ThisType = SlicedGridFunction;
+  using BaseType = XT::Functions::GridFunctionInterface<typename GF::E, r, 1, typename GF::R>;
+
+  class SlicedLocalFunction : public XT::Functions::ElementFunctionInterface<typename GF::E, r, 1, typename GF::R>
   {
-    using BaseType = XT::Functions::ElementFunctionInterface<typename LF::E, r, 1, typename LF::R>;
+    using BaseType = XT::Functions::ElementFunctionInterface<typename GF::E, r, 1, typename GF::R>;
 
   public:
     using typename BaseType::DerivativeRangeReturnType;
@@ -63,10 +65,10 @@ class SlicedGridFunction<LF, r, 1> : public XT::Functions::GridFunctionInterface
     using typename BaseType::RangeReturnType;
     using typename BaseType::RangeType;
 
-
-    SlicedLocalFunction(const LF& function, const std::array<size_t, r>& dims)
-      : BaseType()
-      , local_function_(function.local_function())
+    SlicedLocalFunction(const GF& function, const std::array<size_t, r>& dims)
+      : BaseType(function.parameter_type())
+      , function_(function.copy_as_grid_function())
+      , local_function_(function_->local_function())
       , dims_(dims)
     {}
 
@@ -98,39 +100,60 @@ class SlicedGridFunction<LF, r, 1> : public XT::Functions::GridFunctionInterface
     }
 
   private:
-    std::unique_ptr<typename LF::LocalFunctionType> local_function_;
-    const std::array<size_t, r>& dims_;
+    std::unique_ptr<GridFunctionInterface<typename GF::E, GF::r, GF::rC, typename GF::R>> function_;
+    std::unique_ptr<typename GF::LocalFunctionType> local_function_;
+    const std::array<size_t, r> dims_;
   }; // class SlicedLocalFunction
 
 public:
-  using typename BaseType::ElementType;
+  using typename BaseType::E;
   using typename BaseType::LocalFunctionType;
 
-  SlicedGridFunction(const LF& function, const std::array<size_t, r>& dims, const std::string& nm = "")
-    : function_(function)
+  SlicedGridFunction(const GF& function, const std::array<size_t, r>& dims, const std::string& nm = "")
+    : BaseType(function.parameter_type())
+    , function_(function.copy_as_grid_function())
     , dims_(dims)
     , name_(nm)
   {
     for (size_t ii = 0; ii < r; ++ii)
-      if (dims_[ii] >= LF::r)
+      if (dims_[ii] >= GF::r)
         DUNE_THROW(InvalidStateException,
-                   "LF::r = " << LF::r << "\n   "
+                   "GF::r = " << GF::r << "\n   "
                               << "r = " << r << "\n   "
                               << "dims[" << ii << "] = " << dims_[ii]);
   }
 
+  SlicedGridFunction(const ThisType& other)
+    : BaseType(other)
+    , function_(other.function_->copy_as_grid_function())
+  {}
+
+  SlicedGridFunction(ThisType&&) = default;
+
+
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
   std::string name() const override final
   {
-    return name_.empty() ? "sliced " + function_.name() : name_;
+    return name_.empty() ? "sliced " + function_->name() : name_;
   }
 
   std::unique_ptr<LocalFunctionType> local_function() const override final
   {
-    return std::make_unique<SlicedLocalFunction>(function_, dims_);
+    return std::make_unique<SlicedLocalFunction>(*function_, dims_);
   }
 
 private:
-  const LF& function_;
+  std::unique_ptr<GridFunctionInterface<typename GF::E, GF::r, GF::rC, typename GF::R>> function_;
   const std::array<size_t, r> dims_;
   const std::string& name_;
 }; // class SlicedGridFunction
diff --git a/dune/xt/functions/base/transformed.hh b/dune/xt/functions/base/transformed.hh
index 5df43f34df4dd11312d2531bbd085025fe3b082e..a9a527edc8747e6dac2b09e963f2f89946d80f5b 100644
--- a/dune/xt/functions/base/transformed.hh
+++ b/dune/xt/functions/base/transformed.hh
@@ -49,16 +49,18 @@ const auto to_primitive = [&](const auto& conservative_variables) {
 auto u_primitive = XT::Functions::make_transformed_function<d + 2, 1, R>(u_conservative, to_primitive);
 \endcode
  */
-template <class LF, size_t r = LF::r, size_t rC = LF::rC, class R = typename LF::R>
-class TransformedGridFunction : public XT::Functions::GridFunctionInterface<typename LF::E, r, rC, R>
+template <class GF, size_t r = GF::r, size_t rC = GF::rC, class R = typename GF::R>
+class TransformedGridFunction : public XT::Functions::GridFunctionInterface<typename GF::E, r, rC, R>
 {
-  static_assert(is_grid_function<LF>::value, "");
-  using BaseType = XT::Functions::GridFunctionInterface<typename LF::E, r, rC, R>;
+  static_assert(is_grid_function<GF>::value, "");
 
-  class TransformedLocalFunction : public XT::Functions::ElementFunctionInterface<typename LF::E, r, rC, R>
+  using ThisType = TransformedGridFunction;
+  using BaseType = XT::Functions::GridFunctionInterface<typename GF::E, r, rC, R>;
+
+  class TransformedLocalFunction : public XT::Functions::ElementFunctionInterface<typename GF::E, r, rC, R>
   {
-    using BaseType = XT::Functions::ElementFunctionInterface<typename LF::E, r, rC, R>;
-    using UntransformedLocalFunctionType = typename LF::LocalFunctionType;
+    using BaseType = XT::Functions::ElementFunctionInterface<typename GF::E, r, rC, R>;
+    using UntransformedLocalFunctionType = typename GF::LocalFunctionType;
 
   public:
     using UntransformedRangeType = typename UntransformedLocalFunctionType::RangeType;
@@ -70,9 +72,10 @@ class TransformedGridFunction : public XT::Functions::GridFunctionInterface<type
     using typename BaseType::RangeType;
     using Transformation = std::function<RangeType(const UntransformedRangeType&)>;
 
-    TransformedLocalFunction(const LF& function, const Transformation& transformation)
-      : BaseType()
-      , local_function_(function.local_function())
+    TransformedLocalFunction(const GF& function, const Transformation& transformation)
+      : BaseType(function.paramter_type())
+      , function_(function.copy_as_grid_function())
+      , local_function_(function_->local_function())
       , transformation_(transformation)
     {}
 
@@ -100,6 +103,7 @@ class TransformedGridFunction : public XT::Functions::GridFunctionInterface<type
     }
 
   private:
+    std::unique_ptr<GridFunctionInterface<typename GF::E, GF::r, GF::rC, typename GF::R>> function_;
     std::unique_ptr<UntransformedLocalFunctionType> local_function_;
     const Transformation& transformation_;
   }; // class TransformedLocalFunction
@@ -110,17 +114,39 @@ public:
   using UntransformedRangeType = typename TransformedLocalFunction::UntransformedRangeType;
   using TransformedRangeType = typename TransformedLocalFunction::RangeType;
 
-  TransformedGridFunction(const LF& f,
+  TransformedGridFunction(const GF& func,
                           std::function<TransformedRangeType(const UntransformedRangeType&)> transformation,
                           const std::string& nm = "")
-    : function_(f)
+    : BaseType(func.parameter_type())
+    , function_(func.copy_as_grid_function())
     , transformation_(transformation)
-    , name_(nm)
+    , name_(nm.empty() ? "transformed " + function_.name() : nm)
+  {}
+
+  TransformedGridFunction(const ThisType& other)
+    : BaseType(other)
+    , function_(other.function_->copy_as_grid_function())
+    , transformation_(other.transformation_)
+    , name_(other.name_)
   {}
 
+  TransformedGridFunction(ThisType&&) = default;
+
+
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
   std::string name() const override final
   {
-    return name_.empty() ? "transformed " + function_.name() : name_;
+    return name_;
   }
 
   std::unique_ptr<LocalFunctionType> local_function() const override final
@@ -129,7 +155,7 @@ public:
   }
 
 private:
-  const LF& function_;
+  std::unique_ptr<GridFunctionInterface<typename GF::E, GF::r, GF::rC, typename GF::R>> function_;
   const typename TransformedLocalFunction::Transformation transformation_;
   const std::string name_;
 }; // class TransformedGridFunction
diff --git a/dune/xt/functions/base/visualization.hh b/dune/xt/functions/base/visualization.hh
index a621b9dad8a7110432fbca796cb33a4802758bc4..7b4bb2358bcf45c4aee42bddcc4352e5bdf5fa44 100644
--- a/dune/xt/functions/base/visualization.hh
+++ b/dune/xt/functions/base/visualization.hh
@@ -314,4 +314,6 @@ private:
 } // namespace XT
 } // namespace Dune
 
+#include <dune/xt/functions/interfaces/grid-function.hh>
+
 #endif // DUNE_XT_FUNCTIONS_VISUALIZATION_HH
diff --git a/dune/xt/functions/checkerboard.hh b/dune/xt/functions/checkerboard.hh
index 499fb09014cb4d7fe75cbf48f9de301bfc6fad36..c3aae488e4a4cb9ec8907d33c3d1612d4239fb58 100644
--- a/dune/xt/functions/checkerboard.hh
+++ b/dune/xt/functions/checkerboard.hh
@@ -15,7 +15,6 @@
 #define DUNE_XT_FUNCTIONS_CHECKERBOARD_HH
 
 #include <dune/xt/common/configuration.hh>
-
 #include <dune/xt/functions/interfaces/grid-function.hh>
 
 namespace Dune {
@@ -50,7 +49,7 @@ class CheckerboardFunction : public GridFunctionInterface<E, r, rC, R>
     LocalCheckerboardFunction(const DomainType& lower_left,
                               const DomainType& upper_right,
                               const FieldVector<size_t, domain_dim>& num_elements,
-                              std::vector<RangeType>& values)
+                              std::shared_ptr<std::vector<RangeType>> values)
       : InterfaceType()
       , lower_left_(lower_left)
       , upper_right_(upper_right)
@@ -64,7 +63,7 @@ class CheckerboardFunction : public GridFunctionInterface<E, r, rC, R>
       current_value_ = 0;
       if (is_in_checkerboard(element)) {
         const size_t subdomain = find_subdomain(element);
-        current_value_ = values_[subdomain];
+        current_value_ = (*values_)[subdomain];
       }
     }
 
@@ -126,7 +125,7 @@ class CheckerboardFunction : public GridFunctionInterface<E, r, rC, R>
     const DomainType lower_left_;
     const DomainType upper_right_;
     const FieldVector<size_t, domain_dim> num_elements_;
-    const std::vector<RangeType>& values_;
+    const std::shared_ptr<std::vector<RangeType>> values_;
     RangeType current_value_;
   }; // class LocalCheckerboardFunction
 
@@ -158,12 +157,12 @@ public:
   CheckerboardFunction(const DomainType& lower_left,
                        const DomainType& upper_right,
                        const FieldVector<size_t, domain_dim>& num_elements,
-                       const std::vector<RangeType>& values,
-                       const std::string nm = "checkerboard")
+                       std::shared_ptr<std::vector<RangeType>> values,
+                       const std::string nm = "CheckerboardFunction")
     : lower_left_(lower_left)
     , upper_right_(upper_right)
     , num_elements_(num_elements)
-    , values_(new std::vector<RangeType>(values))
+    , values_(values)
     , name_(nm)
   {
 #ifndef NDEBUG
@@ -183,12 +182,30 @@ public:
 #endif
   } // CheckerboardFunction(...)
 
+  CheckerboardFunction(const DomainType& lower_left,
+                       const DomainType& upper_right,
+                       const FieldVector<size_t, domain_dim>& num_elements,
+                       const std::vector<RangeType>& values,
+                       const std::string nm = "CheckerboardFunction")
+    : CheckerboardFunction(lower_left, upper_right, num_elements, std::make_shared<std::vector<RangeType>>(values), nm)
+  {}
+
   CheckerboardFunction(const ThisType& other) = default;
+
   CheckerboardFunction(ThisType&& source) = default;
 
-  ThisType& operator=(const ThisType& other) = delete;
-  ThisType& operator=(ThisType&& source) = delete;
 
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
   std::string name() const override
   {
     return name_;
@@ -196,7 +213,7 @@ public:
 
   std::unique_ptr<LocalFunctionType> local_function() const override final
   {
-    return std::make_unique<LocalCheckerboardFunction>(lower_left_, upper_right_, num_elements_, *values_);
+    return std::make_unique<LocalCheckerboardFunction>(lower_left_, upper_right_, num_elements_, values_);
   }
 
   size_t subdomain(const ElementType& element) const
diff --git a/dune/xt/functions/constant.hh b/dune/xt/functions/constant.hh
index 772d03b48036631e2e58d6e574633696903bb753..708d22322021094419cf427aeb74d7df11b80dff 100644
--- a/dune/xt/functions/constant.hh
+++ b/dune/xt/functions/constant.hh
@@ -26,10 +26,10 @@ namespace XT {
 namespace Functions {
 
 
-template <size_t d, size_t r = 1, size_t rC = 1, class RangeField = double>
-class ConstantFunction : public FunctionInterface<d, r, rC, RangeField>
+template <size_t d, size_t r = 1, size_t rC = 1, class R = double>
+class ConstantFunction : public FunctionInterface<d, r, rC, R>
 {
-  using BaseType = FunctionInterface<d, r, rC, RangeField>;
+  using BaseType = FunctionInterface<d, r, rC, R>;
   using ThisType = ConstantFunction;
 
 public:
@@ -57,13 +57,25 @@ public:
   explicit ConstantFunction(const RangeReturnType& value, const std::string nm = "")
     : value_(value)
     , name_(nm.empty() ? ((r == 1 && rC == 1) ? std::string("ConstantFunction(" + Common::to_string(value) + ")")
-                                              : static_id())
+                                              : "ConstantFunction")
                        : nm)
   {}
 
-#if !DUNE_XT_WITH_PYTHON_BINDINGS
-  ConstantFunction(const ThisType& other) = default;
-#endif
+  ConstantFunction(const ThisType&) = default;
+
+  ConstantFunction(ThisType&) = default;
+
+private:
+  ThisType* copy_as_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_function_impl());
+  }
 
   int order(const XT::Common::Parameter& /*param*/ = {}) const override final
   {
@@ -92,45 +104,40 @@ public:
 }; // class ConstantFunction
 
 
-template <class Element, size_t rangeDim = 1, size_t rangeDimCols = 1, class RangeField = double>
-class ConstantGridFunction : public GridFunctionInterface<Element, rangeDim, rangeDimCols, RangeField>
+template <class E, size_t r = 1, size_t rC = 1, class R = double>
+class ConstantGridFunction : public FunctionAsGridFunctionWrapper<E, r, rC, R>
 {
-  using BaseType = GridFunctionInterface<Element, rangeDim, rangeDimCols, RangeField>;
+  using ThisType = ConstantGridFunction;
+  using BaseType = FunctionAsGridFunctionWrapper<E, r, rC, R>;
 
 public:
   using typename BaseType::LocalFunctionType;
 
-  ConstantGridFunction(const typename LocalFunctionType::RangeReturnType constant,
-                       const std::string name_in = static_id())
-    : constant_function_(constant, name_in)
-    , constant_grid_function_(constant_function_)
+  ConstantGridFunction(const typename LocalFunctionType::RangeReturnType value, const std::string nm = "")
+    : BaseType(ConstantFunction<E::dimension, r, rC, R>(
+        value,
+        nm.empty() ? ((r == 1 && rC == 1) ? std::string("ConstantGridFunction(" + Common::to_string(value) + ")")
+                                          : "ConstantGridFunction")
+                   : nm))
   {}
-
-  static std::string static_id()
-  {
-    return "dune.xt.functions.constantgridfunction";
-  }
-
-  std::unique_ptr<LocalFunctionType> local_function() const override final
+  std::unique_ptr<ThisType> copy_as_grid_function() const
   {
-    return constant_grid_function_.local_function();
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
   }
 
-  std::string name() const override final
+private:
+  ThisType* copy_as_grid_function_impl() const override
   {
-    return constant_function_.name();
+    return new ThisType(*this);
   }
 
-private:
-  ConstantFunction<BaseType::domain_dim, rangeDim, rangeDimCols, RangeField> constant_function_;
-  FunctionAsGridFunctionWrapper<Element, rangeDim, rangeDimCols, RangeField> constant_grid_function_;
 }; // class ConstantGridFunction
 
 
-template <class Element, size_t stateDim, size_t rangeDim = 1, size_t rangeDimCols = 1, class RangeField = double>
-class ConstantFluxFunction : public FluxFunctionInterface<Element, stateDim, rangeDim, rangeDimCols, RangeField>
+template <class E, size_t stateDim, size_t r = 1, size_t rC = 1, class R = double>
+class ConstantFluxFunction : public FluxFunctionInterface<E, stateDim, r, rC, R>
 {
-  using BaseType = FluxFunctionInterface<Element, stateDim, rangeDim, rangeDimCols, RangeField>;
+  using BaseType = FluxFunctionInterface<E, stateDim, r, rC, R>;
 
 public:
   using typename BaseType::LocalFunctionType;
@@ -162,8 +169,8 @@ public:
   }
 
 private:
-  ConstantFunction<stateDim, rangeDim, rangeDimCols, RangeField> constant_function_;
-  StateFunctionAsFluxFunctionWrapper<Element, stateDim, rangeDim, rangeDimCols, RangeField> constant_flux_function_;
+  ConstantFunction<stateDim, r, rC, R> constant_function_;
+  StateFunctionAsFluxFunctionWrapper<E, stateDim, r, rC, R> constant_flux_function_;
 }; // class ConstantGridFunction
 
 
diff --git a/dune/xt/functions/derivatives.hh b/dune/xt/functions/derivatives.hh
index 497f8a2071da0cdc85e266d1e9bb1a9c12e97e2e..8d2c1ba328126f0852c941c76fb22a842f752877 100644
--- a/dune/xt/functions/derivatives.hh
+++ b/dune/xt/functions/derivatives.hh
@@ -13,7 +13,9 @@
 #define DUNE_XT_FUNCTIONS_DERIVATIVES_HH
 
 #include <dune/xt/functions/base/derivatives-of-element-functions.hh>
+#include <dune/xt/functions/base/derivatives-of-grid-functions.hh>
 #include <dune/xt/functions/interfaces/element-functions.hh>
+#include <dune/xt/functions/interfaces/grid-function.hh>
 
 namespace Dune {
 namespace XT {
@@ -27,13 +29,33 @@ DivergenceElementFunction<ElementFunctionInterface<E, E::dimension, 1, R>>
   return DivergenceElementFunction<ElementFunctionInterface<E, E::dimension, 1, R>>(func);
 }
 
+template <class E, class R>
+DivergenceElementFunction<ElementFunctionInterface<E, E::dimension, 1, R>>
+divergence(const ElementFunctionInterface<E, E::dimension, 1, R>& func)
+{
+  return DivergenceElementFunction<ElementFunctionInterface<E, E::dimension, 1, R>>(func);
+}
+
+template <class E, class R>
+DivergenceGridFunction<GridFunctionInterface<E, E::dimension, 1, R>>
+divergence(const GridFunctionInterface<E, E::dimension, 1, R>& func)
+{
+  return DivergenceGridFunction<GridFunctionInterface<E, E::dimension, 1, R>>(func);
+}
+
 
 template <class E, class R>
-GradientElementFunction<ElementFunctionInterface<E, 1, 1, R>> gradient(ElementFunctionInterface<E, 1, 1, R>& func)
+GradientElementFunction<ElementFunctionInterface<E, 1, 1, R>> gradient(const ElementFunctionInterface<E, 1, 1, R>& func)
 {
   return GradientElementFunction<ElementFunctionInterface<E, 1, 1, R>>(func);
 }
 
+template <class E, class R>
+GradientGridFunction<GridFunctionInterface<E, 1, 1, R>> gradient(const GridFunctionInterface<E, 1, 1, R>& func)
+{
+  return GradientGridFunction<GridFunctionInterface<E, 1, 1, R>>(func);
+}
+
 
 } // namespace Functions
 } // namespace XT
diff --git a/dune/xt/functions/elementwise-diameter.hh b/dune/xt/functions/elementwise-diameter.hh
new file mode 100644
index 0000000000000000000000000000000000000000..16917840edbf78764de1d51f554c8f4490dd5a49
--- /dev/null
+++ b/dune/xt/functions/elementwise-diameter.hh
@@ -0,0 +1,114 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+#ifndef DUNE_XT_FUNCTIONS_ELEMENTWISE_DIAMETER_HH
+#define DUNE_XT_FUNCTIONS_ELEMENTWISE_DIAMETER_HH
+
+#include <dune/xt/functions/interfaces/grid-function.hh>
+
+namespace Dune {
+namespace XT {
+namespace Functions {
+
+
+template <class E>
+class ElementwiseDiameterFunction : public GridFunctionInterface<E>
+{
+  using ThisType = ElementwiseDiameterFunction;
+  using BaseType = GridFunctionInterface<E>;
+
+  class LocalFunction : public XT::Functions::ElementFunctionInterface<E>
+  {
+    using BaseType = XT::Functions::ElementFunctionInterface<E>;
+
+  public:
+    using typename BaseType::DerivativeRangeReturnType;
+    using typename BaseType::DomainType;
+    using typename BaseType::ElementType;
+    using typename BaseType::RangeReturnType;
+
+    LocalFunction()
+      : BaseType()
+      , diameter_(0)
+    {}
+
+  protected:
+    void post_bind(const ElementType& element) override final
+    {
+      diameter_ = Grid::diameter(element);
+    }
+
+  public:
+    int order(const XT::Common::Parameter& /*param*/ = {}) const override final
+    {
+      return 0;
+    }
+
+    RangeReturnType evaluate(const DomainType& /*xx*/, const XT::Common::Parameter& /*param*/ = {}) const override final
+    {
+      return diameter_;
+    }
+
+    DerivativeRangeReturnType jacobian(const DomainType& /*xx*/,
+                                       const XT::Common::Parameter& /*param*/ = {}) const override final
+    {
+      return DerivativeRangeReturnType();
+    }
+
+  private:
+    double diameter_;
+  }; // class LocalFunction
+
+public:
+  using BaseType::d;
+  using BaseType::r;
+  using BaseType::rC;
+  using typename BaseType::LocalFunctionType;
+
+  ElementwiseDiameterFunction(const std::string nm = "ElementwiseDiameterFunction")
+    : BaseType()
+    , name_(nm)
+  {}
+
+  ElementwiseDiameterFunction(const ThisType&) = default;
+
+  ElementwiseDiameterFunction(ThisType&&) = default;
+
+
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
+  std::unique_ptr<LocalFunctionType> local_function() const override final
+  {
+    return std::make_unique<LocalFunction>();
+  }
+
+  std::string name() const override final
+  {
+    return name_;
+  }
+
+private:
+  std::string name_;
+}; // class ElementwiseDiameterFunction
+
+
+} // namespace Functions
+} // namespace XT
+} // namespace Dune
+
+#endif // DUNE_XT_FUNCTIONS_ELEMENTWISE_DIAMETER_HH
diff --git a/dune/xt/functions/elementwise-minimum.hh b/dune/xt/functions/elementwise-minimum.hh
new file mode 100644
index 0000000000000000000000000000000000000000..b36be48d62c5bb152b7d8572c8f405ea0966a5d3
--- /dev/null
+++ b/dune/xt/functions/elementwise-minimum.hh
@@ -0,0 +1,208 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+#ifndef DUNE_XT_FUNCTIONS_ELEMENTWISE_MINIMUM_HH
+#define DUNE_XT_FUNCTIONS_ELEMENTWISE_MINIMUM_HH
+
+#include <dune/geometry/quadraturerules.hh>
+
+#include <dune/xt/la/eigen-solver.hh>
+#include <dune/xt/functions/interfaces/grid-function.hh>
+#include <dune/xt/functions/grid-function.hh>
+#include <dune/xt/functions/type_traits.hh>
+
+namespace Dune {
+namespace XT {
+namespace Functions {
+namespace internal {
+
+
+template <class FunctionType>
+class ElementwiseMinimumFunctionHelper
+{
+  static_assert(is_element_function<FunctionType>::value, "");
+
+public:
+  static const constexpr size_t d = FunctionType::d;
+  static const constexpr size_t r = FunctionType::r;
+  static const constexpr size_t rC = FunctionType::rC;
+  using E = typename FunctionType::E;
+  using R = typename FunctionType::R;
+  using DomainType = Dune::FieldVector<double, d>;
+
+  template <size_t r_ = r, size_t rC_ = rC, bool anything = true>
+  struct dim_switch
+  {};
+
+  template <bool anything>
+  struct dim_switch<1, 1, anything>
+  {
+    static R compute(const FunctionType& func, const int order, const XT::Common::Parameter& param)
+    {
+      // approximate minimum over the element (evaluate at some points)
+      double min = std::numeric_limits<double>::max();
+      for (auto&& quadrature_point : QuadratureRules<double, d>::rule(func.element().type(), order))
+        min = std::min(min, func.evaluate(quadrature_point.position(), param)[0]);
+      return min;
+    }
+  };
+
+  template <size_t r_, bool anything>
+  struct dim_switch<r_, r_, anything>
+  {
+    static R compute(const FunctionType& func, const int order, const XT::Common::Parameter& param)
+    {
+      // approximate minimum eigenvalue over the element (evaluate at some points)
+      double min_EV = std::numeric_limits<double>::max();
+      for (auto&& quadrature_point : QuadratureRules<double, d>::rule(func.element().type(), order)) {
+        auto value = func.evaluate(quadrature_point.position(), param);
+        auto eigen_solver =
+            XT::LA::make_eigen_solver(value,
+                                      {{"type", XT::LA::EigenSolverOptions<decltype(value)>::types().at(0)},
+                                       {"assert_positive_eigenvalues", "1e-15"}});
+        min_EV = std::min(min_EV, eigen_solver.min_eigenvalues(1).at(0));
+      }
+      return min_EV;
+    }
+  };
+
+public:
+  static R compute(const FunctionType& func, const int order, const XT::Common::Parameter& param)
+  {
+    return dim_switch<>::compute(func, order, param);
+  }
+}; // class ElementwiseMinimumFunctionHelper
+
+
+} // namespace internal
+
+
+/// \todo Consider searching the elements corners!
+template <class SomeFunction>
+class ElementwiseMinimumFunction : public GridFunctionInterface<typename SomeFunction::E>
+{
+  static_assert(is_function<SomeFunction>::value || is_grid_function<SomeFunction>::value, "");
+
+  using ThisType = ElementwiseMinimumFunction;
+  using BaseType = GridFunctionInterface<typename SomeFunction::E>;
+
+  static const constexpr size_t r_ = SomeFunction::r;
+  static const constexpr size_t rC_ = SomeFunction::rC;
+
+public:
+  using BaseType::r;
+  using BaseType::rC;
+  using typename BaseType::E;
+  using typename BaseType::LocalFunctionType;
+  using typename BaseType::R;
+
+private:
+  static_assert(r == rC, "");
+
+  class LocalFunction : public XT::Functions::ElementFunctionInterface<E>
+  {
+    using BaseType = XT::Functions::ElementFunctionInterface<E>;
+
+  public:
+    using typename BaseType::DerivativeRangeReturnType;
+    using typename BaseType::DomainType;
+    using typename BaseType::ElementType;
+    using typename BaseType::RangeReturnType;
+
+    LocalFunction(const GridFunctionInterface<E, r_, rC_, R>& some_func, const int search_quadrature_order)
+      : BaseType()
+      , some_func_(some_func.copy_as_grid_function())
+      , some_lf_(some_func_->local_function())
+      , search_quadrature_order_(search_quadrature_order)
+      , min_(0)
+    {}
+
+  protected:
+    void post_bind(const ElementType& element) override final
+    {
+      some_lf_->bind(element);
+      min_ = internal::ElementwiseMinimumFunctionHelper<typename SomeFunction::LocalFunctionType>::compute(
+          *some_lf_, search_quadrature_order_, {});
+    }
+
+  public:
+    int order(const XT::Common::Parameter& /*param*/ = {}) const override final
+    {
+      return 0;
+    }
+
+    RangeReturnType evaluate(const DomainType& /*xx*/, const XT::Common::Parameter& /*param*/ = {}) const override final
+    {
+      return min_;
+    }
+
+  private:
+    std::unique_ptr<GridFunctionInterface<E, r_, rC_, R>> some_func_;
+    std::unique_ptr<typename GridFunctionInterface<E, r_, rC_, R>::LocalFunctionType> some_lf_;
+    const int search_quadrature_order_;
+    double min_;
+  }; // class LocalFunction
+
+public:
+  ElementwiseMinimumFunction(GridFunction<E, r_, rC_> some_func,
+                             const int search_quadrature_order,
+                             const std::string nm = "ElementwiseMinimumFunction")
+    : BaseType()
+    , some_func_(some_func.copy_as_grid_function())
+    , search_quadrature_order_(search_quadrature_order)
+    , name_(nm)
+  {
+    DUNE_THROW_IF(!some_func_->parameter_type().empty(),
+                  Exceptions::parameter_error,
+                  "Not available for parametric functions yet!");
+  }
+
+  ElementwiseMinimumFunction(const ThisType& other)
+    : BaseType(other)
+    , some_func_(other.some_func_->copy_as_grid_function())
+    , search_quadrature_order_(other.search_quadrature_order_)
+    , name_(other.name_)
+  {}
+
+  ElementwiseMinimumFunction(ThisType&&) = default;
+
+
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
+  std::unique_ptr<LocalFunctionType> local_function() const override final
+  {
+    return std::make_unique<LocalFunction>(*some_func_, search_quadrature_order_);
+  }
+
+  std::string name() const override final
+  {
+    return name_;
+  }
+
+private:
+  std::unique_ptr<GridFunctionInterface<E, r_, rC_, R>> some_func_;
+  const int search_quadrature_order_;
+  const std::string name_;
+}; // class ElementwiseMinimumFunction
+
+
+} // namespace Functions
+} // namespace XT
+} // namespace Dune
+
+#endif // DUNE_XT_FUNCTIONS_ELEMENTWISE_MINIMUM_HH
diff --git a/dune/xt/functions/exceptions.hh b/dune/xt/functions/exceptions.hh
index 4ea3254e980fc0f05e857b11ea3c0a6e7d8141f9..d501e25783fbd44a936145a25f738c5bf33cb5c1 100644
--- a/dune/xt/functions/exceptions.hh
+++ b/dune/xt/functions/exceptions.hh
@@ -37,6 +37,21 @@ class parameter_error : public Common::Exceptions::parameter_error
 class spe10_data_file_missing : public Dune::IOError
 {};
 
+class element_function_error : public Dune::Exception
+{};
+
+class combined_error : public Dune::Exception
+{};
+
+class function_error : public Dune::Exception
+{};
+
+class grid_function_error : public Dune::Exception
+{};
+
+class flux_function_error : public Dune::Exception
+{};
+
 
 } // namespace Exceptions
 } // namespace Functions
diff --git a/dune/xt/functions/expression/base.hh b/dune/xt/functions/expression/base.hh
index fbea9ee6cf7b5e1ea597f4b7b8164e8eac09441e..85871cac6d8bbfb6d590bdd2ed560bc2b36572c7 100644
--- a/dune/xt/functions/expression/base.hh
+++ b/dune/xt/functions/expression/base.hh
@@ -57,12 +57,6 @@ public:
   using RangeFieldType = RangeField;
   static constexpr size_t range_dim = rangeDim;
 
-  //  MathExpressionBase(const std::string var, const std::string expr)
-  //  {
-  //    const std::vector<std::string> expressions(1, expr);
-  //    setup(var, expressions);
-  //  }
-
   MathExpressionBase(const std::string& var, const Common::FieldVector<std::string, range_dim>& exprs)
     : variable_(var)
     , expressions_(exprs)
@@ -71,8 +65,10 @@ public:
   }
 
   MathExpressionBase(const ThisType& other)
+    : variable_(other.variable_)
+    , expressions_(other.expressions_)
   {
-    setup(other.variable(), other.expression());
+    setup();
   }
 
   ThisType& operator=(const ThisType& other)
@@ -246,17 +242,7 @@ public:
     setup(other.variables(), other.expressions());
   }
 
-  ThisType& operator=(const ThisType& other)
-  {
-    if (this != &other) {
-      cleanup();
-      originalvars_ = other.originalvars_;
-      variables_ = std::vector<std::string>();
-      expressions_ = std::vector<std::string>();
-      setup(other.originalvars_, other.expressions_);
-    }
-    return this;
-  }
+  DynamicMathExpressionBase(ThisType&&) = default;
 
   ~DynamicMathExpressionBase()
   {
diff --git a/dune/xt/functions/expression/default.hh b/dune/xt/functions/expression/default.hh
index 359cfefd442bcb0734136f912085c85221d180f8..44be3c15ae719cd54595d43be64760bd75d8785f 100644
--- a/dune/xt/functions/expression/default.hh
+++ b/dune/xt/functions/expression/default.hh
@@ -108,11 +108,19 @@ public:
                      const Common::FieldVector<Common::FieldMatrix<std::string, rC, d>, r>& gradient_expressions,
                      const size_t ord,
                      const std::string nm = static_id())
-    : function_(new MathExpressionFunctionType(variable, matrix_to_vector(expressions)))
+    : BaseType()
+    , function_(variable, matrix_to_vector(expressions))
     , order_(ord)
     , name_(nm)
   {
-    build_gradients(variable, gradient_expressions);
+    for (size_t cc = 0; cc < r; ++cc) {
+      gradients_.emplace_back(std::vector<MathExpressionGradientType>());
+      for (size_t rr = 0; rr < rC; ++rr) {
+        const auto& gradient_expression = gradient_expressions[cc][rr];
+        assert(gradient_expression.size() >= d);
+        gradients_[cc].emplace_back(variable, gradient_expression);
+      }
+    }
   }
 
   /**
@@ -123,24 +131,38 @@ public:
                      const Common::FieldMatrix<std::string, r, rC>& expressions,
                      const size_t ord,
                      const std::string nm = static_id())
-    : function_(new MathExpressionFunctionType(variable, matrix_to_vector(expressions)))
+    : BaseType()
+    , function_(variable, matrix_to_vector(expressions))
     , order_(ord)
     , name_(nm)
   {}
 
-  ExpressionFunction(const ThisType& other) = default;
-
-  ThisType& operator=(const ThisType& other)
+  ExpressionFunction(const ThisType& other)
+    : BaseType(other)
+    , function_(other.function_)
+    , order_(other.order_)
+    , name_(other.name_)
   {
-    if (this != &other) {
-      function_ = other.function_;
-      order_ = other.order_;
-      name_ = other.name_;
-      gradients_ = other.gradients_;
+    for (size_t cc = 0; cc < other.gradients_.size(); ++cc) {
+      gradients_.emplace_back(std::vector<MathExpressionGradientType>());
+      for (size_t rr = 0; rr < other.gradients_[cc].size(); ++rr)
+        gradients_[cc].emplace_back(other.gradients_[cc][rr]);
     }
-    return *this;
   }
 
+  ExpressionFunction(ThisType&&) = default;
+
+private:
+  ThisType* copy_as_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_function_impl());
+  }
   std::string name() const override final
   {
     return name_;
@@ -158,7 +180,7 @@ public:
   {
     RangeReturnType ret(0.);
     Common::FieldVector<RangeFieldType, r * rC> tmp_vector_;
-    function_->evaluate(point_in_global_coordinates, tmp_vector_);
+    function_.evaluate(point_in_global_coordinates, tmp_vector_);
     for (size_t rr = 0; rr < r; ++rr) {
       auto& retRow = ret[rr];
       for (size_t cc = 0; cc < rC; ++cc) {
@@ -181,7 +203,7 @@ public:
     for (size_t cc = 0; cc < r; ++cc) {
       assert(gradients_[cc].size() == rC);
       for (size_t rr = 0; rr < rC; ++rr) {
-        gradients_[cc][rr]->evaluate(point_in_global_coordinates, ret[cc][rr]);
+        gradients_[cc][rr].evaluate(point_in_global_coordinates, ret[cc][rr]);
         check_value(point_in_global_coordinates, ret[cc][rr]);
       }
     }
@@ -198,7 +220,7 @@ public:
     size_t range = value.size();
     bool failure = false;
     std::string error_type;
-    for (size_t rr = 0; rr < range; ++rr) {
+    for (size_t rr = 0; rr < value.size(); ++rr) {
       if (Common::isnan(value[rr])) {
         failure = true;
         error_type = "NaN";
@@ -209,40 +231,27 @@ public:
         failure = true;
         error_type = "an unlikely value";
       }
-      if (failure)
-        DUNE_THROW(Common::Exceptions::internal_error,
-                   "evaluating this function yielded "
-                       << error_type << "!\n"
-                       << "The variable of this function is:     " << function_->variable() << "\n"
-                       << "The expression of this function is: " << function_->expression() // at
-                       << "\n"
-                       << "You tried to evaluate it with:   point_in_global_coordinates = "
-                       << point_in_global_coordinates << "\n"
-                       << "The result was:                       " << value[rr] << "\n\n"
-                       << "You can disable this check by defining DUNE_XT_FUNCTIONS_EXPRESSION_DISABLE_CHECKS\n");
+      DUNE_THROW_IF(failure,
+                    Common::Exceptions::internal_error,
+                    "evaluating this function yielded "
+                        << error_type << "!\n"
+                        << "The variable of this function is:     " << function_.variable() << "\n"
+                        << "The expression of this function is: " << function_.expression() // at
+                        << "\n"
+                        << "You tried to evaluate it with:   point_in_global_coordinates = "
+                        << point_in_global_coordinates << "\n"
+                        << "The result was:                       " << value[rr] << "\n\n"
+                        << "You can disable this check by defining DUNE_XT_FUNCTIONS_EXPRESSION_DISABLE_CHECKS\n");
     } // check_value(...)
 #  endif // DUNE_XT_FUNCTIONS_EXPRESSION_DISABLE_CHECKS
 #endif // NDEBUG
   }
 
 private:
-  void build_gradients(const std::string& variable,
-                       const Common::FieldVector<Common::FieldMatrix<std::string, rC, d>, r>& gradient_expressions)
-  {
-    for (size_t cc = 0; cc < r; ++cc) {
-      gradients_.emplace_back(std::vector<std::shared_ptr<const MathExpressionGradientType>>());
-      for (size_t rr = 0; rr < rC; ++rr) {
-        const auto& gradient_expression = gradient_expressions[cc][rr];
-        assert(gradient_expression.size() >= d);
-        gradients_[cc].emplace_back(new MathExpressionGradientType(variable, gradient_expression));
-      }
-    }
-  } // ... build_gradients(...)
-
-  std::shared_ptr<const MathExpressionFunctionType> function_;
+  MathExpressionFunctionType function_;
   size_t order_;
   std::string name_;
-  std::vector<std::vector<std::shared_ptr<const MathExpressionGradientType>>> gradients_;
+  std::vector<std::vector<MathExpressionGradientType>> gradients_;
 }; // class ExpressionFunction
 
 
@@ -303,11 +312,13 @@ public:
                      const Common::FieldMatrix<std::string, r, d>& gradient_expressions,
                      const size_t ord,
                      const std::string nm = static_id())
-    : function_(new MathExpressionFunctionType(variable, expressions))
+    : BaseType()
+    , function_(variable, expressions)
     , order_(ord)
     , name_(nm)
   {
-    build_gradients(variable, gradient_expressions);
+    for (size_t rr = 0; rr < r; ++rr)
+      gradients_.emplace_back(new MathExpressionGradientType(variable, gradient_expressions[rr]));
   }
 
   /**
@@ -318,26 +329,35 @@ public:
                      const Common::FieldVector<std::string, r>& expressions,
                      const size_t ord,
                      const std::string nm = static_id())
-    : function_(new MathExpressionFunctionType(variable, expressions))
+    : BaseType()
+    , function_(variable, expressions)
     , order_(ord)
     , name_(nm)
   {}
 
-#if !DUNE_XT_WITH_PYTHON_BINDINGS
-  ExpressionFunction(const ThisType& other) = default;
-#endif
+  ExpressionFunction(const ThisType& other)
+    : BaseType()
+    , function_(other.function_)
+    , order_(other.order_)
+    , name_(other.name_)
+  {
+    for (size_t ii = 0; ii < other.gradients_.size(); ++ii)
+      gradients_.emplace_back(new MathExpressionGradientType(*other.gradients_[ii]));
+  }
 
-  ThisType& operator=(const ThisType& other)
+  ExpressionFunction(ThisType&&) = default;
+
+private:
+  ThisType* copy_as_function_impl() const override
   {
-    if (this != &other) {
-      function_ = other.function_;
-      order_ = other.order_;
-      name_ = other.name_;
-      gradients_ = other.gradients_;
-    }
-    return *this;
+    return new ThisType(*this);
   }
 
+public:
+  std::unique_ptr<ThisType> copy_as_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_function_impl());
+  }
   std::string name() const override final
   {
     return name_;
@@ -354,7 +374,7 @@ public:
                            const Common::Parameter& /*param*/ = {}) const override final
   {
     RangeReturnType ret(0.);
-    function_->evaluate(point_in_global_coordinates, ret);
+    function_.evaluate(point_in_global_coordinates, ret);
     check_value(point_in_global_coordinates, ret);
     return ret;
   } // ... evaluate(...)
@@ -384,7 +404,7 @@ private:
     size_t range = value.size();
     bool failure = false;
     std::string error_type;
-    for (size_t rr = 0; rr < range; ++rr) {
+    for (size_t rr = 0; rr < value.size(); ++rr) {
       if (Common::isnan(value[rr])) {
         failure = true;
         error_type = "NaN";
@@ -395,38 +415,26 @@ private:
         failure = true;
         error_type = "an unlikely value";
       }
-      if (failure)
-        DUNE_THROW(Common::Exceptions::internal_error,
-                   "evaluating this function yielded "
-                       << error_type << "!\n"
-                       << "The variable of this function is:     " << function_->variable() << "\n"
-                       << "The expression of this function is: " << function_->expression() // at
-                       << "\n"
-                       << "You tried to evaluate it with:   point_in_global_coordinates = "
-                       << point_in_global_coordinates << "\n"
-                       << "The result was:                       " << value[rr] << "\n\n"
-                       << "You can disable this check by defining DUNE_XT_FUNCTIONS_EXPRESSION_DISABLE_CHECKS\n");
+      DUNE_THROW_IF(failure,
+                    Common::Exceptions::internal_error,
+                    "evaluating this function yielded "
+                        << error_type << "!\n"
+                        << "The variable of this function is:     " << function_.variable() << "\n"
+                        << "The expression of this function is: " << function_.expression() << "\n"
+                        << "You tried to evaluate it with:   point_in_global_coordinates = "
+                        << point_in_global_coordinates << "\n"
+                        << "The result was:                       " << value[rr] << "\n\n"
+                        << "You can disable this check by defining DUNE_XT_FUNCTIONS_EXPRESSION_DISABLE_CHECKS\n");
     } // check_value(...)
 #  endif // DUNE_XT_FUNCTIONS_EXPRESSION_DISABLE_CHECKS
 #endif // NDEBUG
-  }
+  } // ... check_value(...)
 
 private:
-  void build_gradients(const std::string& variable, const Common::FieldMatrix<std::string, r, d>& gradient_expressions)
-  {
-    for (size_t rr = 0; rr < r; ++rr)
-      gradients_.emplace_back(new MathExpressionGradientType(variable, gradient_expressions[rr]));
-  }
-
-  void build_gradients(const std::string& variable, const Common::FieldVector<std::string, d>& gradient_expression)
-  {
-    gradients_.emplace_back(new MathExpressionGradientType(variable, gradient_expression));
-  }
-
-  std::shared_ptr<const MathExpressionFunctionType> function_;
+  MathExpressionFunctionType function_;
   size_t order_;
   std::string name_;
-  std::vector<std::shared_ptr<const MathExpressionGradientType>> gradients_;
+  std::vector<std::shared_ptr<MathExpressionGradientType>> gradients_;
 }; // class ExpressionFunction
 
 
diff --git a/dune/xt/functions/expression/parametric.hh b/dune/xt/functions/expression/parametric.hh
index 1711475741c8b0a9d5f44d08e112cb32fb453b4c..7d99457c5d77f69854042b61c78a8b2f8af0a4f0 100644
--- a/dune/xt/functions/expression/parametric.hh
+++ b/dune/xt/functions/expression/parametric.hh
@@ -39,17 +39,21 @@ public:
 template <size_t d, size_t r, class R>
 class ParametricExpressionFunction<d, r, 1, R> : public FunctionInterface<d, r, 1, R>
 {
+  using ThisType = ParametricExpressionFunction;
   using BaseType = FunctionInterface<d, r, 1, R>;
-  using typename BaseType::D;
-  using ActualFunctionType = DynamicMathExpressionBase<D, R, r>;
 
 public:
   using BaseType::domain_dim;
   using BaseType::range_dim;
+  using typename BaseType::D;
   using typename BaseType::DerivativeRangeReturnType;
   using typename BaseType::DomainType;
   using typename BaseType::RangeReturnType;
 
+private:
+  using ActualFunctionType = DynamicMathExpressionBase<D, R, r>;
+
+public:
   static std::string static_id()
   {
     return BaseType::static_id() + ".parametricexpression";
@@ -60,20 +64,19 @@ public:
                                const Common::FieldVector<std::string, r>& expressions,
                                const size_t ord = 0,
                                const std::string nm = static_id())
-    : order_(ord)
+    : BaseType(param_type)
+    , order_(ord)
     , name_(nm)
-    , param_type_(param_type)
     , num_parameter_variables_(0)
   {
-    if (variable.empty())
-      DUNE_THROW(Common::Exceptions::wrong_input_given, "Given variable must not be empty!");
+    DUNE_THROW_IF(variable.empty(), Common::Exceptions::wrong_input_given, "Given variable must not be empty!");
 
     std::vector<std::string> variables;
     std::vector<std::string> expression;
     for (size_t rr = 0; rr < r; ++rr)
       expression.emplace_back(expressions[rr]);
-    for (const auto& key : param_type_.keys()) {
-      const size_t value_size = param_type_.get(key);
+    for (const auto& key : this->parameter_type().keys()) {
+      const size_t value_size = this->parameter_type().get(key);
       if (value_size == 1) {
         variables.push_back(key);
         ++num_parameter_variables_;
@@ -86,27 +89,39 @@ public:
     }
     for (size_t ii = 0; ii < domain_dim; ++ii)
       variables.push_back(variable + "[" + Common::to_string(ii) + "]");
-    function_ = std::make_shared<ActualFunctionType>(variables, expression);
+    function_ = std::make_unique<ActualFunctionType>(variables, expression);
   }
 
-  std::string name() const override final
+  ParametricExpressionFunction(const ThisType& other)
+    : BaseType(other)
+    , order_(other.order_)
+    , name_(other.name_)
+    , num_parameter_variables_(other.num_parameter_variables_)
+    , function_(new ActualFunctionType(*other.function_))
+  {}
+
+  ParametricExpressionFunction(ThisType&&) = default;
+
+private:
+  ThisType* copy_as_function_impl() const override
   {
-    return name_;
+    return new ThisType(*this);
   }
 
-  int order(const Common::Parameter& /*param*/ = {}) const override final
+public:
+  std::unique_ptr<ThisType> copy_as_function() const
   {
-    return static_cast<int>(order_);
+    return std::unique_ptr<ThisType>(this->copy_as_function_impl());
   }
 
-  bool is_parametric() const override final
+  std::string name() const override final
   {
-    return !param_type_.empty();
+    return name_;
   }
 
-  const Common::ParameterType& parameter_type() const override final
+  int order(const Common::Parameter& /*param*/ = {}) const override final
   {
-    return param_type_;
+    return static_cast<int>(order_);
   }
 
   RangeReturnType evaluate(const DomainType& point_in_global_coordinates,
@@ -114,16 +129,16 @@ public:
   {
     RangeReturnType ret(0.);
     Common::Parameter parsed_param;
-    if (!param_type_.empty()) {
+    if (!this->parameter_type().empty()) {
       parsed_param = this->parse_parameter(param);
-      if (parsed_param.type() != param_type_)
+      if (parsed_param.type() != this->parameter_type())
         DUNE_THROW(Common::Exceptions::parameter_error,
-                   "parameter_type(): " << param_type_ << "\n   "
+                   "parameter_type(): " << this->parameter_type() << "\n   "
                                         << "param.type(): " << param.type());
     }
     DynamicVector<D> args(num_parameter_variables_ + domain_dim);
     size_t II = 0;
-    for (const auto& key : param_type_.keys()) {
+    for (const auto& key : this->parameter_type().keys()) {
       for (const auto& value : parsed_param.get(key)) {
         args[II] = value;
         ++II;
@@ -176,9 +191,8 @@ public:
 private:
   size_t order_;
   std::string name_;
-  Common::ParameterType param_type_;
   size_t num_parameter_variables_;
-  std::shared_ptr<const ActualFunctionType> function_;
+  std::unique_ptr<ActualFunctionType> function_;
 }; // class ParametricExpressionFunction
 
 
diff --git a/dune/xt/functions/flattop.hh b/dune/xt/functions/flattop.hh
index edef22418e0a95d4a88d237a8f569bb8735ef4d4..53921bf4d70e90d53c2f95541b3ca85833872b62 100644
--- a/dune/xt/functions/flattop.hh
+++ b/dune/xt/functions/flattop.hh
@@ -73,8 +73,8 @@ public:
   FlatTopFunction(const DomainType& lower_left,
                   const DomainType& upper_right,
                   const DomainType& boundary_layer,
-                  const RangeReturnType& value = defaults().template get<RangeReturnType>("value"),
-                  const std::string name_in = defaults().template get<std::string>("name"))
+                  const RangeReturnType& value = RangeReturnType(1),
+                  const std::string name_in = "FlatTopFunction")
     : lower_left_(lower_left)
     , upper_right_(upper_right)
     , boundary_layer_(boundary_layer)
@@ -84,11 +84,21 @@ public:
     check_input();
   }
 
-  FlatTopFunction(const ThisType& other) = default;
+  FlatTopFunction(const ThisType&) = default;
 
-  ThisType& operator=(const ThisType& other) = delete;
+  FlatTopFunction(ThisType&&) = default;
 
-  virtual ~FlatTopFunction() {}
+private:
+  ThisType* copy_as_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_function_impl());
+  }
 
   std::string name() const override final
   {
diff --git a/dune/xt/functions/generic/function.hh b/dune/xt/functions/generic/function.hh
index 79f572678df1993ac12c6a2f66ead2bd74c98911..e8e6b77a18dfa44934d9b118a7be63e31534c50a 100644
--- a/dune/xt/functions/generic/function.hh
+++ b/dune/xt/functions/generic/function.hh
@@ -33,6 +33,7 @@ namespace Functions {
 template <size_t domain_dim, size_t range_dim = 1, size_t range_dim_cols = 1, class RangeField = double>
 class GenericFunction : public FunctionInterface<domain_dim, range_dim, range_dim_cols, RangeField>
 {
+  using ThisType = GenericFunction;
   using BaseType = FunctionInterface<domain_dim, range_dim, range_dim_cols, RangeField>;
 
 public:
@@ -111,11 +112,21 @@ public:
     , name_(nm)
   {}
 
+  GenericFunction(const ThisType&) = default;
 
-  /**
-   * \name ´´These methods are required by FunctionInterface.''
-   * \{
-   */
+  GenericFunction(ThisType&&) = default;
+
+private:
+  ThisType* copy_as_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_function_impl());
+  }
 
   int order(const Common::Parameter& param = {}) const override final
   {
@@ -154,7 +165,6 @@ public:
   }
 
   /**
-   * \}
    * \name ´´These methods may be used to provide defaults on construction.''
    * \{
    */
diff --git a/dune/xt/functions/generic/grid-function.hh b/dune/xt/functions/generic/grid-function.hh
index 6d2280c1c39a4f0bbb291f09c063d5ed48014fbc..b7e52709a76f7909b4488e3c500c50242ec7e093 100644
--- a/dune/xt/functions/generic/grid-function.hh
+++ b/dune/xt/functions/generic/grid-function.hh
@@ -54,6 +54,7 @@ namespace Functions {
 template <class E, size_t r = 1, size_t rC = 1, class R = double>
 class GenericGridFunction : public GridFunctionInterface<E, r, rC, R>
 {
+  using ThisType = GenericGridFunction;
   using BaseType = GridFunctionInterface<E, r, rC, R>;
 
 public:
@@ -89,11 +90,10 @@ private:
                              const Common::ParameterType& param_type,
                              const GenericJacobianFunctionType& jacobian_func,
                              const GenericDerivativeFunctionType& derivative_func)
-      : BaseType()
+      : BaseType(param_type)
       , order_(order_func)
       , post_bind_(post_bind_func)
       , evaluate_(evaluate_func)
-      , param_type_(param_type)
       , jacobian_(jacobian_func)
       , derivative_(derivative_func)
     {}
@@ -147,18 +147,12 @@ private:
       return derivative_(alpha, point_in_local_coordinates, parsed_param);
     }
 
-    const Common::ParameterType& parameter_type() const override final
-    {
-      return param_type_;
-    }
-
   private:
-    const GenericOrderFunctionType& order_;
-    const GenericPostBindFunctionType& post_bind_;
-    const GenericEvaluateFunctionType& evaluate_;
-    const Common::ParameterType& param_type_;
-    const GenericJacobianFunctionType& jacobian_;
-    const GenericDerivativeFunctionType& derivative_;
+    const GenericOrderFunctionType order_;
+    const GenericPostBindFunctionType post_bind_;
+    const GenericEvaluateFunctionType evaluate_;
+    const GenericJacobianFunctionType jacobian_;
+    const GenericDerivativeFunctionType derivative_;
   }; // class LocalGenericGridFunction
 
 public:
@@ -185,10 +179,10 @@ public:
                       const std::string nm = "GenericGridFunction",
                       GenericJacobianFunctionType jacobian_func = default_jacobian_function(),
                       GenericDerivativeFunctionType derivative_func = default_derivative_function())
-    : order_(default_order_lambda(ord))
+    : BaseType(param_type)
+    , order_(default_order_lambda(ord))
     , post_bind_(post_bind_func)
     , evaluate_(evaluate_func)
-    , param_type_(param_type)
     , name_(nm)
     , jacobian_(jacobian_func)
     , derivative_(derivative_func)
@@ -201,18 +195,29 @@ public:
                       const std::string nm = "GenericGridFunction",
                       GenericJacobianFunctionType jacobian_func = default_jacobian_function(),
                       GenericDerivativeFunctionType derivative_func = default_derivative_function())
-    : order_(order_func)
+    : BaseType(param_type)
+    , order_(order_func)
     , post_bind_(post_bind_func)
     , evaluate_(evaluate_func)
-    , param_type_(param_type)
     , name_(nm)
     , jacobian_(jacobian_func)
     , derivative_(derivative_func)
   {}
 
-  const Common::ParameterType& parameter_type() const override final
+  GenericGridFunction(const ThisType&) = default;
+
+  GenericGridFunction(ThisType&&) = default;
+
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
   {
-    return param_type_;
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
   }
 
   std::string name() const override final
@@ -223,7 +228,7 @@ public:
   std::unique_ptr<LocalFunctionType> local_function() const override final
   {
     return std::make_unique<LocalGenericGridFunction>(
-        order_, post_bind_, evaluate_, param_type_, jacobian_, derivative_);
+        order_, post_bind_, evaluate_, this->parameter_type(), jacobian_, derivative_);
   }
 
   /**
@@ -281,10 +286,9 @@ private:
   const GenericOrderFunctionType order_;
   const GenericPostBindFunctionType post_bind_;
   const GenericEvaluateFunctionType evaluate_;
-  const Common::ParameterType param_type_;
   const std::string name_;
-  GenericJacobianFunctionType jacobian_;
-  GenericDerivativeFunctionType derivative_;
+  const GenericJacobianFunctionType jacobian_;
+  const GenericDerivativeFunctionType derivative_;
 }; // class GenericGridFunction
 
 
diff --git a/dune/xt/functions/grid-function.hh b/dune/xt/functions/grid-function.hh
index dd1adf99bc0f8659f2ff577c2eba4ada3ab6ad46..0621001ae210a9b583eebed99d8a5c9741d618cf 100644
--- a/dune/xt/functions/grid-function.hh
+++ b/dune/xt/functions/grid-function.hh
@@ -13,6 +13,7 @@
 #define DUNE_XT_FUNCTIONS_GRID_FUNCTION_HH
 
 #include <dune/xt/common/memory.hh>
+#include <dune/xt/common/print.hh>
 #include <dune/xt/la/container/eye-matrix.hh>
 #include <dune/xt/functions/base/function-as-grid-function.hh>
 #include <dune/xt/functions/base/combined-grid-functions.hh>
@@ -128,71 +129,123 @@ public:
   using typename BaseType::LocalFunctionType;
   using GenericFunctionType = GenericFunction<d, r, rC>;
 
-  GridFunction(const typename RangeTypeSelector<R, r, rC>::type& value)
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(new ConstantFunction<d, r, rC, R>(value)))
+  GridFunction(const typename RangeTypeSelector<R, r, rC>::type& value,
+               const std::string nm = "GridFunction",
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(new ConstantFunction<d, r, rC, R>(value)))
+    , name_(nm)
   {}
 
-  GridFunction(const FunctionInterface<d, r, rC, R>& func)
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(func))
+  GridFunction(const FunctionInterface<d, r, rC, R>& func, const std::string logging_prefix = "")
+    : BaseType(func.parameter_type(), logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(func))
+    , name_(function_->name())
   {}
 
-  GridFunction(FunctionInterface<d, r, rC, R>*&& func_ptr)
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(std::move(func_ptr)))
+  GridFunction(FunctionInterface<d, r, rC, R>*&& func_ptr, const std::string logging_prefix = "")
+    : BaseType(
+        func_ptr->parameter_type(), logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(std::move(func_ptr)))
+    , name_(function_->name())
   {}
 
-  GridFunction(const GridFunctionInterface<E, r, rC, R>& func)
-    : BaseType()
-    , storage_(func)
+  GridFunction(const GridFunctionInterface<E, r, rC, R>& func, const std::string logging_prefix = "")
+    : BaseType(func.parameter_type(),
+               logging_prefix.empty() ? func.logger.prefix : logging_prefix,
+               logging_prefix.empty() ? !func.logger.debug_enabled : true)
+    , function_(func.copy_as_grid_function())
+    , name_(function_->name())
   {}
 
-  GridFunction(GridFunctionInterface<E, r, rC, R>*&& func_ptr)
-    : BaseType()
-    , storage_(std::move(func_ptr))
+  GridFunction(GridFunctionInterface<E, r, rC, R>*&& func_ptr, const std::string logging_prefix = "")
+    : BaseType(func_ptr->parameter_type(),
+               logging_prefix.empty() ? func_ptr->logger.prefix : logging_prefix,
+               logging_prefix.empty() ? !func_ptr->logger.debug_enabled : true)
+    , function_(std::move(func_ptr))
+    , name_(function_->name())
   {}
 
-  GridFunction(std::tuple<int, typename GenericFunctionType::GenericEvaluateFunctionType> order_evaluate)
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(
+  GridFunction(std::tuple<int, typename GenericFunctionType::GenericEvaluateFunctionType> order_evaluate,
+               const std::string nm = "GridFunction",
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(
           new GenericFunctionType(std::get<0>(order_evaluate), std::get<1>(order_evaluate))))
+    , name_(nm)
+  {}
+
+  GridFunction(std::tuple<int, typename GenericFunctionType::GenericEvaluateFunctionType, const std::string&>
+                   order_evaluate_name,
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(
+          new GenericFunctionType(std::get<0>(order_evaluate_name), std::get<1>(order_evaluate_name))))
+    , name_(std::get<2>(order_evaluate_name))
   {}
 
   GridFunction(std::tuple<int,
                           typename GenericFunctionType::GenericEvaluateFunctionType,
-                          typename GenericFunctionType::GenericJacobianFunctionType> order_evaluate_jacobian)
-    : BaseType()
-    , storage_(
+                          typename GenericFunctionType::GenericJacobianFunctionType> order_evaluate_jacobian,
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(
           new FunctionAsGridFunctionWrapper<E, r, rC, R>(new GenericFunctionType(std::get<0>(order_evaluate_jacobian),
                                                                                  std::get<1>(order_evaluate_jacobian),
                                                                                  /*name=*/"",
                                                                                  /*param_type=*/{},
                                                                                  std::get<2>(order_evaluate_jacobian))))
+    , name_("GridFunction")
+  {}
+
+  GridFunction(std::tuple<int,
+                          typename GenericFunctionType::GenericEvaluateFunctionType,
+                          typename GenericFunctionType::GenericJacobianFunctionType,
+                          const std::string&> order_evaluate_jacobian_name,
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(
+          new GenericFunctionType(std::get<0>(order_evaluate_jacobian_name),
+                                  std::get<1>(order_evaluate_jacobian_name),
+                                  /*name=*/"",
+                                  /*param_type=*/{},
+                                  std::get<2>(order_evaluate_jacobian_name))))
+    , name_(std::get<3>(order_evaluate_jacobian_name))
   {}
 
   GridFunction(const ThisType& other)
     : BaseType(other)
-    , storage_(other.storage_)
+    , function_(other.function_->copy_as_grid_function())
+    , name_(other.name_)
   {}
 
-  GridFunction(ThisType&& source)
-    : BaseType(source)
-    , storage_(std::move(source.storage_))
-  {}
+  GridFunction(ThisType&& source) = default;
+
 
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
   std::unique_ptr<LocalFunctionType> local_function() const override final
   {
-    return storage_.access().local_function();
+    return function_->local_function();
   }
 
   std::string name() const override final
   {
-    return storage_.access().name();
+    return name_;
   }
 
 private:
-  Common::ConstStorageProvider<GridFunctionInterface<E, r, rC, R>> storage_;
+  std::unique_ptr<GridFunctionInterface<E, r, rC, R>> function_;
+  std::string name_;
 }; // class GridFunction<..., r, rC, ...>
 
 
@@ -220,103 +273,164 @@ public:
   using typename BaseType::LocalFunctionType;
   using GenericFunctionType = GenericFunction<d, r, rC>;
 
-  GridFunction(const R& value)
-    : BaseType()
-    , storage_(new ProductGridFunction<GridFunction<E, 1, 1, R>, GridFunctionInterface<E, r, r, R>>(
+  GridFunction(const R& value, const std::string nm = "GridFunction", const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new ProductGridFunction<GridFunction<E, 1, 1, R>, GridFunctionInterface<E, r, r, R>>(
           new GridFunction<E, 1, 1, R>(value), std::move(unit_matrix()), ""))
+    , name_(nm)
   {}
 
-  GridFunction(const FieldMatrix<R, r, r>& value) // <- Must not be XT::Common::FieldMatrix!
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, r, r, R>(new ConstantFunction<d, r, r, R>(value)))
+  GridFunction(const FieldMatrix<R, r, r>& value, // <- Must not be XT::Common::FieldMatrix!
+               const std::string nm = "GridFunction",
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, r, R>(new ConstantFunction<d, r, r, R>(value)))
+    , name_(nm)
   {}
 
-  GridFunction(const FunctionInterface<d, 1, 1, R>& func)
-    : BaseType()
-    , storage_(new ProductGridFunction<FunctionAsGridFunctionWrapper<E, 1, 1, R>, GridFunctionInterface<E, r, r, R>>(
+  GridFunction(const FunctionInterface<d, 1, 1, R>& func, const std::string logging_prefix = "")
+    : BaseType(func.parameter_type(), logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new ProductGridFunction<FunctionAsGridFunctionWrapper<E, 1, 1, R>, GridFunctionInterface<E, r, r, R>>(
           new FunctionAsGridFunctionWrapper<E, 1, 1, R>(func), std::move(unit_matrix()), func.name()))
+    , name_(function_->name())
   {}
 
-  GridFunction(FunctionInterface<d, 1, 1, R>*&& func_ptr)
-    : BaseType()
-    , storage_(new ProductGridFunction<FunctionAsGridFunctionWrapper<E, 1, 1, R>, GridFunctionInterface<E, r, r, R>>(
+  GridFunction(FunctionInterface<d, 1, 1, R>*&& func_ptr, const std::string logging_prefix = "")
+    : BaseType(
+        func_ptr->parameter_type(), logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new ProductGridFunction<FunctionAsGridFunctionWrapper<E, 1, 1, R>, GridFunctionInterface<E, r, r, R>>(
           new FunctionAsGridFunctionWrapper<E, 1, 1, R>(std::move(func_ptr)),
           std::move(unit_matrix()),
           func_ptr->name()))
+    , name_(function_->name())
   {}
 
-  GridFunction(const FunctionInterface<d, r, r, R>& func)
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, r, r, R>(func))
+  GridFunction(const FunctionInterface<d, r, r, R>& func, const std::string logging_prefix = "")
+    : BaseType(func.parameter_type(), logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, r, R>(func))
+    , name_(function_->name())
   {}
 
-  GridFunction(FunctionInterface<d, r, r, R>*&& func_ptr)
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, r, r, R>(std::move(func_ptr)))
+  GridFunction(FunctionInterface<d, r, r, R>*&& func_ptr, const std::string logging_prefix = "")
+    : BaseType(
+        func_ptr->parameter_type(), logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, r, R>(std::move(func_ptr)))
+    , name_(function_->name())
   {}
 
-  GridFunction(const GridFunctionInterface<E, 1, 1, R>& func)
-    : BaseType()
-    , storage_(new ProductGridFunction<GridFunction<E, 1, 1, R>, GridFunctionInterface<E, r, r, R>>(
+  GridFunction(const GridFunctionInterface<E, 1, 1, R>& func, const std::string logging_prefix = "")
+    : BaseType(func.parameter_type(),
+               logging_prefix.empty() ? func.logger.prefix : logging_prefix,
+               logging_prefix.empty() ? !func.logger.debug_enabled : true)
+    , function_(new ProductGridFunction<GridFunction<E, 1, 1, R>, GridFunctionInterface<E, r, r, R>>(
           new GridFunction<E, 1, 1, R>(func), std::move(unit_matrix()), func.name()))
+    , name_(function_->name())
   {}
 
-  GridFunction(GridFunctionInterface<E, 1, 1, R>*&& func_ptr)
-    : BaseType()
-    , storage_(new ProductGridFunction<GridFunctionInterface<E, 1, 1, R>, GridFunctionInterface<E, r, r, R>>(
+  GridFunction(GridFunctionInterface<E, 1, 1, R>*&& func_ptr, const std::string logging_prefix = "")
+    : BaseType(func_ptr->parameter_type(),
+               logging_prefix.empty() ? func_ptr->logger.prefix : logging_prefix,
+               logging_prefix.empty() ? !func_ptr->logger.debug_enabled : true)
+    , function_(new ProductGridFunction<GridFunctionInterface<E, 1, 1, R>, GridFunctionInterface<E, r, r, R>>(
           std::move(func_ptr), std::move(unit_matrix()), func_ptr->name()))
+    , name_(function_->name())
   {}
 
-  GridFunction(const GridFunctionInterface<E, r, r, R>& func)
-    : BaseType()
-    , storage_(func)
+  GridFunction(const GridFunctionInterface<E, r, r, R>& func, const std::string logging_prefix = "")
+    : BaseType(func.parameter_type(),
+               logging_prefix.empty() ? func.logger.prefix : logging_prefix,
+               logging_prefix.empty() ? !func.logger.debug_enabled : true)
+    , function_(func.copy_as_grid_function())
+    , name_(function_->name())
   {}
 
-  GridFunction(GridFunctionInterface<E, r, r, R>*&& func_ptr)
-    : BaseType()
-    , storage_(std::move(func_ptr))
+  GridFunction(GridFunctionInterface<E, r, r, R>*&& func_ptr, const std::string logging_prefix = "")
+    : BaseType(func_ptr->parameter_type(),
+               logging_prefix.empty() ? func_ptr->logger.prefix : logging_prefix,
+               logging_prefix.empty() ? !func_ptr->logger.debug_enabled : true)
+    , function_(std::move(func_ptr))
+    , name_(function_->name())
   {}
 
-  GridFunction(std::tuple<int, typename GenericFunctionType::GenericEvaluateFunctionType> order_evaluate)
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(
+  GridFunction(std::tuple<int, typename GenericFunctionType::GenericEvaluateFunctionType> order_evaluate,
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(
           new GenericFunctionType(std::get<0>(order_evaluate), std::get<1>(order_evaluate))))
+    , name_("GridFunction")
+  {}
+
+  GridFunction(std::tuple<int, typename GenericFunctionType::GenericEvaluateFunctionType, const std::string&>
+                   order_evaluate_name,
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(
+          new GenericFunctionType(std::get<0>(order_evaluate_name), std::get<1>(order_evaluate_name))))
+    , name_(std::get<2>(order_evaluate_name))
   {}
 
   GridFunction(std::tuple<int,
                           typename GenericFunctionType::GenericEvaluateFunctionType,
-                          typename GenericFunctionType::GenericJacobianFunctionType> order_evaluate_jacobian)
-    : BaseType()
-    , storage_(
+                          typename GenericFunctionType::GenericJacobianFunctionType> order_evaluate_jacobian,
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(
           new FunctionAsGridFunctionWrapper<E, r, rC, R>(new GenericFunctionType(std::get<0>(order_evaluate_jacobian),
                                                                                  std::get<1>(order_evaluate_jacobian),
                                                                                  /*name=*/"",
                                                                                  /*param_type=*/{},
                                                                                  std::get<2>(order_evaluate_jacobian))))
+    , name_("GridFunction")
+  {}
+
+  GridFunction(std::tuple<int,
+                          typename GenericFunctionType::GenericEvaluateFunctionType,
+                          typename GenericFunctionType::GenericJacobianFunctionType,
+                          const std::string&> order_evaluate_jacobian_name,
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(
+          new GenericFunctionType(std::get<0>(order_evaluate_jacobian_name),
+                                  std::get<1>(order_evaluate_jacobian_name),
+                                  /*name=*/"",
+                                  /*param_type=*/{},
+                                  std::get<2>(order_evaluate_jacobian_name))))
+    , name_(std::get<3>(order_evaluate_jacobian_name))
   {}
 
   GridFunction(const ThisType& other)
     : BaseType(other)
-    , storage_(other.storage_)
+    , function_(other.function_->copy_as_grid_function())
+    , name_(other.name_)
   {}
 
-  GridFunction(ThisType&& source)
-    : BaseType(source)
-    , storage_(std::move(source.storage_))
-  {}
+  GridFunction(ThisType&&) = default;
+
+
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
 
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
   std::unique_ptr<LocalFunctionType> local_function() const override final
   {
-    return storage_.access().local_function();
+    return function_->local_function();
   }
 
   std::string name() const override final
   {
-    return storage_.access().name();
+    return name_;
   }
 
 private:
-  Common::ConstStorageProvider<GridFunctionInterface<E, r, rC, R>> storage_;
+  std::unique_ptr<GridFunctionInterface<E, r, rC, R>> function_;
+  std::string name_;
 }; // class GridFunction<..., r, r, ...>
 
 
@@ -338,81 +452,170 @@ public:
   using typename BaseType::LocalFunctionType;
   using GenericFunctionType = GenericFunction<d, r, rC>;
 
-  GridFunction(const R& value)
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, 1, 1, R>(new ConstantFunction<d, 1, 1, R>(value)))
-  {}
+  GridFunction(const R& value, const std::string nm = "GridFunction", const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, 1, 1, R>(new ConstantFunction<d, 1, 1, R>(value)))
+    , name_(nm)
+  {
+    LOG_(info) << "GridFunction<1,1>(this=" << this << ", value=" << value << ", nm=\"" << nm << "\")" << std::endl;
+  }
 
-  GridFunction(const FieldVector<R, 1>& value) // <- Must not be XT::Common::FieldVector!
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, 1, 1, R>(new ConstantFunction<d, 1, 1, R>(value)))
-  {}
+  GridFunction(const FieldVector<R, 1>& value, // <- Must not be XT::Common::FieldVector!
+               const std::string nm = "GridFunction",
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, 1, 1, R>(new ConstantFunction<d, 1, 1, R>(value)))
+    , name_(nm)
+  {
+    LOG_(info) << "GridFunction<1,1>(this=" << this << ", value_vec=" << Common::print(value) << ", nm=\"" << nm
+               << "\")" << std::endl;
+  }
 
-  GridFunction(const FieldMatrix<R, 1, 1>& value) // <- Must not be XT::Common::FieldMatrix!
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, 1, 1, R>(new ConstantFunction<d, 1, 1, R>(value[0][0])))
-  {}
+  GridFunction(const FieldMatrix<R, 1, 1>& value, // <- Must not be XT::Common::FieldMatrix!
+               const std::string nm = "GridFunction",
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, 1, 1, R>(new ConstantFunction<d, 1, 1, R>(value[0][0])))
+    , name_(nm)
+  {
+    LOG_(info) << "GridFunction<1,1>(this=" << this << ", value_mat=" << Common::print(value) << ", nm=\"" << nm
+               << "\")" << std::endl;
+  }
 
-  GridFunction(const FunctionInterface<d, 1, 1, R>& func)
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, 1, 1, R>(func))
-  {}
+  GridFunction(const FunctionInterface<d, 1, 1, R>& func, const std::string logging_prefix = "")
+    : BaseType(func.parameter_type(), logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, 1, 1, R>(func))
+    , name_(function_->name())
+  {
+    LOG_(info) << "GridFunction<1,1>(this=" << this << ", func=" << &func << ", func.name()=" << name_ << ")"
+               << std::endl;
+  }
 
-  GridFunction(FunctionInterface<d, 1, 1, R>*&& func_ptr)
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, 1, 1, R>(std::move(func_ptr)))
-  {}
+  GridFunction(FunctionInterface<d, 1, 1, R>*&& func_ptr, const std::string logging_prefix = "")
+    : BaseType(
+        func_ptr->parameter_type(), logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, 1, 1, R>(std::move(func_ptr)))
+    , name_(function_->name())
+  {
+    LOG_(info) << "GridFunction<1,1>(this=" << this << ", func_ptr=" << func_ptr << ", func_ptr->name()=" << name_
+               << ")" << std::endl;
+  }
 
-  GridFunction(const GridFunctionInterface<E, 1, 1, R>& func)
-    : BaseType()
-    , storage_(func)
-  {}
+  GridFunction(const GridFunctionInterface<E, 1, 1, R>& func, const std::string logging_prefix = "")
+    : BaseType(func.parameter_type(),
+               logging_prefix.empty() ? "GridFunction(" + func.logger.prefix + ")" : logging_prefix,
+               logging_prefix.empty() ? !func.logger.debug_enabled : true)
+    , function_(func.copy_as_grid_function())
+    , name_(function_->name())
+  {
+    LOG_(info) << "GridFunction<1,1>(this=" << this << ", grid_func=" << &func << ", grid_func.name()=" << name_ << ")"
+               << std::endl;
+  }
 
-  GridFunction(GridFunctionInterface<E, 1, 1, R>*&& func_ptr)
-    : BaseType()
-    , storage_(std::move(func_ptr))
-  {}
+  GridFunction(GridFunctionInterface<E, 1, 1, R>*&& func_ptr, const std::string logging_prefix = "")
+    : BaseType(func_ptr->parameter_type(),
+               logging_prefix.empty() ? "GridFunction(" + func_ptr->logger.prefix + ")" : logging_prefix,
+               logging_prefix.empty() ? !func_ptr->logger.debug_enabled : true)
+    , function_(std::move(func_ptr))
+    , name_(function_->name())
+  {
+    LOG_(info) << "GridFunction<1,1>(this=" << this << ", grid_func_ptr=" << func_ptr
+               << ", grid_func_ptr->name()=" << name_ << ")" << std::endl;
+  }
 
-  GridFunction(std::tuple<int, typename GenericFunctionType::GenericEvaluateFunctionType> order_evaluate)
-    : BaseType()
-    , storage_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(
+  GridFunction(std::tuple<int, typename GenericFunctionType::GenericEvaluateFunctionType> order_evaluate,
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(
           new GenericFunctionType(std::get<0>(order_evaluate), std::get<1>(order_evaluate))))
-  {}
+    , name_("GridFunction")
+  {
+    LOG_(info) << "GridFunction<1,1>(this=" << this << ", order_evaluate_lambda=" << &order_evaluate << ")"
+               << std::endl;
+  }
+
+  GridFunction(std::tuple<int, typename GenericFunctionType::GenericEvaluateFunctionType, const std::string&>
+                   order_evaluate_name,
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(
+          new GenericFunctionType(std::get<0>(order_evaluate_name), std::get<1>(order_evaluate_name))))
+    , name_(std::get<2>(order_evaluate_name))
+  {
+    LOG_(info) << "GridFunction<1,1>(this=" << this << ", order_evaluate_name_lambda=" << &order_evaluate_name << ")"
+               << std::endl;
+  }
 
   GridFunction(std::tuple<int,
                           typename GenericFunctionType::GenericEvaluateFunctionType,
-                          typename GenericFunctionType::GenericJacobianFunctionType> order_evaluate_jacobian)
-    : BaseType()
-    , storage_(
+                          typename GenericFunctionType::GenericJacobianFunctionType> order_evaluate_jacobian,
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(
           new FunctionAsGridFunctionWrapper<E, r, rC, R>(new GenericFunctionType(std::get<0>(order_evaluate_jacobian),
                                                                                  std::get<1>(order_evaluate_jacobian),
                                                                                  /*name=*/"",
                                                                                  /*param_type=*/{},
                                                                                  std::get<2>(order_evaluate_jacobian))))
-  {}
+    , name_("GridFunction")
+  {
+    LOG_(info) << "GridFunction<1,1>(this=" << this << ", order_evaluate_jacobian_lambda=" << &order_evaluate_jacobian
+               << ")" << std::endl;
+  }
+
+  GridFunction(std::tuple<int,
+                          typename GenericFunctionType::GenericEvaluateFunctionType,
+                          typename GenericFunctionType::GenericJacobianFunctionType,
+                          const std::string&> order_evaluate_jacobian_name,
+               const std::string logging_prefix = "")
+    : BaseType({}, logging_prefix.empty() ? "GridFunction" : logging_prefix, logging_prefix.empty())
+    , function_(new FunctionAsGridFunctionWrapper<E, r, rC, R>(
+          new GenericFunctionType(std::get<0>(order_evaluate_jacobian_name),
+                                  std::get<1>(order_evaluate_jacobian_name),
+                                  /*name=*/"",
+                                  /*param_type=*/{},
+                                  std::get<2>(order_evaluate_jacobian_name))))
+    , name_(std::get<3>(order_evaluate_jacobian_name))
+  {
+    LOG_(info) << "GridFunction<1,1>(this=" << this
+               << ", order_evaluate_jacobian_name_lambda=" << &order_evaluate_jacobian_name << ")" << std::endl;
+  }
 
   GridFunction(const ThisType& other)
     : BaseType(other)
-    , storage_(other.storage_)
+    , function_(other.function_->copy_as_grid_function())
+    , name_(other.name_)
   {}
 
-  GridFunction(ThisType&& source)
-    : BaseType(source)
-    , storage_(std::move(source.storage_))
-  {}
+  GridFunction(ThisType&&) = default;
+
+
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
 
   std::unique_ptr<LocalFunctionType> local_function() const override final
   {
-    return storage_.access().local_function();
+    LOG_(info) << "GridFunction<1,1>::local_function()" << std::endl;
+    return function_->local_function();
   }
 
   std::string name() const override final
   {
-    return storage_.access().name();
+    return name_;
   }
 
 private:
-  Common::ConstStorageProvider<GridFunctionInterface<E, 1, 1, R>> storage_;
+  std::unique_ptr<GridFunctionInterface<E, 1, 1, R>> function_;
+  std::string name_;
 }; // class GridFunction<..., 1, 1, ...>
 
 
diff --git a/dune/xt/functions/indicator.hh b/dune/xt/functions/indicator.hh
index d91809e547012763dd866a2118ae3813df77245e..22c8d0aa83f46626f1f107ec2d4d1e514a3b5678 100644
--- a/dune/xt/functions/indicator.hh
+++ b/dune/xt/functions/indicator.hh
@@ -45,7 +45,7 @@ class IndicatorGridFunction : public GridFunctionInterface<E, r, rC, R>
     using GeometryType = typename ElementType::Geometry;
 
     LocalIndicatorGridFunction(
-        const std::vector<std::tuple<DomainType, DomainType, RangeType>>& subdomain_and_value_tuples)
+        const std::shared_ptr<std::vector<std::tuple<DomainType, DomainType, RangeType>>> subdomain_and_value_tuples)
       : InterfaceType()
       , subdomain_and_value_tuples_(subdomain_and_value_tuples)
     {}
@@ -55,7 +55,7 @@ class IndicatorGridFunction : public GridFunctionInterface<E, r, rC, R>
     {
       current_value_ = 0.;
       const auto center = element.geometry().center();
-      for (const auto& subdomain_and_value_tuple : subdomain_and_value_tuples_) {
+      for (const auto& subdomain_and_value_tuple : *subdomain_and_value_tuples_) {
         const auto& subdomain_ll = std::get<0>(subdomain_and_value_tuple);
         const auto& subdomain_ur = std::get<1>(subdomain_and_value_tuple);
         if (Common::FloatCmp::le(subdomain_ll, center) && Common::FloatCmp::lt(center, subdomain_ur))
@@ -84,7 +84,7 @@ class IndicatorGridFunction : public GridFunctionInterface<E, r, rC, R>
     }
 
   private:
-    const std::vector<std::tuple<DomainType, DomainType, RangeType>>& subdomain_and_value_tuples_;
+    const std::shared_ptr<std::vector<std::tuple<DomainType, DomainType, RangeType>>> subdomain_and_value_tuples_;
     RangeType current_value_;
   }; // class LocalIndicatorGridFunction
 
@@ -117,6 +117,12 @@ public:
     return config;
   } // ... defaults(...)
 
+  IndicatorGridFunction(std::shared_ptr<std::vector<std::tuple<DomainType, DomainType, RangeType>>> values,
+                        const std::string name_in = "IndicatorGridFunction")
+    : subdomain_and_value_tuples_(values)
+    , name_(name_in)
+  {}
+
   /**
    *        Can be used for declaration of lower left corner and upper right corner of the desired domains.
 \code
@@ -124,9 +130,8 @@ FunctionType function({{lowerleft_1, upperright_1, value_1}, {lowerleft_2, upper
 \endcode
    */
   IndicatorGridFunction(const std::vector<std::tuple<DomainType, DomainType, RangeType>>& values,
-                        const std::string name_in = "indicator")
-    : subdomain_and_value_tuples_(values)
-    , name_(name_in)
+                        const std::string nm = "IndicatorGridFunction")
+    : IndicatorGridFunction(std::make_shared<std::vector<std::tuple<DomainType, DomainType, RangeType>>>(values), nm)
   {}
 
   /**
@@ -139,11 +144,27 @@ FunctionType function({{{{0., 1.}, {0., 1.}}, 0.7}, {{{6., 10.}, {8., 10.}}, 0.9
    * if you want to set indicator intervals [0,1] x [0,1] with value 0.7 and [6,10] x [8,10] with value 0.9.
    */
   IndicatorGridFunction(const std::vector<std::pair<Common::FieldMatrix<D, d, 2>, RangeType>>& values,
-                        const std::string name_in = "indicator")
-    : subdomain_and_value_tuples_(convert_from_domains(values))
-    , name_(name_in)
+                        const std::string nm = "IndicatorGridFunction")
+    : IndicatorGridFunction(convert_from_domains(values), nm)
   {}
 
+  IndicatorGridFunction(const ThisType&) = default;
+
+  IndicatorGridFunction(ThisType&&) = default;
+
+
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
+
   std::string name() const override final
   {
     return name_;
@@ -155,10 +176,10 @@ FunctionType function({{{{0., 1.}, {0., 1.}}, 0.7}, {{{6., 10.}, {8., 10.}}, 0.9
   }
 
 private:
-  static std::vector<std::tuple<DomainType, DomainType, RangeType>>
+  static std::shared_ptr<std::vector<std::tuple<DomainType, DomainType, RangeType>>>
   convert_from_domains(const std::vector<std::pair<Common::FieldMatrix<D, d, 2>, RangeType>>& values)
   {
-    std::vector<std::tuple<DomainType, DomainType, RangeType>> ret;
+    auto ret = std::make_shared<std::vector<std::tuple<DomainType, DomainType, RangeType>>>();
     for (const auto& element : values) {
       DomainType tmp_coordinates_ll(0.);
       DomainType tmp_coordinates_ur(0.);
@@ -166,12 +187,12 @@ private:
         tmp_coordinates_ll[dd] = element.first[dd][0];
         tmp_coordinates_ur[dd] = element.first[dd][1];
       }
-      ret.emplace_back(tmp_coordinates_ll, tmp_coordinates_ur, element.second);
+      ret->emplace_back(tmp_coordinates_ll, tmp_coordinates_ur, element.second);
     }
     return ret;
   } // convert_from_tuples(...)
 
-  const std::vector<std::tuple<DomainType, DomainType, RangeType>> subdomain_and_value_tuples_;
+  const std::shared_ptr<std::vector<std::tuple<DomainType, DomainType, RangeType>>> subdomain_and_value_tuples_;
   const std::string name_;
 }; // class IndicatorGridFunction
 
@@ -179,6 +200,7 @@ private:
 template <size_t d, size_t r = 1, size_t rC = 1, class R = double>
 class IndicatorFunction : public FunctionInterface<d, r, rC, R>
 {
+  using ThisType = IndicatorFunction;
   using BaseType = FunctionInterface<d, r, rC, R>;
 
 public:
@@ -187,18 +209,38 @@ public:
   using typename BaseType::DomainType;
   using typename BaseType::RangeReturnType;
 
-  IndicatorFunction(const std::vector<std::tuple<DomainType, DomainType, RangeReturnType>>& values,
-                    const std::string nm = "indicator")
+  IndicatorFunction(std::shared_ptr<std::vector<std::tuple<DomainType, DomainType, RangeReturnType>>> values,
+                    const std::string nm = "IndicatorFunction")
     : subdomain_and_value_tuples_(values)
     , name_(nm)
   {}
 
+  IndicatorFunction(const std::vector<std::tuple<DomainType, DomainType, RangeReturnType>>& values,
+                    const std::string nm = "IndicatorFunction")
+    : IndicatorFunction(std::make_shared<std::vector<std::tuple<DomainType, DomainType, RangeReturnType>>>(values), nm)
+  {}
+
   IndicatorFunction(const std::vector<std::pair<Common::FieldMatrix<D, d, 2>, RangeReturnType>>& values,
-                    const std::string nm = "indicator")
-    : subdomain_and_value_tuples_(convert_from_domains(values))
-    , name_(nm)
+                    const std::string nm = "IndicatorFunction")
+    : IndicatorFunction(convert_from_domains(values), nm)
   {}
 
+  IndicatorFunction(const ThisType&) = default;
+
+  IndicatorFunction(ThisType&&) = default;
+
+private:
+  ThisType* copy_as_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_function_impl());
+  }
+
   int order(const XT::Common::Parameter& /*param*/ = {}) const override final
   {
     return 0;
@@ -223,7 +265,7 @@ public:
 
   std::string name() const override final
   {
-    return "dune.xt.functions.indicatorfunction";
+    return name_;
   }
 
   using BaseType::evaluate;
@@ -232,7 +274,7 @@ public:
                            const Common::Parameter& /*param*/ = {}) const override final
   {
     RangeReturnType value(0.);
-    for (const auto& subdomain_and_value_tuple : subdomain_and_value_tuples_) {
+    for (const auto& subdomain_and_value_tuple : *subdomain_and_value_tuples_) {
       const auto& subdomain_ll = std::get<0>(subdomain_and_value_tuple);
       const auto& subdomain_ur = std::get<1>(subdomain_and_value_tuple);
       if (Common::FloatCmp::le(subdomain_ll, point_in_global_coordinates)
@@ -251,10 +293,10 @@ public:
   }
 
 private:
-  static std::vector<std::tuple<DomainType, DomainType, RangeReturnType>>
+  static std::shared_ptr<std::vector<std::tuple<DomainType, DomainType, RangeReturnType>>>
   convert_from_domains(const std::vector<std::pair<Common::FieldMatrix<D, d, 2>, RangeReturnType>>& values)
   {
-    std::vector<std::tuple<DomainType, DomainType, RangeReturnType>> ret;
+    auto ret = std::make_shared<std::vector<std::tuple<DomainType, DomainType, RangeReturnType>>>();
     for (const auto& element : values) {
       DomainType tmp_coordinates_ll(0.);
       DomainType tmp_coordinates_ur(0.);
@@ -262,12 +304,12 @@ private:
         tmp_coordinates_ll[dd] = element.first[dd][0];
         tmp_coordinates_ur[dd] = element.first[dd][1];
       }
-      ret.emplace_back(tmp_coordinates_ll, tmp_coordinates_ur, element.second);
+      ret->emplace_back(tmp_coordinates_ll, tmp_coordinates_ur, element.second);
     }
     return ret;
   } // convert_from_tuples(...)
 
-  const std::vector<std::tuple<DomainType, DomainType, RangeReturnType>> subdomain_and_value_tuples_;
+  const std::shared_ptr<std::vector<std::tuple<DomainType, DomainType, RangeReturnType>>> subdomain_and_value_tuples_;
   const std::string name_;
 }; // class IndicatorFunction
 
diff --git a/dune/xt/functions/interfaces/element-functions.hh b/dune/xt/functions/interfaces/element-functions.hh
index f97daea9278723e952be950fc26017afa98a5979..3a93d69fcd0106757d68cff28c840873d7475401 100644
--- a/dune/xt/functions/interfaces/element-functions.hh
+++ b/dune/xt/functions/interfaces/element-functions.hh
@@ -58,12 +58,20 @@ class ConstProductElementFunction;
 template <class LeftFactorType, class RightFactorType>
 class ProductElementFunction;
 
+template <class LeftFactorType, class RightFactorType>
+class ConstFractionElementFunction;
+
+template <class LeftFactorType, class RightFactorType>
+class FractionElementFunction;
+
 
 namespace internal {
 
 
-template <class LeftType, class RightType, CombinationType>
-class CombinedElementFunctionHelper;
+template <class, class, class CombinationType>
+struct CombinedHelper;
+
+
 }
 
 
@@ -652,25 +660,47 @@ public:
     result[0] = this->derivative(alpha, point_in_reference_element, param);
   }
 
-  /**
-   * \}
-   * \name ´´These operators are provided for convenience.''
-   * \{
-   **/
+  /// \}
+
+  /// \name Numerical operators (const variants).
+  /// \{
 
   ConstDifferenceElementFunction<ThisType, ThisType> operator-(const ThisType& other) const
   {
     return ConstDifferenceElementFunction<ThisType, ThisType>(*this, other);
   }
 
-  DifferenceElementFunction<ThisType, ThisType> operator-(ThisType& other)
+  ConstSumElementFunction<ThisType, ThisType> operator+(const ThisType& other) const
   {
-    return DifferenceElementFunction<ThisType, ThisType>(*this, other);
+    return ConstSumElementFunction<ThisType, ThisType>(*this, other);
   }
 
-  ConstSumElementFunction<ThisType, ThisType> operator+(const ThisType& other) const
+  template <class OtherType>
+  std::enable_if_t<is_element_function<OtherType>::value
+                       && internal::CombinedHelper<ThisType, OtherType, CombinationType::product>::available,
+                   ConstProductElementFunction<ThisType, as_element_function_interface_t<OtherType>>>
+  operator*(const OtherType& other) const
   {
-    return ConstSumElementFunction<ThisType, ThisType>(*this, other);
+    return ConstProductElementFunction<ThisType, as_element_function_interface_t<OtherType>>(*this, other);
+  }
+
+  template <class OtherType>
+  std::enable_if_t<is_element_function<OtherType>::value
+                       && internal::CombinedHelper<ThisType, OtherType, CombinationType::fraction>::available,
+                   ConstFractionElementFunction<ThisType, as_element_function_interface_t<OtherType>>>
+  operator/(const OtherType& other) const
+  {
+    return ConstFractionElementFunction<ThisType, as_element_function_interface_t<OtherType>>(*this, other);
+  }
+
+  /// \}
+
+  /// \name Numerical operators (mutable variants).
+  /// \{
+
+  DifferenceElementFunction<ThisType, ThisType> operator-(ThisType& other)
+  {
+    return DifferenceElementFunction<ThisType, ThisType>(*this, other);
   }
 
   SumElementFunction<ThisType, ThisType> operator+(ThisType& other)
@@ -679,28 +709,24 @@ public:
   }
 
   template <class OtherType>
-  std::enable_if_t<
-      is_element_function<OtherType>::value
-          && internal::CombinedElementFunctionHelper<ThisType, OtherType, CombinationType::product>::available,
-      ConstProductElementFunction<ThisType, OtherType>>
-  operator*(const OtherType& other) const
+  std::enable_if_t<is_element_function<OtherType>::value
+                       && internal::CombinedHelper<ThisType, OtherType, CombinationType::product>::available,
+                   ProductElementFunction<ThisType, as_element_function_interface_t<OtherType>>>
+  operator*(OtherType& other)
   {
-    return ConstProductElementFunction<ThisType, OtherType>(*this, other);
+    return ProductElementFunction<ThisType, as_element_function_interface_t<OtherType>>(*this, other);
   }
 
   template <class OtherType>
-  std::enable_if_t<
-      is_element_function<OtherType>::value
-          && internal::CombinedElementFunctionHelper<ThisType, OtherType, CombinationType::product>::available,
-      ProductElementFunction<ThisType, OtherType>>
-  operator*(OtherType& other)
+  std::enable_if_t<is_element_function<OtherType>::value
+                       && internal::CombinedHelper<ThisType, OtherType, CombinationType::fraction>::available,
+                   FractionElementFunction<ThisType, as_element_function_interface_t<OtherType>>>
+  operator/(OtherType& other)
   {
-    return ProductElementFunction<ThisType, OtherType>(*this, other);
+    return FractionElementFunction<ThisType, as_element_function_interface_t<OtherType>>(*this, other);
   }
 
-  /**
-   * \{
-   **/
+  /// \}
 
 private:
   template <class FullType>
diff --git a/dune/xt/functions/interfaces/function.hh b/dune/xt/functions/interfaces/function.hh
index 23544a448f8f35b19ec237a941db44c23799190d..5fd99a6785668b5870c576808f24561dddc43ccc 100644
--- a/dune/xt/functions/interfaces/function.hh
+++ b/dune/xt/functions/interfaces/function.hh
@@ -31,16 +31,29 @@ namespace XT {
 namespace Functions {
 
 
-// forwards, required in operator+-*
-template <class MinuendType, class SubtrahendType>
+// forwards, required in operator+-*, includes are below
+namespace internal {
+
+
+template <class, class, class CombinationType>
+struct CombinedHelper;
+
+
+} // namespace internal
+
+
+template <class, class>
 class DifferenceFunction;
 
-template <class LeftSummandType, class RightSummandType>
+template <class, class>
 class SumFunction;
 
-template <class LeftSummandType, class RightSummandType>
+template <class, class>
 class ProductFunction;
 
+template <class, class>
+class FractionFunction;
+
 // forward, required in FunctionInterface::as_grid_function
 template <class E, size_t r, size_t rC, class R>
 class FunctionAsGridFunctionWrapper;
@@ -52,7 +65,7 @@ class FunctionAsGridFunctionWrapper;
  *        These functions do not depend on a grid, but only on the dimensions and fields of their domain and range.
  *        See in particular RangeReturnTypeSelector and DerivativeRangeReturnTypeSelector for the interpretation of a
  *        function and its derivatives, and GridFunctionInterface for functions which may have discontinuities between
- *        entites (as in: which are double-valued on intersections).
+ *        entities (as in: which are double-valued on intersections).
  *
  *        To turn a function into a function which is localizable w.r.t. a GridView of matching dimension (e.g.,
  *        to visualize it or to use it in a discretization scheme), use as_grid_function to obtain a const reference to
@@ -120,13 +133,40 @@ public:
     : Common::ParametricInterface(param_type)
   {}
 
+  FunctionInterface(const ThisType& other)
+    : Common::ParametricInterface(other)
+  {}
+
+  FunctionInterface(ThisType&) = default;
+
   virtual ~FunctionInterface() = default;
 
+  ThisType& operator=(const ThisType&) = delete;
+
+  ThisType& operator=(ThisType&&) = delete;
+
   /**
    * \name ´´These methods have to be implemented.''
    * \{
    **/
 
+  /**
+   * \brief Returns a (shallow) copy of the function.
+   * actual implementation work is delegated to the private `copy_as_function_impl`
+   * combined with hiding `copy_as_function` in derived classes, this allows us the a
+   * unique_ptr with correct type at all levels of the polymorphic hierarchy
+   *
+   * \note This is intended to be cheap, so make sure to share resources (but in a thread-safe way)!
+   */
+  std::unique_ptr<ThisType> copy_as_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_function_impl());
+  }
+
+private:
+  virtual ThisType* copy_as_function_impl() const = 0;
+
+public:
   virtual int order(const XT::Common::Parameter& /*param*/ = {}) const = 0;
 
   /**
@@ -165,28 +205,47 @@ public:
     return "dune.xt.functions.function";
   }
 
-  /**
-   * \}
-   * \name ´´These methods are default implemented and should be overridden to improve their performance.''
-   * \{
-   **/
+  /// \}
+  /// \name Operators emulating numeric types.
+  /// \{
+
+  Functions::DifferenceFunction<ThisType, ThisType> operator-(const ThisType& other) const
+  {
+    return Functions::DifferenceFunction<ThisType, ThisType>(
+        *this, other, "(" + this->name() + " - " + other.name() + ")");
+  }
 
-  DifferenceType operator-(const ThisType& other) const
+  Functions::SumFunction<ThisType, ThisType> operator+(const ThisType& other) const
   {
-    return DifferenceType(*this, other);
+    return Functions::SumFunction<ThisType, ThisType>(*this, other, "(" + this->name() + " + " + other.name() + ")");
   }
 
-  SumType operator+(const ThisType& other) const
+  template <class OtherType>
+  std::enable_if_t<is_function<OtherType>::value
+                       && internal::CombinedHelper<ThisType, OtherType, CombinationType::product>::available,
+                   Functions::ProductFunction<ThisType, as_function_interface_t<OtherType>>>
+  operator*(const OtherType& other) const
   {
-    return SumType(*this, other);
+    return Functions::ProductFunction<ThisType, as_function_interface_t<OtherType>>(
+        *this, other, "(" + this->name() + "*" + other.name() + ")");
   }
 
   template <class OtherType>
-  auto operator*(const OtherType& other) const
+  std::enable_if_t<is_function<OtherType>::value
+                       && internal::CombinedHelper<ThisType, OtherType, CombinationType::fraction>::available,
+                   Functions::FractionFunction<ThisType, as_function_interface_t<OtherType>>>
+  operator/(const OtherType& other) const
   {
-    return Functions::ProductFunction<ThisType, OtherType>(*this, other);
+    return Functions::FractionFunction<ThisType, as_function_interface_t<OtherType>>(
+        *this, other, "(" + this->name() + "/" + other.name() + ")");
   }
 
+  /**
+   * \}
+   * \name ´´These methods are default implemented and should be overridden to improve their performance.''
+   * \{
+   **/
+
   virtual R evaluate(const DomainType& point_in_global_coordinates,
                      const size_t row,
                      const size_t col = 0,
@@ -258,22 +317,24 @@ public:
   /**
    * \note This function keeps a map of all wrappers in a local static map, to avoid temporaries.
    * \todo Check if this implementation is thread safe!
+   * \todo nvm thread safe, the caching is already broken for multiple functions in sequence
    */
   template <class E>
   const typename std::enable_if<XT::Grid::is_entity<E>::value && E::dimension == d,
-                                FunctionAsGridFunctionWrapper<E, r, rC, R>>::type&
+                                FunctionAsGridFunctionWrapper<E, r, rC, R>>::type
   as_grid_function() const
   {
-    static std::map<const ThisType*, std::unique_ptr<FunctionAsGridFunctionWrapper<E, r, rC, R>>> wrappers;
-    if (wrappers.find(this) == wrappers.end())
-      wrappers[this] = std::make_unique<FunctionAsGridFunctionWrapper<E, r, rC, R>>(*this);
-    return *(wrappers[this]);
+    //    static std::map<const ThisType*, std::unique_ptr<FunctionAsGridFunctionWrapper<E, r, rC, R>>> wrappers;
+    //    if (wrappers.find(this) == wrappers.end())
+    //      wrappers[this] = std::make_unique<FunctionAsGridFunctionWrapper<E, r, rC, R>>(*this);
+    //    return *(wrappers[this]);
+    return FunctionAsGridFunctionWrapper<E, r, rC, R>(copy_as_function());
   }
 
   template <class ViewTraits>
   const typename std::enable_if<
       (ViewTraits::Grid::dimension == d),
-      FunctionAsGridFunctionWrapper<typename ViewTraits::template Codim<0>::Entity, r, rC, R>>::type&
+      FunctionAsGridFunctionWrapper<typename ViewTraits::template Codim<0>::Entity, r, rC, R>>::type
   as_grid_function(const GridView<ViewTraits>& /*grid_view*/) const
   {
     return this->as_grid_function<typename ViewTraits::template Codim<0>::Entity>();
diff --git a/dune/xt/functions/interfaces/grid-function.hh b/dune/xt/functions/interfaces/grid-function.hh
index a7e2e8fcccec2d139053cf8f1205575106b28634..ba8eb67114102e6cb600129b94a67963a76177f4 100644
--- a/dune/xt/functions/interfaces/grid-function.hh
+++ b/dune/xt/functions/interfaces/grid-function.hh
@@ -25,15 +25,16 @@
 
 #include <dune/xt/common/filesystem.hh>
 #include <dune/xt/common/memory.hh>
+#include <dune/xt/common/timedlogging.hh>
 #include <dune/xt/common/type_traits.hh>
 #include <dune/xt/grid/grids.hh>
 #include <dune/xt/grid/layers.hh>
 #include <dune/xt/grid/view/from-part.hh>
 #include <dune/xt/grid/type_traits.hh>
 
+#include <dune/xt/functions/base/visualization.hh>
 #include <dune/xt/functions/exceptions.hh>
 #include <dune/xt/functions/type_traits.hh>
-#include <dune/xt/functions/base/visualization.hh>
 
 #include "element-functions.hh"
 
@@ -42,17 +43,21 @@ namespace XT {
 namespace Functions {
 
 
-template <class MinuendType, class SubtrahendType>
+// forwards, includes are below
+template <class, class, class CombinationType>
+struct CombinedHelper;
+
+template <class, class>
 class DifferenceGridFunction;
 
-template <class LeftSummandType, class RightSummandType>
+template <class, class>
 class SumGridFunction;
 
-template <class LeftSummandType, class RightSummandType>
+template <class, class>
 class ProductGridFunction;
 
-// template <class Function>
-// class DivergenceFunction;
+template <class, class>
+class FractionGridFunction;
 
 
 /**
@@ -67,9 +72,12 @@ class ProductGridFunction;
  *        situations which could not be handled generically later on.
  */
 template <class Element, size_t rangeDim = 1, size_t rangeDimCols = 1, class RangeField = double>
-class GridFunctionInterface : public Common::ParametricInterface
+class GridFunctionInterface
+  : public Common::ParametricInterface
+  , public Common::WithLogger<GridFunctionInterface<Element, rangeDim, rangeDimCols, RangeField>>
 {
-  using ThisType = GridFunctionInterface<Element, rangeDim, rangeDimCols, RangeField>;
+  using ThisType = GridFunctionInterface;
+  using Logger = Common::WithLogger<GridFunctionInterface<Element, rangeDim, rangeDimCols, RangeField>>;
 
 public:
   using LocalFunctionType = ElementFunctionInterface<Element, rangeDim, rangeDimCols, RangeField>;
@@ -90,25 +98,67 @@ public:
 
   static constexpr bool available = false;
 
-  using DifferenceType = Functions::DifferenceGridFunction<ThisType, ThisType>;
-  using SumType = Functions::SumGridFunction<ThisType, ThisType>;
+private:
+  std::string logging_id() const
+  {
+    return "GridFunctionInterface<" + Common::to_string(size_t(r)) + "," + Common::to_string(size_t(rC)) + ">";
+  }
 
-  GridFunctionInterface(const Common::ParameterType& param_type = {})
+public:
+  GridFunctionInterface(const Common::ParameterType& param_type = {},
+                        const std::string& logging_prefix = "",
+                        const bool logging_disabled = true)
     : Common::ParametricInterface(param_type)
+    , Logger(logging_prefix.empty() ? "GridFunctionInterface" : logging_prefix, logging_disabled)
+  {
+    LOG_(debug) << logging_id() << "(param_type=" << param_type << ")" << std::endl;
+  }
+
+  GridFunctionInterface(const ThisType& other)
+    : Common::ParametricInterface(other)
+    , Logger(other)
   {}
 
+  GridFunctionInterface(ThisType&) = default;
+
   virtual ~GridFunctionInterface() = default;
 
+  ThisType& operator=(const ThisType&) = delete;
+
+  ThisType& operator=(ThisType&&) = delete;
+
   static std::string static_id()
   {
     return "dune.xt.functions.gridfunction";
   }
 
   /**
-   * \name ´´This method has to be implemented.''
+   * \name ´´These methods have to be implemented.''
    * \{
    **/
 
+  /**
+   * \brief Returns a (shallow) copy of the function.
+   * actual implementation work is delegated to the private `copy_as_function_impl`
+   * combined with hiding `copy_as_function` in dervived classes, this allows us the a
+   * unique_ptr with correct type at all levels of the polymorphic hierarchy
+   *
+   * \note This is intended to be cheap, so make sure to share resources (but in a thread-safe way)!
+   */
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
+
+private:
+  virtual ThisType* copy_as_grid_function_impl() const = 0;
+
+public:
+  /**
+   * \brief Returns the local function which can be bound to grid elements.
+   *
+   * \note If possible, the returned function should be able to live on its own, e.g. by copying the grid function.
+   */
   virtual std::unique_ptr<LocalFunctionType> local_function() const = 0;
 
   /**
@@ -119,28 +169,69 @@ public:
 
   virtual std::string name() const
   {
-    return "dune.xt.functions.gridfunction";
+    LOG_(debug) << logging_id() << "::name()\n   returning \"GridFunction\"" << std::endl;
+    return "GridFunction";
   }
 
   /// \}
 
-  DifferenceType operator-(const ThisType& other) const
+  /// \name Operators emulating numeric types.
+  /// \{
+
+  Functions::DifferenceGridFunction<ThisType, ThisType> operator-(const ThisType& other) const
   {
-    return DifferenceType(*this, other);
+    std::string derived_logging_prefix = "";
+    if (this->logger.debug_enabled || other.logger.debug_enabled) {
+      derived_logging_prefix = "(" + this->logger.prefix + " - " + other.logger.prefix + ")";
+      this->logger.debug() << logging_id() << "::operator-(other=" << &other << ")" << std::endl;
+    }
+    return Functions::DifferenceGridFunction<ThisType, ThisType>(
+        *this, other, "(" + this->name() + " - " + other.name() + ")", derived_logging_prefix);
   }
 
-  SumType operator+(const ThisType& other) const
+  Functions::SumGridFunction<ThisType, ThisType> operator+(const ThisType& other) const
   {
-    return SumType(*this, other);
+    std::string derived_logging_prefix = "";
+    if (this->logger.debug_enabled || other.logger.debug_enabled) {
+      derived_logging_prefix = "(" + this->logger.prefix + " - " + other.logger.prefix + ")";
+      this->logger.debug() << logging_id() << "::operator+(other=" << &other << ")" << std::endl;
+    }
+    return Functions::SumGridFunction<ThisType, ThisType>(
+        *this, other, "(" + this->name() + " + " + other.name() + ")", derived_logging_prefix);
   }
 
   template <class OtherType>
-  typename std::enable_if<is_grid_function<OtherType>::value, Functions::ProductGridFunction<ThisType, OtherType>>::type
+  std::enable_if_t<is_grid_function<OtherType>::value
+                       && internal::CombinedHelper<ThisType, OtherType, CombinationType::product>::available,
+                   Functions::ProductGridFunction<ThisType, as_grid_function_interface_t<OtherType>>>
   operator*(const OtherType& other) const
   {
-    return Functions::ProductGridFunction<ThisType, OtherType>(*this, other);
+    std::string derived_logging_prefix = "";
+    if (this->logger.debug_enabled || other.logger.debug_enabled) {
+      derived_logging_prefix = "(" + this->logger.prefix + "*" + other.logger.prefix + ")";
+      this->logger.debug() << logging_id() << "::operator*(other=" << &other << ")" << std::endl;
+    }
+    return Functions::ProductGridFunction<ThisType, as_grid_function_interface_t<OtherType>>(
+        *this, other, "(" + this->name() + "*" + other.name() + ")", derived_logging_prefix);
   }
 
+  template <class OtherType>
+  std::enable_if_t<is_grid_function<OtherType>::value
+                       && internal::CombinedHelper<ThisType, OtherType, CombinationType::fraction>::available,
+                   Functions::FractionGridFunction<ThisType, as_grid_function_interface_t<OtherType>>>
+  operator/(const OtherType& other) const
+  {
+    std::string derived_logging_prefix = "";
+    if (this->logger.debug_enabled || other.logger.debug_enabled) {
+      derived_logging_prefix = "(" + this->logger.prefix + "/" + other.logger.prefix + ")";
+      this->logger.debug() << logging_id() << "::operator/(other=" << &other << ")" << std::endl;
+    }
+    return Functions::FractionGridFunction<ThisType, as_grid_function_interface_t<OtherType>>(
+        *this, other, "(" + this->name() + "/" + other.name() + ")", derived_logging_prefix);
+  }
+
+  /// \}
+
   /**
    * \note  We use the SubsamplingVTKWriter (which is better for higher orders) by default: the grid you see in the
    *        visualization may thus be a refinement of the actual grid!
@@ -234,6 +325,5 @@ public:
 } // namespace Dune
 
 #include <dune/xt/functions/base/combined-grid-functions.hh>
-#include <dune/xt/functions/base/visualization.hh>
 
 #endif // DUNE_XT_FUNCTIONS_INTERFACES_GRID_FUNCTION_HH
diff --git a/dune/xt/functions/inverse.hh b/dune/xt/functions/inverse.hh
index 0c7d3f1f1aa701bc56f28a50530235fa3da43a00..f70ad2212a4e0af09db286ea3fd164780b0ca65e 100644
--- a/dune/xt/functions/inverse.hh
+++ b/dune/xt/functions/inverse.hh
@@ -15,7 +15,10 @@
 #include <dune/xt/common/float_cmp.hh>
 #include <dune/xt/common/memory.hh>
 #include <dune/xt/la/matrix-inverter.hh>
+#include <dune/xt/functions/grid-function.hh>
 #include <dune/xt/functions/interfaces/element-functions.hh>
+#include <dune/xt/functions/interfaces/function.hh>
+#include <dune/xt/functions/interfaces/grid-function.hh>
 #include <dune/xt/functions/exceptions.hh>
 #include <dune/xt/functions/type_traits.hh>
 
@@ -43,27 +46,26 @@ public:
 public:
   static constexpr bool available = (FunctionType::rC == FunctionType::r);
 
-  static RangeReturnType compute(const FunctionType& func, const DomainType& xx, const XT::Common::Parameter& param)
+  static RangeReturnType compute(const RangeReturnType& value)
   {
     if constexpr (FunctionType::rC == 1 && FunctionType::r == 1) {
-      auto value_to_invert = func.evaluate(xx, param);
+      auto value_to_invert = value[0];
       DUNE_THROW_IF(XT::Common::FloatCmp::eq(value_to_invert, 0.),
                     Exceptions::wrong_input_given,
                     "Scalar function value was not invertible!\n\nvalue_to_invert = " << value_to_invert);
       return 1. / value_to_invert;
     } else if constexpr (available) {
-      auto matrix_to_invert = func.evaluate(xx, param);
       RangeReturnType inverse_matrix;
       try {
-        inverse_matrix = XT::LA::invert_matrix(matrix_to_invert);
+        inverse_matrix = XT::LA::invert_matrix(value);
       } catch (const XT::LA::Exceptions::matrix_invert_failed& ee) {
         DUNE_THROW(Exceptions::wrong_input_given,
                    "Matrix-valued function value was not invertible!\n\nmatrix_to_invert = "
-                       << matrix_to_invert << "\n\nThis was the original error: " << ee.what());
+                       << value << "\n\nThis was the original error: " << ee.what());
       }
       return inverse_matrix;
     } else {
-      static_assert(AlwaysFalse<FunctionType>::value, "compute not available");
+      static_assert(AlwaysFalse<FunctionType>::value, "Not available for these dimensions!");
     }
   }
 }; // class InverseFunctionHelper
@@ -79,6 +81,8 @@ class InverseElementFunction
                                     internal::InverseFunctionHelper<ElementFunctionType>::rC,
                                     typename internal::InverseFunctionHelper<ElementFunctionType>::R>
 {
+  static_assert(is_element_function<ElementFunctionType>::value, "");
+
   using BaseType = ElementFunctionInterface<typename ElementFunctionType::E,
                                             internal::InverseFunctionHelper<ElementFunctionType>::r,
                                             internal::InverseFunctionHelper<ElementFunctionType>::rC,
@@ -88,6 +92,7 @@ class InverseElementFunction
 
 public:
   using typename BaseType::DomainType;
+  using typename BaseType::E;
   using typename BaseType::ElementType;
   using typename BaseType::RangeReturnType;
 
@@ -107,7 +112,7 @@ public:
   {}
 
 protected:
-  void post_bind(const ElementType& element)
+  void post_bind(const ElementType& element) override final
   {
     func_.access().bind(element);
   }
@@ -120,7 +125,7 @@ public:
 
   RangeReturnType evaluate(const DomainType& xx, const Common::Parameter& param = {}) const override final
   {
-    return Helper::compute(func_.access(), xx, param);
+    return Helper::compute(func_.access().evaluate(xx, param));
   }
 
 private:
@@ -136,6 +141,9 @@ class InverseFunction
                              internal::InverseFunctionHelper<FunctionType>::rC,
                              typename internal::InverseFunctionHelper<FunctionType>::R>
 {
+  static_assert(is_function<FunctionType>::value, "");
+
+  using ThisType = InverseFunction;
   using BaseType = FunctionInterface<FunctionType::d,
                                      internal::InverseFunctionHelper<FunctionType>::r,
                                      internal::InverseFunctionHelper<FunctionType>::rC,
@@ -147,21 +155,37 @@ public:
   using typename BaseType::DomainType;
   using typename BaseType::RangeReturnType;
 
-  InverseFunction(FunctionType& func, const int ord)
-    : func_(func)
+  InverseFunction(const FunctionType& func, const int ord)
+    : BaseType(func.parameter_type())
+    , func_(func.copy_as_function())
     , order_(ord)
   {}
 
-  InverseFunction(std::shared_ptr<FunctionType> func, const int ord)
-    : func_(func)
+  InverseFunction(FunctionType&& func, const int ord)
+    : BaseType(func->parameter_type())
+    , func_(std::move(func))
     , order_(ord)
   {}
 
-  InverseFunction(std::unique_ptr<FunctionType>&& func, const int ord)
-    : func_(std::move(func))
-    , order_(ord)
+  InverseFunction(const ThisType& other)
+    : BaseType(other)
+    , func_(other.func_->copy_as_function())
+    , order_(order)
   {}
 
+  InverseFunction(ThisType&&) = default;
+
+private:
+  ThisType* copy_as_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_function_impl());
+  }
   int order(const XT::Common::Parameter& /*param*/ = {}) const override final
   {
     return order_;
@@ -169,15 +193,18 @@ public:
 
   RangeReturnType evaluate(const DomainType& xx, const Common::Parameter& param = {}) const override final
   {
-    return Helper::compute(func_.access(), xx, param);
+    return Helper::compute(func_.evaluate(xx, param));
   }
 
 private:
-  XT::Common::StorageProvider<FunctionType> func_;
+  const std::unique_ptr<FunctionType> func_;
   const int order_;
 }; // class InverseFunction
 
 
+/**
+ * \todo Write custom local function to hold a copy af this!
+ */
 template <class GridFunctionType>
 class InverseGridFunction
   : public GridFunctionInterface<typename GridFunctionType::E,
@@ -185,46 +212,69 @@ class InverseGridFunction
                                  internal::InverseFunctionHelper<GridFunctionType>::rC,
                                  typename internal::InverseFunctionHelper<GridFunctionType>::R>
 {
-  using BaseType = GridFunctionInterface<typename GridFunctionType::E,
-                                         internal::InverseFunctionHelper<GridFunctionType>::r,
-                                         internal::InverseFunctionHelper<GridFunctionType>::rC,
-                                         typename internal::InverseFunctionHelper<GridFunctionType>::R>;
+  static_assert(is_grid_function<GridFunctionType>::value, "");
 
   using Helper = internal::InverseFunctionHelper<GridFunctionType>;
+  static const constexpr size_t r_ = GridFunctionType::r;
+  static const constexpr size_t rC_ = GridFunctionType::rC;
 
 public:
+  using ThisType = InverseGridFunction;
+  using BaseType = GridFunctionInterface<typename GridFunctionType::E,
+                                         internal::InverseFunctionHelper<GridFunctionType>::r,
+                                         internal::InverseFunctionHelper<GridFunctionType>::rC,
+                                         typename internal::InverseFunctionHelper<GridFunctionType>::R>;
+  using typename BaseType::E;
   using typename BaseType::LocalFunctionType;
+  using typename BaseType::R;
 
-  InverseGridFunction(const GridFunctionType& func, const int ord)
-    : func_(func)
+  InverseGridFunction(GridFunction<E, r_, rC_, R> func, const int ord, const std::string nm = "")
+    : func_(func.copy_as_grid_function())
     , order_(ord)
+    , name_(nm.empty() ? ("inverse of " + func_->name()) : nm)
   {}
 
-  InverseGridFunction(std::shared_ptr<const GridFunctionType> func, const int ord)
-    : func_(func)
+  InverseGridFunction(GridFunctionInterface<E, r_, rC_, R>&& func, const int ord, const std::string nm = "")
+    : func_(std::move(func))
     , order_(ord)
+    , name_(nm.empty() ? ("inverse of " + func_->name()) : nm)
   {}
 
-  InverseGridFunction(std::unique_ptr<const GridFunctionType>&& func, const int ord)
-    : func_(std::move(func))
-    , order_(ord)
+  InverseGridFunction(const ThisType& other)
+    : BaseType(other)
+    , func_(other.func_->copy_as_grid_function())
+    , order_(other.order_)
   {}
 
+  InverseGridFunction(ThisType&&) = default;
+
+
+private:
+  ThisType* copy_as_grid_function_impl() const override
+  {
+    return new ThisType(*this);
+  }
+
+public:
+  std::unique_ptr<ThisType> copy_as_grid_function() const
+  {
+    return std::unique_ptr<ThisType>(this->copy_as_grid_function_impl());
+  }
   std::unique_ptr<LocalFunctionType> local_function() const override final
   {
     using LocalFunction = InverseElementFunction<typename GridFunctionType::LocalFunctionType>;
-    return std::unique_ptr<LocalFunction>(new LocalFunction(std::move(func_.access().local_function()), order_));
+    return std::make_unique<LocalFunction>(func_->local_function(), order_);
   }
 
   std::string name() const override final
   {
-    auto func_name = func_.access().name();
-    return func_name.empty() ? ("dune.xt.functions.inversegridfunction") : ("inverse of " + func_name);
+    return name_;
   }
 
 private:
-  const XT::Common::ConstStorageProvider<GridFunctionType> func_;
+  const std::unique_ptr<GridFunctionInterface<E, r_, rC_, R>> func_;
   const int order_;
+  const std::string name_;
 }; // class InverseGridFunction
 
 
@@ -234,7 +284,7 @@ auto inverse(ElementFunctionInterface<E, r, rC, R>& func, const int order)
   if constexpr (internal::InverseFunctionHelper<ElementFunctionInterface<E, r, rC, R>>::available) {
     return InverseElementFunction<ElementFunctionInterface<E, r, rC, R>>(func, order);
   } else {
-    static_assert(AlwaysFalse<R>::value, "No inverse implementation available");
+    static_assert(AlwaysFalse<R>::value, "Not available for these dimensions!");
   }
 }
 
@@ -245,7 +295,7 @@ auto inverse(const FunctionInterface<d, r, rC, R>& func, const int order)
   if constexpr (internal::InverseFunctionHelper<FunctionInterface<d, r, rC, R>>::available) {
     return InverseFunction<FunctionInterface<d, r, rC, R>>(func, order);
   } else {
-    static_assert(AlwaysFalse<R>::value, "No inverse implementation available");
+    static_assert(AlwaysFalse<R>::value, "Not available for these dimensions!");
   }
 }
 
@@ -256,7 +306,7 @@ auto inverse(const GridFunctionInterface<E, r, rC, R>& func, const int order)
   if constexpr (internal::InverseFunctionHelper<GridFunctionInterface<E, r, rC, R>>::available) {
     return InverseGridFunction<GridFunctionInterface<E, r, rC, R>>(func, order);
   } else {
-    static_assert(AlwaysFalse<R>::value, "No inverse implementation available");
+    static_assert(AlwaysFalse<R>::value, "Not available for these dimensions!");
   }
 }
 
diff --git a/dune/xt/functions/spe10/model1.hh b/dune/xt/functions/spe10/model1.hh
index c78b6fb6490f2fa88612439a8db08d60be2a6dc4..c6e298f3892f8e40324e78310bedf70a33238a04 100644
--- a/dune/xt/functions/spe10/model1.hh
+++ b/dune/xt/functions/spe10/model1.hh
@@ -60,10 +60,10 @@ public:
   } // ... static_id(...)
 
 private:
-  static std::vector<RangeType> read_values_from_file(const std::string& filename,
-                                                      const RangeFieldType& min,
-                                                      const RangeFieldType& max,
-                                                      const RangeType& unit_range)
+  static std::shared_ptr<std::vector<RangeType>> read_values_from_file(const std::string& filename,
+                                                                       const RangeFieldType& min,
+                                                                       const RangeFieldType& max,
+                                                                       const RangeType& unit_range)
 
   {
     if (!(max > min))
@@ -76,11 +76,11 @@ private:
       DUNE_THROW(Exceptions::spe10_data_file_missing, "could not open '" << filename << "'!");
     static constexpr size_t entriesPerDim = model1_x_elements * model1_y_elements * model1_z_elements;
     // create storage (there should be exactly 6000 values in the file, but we only read the first 2000)
-    std::vector<RangeType> data(entriesPerDim, unit_range);
+    auto data = std::make_shared<std::vector<RangeType>>(entriesPerDim, unit_range);
     double tmp = 0;
     size_t counter = 0;
     while (datafile >> tmp && counter < entriesPerDim)
-      data[counter++] *= (tmp * scale) + shift;
+      (*data)[counter++] *= (tmp * scale) + shift;
     datafile.close();
     if (counter != entriesPerDim)
       DUNE_THROW(Dune::IOError,
diff --git a/dune/xt/functions/spe10/model2.hh b/dune/xt/functions/spe10/model2.hh
index 4464b5d2666e5c4547d1a3dcea7bd9129a7adcb2..10fb5a3f7ed01c09db80476c2b9b925fa97ad758 100644
--- a/dune/xt/functions/spe10/model2.hh
+++ b/dune/xt/functions/spe10/model2.hh
@@ -73,8 +73,8 @@ public:
   } // ... static_id(...)
 
 private:
-  static std::vector<RangeType> read_values_from_file(const std::string& filename,
-                                                      const Common::FieldVector<size_t, domain_dim>& number_of_elements)
+  static std::shared_ptr<std::vector<RangeType>>
+  read_values_from_file(const std::string& filename, const Common::FieldVector<size_t, domain_dim>& number_of_elements)
 
   {
     // read all the data from the file
@@ -104,12 +104,12 @@ private:
     // if (filecounter != 3366000)
     //#warning you are not using the entire data file. Use default number_of_elements instead.
 
-    std::vector<RangeType> data(entries_per_coordinate, RangeType(0));
+    auto data = std::make_shared<std::vector<RangeType>>(entries_per_coordinate, RangeType(0));
 
     for (size_t ii = 0; ii < entries_per_coordinate; ++ii) {
       for (size_t dim = 0; dim < domain_dim; ++dim) {
         const auto idx = ii + dim * entries_per_coordinate;
-        data[ii][dim][dim] = values_in_file[idx];
+        (*data)[ii][dim][dim] = values_in_file[idx];
       }
     }
     return data;
diff --git a/dune/xt/functions/test/.gitignore b/dune/xt/functions/test/.gitignore
deleted file mode 100644
index 6cc7cd31f48fdd0d5b7faa22d8441afe540f184e..0000000000000000000000000000000000000000
--- a/dune/xt/functions/test/.gitignore
+++ /dev/null
@@ -1,42 +0,0 @@
-# ~~~
-# This file is part of the dune-xt project:
-#   https://github.com/dune-community/dune-xt
-# Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
-# License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
-#      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
-#          with "runtime exception" (http://www.dune-project.org/license.html)
-# Authors:
-#   Felix Schindler (2013, 2016 - 2017)
-#   René Fritze     (2012, 2015 - 2019)
-#   Tobias Leibner  (2016, 2020)
-# ~~~
-
-test_validation
-*.o
-*.gcda
-*.gcno
-common_color
-common_logger
-common_parameter_validation
-function_expression_2
-common_math
-common_profiler
-common_string
-common_typenames
-data/
-fem_error
-fem_projection
-function_expression
-grid_information
-grid_provider
-function.param
-provider.param
-pgfoutput_*
-common_tuple
-grid_output_pgf
-functions_constant
-functions_expression
-functions_checkerboard
-functions_functions
-perm_case1.dat
-!builder_definitions.cmake
diff --git a/dune/xt/functions/type_traits.hh b/dune/xt/functions/type_traits.hh
index 25966a89bd6943303e16b1154568ca9af094eda6..0bc32873a513792ce8807d91a6d3b708bf87c4ca 100644
--- a/dune/xt/functions/type_traits.hh
+++ b/dune/xt/functions/type_traits.hh
@@ -16,6 +16,7 @@
 #include <dune/common/dynmatrix.hh>
 #include <dune/common/dynvector.hh>
 
+#include <dune/xt/common/exceptions.hh>
 #include <dune/xt/common/fmatrix.hh>
 #include <dune/xt/common/fvector.hh>
 #include <dune/xt/common/type_traits.hh>
@@ -125,8 +126,8 @@ struct RangeTypeSelector<R, r, 1>
 template <size_t d, class R, size_t r, size_t rC>
 struct DerivativeRangeTypeSelector
 {
-  using single_type = XT::Common::FieldVector<R, d>;
-  using return_single_type = FieldVector<R, d>;
+  using single_type = FieldVector<R, d>;
+  using return_single_type = XT::Common::FieldVector<R, d>;
   using dynamic_single_type = DynamicVector<R>;
 
   using row_derivative_type = FieldMatrix<R, rC, d>;
@@ -284,12 +285,106 @@ struct is_grid_function<T, true> : std::is_base_of<GridFunctionInterface<typenam
 {};
 
 
-enum class CombinationType
+template <class F>
+struct as_element_function_interface
+{
+  static_assert(is_element_function<F>::value, "");
+  using type = Functions::ElementFunctionInterface<typename F::E, F::r, F::rC, typename F::R>;
+};
+
+template <class F>
+using as_element_function_interface_t = typename as_element_function_interface<F>::type;
+
+
+template <class F>
+struct as_function_interface
+{
+  static_assert(is_function<F>::value, "");
+  using type = Functions::FunctionInterface<F::d, F::r, F::rC, typename F::R>;
+};
+
+template <class F>
+using as_function_interface_t = typename as_function_interface<F>::type;
+
+
+template <class F>
+struct as_grid_function_interface
+{
+  static_assert(is_grid_function<F>::value, "");
+  using type = Functions::GridFunctionInterface<typename F::E, F::r, F::rC, typename F::R>;
+};
+
+template <class F>
+using as_grid_function_interface_t = typename as_grid_function_interface<F>::type;
+
+
+struct CombinationType
+{
+  struct difference
+  {};
+  struct fraction
+  {};
+  struct product
+  {};
+  struct sum
+  {};
+}; // struct CombinationType
+
+
+template <typename CombinationType>
+struct GetCombination
+{
+  static std::string name()
+  {
+    return get_combination_name(CombinationType());
+  }
+
+  static std::string symbol()
+  {
+    return get_combination_symbol(CombinationType());
+  }
+}; // struct GetCombination
+
+
+inline std::string get_combination_name(CombinationType::difference)
+{
+  return "difference";
+}
+
+inline std::string get_combination_symbol(CombinationType::difference)
+{
+  return "-";
+}
+
+inline std::string get_combination_name(CombinationType::fraction)
+{
+  return "fraction";
+}
+
+inline std::string get_combination_symbol(CombinationType::fraction)
+{
+  return "/";
+}
+
+inline std::string get_combination_name(CombinationType::product)
+{
+  return "product";
+}
+
+inline std::string get_combination_symbol(CombinationType::product)
+{
+  return "*";
+}
+
+inline std::string get_combination_name(CombinationType::sum)
+{
+  return "sum";
+}
+
+inline std::string get_combination_symbol(CombinationType::sum)
 {
-  difference,
-  product,
-  sum
-}; // enum class CombinationType
+  return "+";
+}
 
 
 enum class DerivativeType
diff --git a/dune/xt/grid/element.hh b/dune/xt/grid/element.hh
new file mode 100644
index 0000000000000000000000000000000000000000..f900f0c8ff7e4af91556d9588c9b2b789736a80d
--- /dev/null
+++ b/dune/xt/grid/element.hh
@@ -0,0 +1,76 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+#ifndef DUNE_XT_GRID_ELEMENT_HH
+#define DUNE_XT_GRID_ELEMENT_HH
+
+#include <dune/grid/common/entity.hh>
+
+#include <dune/xt/common/numeric_cast.hh>
+#include <dune/xt/grid/type_traits.hh>
+
+namespace Dune {
+namespace XT {
+namespace Grid {
+
+
+template <class E>
+class SubEntityCenter
+{
+  static_assert(is_entity<E>::value, "");
+  using G = extract_grid_t<E>;
+  using D = typename G::ctype;
+  static const constexpr size_t d = G::dimension;
+
+  template <int cd = d, bool anything = true>
+  struct subEntity
+  {
+    static FieldVector<D, d> center(const E& element, const int codim, const size_t i)
+    {
+      if (codim == cd) {
+        DUNE_THROW_IF(i >= element.subEntities(codim),
+                      Common::Exceptions::index_out_of_range,
+                      "element.subEntities(" << codim << ") = " << element.subEntities(codim) << "\n   i = " << i);
+        return element.template subEntity<cd>(Common::numeric_cast<int>(i)).geometry().center();
+      } else
+        return subEntity<cd - 1>::center(element, codim, i);
+    } // ... center(...)
+  }; // struct subEntity
+
+  template <bool anything>
+  struct subEntity<0, anything>
+  {
+    static FieldVector<D, d> center(const E& element, const int codim, const size_t i)
+    {
+      DUNE_THROW_IF(codim != 0, Common::Exceptions::internal_error, "This must not happen");
+      return element.template subEntity<0>(Common::numeric_cast<int>(i)).geometry().center();
+    }
+  }; // struct subEntity
+
+public:
+  static FieldVector<D, d> get(const E& element, const int codim, const size_t i)
+  {
+    return subEntity<>::center(element, codim, i);
+  }
+}; // class SubEntityCenter
+
+
+template <int dim, class GridImp, template <int, int, class> class EntityImp>
+FieldVector<typename GridImp::ctype, GridImp::dimension>
+sub_entity_center(const Dune::Entity<0, dim, GridImp, EntityImp> element, const int codim, const size_t i)
+{
+  return SubEntityCenter<Dune::Entity<0, dim, GridImp, EntityImp>>::get(element, codim, i);
+}
+
+
+} // namespace Grid
+} // namespace XT
+} // namespace Dune
+
+#endif // DUNE_XT_GRID_ELEMENT_HH
diff --git a/dune/xt/grid/filters/intersection.hh b/dune/xt/grid/filters/intersection.hh
index 206163b6cdc1a65b998dcfc8450070a291a013d3..4ffa2d5e8658735de26c0997f6db366e9c720979 100644
--- a/dune/xt/grid/filters/intersection.hh
+++ b/dune/xt/grid/filters/intersection.hh
@@ -14,14 +14,15 @@
 
 #include <functional>
 
+#include <dune/grid/common/partitionset.hh>
+
 #include <dune/xt/common/memory.hh>
 #include <dune/xt/common/timedlogging.hh>
 
 #include <dune/xt/grid/boundaryinfo.hh>
 #include <dune/xt/grid/print.hh>
 #include <dune/xt/grid/type_traits.hh>
-
-#include <dune/grid/common/partitionset.hh>
+#include <dune/xt/grid/print.hh>
 
 #include "base.hh"
 
diff --git a/dune/xt/grid/intersection.hh b/dune/xt/grid/intersection.hh
index 0519110aad00197e070efa489c5819b1ece134b3..bc4ad8bf1379a1c6abb37d79066edb08add1b9ca 100644
--- a/dune/xt/grid/intersection.hh
+++ b/dune/xt/grid/intersection.hh
@@ -169,6 +169,7 @@ bool contains(const Dune::Intersection<G, I>& intersection,
 } // namespace XT
 
 
+// DXT_DEPRECATED_MSG did not work here
 template <class G, class I>
 [[deprecated("Use out << print(intersection) from <dune/xt/grid/print.hh> instead (05.07.2020)!")]] void
 operator<<(std::ostream& /*out*/, const Dune::Intersection<G, I>& intersection)
diff --git a/dune/xt/grid/mapper.hh b/dune/xt/grid/mapper.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c6b098d157d9e22a4ceef791a863d797dedcb6e6
--- /dev/null
+++ b/dune/xt/grid/mapper.hh
@@ -0,0 +1,88 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+#ifndef DUNE_XT_GRID_MAPPER_HH
+#define DUNE_XT_GRID_MAPPER_HH
+
+#include <dune/grid/common/mapper.hh>
+
+#include <dune/xt/common/numeric_cast.hh>
+#include <dune/xt/grid/type_traits.hh>
+
+namespace Dune {
+namespace XT {
+namespace Grid {
+
+
+template <class M, class E>
+class SubEntityIndex
+{
+  static_assert(is_entity<E>::value, "");
+  using G = extract_grid_t<E>;
+  using D = typename G::ctype;
+  static const constexpr size_t d = G::dimension;
+
+  template <int cd = d, bool anything = true>
+  struct subEntity
+  {
+    static size_t index(const M& mapper, const E& element, const int codim, const size_t i)
+    {
+      if (codim == cd) {
+        DUNE_THROW_IF(i >= element.subEntities(codim),
+                      Common::Exceptions::index_out_of_range,
+                      "element.subEntities(" << codim << ") = " << element.subEntities(codim) << "\n   i = " << i);
+        return Common::numeric_cast<size_t>(mapper.index(element.template subEntity<cd>(Common::numeric_cast<int>(i))));
+      } else
+        return subEntity<cd - 1>::index(mapper, element, codim, i);
+    } // ... center(...)
+  }; // struct subEntity
+
+  template <bool anything>
+  struct subEntity<0, anything>
+  {
+    static size_t index(const M& mapper, const E& element, const int codim, const size_t i)
+    {
+      DUNE_THROW_IF(codim != 0, Common::Exceptions::internal_error, "This must not happen");
+      DUNE_THROW_IF(i >= element.subEntities(0),
+                    Common::Exceptions::index_out_of_range,
+                    "element.subEntities(0) = " << element.subEntities(0) << "\n   i = " << i);
+      return Common::numeric_cast<size_t>(mapper.index(element.template subEntity<0>(Common::numeric_cast<int>(i))));
+    }
+  }; // struct subEntity
+
+public:
+  static size_t get(const M& mapper, const E& element, const int codim, const size_t i)
+  {
+    return subEntity<>::index(mapper, element, codim, i);
+  }
+}; // class SubEntityIndex
+
+
+template <typename G,
+          typename MapperImp,
+          typename IndexType,
+          int dim,
+          class GridImp,
+          template <int, int, class>
+          class EntityImp>
+size_t sub_entity_index(const Dune::Mapper<G, MapperImp, IndexType>& mapper,
+                        const Dune::Entity<0, dim, GridImp, EntityImp> element,
+                        const int codim,
+                        const size_t i)
+{
+  return SubEntityIndex<Dune::Mapper<G, MapperImp, IndexType>, Dune::Entity<0, dim, GridImp, EntityImp>>::get(
+      mapper, element, codim, i);
+}
+
+
+} // namespace Grid
+} // namespace XT
+} // namespace Dune
+
+#endif // DUNE_XT_GRID_MAPPER_HH
diff --git a/dune/xt/la/container/container-interface.hh b/dune/xt/la/container/container-interface.hh
index 08b66562b61cba2544fe21f91a7e8c4a073403e8..7507ca3641b1e1912b5e85ace2bcb69b005382f7 100644
--- a/dune/xt/la/container/container-interface.hh
+++ b/dune/xt/la/container/container-interface.hh
@@ -167,6 +167,12 @@ public:
     return this->as_imp();
   }
 
+  virtual derived_type& operator/=(const ScalarType& alpha)
+  {
+    scal(1. / alpha);
+    return this->as_imp();
+  }
+
   /**
    *  \brief  Multiplies every component of this by a scalar.
    *  \param  alpha The scalar.
diff --git a/dune/xt/la/container/matrix-interface.hh b/dune/xt/la/container/matrix-interface.hh
index 77c63f8a708cad0b339caea99344d1b093afc88e..1cccb033fec0a2ccd58b4f49de62a56e25094f91 100644
--- a/dune/xt/la/container/matrix-interface.hh
+++ b/dune/xt/la/container/matrix-interface.hh
@@ -417,11 +417,11 @@ protected:
   template <class MM>
   derived_type& add_assign(const MatrixInterface<MM, ScalarType>& other)
   {
-    const auto& other_pattern = other.pattern();
+    const auto other_pattern = other.pattern();
 #ifndef NDEBUG
     if (other.rows() != rows() || other.cols() != cols())
       DUNE_THROW(XT::Common::Exceptions::shapes_do_not_match, "Dimensions of matrices to be added do not match!");
-    const auto& this_pattern = pattern();
+    const auto this_pattern = pattern();
     if (!this_pattern.contains(other_pattern))
       DUNE_THROW(XT::Common::Exceptions::shapes_do_not_match,
                  "The matrix to be added contains entries that are not in this' pattern!");
@@ -435,11 +435,11 @@ protected:
   template <class MM>
   derived_type& subtract_assign(const MatrixInterface<MM, ScalarType>& other)
   {
-    const auto& other_pattern = other.pattern();
+    const auto other_pattern = other.pattern();
 #ifndef NDEBUG
     if (other.rows() != rows() || other.cols() != cols())
       DUNE_THROW(XT::Common::Exceptions::shapes_do_not_match, "Dimensions of matrices to be added do not match!");
-    const auto& this_pattern = pattern();
+    const auto this_pattern = pattern();
     if (!this_pattern.contains(other_pattern))
       DUNE_THROW(XT::Common::Exceptions::shapes_do_not_match,
                  "The matrix to be added contains entries that are not in this' pattern!");
diff --git a/dune/xt/test/functions/ESV2007_exact_solution.tpl b/dune/xt/test/functions/ESV2007_exact_solution.tpl
index 3bf28205c281b3d2426f61ae1522461afc3dbdc4..568476cc6e54d666ff788d2e3e173b3ebf1f46a8 100644
--- a/dune/xt/test/functions/ESV2007_exact_solution.tpl
+++ b/dune/xt/test/functions/ESV2007_exact_solution.tpl
@@ -114,7 +114,7 @@ TEST_F(ESV2007ExactSolutionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, glo
 TEST_F(ESV2007ExactSolutionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, is_bindable)
 {
   FunctionType default_function(3);
-  const auto& localizable_function = default_function.template as_grid_function<ElementType>();
+  auto&& localizable_function = default_function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
@@ -127,7 +127,7 @@ TEST_F(ESV2007ExactSolutionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, loc
 {
   const int expected_order = 3;
   FunctionType default_function(3);
-  const auto& localizable_function = default_function.template as_grid_function<ElementType>();
+  auto&& localizable_function = default_function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
@@ -141,7 +141,7 @@ TEST_F(ESV2007ExactSolutionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, loc
 TEST_F(ESV2007ExactSolutionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_evaluate)
 {
   FunctionType function(3);
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
@@ -160,7 +160,7 @@ TEST_F(ESV2007ExactSolutionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, loc
 TEST_F(ESV2007ExactSolutionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_jacobian)
 {
   FunctionType function(3);
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
diff --git a/dune/xt/test/functions/ESV2007_force.tpl b/dune/xt/test/functions/ESV2007_force.tpl
index a5e589fff454550a04a328aa94645d988eef9fb0..14704abe9e81ed95384559e31c614f65d8e906ca 100644
--- a/dune/xt/test/functions/ESV2007_force.tpl
+++ b/dune/xt/test/functions/ESV2007_force.tpl
@@ -114,7 +114,7 @@ TEST_F(ESV2007ForceFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, global_jaco
 TEST_F(ESV2007ForceFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, is_bindable)
 {
   FunctionType default_function(3);
-  const auto& localizable_function = default_function.template as_grid_function<ElementType>();
+  auto&& localizable_function = default_function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
@@ -127,7 +127,7 @@ TEST_F(ESV2007ForceFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_order
 {
   const int expected_order = 3;
   FunctionType default_function(3);
-  const auto& localizable_function = default_function.template as_grid_function<ElementType>();
+  auto&& localizable_function = default_function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
@@ -141,7 +141,7 @@ TEST_F(ESV2007ForceFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_order
 TEST_F(ESV2007ForceFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_evaluate)
 {
   FunctionType function(3);
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
@@ -160,7 +160,7 @@ TEST_F(ESV2007ForceFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_evalu
 TEST_F(ESV2007ForceFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_jacobian)
 {
   FunctionType function(3);
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
diff --git a/dune/xt/test/functions/constant.tpl b/dune/xt/test/functions/constant.tpl
index 64ad07351e25eaf74451ceb4957a74a36315b30a..4a8154dca99f5bdf90142f92e315a524bb0ef4ca 100644
--- a/dune/xt/test/functions/constant.tpl
+++ b/dune/xt/test/functions/constant.tpl
@@ -109,7 +109,7 @@ TEST_F(ConstantFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, global_jacobian
 TEST_F(ConstantFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, is_bindable)
 {
   FunctionType function(1.);
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
@@ -123,7 +123,7 @@ TEST_F(ConstantFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_order)
   for (auto vv : {-10., 3., 17., 41.}) {
     const RangeReturnType value(vv);
     FunctionType function(value);
-    const auto& localizable_function = function.template as_grid_function<ElementType>();
+    auto&& localizable_function = function.template as_grid_function<ElementType>();
     auto local_f = localizable_function.local_function();
     const auto leaf_view = grid_.leaf_view();
     for (auto&& element : Dune::elements(leaf_view)) {
@@ -139,7 +139,7 @@ TEST_F(ConstantFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_evaluate)
   for (auto value : {-10., 3., 17., 41.}) {
     const RangeReturnType expected_value(value);
     FunctionType function(expected_value);
-    const auto& localizable_function = function.template as_grid_function<ElementType>();
+    auto&& localizable_function = function.template as_grid_function<ElementType>();
     auto local_f = localizable_function.local_function();
     const auto leaf_view = grid_.leaf_view();
     for (auto&& element : Dune::elements(leaf_view)) {
@@ -160,7 +160,7 @@ TEST_F(ConstantFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_jacobian)
     DerivativeRangeReturnType expected_jacobian;
     //expected_jacobian *= 0;
     FunctionType function(value);
-    const auto& localizable_function = function.template as_grid_function<ElementType>();
+    auto&& localizable_function = function.template as_grid_function<ElementType>();
     auto local_f = localizable_function.local_function();
     const auto leaf_view = grid_.leaf_view();
     for (auto&& element : Dune::elements(leaf_view)) {
diff --git a/dune/xt/test/functions/element_function_interface_operators.cc b/dune/xt/test/functions/element_function_interface_operators.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a982f75902c412329f7e3da39045cb992ff575f4
--- /dev/null
+++ b/dune/xt/test/functions/element_function_interface_operators.cc
@@ -0,0 +1,135 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+
+#include <dune/xt/test/main.hxx> // <- has to come first, include config.h!
+#include <dune/xt/functions/grid-function.hh>
+
+using namespace Dune::XT;
+
+using E1 = Grid::extract_entity_t<YASP_1D_EQUIDISTANT_OFFSET>;
+using E2 = Grid::extract_entity_t<YASP_2D_EQUIDISTANT_OFFSET>;
+
+#define VAL(r, rC) Functions::RangeTypeSelector<double, r, rC>::return_type(0)
+
+
+template <class L, class R>
+void test_difference(const L& left, const R& right)
+{
+  auto ll = left.local_function();
+  auto lr = right.local_function();
+  (*ll) - (*lr);
+}
+
+
+template <class L, class R>
+void test_fraction(const L& left, const R& right)
+{
+  auto ll = left.local_function();
+  auto lr = right.local_function();
+  (*ll) / (*lr);
+}
+
+
+template <class L, class R>
+void test_product(const L& left, const R& right)
+{
+  auto ll = left.local_function();
+  auto lr = right.local_function();
+  (*ll) * (*lr);
+}
+
+
+template <class L, class R>
+void test_sum(const L& left, const R& right)
+{
+  auto ll = left.local_function();
+  auto lr = right.local_function();
+  (*ll) + (*lr);
+}
+
+
+GTEST_TEST(grid_function_interface_operators, main)
+{
+  test_difference(Functions::GridFunction<E1, 1, 1>(VAL(1, 1)), Functions::GridFunction<E1, 1, 1>(VAL(1, 1)));
+  test_difference(Functions::GridFunction<E2, 1, 1>(VAL(1, 1)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_difference(Functions::GridFunction<E2, 1, 2>(VAL(1, 2)), Functions::GridFunction<E2, 1, 2>(VAL(1, 2)));
+  test_difference(Functions::GridFunction<E2, 1, 3>(VAL(1, 3)), Functions::GridFunction<E2, 1, 3>(VAL(1, 3)));
+  test_difference(Functions::GridFunction<E2, 2, 1>(VAL(2, 1)), Functions::GridFunction<E2, 2, 1>(VAL(2, 1)));
+  test_difference(Functions::GridFunction<E2, 2, 2>(VAL(2, 2)), Functions::GridFunction<E2, 2, 2>(VAL(2, 2)));
+  test_difference(Functions::GridFunction<E2, 2, 3>(VAL(2, 3)), Functions::GridFunction<E2, 2, 3>(VAL(2, 3)));
+  test_difference(Functions::GridFunction<E2, 3, 1>(VAL(3, 1)), Functions::GridFunction<E2, 3, 1>(VAL(3, 1)));
+  test_difference(Functions::GridFunction<E2, 3, 2>(VAL(3, 2)), Functions::GridFunction<E2, 3, 2>(VAL(3, 2)));
+  test_difference(Functions::GridFunction<E2, 3, 3>(VAL(3, 3)), Functions::GridFunction<E2, 3, 3>(VAL(3, 3)));
+
+  test_sum(Functions::GridFunction<E1, 1, 1>(VAL(1, 1)), Functions::GridFunction<E1, 1, 1>(VAL(1, 1)));
+  test_sum(Functions::GridFunction<E2, 1, 1>(VAL(1, 1)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_sum(Functions::GridFunction<E2, 1, 2>(VAL(1, 2)), Functions::GridFunction<E2, 1, 2>(VAL(1, 2)));
+  test_sum(Functions::GridFunction<E2, 1, 3>(VAL(1, 3)), Functions::GridFunction<E2, 1, 3>(VAL(1, 3)));
+  test_sum(Functions::GridFunction<E2, 2, 1>(VAL(2, 1)), Functions::GridFunction<E2, 2, 1>(VAL(2, 1)));
+  test_sum(Functions::GridFunction<E2, 2, 2>(VAL(2, 2)), Functions::GridFunction<E2, 2, 2>(VAL(2, 2)));
+  test_sum(Functions::GridFunction<E2, 2, 3>(VAL(2, 3)), Functions::GridFunction<E2, 2, 3>(VAL(2, 3)));
+  test_sum(Functions::GridFunction<E2, 3, 1>(VAL(3, 1)), Functions::GridFunction<E2, 3, 1>(VAL(3, 1)));
+  test_sum(Functions::GridFunction<E2, 3, 2>(VAL(3, 2)), Functions::GridFunction<E2, 3, 2>(VAL(3, 2)));
+  test_sum(Functions::GridFunction<E2, 3, 3>(VAL(3, 3)), Functions::GridFunction<E2, 3, 3>(VAL(3, 3)));
+
+  test_fraction(Functions::GridFunction<E1, 1, 1>(VAL(1, 1)), Functions::GridFunction<E1, 1, 1>(VAL(1, 1)));
+  test_fraction(Functions::GridFunction<E2, 1, 1>(VAL(1, 1)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_fraction(Functions::GridFunction<E2, 1, 2>(VAL(1, 2)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_fraction(Functions::GridFunction<E2, 1, 3>(VAL(1, 3)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_fraction(Functions::GridFunction<E2, 2, 1>(VAL(2, 1)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_fraction(Functions::GridFunction<E2, 2, 2>(VAL(2, 2)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_fraction(Functions::GridFunction<E2, 2, 3>(VAL(2, 3)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_fraction(Functions::GridFunction<E2, 3, 1>(VAL(3, 1)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_fraction(Functions::GridFunction<E2, 3, 2>(VAL(3, 2)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_fraction(Functions::GridFunction<E2, 3, 3>(VAL(3, 3)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+
+  test_product(Functions::GridFunction<E1, 1, 1>(VAL(1, 1)), Functions::GridFunction<E1, 1, 1>(VAL(1, 1)));
+  test_product(Functions::GridFunction<E2, 1, 1>(VAL(1, 1)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_product(Functions::GridFunction<E2, 1, 2>(VAL(1, 2)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_product(Functions::GridFunction<E2, 1, 3>(VAL(1, 3)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_product(Functions::GridFunction<E2, 2, 1>(VAL(2, 1)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_product(Functions::GridFunction<E2, 2, 2>(VAL(2, 2)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_product(Functions::GridFunction<E2, 2, 3>(VAL(2, 3)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_product(Functions::GridFunction<E2, 3, 1>(VAL(3, 1)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_product(Functions::GridFunction<E2, 3, 2>(VAL(3, 2)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+  test_product(Functions::GridFunction<E2, 3, 3>(VAL(3, 3)), Functions::GridFunction<E2, 1, 1>(VAL(1, 1)));
+
+  test_product(Functions::GridFunction<E2, 1, 2>(VAL(1, 2)), Functions::GridFunction<E2, 2, 1>(VAL(2, 1)));
+  test_product(Functions::GridFunction<E2, 1, 2>(VAL(1, 2)), Functions::GridFunction<E2, 2, 2>(VAL(2, 2)));
+  test_product(Functions::GridFunction<E2, 1, 2>(VAL(1, 2)), Functions::GridFunction<E2, 2, 3>(VAL(2, 3)));
+
+  test_product(Functions::GridFunction<E2, 1, 3>(VAL(1, 3)), Functions::GridFunction<E2, 3, 1>(VAL(3, 1)));
+  test_product(Functions::GridFunction<E2, 1, 3>(VAL(1, 3)), Functions::GridFunction<E2, 3, 2>(VAL(3, 2)));
+  test_product(Functions::GridFunction<E2, 1, 3>(VAL(1, 3)), Functions::GridFunction<E2, 3, 3>(VAL(3, 3)));
+
+  test_product(Functions::GridFunction<E2, 2, 1>(VAL(2, 1)), Functions::GridFunction<E2, 1, 2>(VAL(1, 2)));
+  test_product(Functions::GridFunction<E2, 2, 1>(VAL(2, 1)), Functions::GridFunction<E2, 1, 3>(VAL(1, 3)));
+  test_product(Functions::GridFunction<E2, 2, 1>(VAL(2, 1)), Functions::GridFunction<E2, 2, 1>(VAL(2, 1)));
+
+  test_product(Functions::GridFunction<E2, 2, 2>(VAL(2, 2)), Functions::GridFunction<E2, 2, 1>(VAL(2, 1)));
+  test_product(Functions::GridFunction<E2, 2, 2>(VAL(2, 2)), Functions::GridFunction<E2, 2, 2>(VAL(2, 2)));
+  test_product(Functions::GridFunction<E2, 2, 2>(VAL(2, 2)), Functions::GridFunction<E2, 2, 3>(VAL(2, 3)));
+
+  test_product(Functions::GridFunction<E2, 2, 3>(VAL(2, 3)), Functions::GridFunction<E2, 3, 1>(VAL(3, 1)));
+  test_product(Functions::GridFunction<E2, 2, 3>(VAL(2, 3)), Functions::GridFunction<E2, 3, 2>(VAL(3, 2)));
+  test_product(Functions::GridFunction<E2, 2, 3>(VAL(2, 3)), Functions::GridFunction<E2, 3, 3>(VAL(3, 3)));
+
+  test_product(Functions::GridFunction<E2, 3, 1>(VAL(3, 1)), Functions::GridFunction<E2, 1, 2>(VAL(1, 2)));
+  test_product(Functions::GridFunction<E2, 3, 1>(VAL(3, 1)), Functions::GridFunction<E2, 1, 3>(VAL(1, 3)));
+  test_product(Functions::GridFunction<E2, 3, 1>(VAL(3, 1)), Functions::GridFunction<E2, 3, 1>(VAL(3, 1)));
+
+  test_product(Functions::GridFunction<E2, 3, 2>(VAL(3, 2)), Functions::GridFunction<E2, 2, 1>(VAL(2, 1)));
+  test_product(Functions::GridFunction<E2, 3, 2>(VAL(3, 2)), Functions::GridFunction<E2, 2, 2>(VAL(2, 2)));
+  test_product(Functions::GridFunction<E2, 3, 2>(VAL(3, 2)), Functions::GridFunction<E2, 2, 3>(VAL(2, 3)));
+
+  test_product(Functions::GridFunction<E2, 3, 3>(VAL(3, 3)), Functions::GridFunction<E2, 3, 1>(VAL(3, 1)));
+  test_product(Functions::GridFunction<E2, 3, 3>(VAL(3, 3)), Functions::GridFunction<E2, 3, 2>(VAL(3, 2)));
+  test_product(Functions::GridFunction<E2, 3, 3>(VAL(3, 3)), Functions::GridFunction<E2, 3, 3>(VAL(3, 3)));
+}
diff --git a/dune/xt/test/functions/expression_matrix_valued.tpl b/dune/xt/test/functions/expression_matrix_valued.tpl
index ddfe0bf315604bc3f83fd100197f70cc17db1859..b4551942d2cf5dd33b5057a16811cf5ea91b8cd9 100644
--- a/dune/xt/test/functions/expression_matrix_valued.tpl
+++ b/dune/xt/test/functions/expression_matrix_valued.tpl
@@ -199,7 +199,7 @@ TEST_F(ExpressionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, is_bindable)
   RangeExpressionType expr_1(std::string("x[0]*x[0]"));
   FunctionType default_function("x", expr_1, 2);
 
-  const auto& localizable_function = default_function.template as_grid_function<ElementType>();
+  auto&& localizable_function = default_function.template as_grid_function<ElementType>();
 
   auto local_f = localizable_function.local_function();
 
@@ -218,7 +218,7 @@ TEST_F(ExpressionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_order)
 
   const auto leaf_view = grid_.leaf_view();
 
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   for (auto&& element : Dune::elements(leaf_view)) {
     local_f->bind(element);
@@ -236,7 +236,7 @@ TEST_F(ExpressionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_evaluat
     const RangeReturnType expected_value(value);
     const RangeExpressionType constant_expr(Common::to_string(value));
     FunctionType function("x", constant_expr, 0);
-    const auto& localizable_function = function.template as_grid_function<ElementType>();
+    auto&& localizable_function = function.template as_grid_function<ElementType>();
     auto local_f = localizable_function.local_function();
     for (auto&& element : Dune::elements(leaf_view)) {
       local_f->bind(element);
@@ -261,7 +261,7 @@ TEST_F(ExpressionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_jacobia
     DerivativeRangeExpressionType constant_grad(constant_grad_single);
     DerivativeRangeReturnType expected_jacobian(Common::FieldMatrix<double, rC, d>(0.));
     FunctionType function("x", constant_expr, constant_grad, 0);
-    const auto& localizable_function = function.template as_grid_function<ElementType>();
+    auto&& localizable_function = function.template as_grid_function<ElementType>();
     auto local_f = localizable_function.local_function();
     for (auto&& element : Dune::elements(leaf_view)) {
       local_f->bind(element);
diff --git a/dune/xt/test/functions/expression_parametric.tpl b/dune/xt/test/functions/expression_parametric.tpl
index ca10ab8491aaa24529dde55f6d1196515a022178..d6f3b4b5b8b274c72c543dba82df7a5ba49bd68d 100644
--- a/dune/xt/test/functions/expression_parametric.tpl
+++ b/dune/xt/test/functions/expression_parametric.tpl
@@ -102,7 +102,7 @@ TEST_F(ParametricExpressionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, is_
   const RangeExpressionType expr(std::string("sin(x[0]t_)"));
   FunctionType function("x", {"t_", 1}, expr, 3);
 
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
 
   for (auto&& element : Dune::elements(leaf_view)) {
@@ -119,7 +119,7 @@ TEST_F(ParametricExpressionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, loc
 
   const auto leaf_view = grid_.leaf_view();
 
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   for (auto&& element : Dune::elements(leaf_view)) {
     local_f->bind(element);
@@ -134,7 +134,7 @@ TEST_F(ParametricExpressionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, loc
 
   const RangeExpressionType expr(std::string("sin(x[0]t_)"));
   FunctionType function("x", {"t_", 1}, expr, 3);
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   for (auto&& element : Dune::elements(leaf_view)) {
     const auto geometry = element.geometry();
diff --git a/dune/xt/test/functions/expression_vector_valued.tpl b/dune/xt/test/functions/expression_vector_valued.tpl
index ddccfabf4317c23552c06091271f030e36dcb880..3f03849e3fa3279eaaad11f5919ee2de363ad77e 100644
--- a/dune/xt/test/functions/expression_vector_valued.tpl
+++ b/dune/xt/test/functions/expression_vector_valued.tpl
@@ -200,7 +200,7 @@ TEST_F(ExpressionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, is_bindable)
   RangeExpressionType expr_1(std::string("x[0]*x[0]"));
   FunctionType default_function("x", expr_1, 2);
 
-  const auto& localizable_function = default_function.template as_grid_function<ElementType>();
+  auto&& localizable_function = default_function.template as_grid_function<ElementType>();
 
   auto local_f = localizable_function.local_function();
 
@@ -219,7 +219,7 @@ TEST_F(ExpressionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_order)
 
   const auto leaf_view = grid_.leaf_view();
 
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   for (auto&& element : Dune::elements(leaf_view)) {
     local_f->bind(element);
@@ -237,7 +237,7 @@ TEST_F(ExpressionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_evaluat
     const RangeReturnType expected_value(value);
     const RangeExpressionType constant_expr(Common::to_string(value));
     FunctionType function("x", constant_expr, 0);
-    const auto& localizable_function = function.template as_grid_function<ElementType>();
+    auto&& localizable_function = function.template as_grid_function<ElementType>();
     auto local_f = localizable_function.local_function();
     for (auto&& element : Dune::elements(leaf_view)) {
       local_f->bind(element);
@@ -264,7 +264,7 @@ TEST_F(ExpressionFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_jacobia
     }
     const DerivativeRangeReturnType expected_jacobian;
     FunctionType function("x", constant_expr, constant_grad, 0);
-    const auto& localizable_function = function.template as_grid_function<ElementType>();
+    auto&& localizable_function = function.template as_grid_function<ElementType>();
     auto local_f = localizable_function.local_function();
     for (auto&& element : Dune::elements(leaf_view)) {
       local_f->bind(element);
diff --git a/dune/xt/test/functions/flattop.tpl b/dune/xt/test/functions/flattop.tpl
index 5b742c4759d7484def3b118392429eab9e7c4448..18173c38e947a7f9e1d027c8b8f53a9f6f83efb3 100644
--- a/dune/xt/test/functions/flattop.tpl
+++ b/dune/xt/test/functions/flattop.tpl
@@ -120,7 +120,7 @@ TEST_F(FlattopFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, is_bindable)
   const DomainType delta(1e-6);
   const double top_value = 20;
   FunctionType function(left, right, delta, top_value);
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
@@ -137,7 +137,7 @@ TEST_F(FlattopFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_order)
   const DomainType delta(1e-6);
   const double top_value = 20;
   FunctionType function(left, right, delta, top_value);
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
@@ -155,7 +155,7 @@ TEST_F(FlattopFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_evaluate)
   const DomainType delta(1e-6);
   const double top_value = 20;
   FunctionType func(left, right, delta, top_value);
-  const auto& localizable_function = func.template as_grid_function<ElementType>();
+  auto&& localizable_function = func.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
diff --git a/dune/xt/test/functions/function_interface_operators.cc b/dune/xt/test/functions/function_interface_operators.cc
new file mode 100644
index 0000000000000000000000000000000000000000..345228ebb1d1a2e5b4438ae04152f90bfd293530
--- /dev/null
+++ b/dune/xt/test/functions/function_interface_operators.cc
@@ -0,0 +1,94 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+
+#include <dune/xt/test/main.hxx> // <- has to come first, include config.h!
+#include <dune/xt/functions/constant.hh>
+
+
+GTEST_TEST(function_interface_operators, main)
+{
+  using namespace Dune::XT;
+
+  Functions::ConstantFunction<1, 1, 1>({}) - Functions::ConstantFunction<1, 1, 1>({});
+  Functions::ConstantFunction<2, 1, 1>({}) - Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 1, 2>({}) - Functions::ConstantFunction<2, 1, 2>({});
+  Functions::ConstantFunction<2, 1, 3>({}) - Functions::ConstantFunction<2, 1, 3>({});
+  Functions::ConstantFunction<2, 2, 1>({}) - Functions::ConstantFunction<2, 2, 1>({});
+  Functions::ConstantFunction<2, 2, 2>({}) - Functions::ConstantFunction<2, 2, 2>({});
+  Functions::ConstantFunction<2, 2, 3>({}) - Functions::ConstantFunction<2, 2, 3>({});
+  Functions::ConstantFunction<2, 3, 1>({}) - Functions::ConstantFunction<2, 3, 1>({});
+  Functions::ConstantFunction<2, 3, 2>({}) - Functions::ConstantFunction<2, 3, 2>({});
+  Functions::ConstantFunction<2, 3, 3>({}) - Functions::ConstantFunction<2, 3, 3>({});
+
+  Functions::ConstantFunction<1, 1, 1>({}) + Functions::ConstantFunction<1, 1, 1>({});
+  Functions::ConstantFunction<2, 1, 1>({}) + Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 1, 2>({}) + Functions::ConstantFunction<2, 1, 2>({});
+  Functions::ConstantFunction<2, 1, 3>({}) + Functions::ConstantFunction<2, 1, 3>({});
+  Functions::ConstantFunction<2, 2, 1>({}) + Functions::ConstantFunction<2, 2, 1>({});
+  Functions::ConstantFunction<2, 2, 2>({}) + Functions::ConstantFunction<2, 2, 2>({});
+  Functions::ConstantFunction<2, 2, 3>({}) + Functions::ConstantFunction<2, 2, 3>({});
+  Functions::ConstantFunction<2, 3, 1>({}) + Functions::ConstantFunction<2, 3, 1>({});
+  Functions::ConstantFunction<2, 3, 2>({}) + Functions::ConstantFunction<2, 3, 2>({});
+  Functions::ConstantFunction<2, 3, 3>({}) + Functions::ConstantFunction<2, 3, 3>({});
+
+  Functions::ConstantFunction<1, 1, 1>({}) / Functions::ConstantFunction<1, 1, 1>({});
+  Functions::ConstantFunction<2, 1, 1>({}) / Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 1, 2>({}) / Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 1, 3>({}) / Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 2, 1>({}) / Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 2, 2>({}) / Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 2, 3>({}) / Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 3, 1>({}) / Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 3, 2>({}) / Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 3, 3>({}) / Functions::ConstantFunction<2, 1, 1>({});
+
+  Functions::ConstantFunction<1, 1, 1>({}) * Functions::ConstantFunction<1, 1, 1>({});
+  Functions::ConstantFunction<2, 1, 1>({}) * Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 1, 2>({}) * Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 1, 3>({}) * Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 2, 1>({}) * Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 2, 2>({}) * Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 2, 3>({}) * Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 3, 1>({}) * Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 3, 2>({}) * Functions::ConstantFunction<2, 1, 1>({});
+  Functions::ConstantFunction<2, 3, 3>({}) * Functions::ConstantFunction<2, 1, 1>({});
+
+  Functions::ConstantFunction<2, 1, 2>({}) * Functions::ConstantFunction<2, 2, 1>({});
+  Functions::ConstantFunction<2, 1, 2>({}) * Functions::ConstantFunction<2, 2, 2>({});
+  Functions::ConstantFunction<2, 1, 2>({}) * Functions::ConstantFunction<2, 2, 3>({});
+
+  Functions::ConstantFunction<2, 1, 3>({}) * Functions::ConstantFunction<2, 3, 1>({});
+  Functions::ConstantFunction<2, 1, 3>({}) * Functions::ConstantFunction<2, 3, 2>({});
+  Functions::ConstantFunction<2, 1, 3>({}) * Functions::ConstantFunction<2, 3, 3>({});
+
+  Functions::ConstantFunction<2, 2, 1>({}) * Functions::ConstantFunction<2, 1, 2>({});
+  Functions::ConstantFunction<2, 2, 1>({}) * Functions::ConstantFunction<2, 1, 3>({});
+  Functions::ConstantFunction<2, 2, 1>({}) * Functions::ConstantFunction<2, 2, 1>({});
+
+  Functions::ConstantFunction<2, 2, 2>({}) * Functions::ConstantFunction<2, 2, 1>({});
+  Functions::ConstantFunction<2, 2, 2>({}) * Functions::ConstantFunction<2, 2, 2>({});
+  Functions::ConstantFunction<2, 2, 2>({}) * Functions::ConstantFunction<2, 2, 3>({});
+
+  Functions::ConstantFunction<2, 2, 3>({}) * Functions::ConstantFunction<2, 3, 1>({});
+  Functions::ConstantFunction<2, 2, 3>({}) * Functions::ConstantFunction<2, 3, 2>({});
+  Functions::ConstantFunction<2, 2, 3>({}) * Functions::ConstantFunction<2, 3, 3>({});
+
+  Functions::ConstantFunction<2, 3, 1>({}) * Functions::ConstantFunction<2, 1, 2>({});
+  Functions::ConstantFunction<2, 3, 1>({}) * Functions::ConstantFunction<2, 1, 3>({});
+  Functions::ConstantFunction<2, 3, 1>({}) * Functions::ConstantFunction<2, 3, 1>({});
+
+  Functions::ConstantFunction<2, 3, 2>({}) * Functions::ConstantFunction<2, 2, 1>({});
+  Functions::ConstantFunction<2, 3, 2>({}) * Functions::ConstantFunction<2, 2, 2>({});
+  Functions::ConstantFunction<2, 3, 2>({}) * Functions::ConstantFunction<2, 2, 3>({});
+
+  Functions::ConstantFunction<2, 3, 3>({}) * Functions::ConstantFunction<2, 3, 1>({});
+  Functions::ConstantFunction<2, 3, 3>({}) * Functions::ConstantFunction<2, 3, 2>({});
+  Functions::ConstantFunction<2, 3, 3>({}) * Functions::ConstantFunction<2, 3, 3>({});
+}
diff --git a/dune/xt/test/functions/generic_function.tpl b/dune/xt/test/functions/generic_function.tpl
index 5e82716c6d883fb076eacb9b836506f957983c44..f5a0a4be6fcc3206824def7594921d04a9e56d5e 100644
--- a/dune/xt/test/functions/generic_function.tpl
+++ b/dune/xt/test/functions/generic_function.tpl
@@ -97,7 +97,7 @@ TEST_F(GenericFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, global_jacobian)
 TEST_F(GenericFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, is_bindable)
 {
   LambdaType function(1.);
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
@@ -110,7 +110,7 @@ TEST_F(GenericFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_order)
   for (auto vv : {1, 3, 5}) {
     const int expected_order = vv;
     LambdaType function(vv);
-    const auto& localizable_function = function.template as_grid_function<ElementType>();
+    auto&& localizable_function = function.template as_grid_function<ElementType>();
     auto local_f = localizable_function.local_function();
     const auto leaf_view = grid_.leaf_view();
     for (auto&& element : Dune::elements(leaf_view)) {
@@ -127,7 +127,7 @@ TEST_F(GenericFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_evaluate)
                       [](const auto& xx, const auto& param) { RangeReturnType ret(std::pow(xx[0], param.get("power").at(0))); return ret;},
                       "x_power_p",
                       Common::ParameterType("power", 1));
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
@@ -151,7 +151,7 @@ TEST_F(GenericFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_jacobian)
                       "jacobian.function",
                       {},
                       [](const auto& /*xx*/, const auto& /*param*/) { return DerivativeRangeReturnType();});
-  const auto& localizable_function = function.template as_grid_function<ElementType>();
+  auto&& localizable_function = function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : Dune::elements(leaf_view)) {
diff --git a/dune/xt/test/functions/grid_function_interface_operators.cc b/dune/xt/test/functions/grid_function_interface_operators.cc
new file mode 100644
index 0000000000000000000000000000000000000000..de912a85bce2b87f0737aef077639033a04da064
--- /dev/null
+++ b/dune/xt/test/functions/grid_function_interface_operators.cc
@@ -0,0 +1,99 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+
+#include <dune/xt/test/main.hxx> // <- has to come first, include config.h!
+#include <dune/xt/functions/grid-function.hh>
+
+using namespace Dune::XT;
+
+using E1 = Grid::extract_entity_t<YASP_1D_EQUIDISTANT_OFFSET>;
+using E2 = Grid::extract_entity_t<YASP_2D_EQUIDISTANT_OFFSET>;
+
+#define VAL(r, rC) Functions::RangeTypeSelector<double, r, rC>::return_type(0)
+
+
+GTEST_TEST(grid_function_interface_operators, main)
+{
+  Functions::GridFunction<E1, 1, 1>(VAL(1, 1)) - Functions::GridFunction<E1, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 1, 1>(VAL(1, 1)) - Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 1, 2>(VAL(1, 2)) - Functions::GridFunction<E2, 1, 2>(VAL(1, 2));
+  Functions::GridFunction<E2, 1, 3>(VAL(1, 3)) - Functions::GridFunction<E2, 1, 3>(VAL(1, 3));
+  Functions::GridFunction<E2, 2, 1>(VAL(2, 1)) - Functions::GridFunction<E2, 2, 1>(VAL(2, 1));
+  Functions::GridFunction<E2, 2, 2>(VAL(2, 2)) - Functions::GridFunction<E2, 2, 2>(VAL(2, 2));
+  Functions::GridFunction<E2, 2, 3>(VAL(2, 3)) - Functions::GridFunction<E2, 2, 3>(VAL(2, 3));
+  Functions::GridFunction<E2, 3, 1>(VAL(3, 1)) - Functions::GridFunction<E2, 3, 1>(VAL(3, 1));
+  Functions::GridFunction<E2, 3, 2>(VAL(3, 2)) - Functions::GridFunction<E2, 3, 2>(VAL(3, 2));
+  Functions::GridFunction<E2, 3, 3>(VAL(3, 3)) - Functions::GridFunction<E2, 3, 3>(VAL(3, 3));
+
+  Functions::GridFunction<E1, 1, 1>(VAL(1, 1)) + Functions::GridFunction<E1, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 1, 1>(VAL(1, 1)) + Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 1, 2>(VAL(1, 2)) + Functions::GridFunction<E2, 1, 2>(VAL(1, 2));
+  Functions::GridFunction<E2, 1, 3>(VAL(1, 3)) + Functions::GridFunction<E2, 1, 3>(VAL(1, 3));
+  Functions::GridFunction<E2, 2, 1>(VAL(2, 1)) + Functions::GridFunction<E2, 2, 1>(VAL(2, 1));
+  Functions::GridFunction<E2, 2, 2>(VAL(2, 2)) + Functions::GridFunction<E2, 2, 2>(VAL(2, 2));
+  Functions::GridFunction<E2, 2, 3>(VAL(2, 3)) + Functions::GridFunction<E2, 2, 3>(VAL(2, 3));
+  Functions::GridFunction<E2, 3, 1>(VAL(3, 1)) + Functions::GridFunction<E2, 3, 1>(VAL(3, 1));
+  Functions::GridFunction<E2, 3, 2>(VAL(3, 2)) + Functions::GridFunction<E2, 3, 2>(VAL(3, 2));
+  Functions::GridFunction<E2, 3, 3>(VAL(3, 3)) + Functions::GridFunction<E2, 3, 3>(VAL(3, 3));
+
+  Functions::GridFunction<E1, 1, 1>(VAL(1, 1)) / Functions::GridFunction<E1, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 1, 1>(VAL(1, 1)) / Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 1, 2>(VAL(1, 2)) / Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 1, 3>(VAL(1, 3)) / Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 2, 1>(VAL(2, 1)) / Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 2, 2>(VAL(2, 2)) / Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 2, 3>(VAL(2, 3)) / Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 3, 1>(VAL(3, 1)) / Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 3, 2>(VAL(3, 2)) / Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 3, 3>(VAL(3, 3)) / Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+
+  Functions::GridFunction<E1, 1, 1>(VAL(1, 1)) * Functions::GridFunction<E1, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 1, 1>(VAL(1, 1)) * Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 1, 2>(VAL(1, 2)) * Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 1, 3>(VAL(1, 3)) * Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 2, 1>(VAL(2, 1)) * Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 2, 2>(VAL(2, 2)) * Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 2, 3>(VAL(2, 3)) * Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 3, 1>(VAL(3, 1)) * Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 3, 2>(VAL(3, 2)) * Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+  Functions::GridFunction<E2, 3, 3>(VAL(3, 3)) * Functions::GridFunction<E2, 1, 1>(VAL(1, 1));
+
+  Functions::GridFunction<E2, 1, 2>(VAL(1, 2)) * Functions::GridFunction<E2, 2, 1>(VAL(2, 1));
+  Functions::GridFunction<E2, 1, 2>(VAL(1, 2)) * Functions::GridFunction<E2, 2, 2>(VAL(2, 2));
+  Functions::GridFunction<E2, 1, 2>(VAL(1, 2)) * Functions::GridFunction<E2, 2, 3>(VAL(2, 3));
+
+  Functions::GridFunction<E2, 1, 3>(VAL(1, 3)) * Functions::GridFunction<E2, 3, 1>(VAL(3, 1));
+  Functions::GridFunction<E2, 1, 3>(VAL(1, 3)) * Functions::GridFunction<E2, 3, 2>(VAL(3, 2));
+  Functions::GridFunction<E2, 1, 3>(VAL(1, 3)) * Functions::GridFunction<E2, 3, 3>(VAL(3, 3));
+
+  Functions::GridFunction<E2, 2, 1>(VAL(2, 1)) * Functions::GridFunction<E2, 1, 2>(VAL(1, 2));
+  Functions::GridFunction<E2, 2, 1>(VAL(2, 1)) * Functions::GridFunction<E2, 1, 3>(VAL(1, 3));
+  Functions::GridFunction<E2, 2, 1>(VAL(2, 1)) * Functions::GridFunction<E2, 2, 1>(VAL(2, 1));
+
+  Functions::GridFunction<E2, 2, 2>(VAL(2, 2)) * Functions::GridFunction<E2, 2, 1>(VAL(2, 1));
+  Functions::GridFunction<E2, 2, 2>(VAL(2, 2)) * Functions::GridFunction<E2, 2, 2>(VAL(2, 2));
+  Functions::GridFunction<E2, 2, 2>(VAL(2, 2)) * Functions::GridFunction<E2, 2, 3>(VAL(2, 3));
+
+  Functions::GridFunction<E2, 2, 3>(VAL(2, 3)) * Functions::GridFunction<E2, 3, 1>(VAL(3, 1));
+  Functions::GridFunction<E2, 2, 3>(VAL(2, 3)) * Functions::GridFunction<E2, 3, 2>(VAL(3, 2));
+  Functions::GridFunction<E2, 2, 3>(VAL(2, 3)) * Functions::GridFunction<E2, 3, 3>(VAL(3, 3));
+
+  Functions::GridFunction<E2, 3, 1>(VAL(3, 1)) * Functions::GridFunction<E2, 1, 2>(VAL(1, 2));
+  Functions::GridFunction<E2, 3, 1>(VAL(3, 1)) * Functions::GridFunction<E2, 1, 3>(VAL(1, 3));
+  Functions::GridFunction<E2, 3, 1>(VAL(3, 1)) * Functions::GridFunction<E2, 3, 1>(VAL(3, 1));
+
+  Functions::GridFunction<E2, 3, 2>(VAL(3, 2)) * Functions::GridFunction<E2, 2, 1>(VAL(2, 1));
+  Functions::GridFunction<E2, 3, 2>(VAL(3, 2)) * Functions::GridFunction<E2, 2, 2>(VAL(2, 2));
+  Functions::GridFunction<E2, 3, 2>(VAL(3, 2)) * Functions::GridFunction<E2, 2, 3>(VAL(2, 3));
+
+  Functions::GridFunction<E2, 3, 3>(VAL(3, 3)) * Functions::GridFunction<E2, 3, 1>(VAL(3, 1));
+  Functions::GridFunction<E2, 3, 3>(VAL(3, 3)) * Functions::GridFunction<E2, 3, 2>(VAL(3, 2));
+  Functions::GridFunction<E2, 3, 3>(VAL(3, 3)) * Functions::GridFunction<E2, 3, 3>(VAL(3, 3));
+}
diff --git a/dune/xt/test/functions/indicator_function.tpl b/dune/xt/test/functions/indicator_function.tpl
index cbd5e51edaeded05dcb0baa21852b1ca87582bf8..8eaefcebfaebf4d8bd3dce90bab0785afacc1dca 100644
--- a/dune/xt/test/functions/indicator_function.tpl
+++ b/dune/xt/test/functions/indicator_function.tpl
@@ -186,7 +186,7 @@ TEST_F(IndicatorFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, is_bindable)
   RangeType first_value(1.);
 
   FunctionType default_function({std::make_tuple(lower_left, upper_right, first_value)});
-  const auto& localizable_function = default_function.template as_grid_function<ElementType>();
+  auto&& localizable_function = default_function.template as_grid_function<ElementType>();
   auto local_f = localizable_function.local_function();
   const auto leaf_view = grid_.leaf_view();
   for (auto&& element : elements(leaf_view)) {
@@ -205,7 +205,7 @@ TEST_F(IndicatorFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_order)
       for (auto vv : {1., 2., 3., 4.}) {
         RangeType value(vv);
         FunctionType function({std::make_tuple(lower_left, upper_right, value)});
-        const auto& localizable_function = function.template as_grid_function<ElementType>();
+        auto&& localizable_function = function.template as_grid_function<ElementType>();
         auto local_f = localizable_function.local_function();
         for (auto&& element : elements(leaf_view)) {
           local_f->bind(element);
@@ -229,7 +229,7 @@ TEST_F(IndicatorFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_evaluate
       for (auto vv : {1., 2., 3., 4.}) {
         RangeType value(vv);
         FunctionType function({std::make_tuple(lower_left, upper_right, value)});
-        const auto& localizable_function = function.template as_grid_function<ElementType>();
+        auto&& localizable_function = function.template as_grid_function<ElementType>();
         auto local_f = localizable_function.local_function();
         for (auto&& element : elements(leaf_view)) {
           local_f->bind(element);
@@ -313,7 +313,7 @@ TEST_F(IndicatorFunction_from_{{GRIDNAME}}_to_{{r}}_times_{{rC}}, local_jacobian
       for (auto vv : {1., 2., 3., 4.}) {
         RangeType value(vv);
         FunctionType function({std::make_tuple(lower_left, upper_right, value)});
-        const auto& localizable_function = function.template as_grid_function<ElementType>();
+        auto&& localizable_function = function.template as_grid_function<ElementType>();
         auto local_f = localizable_function.local_function();
         for (auto&& element : elements(leaf_view)) {
           local_f->bind(element);
diff --git a/python/dune/xt/functions/CMakeLists.txt b/python/dune/xt/functions/CMakeLists.txt
index 46fcaf780c485dcfbde4051dc0822b8d07158dfd..3212c5ac29bc06393c19c3c67cece795df13d8ef 100644
--- a/python/dune/xt/functions/CMakeLists.txt
+++ b/python/dune/xt/functions/CMakeLists.txt
@@ -14,14 +14,20 @@
 
 dune_pybindxi_add_module(_functions_checkerboard EXCLUDE_FROM_ALL checkerboard.cc)
 dune_pybindxi_add_module(_functions_constant EXCLUDE_FROM_ALL constant.cc)
+dune_pybindxi_add_module(_functions_elementwise_diameter EXCLUDE_FROM_ALL elementwise-diameter.cc)
+dune_pybindxi_add_module(_functions_elementwise_minimum EXCLUDE_FROM_ALL elementwise-minimum.cc)
 dune_pybindxi_add_module(_functions_expression EXCLUDE_FROM_ALL expression.cc)
+dune_pybindxi_add_module(_functions_divergence EXCLUDE_FROM_ALL divergence.cc)
 dune_pybindxi_add_module(_functions_function_as_grid_function EXCLUDE_FROM_ALL function-as-grid-function.cc)
 dune_pybindxi_add_module(_functions_function_interface_1d EXCLUDE_FROM_ALL function-interface-1d.cc)
 dune_pybindxi_add_module(_functions_function_interface_2d EXCLUDE_FROM_ALL function-interface-2d.cc)
 dune_pybindxi_add_module(_functions_function_interface_3d EXCLUDE_FROM_ALL function-interface-3d.cc)
+dune_pybindxi_add_module(_functions_gradient EXCLUDE_FROM_ALL gradient.cc)
 dune_pybindxi_add_module(_functions_gridfunction EXCLUDE_FROM_ALL gridfunction.cc)
-dune_pybindxi_add_module(_functions_gridfunction_interface_1d EXCLUDE_FROM_ALL gridfunction-interface-1d.cc)
-dune_pybindxi_add_module(_functions_gridfunction_interface_2d EXCLUDE_FROM_ALL gridfunction-interface-2d.cc)
-dune_pybindxi_add_module(_functions_gridfunction_interface_3d EXCLUDE_FROM_ALL gridfunction-interface-3d.cc)
 dune_pybindxi_add_module(_functions_indicator EXCLUDE_FROM_ALL indicator.cc)
+dune_pybindxi_add_module(_functions_interfaces_grid_function_1d EXCLUDE_FROM_ALL interfaces/grid-function_1d.cc)
+dune_pybindxi_add_module(_functions_interfaces_grid_function_2d EXCLUDE_FROM_ALL interfaces/grid-function_2d.cc)
+dune_pybindxi_add_module(_functions_interfaces_grid_function_3d EXCLUDE_FROM_ALL interfaces/grid-function_3d.cc)
+dune_pybindxi_add_module(_functions_inverse EXCLUDE_FROM_ALL inverse.cc)
+dune_pybindxi_add_module(_functions_parametric_expression EXCLUDE_FROM_ALL parametric-expression.cc)
 dune_pybindxi_add_module(_functions_spe10 EXCLUDE_FROM_ALL spe10.cc)
diff --git a/python/dune/xt/functions/ESV2007.bindings.hh b/python/dune/xt/functions/ESV2007.bindings.hh
index 5ef91a172deb79a512ebe0b6bb1a794dd51bc8a3..faafd44ea0c60f929f621d580ad03acb7543603f 100644
--- a/python/dune/xt/functions/ESV2007.bindings.hh
+++ b/python/dune/xt/functions/ESV2007.bindings.hh
@@ -98,7 +98,7 @@ class CutoffFunction
              const ScalarFunction& diffusion_factor,
              const TensorFunction& diffusion_tensor,
              const R& poincare_constant,
-             const std::string& name) { return type(diffusion_factor, diffusion_tensor, poincare_constant, name); },
+             const std::string& name) { return new type(diffusion_factor, diffusion_tensor, poincare_constant, name); },
           "grid_provider"_a,
           "diffusion_factor"_a,
           "diffusion_tesor"_a,
diff --git a/python/dune/xt/functions/__init__.py b/python/dune/xt/functions/__init__.py
index 5b57f7ab3c1472521f776aaa00791b1cdc4bb475..2a9d186cd883395dd02b7c96f05b04abc02e5d27 100644
--- a/python/dune/xt/functions/__init__.py
+++ b/python/dune/xt/functions/__init__.py
@@ -12,21 +12,43 @@
 #   Tobias Leibner  (2019 - 2020)
 # ~~~
 
+from tempfile import NamedTemporaryFile
+
 from dune.xt import guarded_import
+from dune.xt.common.vtk.plot import plot
 
 for mod_name in (
         '_functions_function_interface_1d',
         '_functions_function_interface_2d',
         '_functions_checkerboard',
         '_functions_constant',
+        '_functions_elementwise_diameter',
+        '_functions_elementwise_minimum',
         '_functions_expression',
+        '_functions_divergence',
         '_functions_function_as_grid_function',
         '_functions_function_interface_3d',
+        '_functions_gradient',
         '_functions_gridfunction',
-        '_functions_gridfunction_interface_1d',
-        '_functions_gridfunction_interface_2d',
-        '_functions_gridfunction_interface_3d',
         '_functions_indicator',
+        '_functions_interfaces_grid_function_1d',
+        '_functions_interfaces_grid_function_2d',
+        '_functions_interfaces_grid_function_3d',
+        '_functions_inverse',
+        '_functions_parametric_expression',
         '_functions_spe10',
 ):
     guarded_import(globals(), 'dune.xt.functions', mod_name)
+
+from dune.xt.functions._functions_gridfunction import GridFunction
+
+
+def visualize_function(function, grid, subsampling=False):
+    assert function.dim_domain == 2, f'Not implemented yet for {function.dim_domain}-dimensional grids!'
+    assert function.dim_range == 1, f'Not implemented yet for {function.dim_domain}-dimensional functions!'
+    tmpfile = NamedTemporaryFile(mode='wb', delete=False, suffix='.vtu').name
+    try:
+        function.visualize(grid, filename=tmpfile[:-4], subsampling=subsampling)
+    except AttributeError:
+        GridFunction(grid, function).visualize(grid, filename=tmpfile[:-4], subsampling=subsampling)
+    return plot(tmpfile, color_attribute_name=function.name)
diff --git a/python/dune/xt/functions/base/combined-grid-function.hh b/python/dune/xt/functions/base/combined-grid-function.hh
new file mode 100644
index 0000000000000000000000000000000000000000..2dc2b80e0421fc73493ea90501b352158c74dda7
--- /dev/null
+++ b/python/dune/xt/functions/base/combined-grid-function.hh
@@ -0,0 +1,161 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2018 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License
+// (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+#ifndef PYTHON_DUNE_XT_FUNCTIONS_BASE_COMBINED_GRID_FUNCTION_HH
+#define PYTHON_DUNE_XT_FUNCTIONS_BASE_COMBINED_GRID_FUNCTION_HH
+
+#include <python/dune/xt/functions/interfaces/grid-function.hh>
+
+namespace Dune {
+namespace XT {
+namespace Functions {
+namespace bindings {
+
+
+template <class G, class E, size_t r = 1, size_t rC = 1, class R = double>
+class DifferenceGridFunction : public GridFunctionInterface<G, E, r, rC, R>
+{
+  using BaseType = GridFunctionInterface<G, E, r, rC, R>;
+
+public:
+  using base_type = typename BaseType::type;
+  using type = Functions::DifferenceGridFunction<base_type, base_type>;
+  using bound_type = pybind11::class_<type, base_type>;
+
+  static bound_type bind(pybind11::module& m,
+                         const std::string& grid_id,
+                         const std::string& layer_id = "",
+                         const std::string& class_id = "difference_grid_function")
+  {
+    namespace py = pybind11;
+    using namespace pybind11::literals;
+
+    const auto ClassName = Common::to_camel_case(BaseType::class_name(grid_id, layer_id, class_id));
+    bound_type c(m, ClassName.c_str(), Common::to_camel_case(class_id).c_str());
+
+    c.def(py::init<const base_type&, const base_type&>());
+
+    BaseType::addbind_methods(c);
+
+    return c;
+  }
+}; // class DifferenceGridFunction
+
+
+template <class G, class E, size_t r = 1, size_t rC = 1, class R = double>
+class SumGridFunction : public GridFunctionInterface<G, E, r, rC, R>
+{
+  using BaseType = GridFunctionInterface<G, E, r, rC, R>;
+
+public:
+  using base_type = typename BaseType::type;
+  using type = Functions::SumGridFunction<base_type, base_type>;
+  using bound_type = pybind11::class_<type, base_type>;
+
+  static bound_type bind(pybind11::module& m,
+                         const std::string& grid_id,
+                         const std::string& layer_id = "",
+                         const std::string& class_id = "sum_grid_function")
+  {
+    namespace py = pybind11;
+    using namespace pybind11::literals;
+
+    const auto ClassName = Common::to_camel_case(BaseType::class_name(grid_id, layer_id, class_id));
+    bound_type c(m, ClassName.c_str(), Common::to_camel_case(class_id).c_str());
+
+    c.def(py::init<const base_type&, const base_type&>());
+
+    BaseType::addbind_methods(c);
+
+    return c;
+  }
+}; // class SumGridFunction
+
+
+template <class G, class E, class R = double>
+class FractionGridFunction : public GridFunctionInterface<G, E, 1, 1, R>
+{
+  using BaseType = GridFunctionInterface<G, E, 1, 1, R>;
+
+public:
+  using base_type = typename BaseType::type;
+  using type = Functions::FractionGridFunction<base_type, base_type>;
+  using bound_type = pybind11::class_<type, base_type>;
+
+  static bound_type bind(pybind11::module& m,
+                         const std::string& grid_id,
+                         const std::string& layer_id = "",
+                         const std::string& class_id = "fraction_grid_function")
+  {
+    namespace py = pybind11;
+    using namespace pybind11::literals;
+
+    const auto ClassName = Common::to_camel_case(BaseType::class_name(grid_id, layer_id, class_id));
+    bound_type c(m, ClassName.c_str(), Common::to_camel_case(class_id).c_str());
+
+    c.def(py::init<const base_type&, const base_type&>());
+
+    BaseType::addbind_methods(c);
+
+    return c;
+  }
+}; // class FractionGridFunction
+
+
+template <class G, class E, size_t Lr = 1, size_t LrC = 1, size_t Rr = 1, size_t RrC = 1, class R = double>
+class ProductGridFunction
+  : public GridFunctionInterface<G,
+                                 E,
+                                 internal::CombinedDim<CombinationType::product, Lr, LrC, Rr, RrC>::r(),
+                                 internal::CombinedDim<CombinationType::product, Lr, LrC, Rr, RrC>::rC(),
+                                 R>
+{
+  using BaseType = GridFunctionInterface<G,
+                                         E,
+                                         internal::CombinedDim<CombinationType::product, Lr, LrC, Rr, RrC>::r(),
+                                         internal::CombinedDim<CombinationType::product, Lr, LrC, Rr, RrC>::rC(),
+                                         R>;
+
+  using Left = Functions::GridFunctionInterface<E, Lr, LrC, R>;
+  using Right = Functions::GridFunctionInterface<E, Rr, RrC, R>;
+
+public:
+  using base_type = typename BaseType::type;
+  using type = Functions::ProductGridFunction<Left, Right>;
+  using bound_type = pybind11::class_<type, base_type>;
+
+  static bound_type bind(pybind11::module& m,
+                         const std::string& grid_id,
+                         const std::string& layer_id = "",
+                         const std::string& class_id = "product_grid_function")
+  {
+    namespace py = pybind11;
+    using namespace pybind11::literals;
+
+    const auto ClassName =
+        Common::to_camel_case(BaseType::class_name(grid_id, layer_id, class_id) + "_from_" + Common::to_string(Lr) + "x"
+                              + Common::to_string(LrC) + "X" + Common::to_string(Rr) + "x" + Common::to_string(RrC));
+    bound_type c(m, ClassName.c_str(), Common::to_camel_case(class_id).c_str());
+
+    c.def(py::init<const Left&, const Right&>());
+
+    BaseType::addbind_methods(c);
+
+    return c;
+  }
+}; // class ProductGridFunction
+
+
+} // namespace bindings
+} // namespace Functions
+} // namespace XT
+} // namespace Dune
+
+#endif // PYTHON_DUNE_XT_FUNCTIONS_BASE_COMBINED_GRID_FUNCTION_HH
diff --git a/python/dune/xt/functions/checkerboard.cc b/python/dune/xt/functions/checkerboard.cc
index 43ec993401c1c0c245e8b3792088035dd52e227a..8ae33249bc6e77ca3030e4800f4741c661136718 100644
--- a/python/dune/xt/functions/checkerboard.cc
+++ b/python/dune/xt/functions/checkerboard.cc
@@ -146,9 +146,9 @@ PYBIND11_MODULE(_functions_checkerboard, m)
   py::module::import("dune.xt.common");
   py::module::import("dune.xt.grid");
   py::module::import("dune.xt.la");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_1d");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_2d");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_3d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_1d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_2d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_3d");
 
   CheckerboardFunction_for_all_grids<>::bind(m);
 } // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/divergence.cc b/python/dune/xt/functions/divergence.cc
new file mode 100644
index 0000000000000000000000000000000000000000..962b1100f6d1d63f1e827cd73a33c044f0dd9c21
--- /dev/null
+++ b/python/dune/xt/functions/divergence.cc
@@ -0,0 +1,131 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2018 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2019)
+
+#include "config.h"
+
+#include <string>
+#include <vector>
+
+#include <dune/common/parallel/mpihelper.hh>
+
+#include <dune/pybindxi/pybind11.h>
+#include <dune/pybindxi/functional.h>
+#include <dune/pybindxi/stl.h>
+
+#include <dune/xt/common/string.hh>
+#include <dune/xt/grid/gridprovider/provider.hh>
+#include <dune/xt/grid/type_traits.hh>
+#include <dune/xt/functions/base/derivatives-of-grid-functions.hh>
+
+#include <python/dune/xt/common/parameter.hh>
+#include <python/dune/xt/common/fvector.hh>
+#include <python/dune/xt/common/fmatrix.hh>
+#include <python/dune/xt/common/bindings.hh>
+#include <python/dune/xt/grid/traits.hh>
+#include <python/dune/xt/common/exceptions.bindings.hh>
+
+namespace Dune {
+namespace XT {
+namespace Functions {
+namespace bindings {
+
+
+template <class G, class E>
+class DivergenceGridFunction
+{
+  using GP = XT::Grid::GridProvider<G>;
+  static const size_t d = G::dimension;
+  using GF = Functions::GridFunction<E, d>;
+
+public:
+  using type = Functions::DivergenceGridFunction<GF>;
+  using base_type = Functions::GridFunctionInterface<E>;
+  using bound_type = pybind11::class_<type, base_type>;
+
+public:
+  static bound_type bind(pybind11::module& m,
+                         const std::string& grid_id = Grid::bindings::grid_name<G>::value(),
+                         const std::string& layer_id = "",
+                         const std::string& class_id = "divergence_grid_function")
+  {
+    namespace py = pybind11;
+    using namespace pybind11::literals;
+
+    std::string class_name = class_id;
+    class_name += "_" + grid_id;
+    if (!layer_id.empty())
+      class_name += "_" + layer_id;
+    class_name += "_to_1d";
+    const auto ClassName = Common::to_camel_case(class_name);
+    bound_type c(m, ClassName.c_str(), Common::to_camel_case(class_id).c_str());
+    c.def(py::init<GF, const std::string&>(), "grid_function"_a, "name"_a = "DivergenceGridFunction");
+
+    m.def(
+        "divergence",
+        [](const GridFunctionInterface<E, d>& grid_function, const std::string& name) {
+          return new type(grid_function, name);
+        },
+        "grid_function"_a,
+        "name"_a = "DivergenceGridFunction");
+    m.def(
+        "divergence",
+        [](GF grid_function, const std::string& name) { return new type(grid_function, name); },
+        "grid_function"_a,
+        "name"_a = "DivergenceGridFunction");
+
+    return c;
+  }
+}; // class GridFunction
+
+
+} // namespace bindings
+} // namespace Functions
+} // namespace XT
+} // namespace Dune
+
+
+template <class GridTypes = Dune::XT::Grid::AvailableGridTypes>
+struct DivergenceGridFunction_for_all_grids
+{
+  using G = typename GridTypes::head_type;
+  using GV = typename G::LeafGridView;
+  using E = Dune::XT::Grid::extract_entity_t<GV>;
+
+  static void bind(pybind11::module& m)
+  {
+    using Dune::XT::Functions::bindings::DivergenceGridFunction;
+    using Dune::XT::Grid::bindings::grid_name;
+
+    DivergenceGridFunction<G, E>::bind(m, grid_name<G>::value());
+
+    DivergenceGridFunction_for_all_grids<typename GridTypes::tail_type>::bind(m);
+  }
+};
+
+template <>
+struct DivergenceGridFunction_for_all_grids<boost::tuples::null_type>
+{
+  static void bind(pybind11::module& /*m*/) {}
+};
+
+
+PYBIND11_MODULE(_functions_divergence, m)
+{
+  namespace py = pybind11;
+
+  py::module::import("dune.xt.common");
+  py::module::import("dune.xt.grid");
+  py::module::import("dune.xt.la");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_1d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_2d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_3d");
+  py::module::import("dune.xt.functions._functions_gridfunction");
+
+  DivergenceGridFunction_for_all_grids<>::bind(m);
+} // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/elementwise-diameter.cc b/python/dune/xt/functions/elementwise-diameter.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2b1c90fdf2c91343660bdaed27d4696e1a5f5051
--- /dev/null
+++ b/python/dune/xt/functions/elementwise-diameter.cc
@@ -0,0 +1,123 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2018 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2019)
+
+#include "config.h"
+
+#include <string>
+#include <vector>
+
+#include <dune/common/parallel/mpihelper.hh>
+
+#include <dune/pybindxi/pybind11.h>
+#include <dune/pybindxi/functional.h>
+#include <dune/pybindxi/stl.h>
+
+#include <dune/xt/common/string.hh>
+#include <dune/xt/grid/gridprovider/provider.hh>
+#include <dune/xt/grid/type_traits.hh>
+#include <dune/xt/functions/elementwise-diameter.hh>
+
+#include <python/dune/xt/common/parameter.hh>
+#include <python/dune/xt/common/fvector.hh>
+#include <python/dune/xt/common/fmatrix.hh>
+#include <python/dune/xt/common/bindings.hh>
+#include <python/dune/xt/grid/traits.hh>
+#include <python/dune/xt/common/exceptions.bindings.hh>
+
+namespace Dune {
+namespace XT {
+namespace Functions {
+namespace bindings {
+
+
+template <class G, class E>
+class ElementwiseDiameterFunction
+{
+  using GP = XT::Grid::GridProvider<G>;
+  static const size_t d = G::dimension;
+
+public:
+  using type = Functions::ElementwiseDiameterFunction<E>;
+  using base_type = Functions::GridFunctionInterface<E>;
+  using bound_type = pybind11::class_<type, base_type>;
+
+public:
+  static bound_type bind(pybind11::module& m,
+                         const std::string& grid_id = Grid::bindings::grid_name<G>::value(),
+                         const std::string& layer_id = "",
+                         const std::string& class_id = "elementwise_diameter_function")
+  {
+    namespace py = pybind11;
+    using namespace pybind11::literals;
+
+    std::string class_name = class_id;
+    class_name += "_" + grid_id;
+    if (!layer_id.empty())
+      class_name += "_" + layer_id;
+    class_name += "_to_1d";
+    const auto ClassName = Common::to_camel_case(class_name);
+    bound_type c(m, ClassName.c_str(), Common::to_camel_case(class_id).c_str());
+    c.def(py::init<const std::string&>(), "name"_a = "ElementwiseDiameterFunction");
+
+    const auto FactoryName = Common::to_camel_case(class_id);
+    m.def(
+        FactoryName.c_str(),
+        [](const GP&, const std::string& name) { return new type(name); },
+        "grid"_a,
+        "name"_a = "ElementwiseDiameterFunction");
+
+    return c;
+  }
+}; // class GridFunction
+
+
+} // namespace bindings
+} // namespace Functions
+} // namespace XT
+} // namespace Dune
+
+
+template <class GridTypes = Dune::XT::Grid::AvailableGridTypes>
+struct ElementwiseDiameterFunction_for_all_grids
+{
+  using G = typename GridTypes::head_type;
+  using GV = typename G::LeafGridView;
+  using E = Dune::XT::Grid::extract_entity_t<GV>;
+
+  static void bind(pybind11::module& m)
+  {
+    using Dune::XT::Functions::bindings::ElementwiseDiameterFunction;
+    using Dune::XT::Grid::bindings::grid_name;
+
+    ElementwiseDiameterFunction<G, E>::bind(m, grid_name<G>::value());
+
+    ElementwiseDiameterFunction_for_all_grids<typename GridTypes::tail_type>::bind(m);
+  }
+};
+
+template <>
+struct ElementwiseDiameterFunction_for_all_grids<boost::tuples::null_type>
+{
+  static void bind(pybind11::module& /*m*/) {}
+};
+
+
+PYBIND11_MODULE(_functions_elementwise_diameter, m)
+{
+  namespace py = pybind11;
+
+  py::module::import("dune.xt.common");
+  py::module::import("dune.xt.grid");
+  py::module::import("dune.xt.la");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_1d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_2d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_3d");
+
+  ElementwiseDiameterFunction_for_all_grids<>::bind(m);
+} // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/elementwise-minimum.cc b/python/dune/xt/functions/elementwise-minimum.cc
new file mode 100644
index 0000000000000000000000000000000000000000..aff47d85866733f4a19437b84b176b870e079acb
--- /dev/null
+++ b/python/dune/xt/functions/elementwise-minimum.cc
@@ -0,0 +1,144 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2018 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2019)
+
+#include "config.h"
+
+#include <string>
+#include <vector>
+
+#include <dune/common/parallel/mpihelper.hh>
+
+#include <dune/pybindxi/pybind11.h>
+#include <dune/pybindxi/functional.h>
+#include <dune/pybindxi/stl.h>
+
+#include <dune/xt/common/string.hh>
+#include <dune/xt/grid/gridprovider/provider.hh>
+#include <dune/xt/grid/type_traits.hh>
+#include <dune/xt/functions/elementwise-minimum.hh>
+
+#include <python/dune/xt/common/parameter.hh>
+#include <python/dune/xt/common/fvector.hh>
+#include <python/dune/xt/common/fmatrix.hh>
+#include <python/dune/xt/common/bindings.hh>
+#include <python/dune/xt/grid/traits.hh>
+#include <python/dune/xt/common/exceptions.bindings.hh>
+
+namespace Dune {
+namespace XT {
+namespace Functions {
+namespace bindings {
+
+
+template <class G, class E, size_t r = 1, size_t rC = 1>
+class ElementwiseMinimumFunction
+{
+  using GP = XT::Grid::GridProvider<G>;
+  using GF = Functions::GridFunction<E, r, rC>;
+  static const size_t d = G::dimension;
+
+public:
+  using type = Functions::ElementwiseMinimumFunction<Functions::GridFunctionInterface<E, r, rC>>;
+  using base_type = Functions::GridFunctionInterface<E>;
+  using bound_type = pybind11::class_<type, base_type>;
+
+public:
+  static bound_type bind(pybind11::module& m,
+                         const std::string& grid_id = Grid::bindings::grid_name<G>::value(),
+                         const std::string& layer_id = "",
+                         const std::string& class_id = "elementwise_minimum_function")
+  {
+    namespace py = pybind11;
+    using namespace pybind11::literals;
+
+    std::string class_name = class_id;
+    class_name += "_" + grid_id;
+    if (!layer_id.empty())
+      class_name += "_" + layer_id;
+    class_name += "_to_" + Common::to_string(r);
+    if (rC > 1)
+      class_name += "x" + Common::to_string(rC);
+    class_name += "d";
+    const auto ClassName = Common::to_camel_case(class_name);
+    bound_type c(m, ClassName.c_str(), Common::to_camel_case(class_id).c_str());
+    c.def(py::init<GF, const int, const std::string&>(),
+          "grid_function"_a,
+          "search_quadrature_order"_a,
+          "name"_a = "ElementwiseMinimumFunction");
+
+    const auto FactoryName = Common::to_camel_case(class_id);
+    m.def(
+        FactoryName.c_str(),
+        [](GF grid_function, const int search_quadrature_order, const std::string& name) {
+          return new type(grid_function, search_quadrature_order, name);
+        },
+        "grid_function"_a,
+        "search_quadrature_order"_a,
+        "name"_a = "ElementwiseMinimumFunction");
+    m.def(
+        FactoryName.c_str(),
+        [](const Functions::GridFunction<E, r, rC>& grid_function,
+           const int search_quadrature_order,
+           const std::string& name) { return new type(grid_function, search_quadrature_order, name); },
+        "grid_function"_a,
+        "search_quadrature_order"_a,
+        "name"_a = "ElementwiseMinimumFunction");
+
+    return c;
+  }
+}; // class GridFunction
+
+
+} // namespace bindings
+} // namespace Functions
+} // namespace XT
+} // namespace Dune
+
+
+template <class GridTypes = Dune::XT::Grid::AvailableGridTypes>
+struct ElementwiseMinimumFunction_for_all_grids
+{
+  using G = typename GridTypes::head_type;
+  using GV = typename G::LeafGridView;
+  using E = Dune::XT::Grid::extract_entity_t<GV>;
+
+  static void bind(pybind11::module& m)
+  {
+    using Dune::XT::Functions::bindings::ElementwiseMinimumFunction;
+    using Dune::XT::Grid::bindings::grid_name;
+
+    ElementwiseMinimumFunction<G, E, 1, 1>::bind(m, grid_name<G>::value());
+    ElementwiseMinimumFunction<G, E, 2, 2>::bind(m, grid_name<G>::value());
+    ElementwiseMinimumFunction<G, E, 3, 3>::bind(m, grid_name<G>::value());
+
+    ElementwiseMinimumFunction_for_all_grids<typename GridTypes::tail_type>::bind(m);
+  }
+};
+
+template <>
+struct ElementwiseMinimumFunction_for_all_grids<boost::tuples::null_type>
+{
+  static void bind(pybind11::module& /*m*/) {}
+};
+
+
+PYBIND11_MODULE(_functions_elementwise_minimum, m)
+{
+  namespace py = pybind11;
+
+  py::module::import("dune.xt.common");
+  py::module::import("dune.xt.grid");
+  py::module::import("dune.xt.la");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_1d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_2d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_3d");
+  py::module::import("dune.xt.functions._functions_gridfunction");
+
+  ElementwiseMinimumFunction_for_all_grids<>::bind(m);
+} // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/function-as-grid-function.cc b/python/dune/xt/functions/function-as-grid-function.cc
index 68ee9187284544b9e36fdbcedae5e73dc623132c..3ac206f7602654ca859d4227927fbb0d6df8f31e 100644
--- a/python/dune/xt/functions/function-as-grid-function.cc
+++ b/python/dune/xt/functions/function-as-grid-function.cc
@@ -66,9 +66,9 @@ PYBIND11_MODULE(_functions_function_as_grid_function, m)
   py::module::import("dune.xt.functions._functions_function_interface_1d");
   py::module::import("dune.xt.functions._functions_function_interface_2d");
   py::module::import("dune.xt.functions._functions_function_interface_3d");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_1d");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_2d");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_3d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_1d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_2d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_3d");
 
   all_grids(m);
 }
diff --git a/python/dune/xt/functions/function-as-grid-function.hh b/python/dune/xt/functions/function-as-grid-function.hh
index 62c12a304801c203842e8525c4ce6337d9b006cf..24b372762a7ef5ffbba00ea7ee0ce5edc78c5bfb 100644
--- a/python/dune/xt/functions/function-as-grid-function.hh
+++ b/python/dune/xt/functions/function-as-grid-function.hh
@@ -46,7 +46,6 @@ auto bind_FunctionAsGridFunctionWrapper(pybind11::module& m, const std::string&
       "function_to_grid_function",
       [](XT::Functions::FunctionInterface<d, r, rC, R>& func,
          const XT::Grid::GridProvider<G>& /*only_here_to_select_grid_type*/) { return std::make_unique<C>(func); },
-      py::keep_alive<0, 1>(),
       "function"_a,
       "grid_provider"_a);
 
diff --git a/python/dune/xt/functions/function-interface-1d.cc b/python/dune/xt/functions/function-interface-1d.cc
index 55a48b8fe73da3c9ce9c89f12ba303fda3921ec2..a8fc131a5860b46c8709a15640d4c39cb20b2f37 100644
--- a/python/dune/xt/functions/function-interface-1d.cc
+++ b/python/dune/xt/functions/function-interface-1d.cc
@@ -35,9 +35,9 @@ PYBIND11_MODULE(_functions_function_interface_1d, m)
 
   using namespace Dune::XT::Functions;
 
-  const auto diff = CombinationType::difference;
-  const auto sum = CombinationType::sum;
-  const auto prod = CombinationType::product;
+  using diff = CombinationType::difference;
+  using sum = CombinationType::sum;
+  using prod = CombinationType::product;
 
   auto i_1_1_1 = bind_FunctionInterface<1, 1, 1>(m);
   auto i_1_2_1 = bind_FunctionInterface<1, 2, 1>(m);
diff --git a/python/dune/xt/functions/function-interface-2d.cc b/python/dune/xt/functions/function-interface-2d.cc
index 8342ff43d426eb521d086ad58877f36b966a2331..d7caf3c8ce5855ab4da82bcb67d8b230845712bf 100644
--- a/python/dune/xt/functions/function-interface-2d.cc
+++ b/python/dune/xt/functions/function-interface-2d.cc
@@ -35,9 +35,9 @@ PYBIND11_MODULE(_functions_function_interface_2d, m)
 
   using namespace Dune::XT::Functions;
 
-  const auto diff = CombinationType::difference;
-  const auto sum = CombinationType::sum;
-  const auto prod = CombinationType::product;
+  using diff = CombinationType::difference;
+  using sum = CombinationType::sum;
+  using prod = CombinationType::product;
 
   auto i_2_1_1 = bind_FunctionInterface<2, 1, 1>(m);
   auto i_2_2_1 = bind_FunctionInterface<2, 2, 1>(m);
diff --git a/python/dune/xt/functions/function-interface-3d.cc b/python/dune/xt/functions/function-interface-3d.cc
index ff1fe0f6581fe9c34aa62b81744f46074fa63370..ee9186738dbda2c671f2ca474fae377ea7c3844e 100644
--- a/python/dune/xt/functions/function-interface-3d.cc
+++ b/python/dune/xt/functions/function-interface-3d.cc
@@ -35,9 +35,9 @@ PYBIND11_MODULE(_functions_function_interface_3d, m)
 
   using namespace Dune::XT::Functions;
 
-  const auto diff = CombinationType::difference;
-  const auto sum = CombinationType::sum;
-  const auto prod = CombinationType::product;
+  using diff = CombinationType::difference;
+  using sum = CombinationType::sum;
+  using prod = CombinationType::product;
 
   auto i_3_1_1 = bind_FunctionInterface<3, 1, 1>(m);
   auto i_3_2_1 = bind_FunctionInterface<3, 2, 1>(m);
diff --git a/python/dune/xt/functions/function-interface.hh b/python/dune/xt/functions/function-interface.hh
index 61e2aa9f75b6982f50376427363a89c67057f975..96c3cb2d020e6e84d132ef590f87ce233ef54021 100644
--- a/python/dune/xt/functions/function-interface.hh
+++ b/python/dune/xt/functions/function-interface.hh
@@ -27,7 +27,7 @@ namespace Functions {
 namespace internal {
 
 
-template <class L, class R, CombinationType comb>
+template <class L, class R, class comb>
 struct get_combined
 {}; // struct get_combined
 
@@ -114,7 +114,7 @@ struct get_combined<L, R, CombinationType::product>
 
 
 template <size_t d,
-          CombinationType comb,
+          class comb,
           size_t lr,
           size_t lrC,
           size_t rr,
@@ -147,7 +147,7 @@ bind_combined_Function(pybind11::module& m)
 } // ... bind_combined_Function(...)
 
 
-template <size_t d, CombinationType comb, size_t r, size_t rC, size_t oR, size_t orC, class C>
+template <size_t d, class comb, size_t r, size_t rC, size_t oR, size_t orC, class C>
 void addbind_FunctionInterface_combined_op(C& c)
 {
   namespace py = pybind11;
@@ -158,8 +158,7 @@ void addbind_FunctionInterface_combined_op(C& c)
   c.def(
       internal::get_combined<S, O, comb>::op().c_str(),
       [](const S& self, const O& other) { return internal::get_combined<S, O, comb>::call(self, other); },
-      py::keep_alive<0, 1>(),
-      py::keep_alive<0, 2>());
+      py::is_operator());
 } // ... addbind_FunctionInterface_combined_op(...)
 
 
@@ -179,6 +178,11 @@ pybind11::class_<FunctionInterface<d, r, rC, double>> bind_FunctionInterface(pyb
                               + Common::to_string(rC))
                       .c_str());
 
+  c.def_property_readonly("dim_domain", [](const C& /*self*/) { return size_t(d); });
+  if (rC == 1)
+    c.def_property_readonly("dim_range", [](const C& /*self*/) { return size_t(r); });
+  else
+    c.def_property_readonly("dim_range", [](const C& /*self*/) { return std::make_pair(size_t(r), size_t(rC)); });
   c.def_property_readonly("static_id", [](const C& /*self*/) { return C::static_id(); });
   c.def_property_readonly("name", [](const C& self) { return self.name(); });
 
diff --git a/python/dune/xt/functions/gradient.cc b/python/dune/xt/functions/gradient.cc
new file mode 100644
index 0000000000000000000000000000000000000000..34413212c35a4111dac4e1cf5f1676b650397d99
--- /dev/null
+++ b/python/dune/xt/functions/gradient.cc
@@ -0,0 +1,129 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2018 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2019)
+
+#include "config.h"
+
+#include <string>
+#include <vector>
+
+#include <dune/common/parallel/mpihelper.hh>
+
+#include <dune/pybindxi/pybind11.h>
+#include <dune/pybindxi/functional.h>
+#include <dune/pybindxi/stl.h>
+
+#include <dune/xt/common/string.hh>
+#include <dune/xt/grid/gridprovider/provider.hh>
+#include <dune/xt/grid/type_traits.hh>
+#include <dune/xt/functions/base/derivatives-of-grid-functions.hh>
+
+#include <python/dune/xt/common/parameter.hh>
+#include <python/dune/xt/common/fvector.hh>
+#include <python/dune/xt/common/fmatrix.hh>
+#include <python/dune/xt/common/bindings.hh>
+#include <python/dune/xt/grid/traits.hh>
+#include <python/dune/xt/common/exceptions.bindings.hh>
+
+namespace Dune {
+namespace XT {
+namespace Functions {
+namespace bindings {
+
+
+template <class G, class E>
+class GradientGridFunction
+{
+  using GP = XT::Grid::GridProvider<G>;
+  static const size_t d = G::dimension;
+
+public:
+  using type = Functions::GradientGridFunction<GridFunctionInterface<E>>;
+  using base_type = Functions::GridFunctionInterface<E, d>;
+  using bound_type = pybind11::class_<type, base_type>;
+
+public:
+  static bound_type bind(pybind11::module& m,
+                         const std::string& grid_id = Grid::bindings::grid_name<G>::value(),
+                         const std::string& layer_id = "",
+                         const std::string& class_id = "gradient_grid_function")
+  {
+    namespace py = pybind11;
+    using namespace pybind11::literals;
+
+    std::string class_name = class_id;
+    class_name += "_" + grid_id;
+    if (!layer_id.empty())
+      class_name += "_" + layer_id;
+    class_name += "_to_" + Common::to_string(size_t(d)) + "d";
+    const auto ClassName = Common::to_camel_case(class_name);
+    bound_type c(m, ClassName.c_str(), Common::to_camel_case(class_id).c_str());
+    c.def(py::init([](const Functions::GridFunctionInterface<E>& grid_function, const std::string& logging_prefix) {
+            return new type(grid_function, logging_prefix);
+          }),
+          "grid_function"_a,
+          "name"_a = "GradientGridFunction");
+
+    m.def(
+        "gradient",
+        [](const GridFunctionInterface<E>& grid_function, const std::string& name) {
+          return new type(grid_function, name);
+        },
+        "grid_function"_a,
+        "name"_a = "GradientGridFunction");
+
+    return c;
+  }
+}; // class GridFunction
+
+
+} // namespace bindings
+} // namespace Functions
+} // namespace XT
+} // namespace Dune
+
+
+template <class GridTypes = Dune::XT::Grid::AvailableGridTypes>
+struct GradientGridFunction_for_all_grids
+{
+  using G = typename GridTypes::head_type;
+  using GV = typename G::LeafGridView;
+  using E = Dune::XT::Grid::extract_entity_t<GV>;
+
+  static void bind(pybind11::module& m)
+  {
+    using Dune::XT::Functions::bindings::GradientGridFunction;
+    using Dune::XT::Grid::bindings::grid_name;
+
+    GradientGridFunction<G, E>::bind(m, grid_name<G>::value());
+
+    GradientGridFunction_for_all_grids<typename GridTypes::tail_type>::bind(m);
+  }
+};
+
+template <>
+struct GradientGridFunction_for_all_grids<boost::tuples::null_type>
+{
+  static void bind(pybind11::module& /*m*/) {}
+};
+
+
+PYBIND11_MODULE(_functions_gradient, m)
+{
+  namespace py = pybind11;
+
+  py::module::import("dune.xt.common");
+  py::module::import("dune.xt.grid");
+  py::module::import("dune.xt.la");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_1d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_2d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_3d");
+  py::module::import("dune.xt.functions._functions_gridfunction");
+
+  GradientGridFunction_for_all_grids<>::bind(m);
+} // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/gridfunction-interface-1d.cc b/python/dune/xt/functions/gridfunction-interface-1d.cc
deleted file mode 100644
index 342ccc44169647247b9adf9f8d0e72a4f53876ec..0000000000000000000000000000000000000000
--- a/python/dune/xt/functions/gridfunction-interface-1d.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// This file is part of the dune-xt project:
-//   https://github.com/dune-community/dune-xt
-// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
-// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
-//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
-//          with "runtime exception" (http://www.dune-project.org/license.html)
-// Authors:
-//   Felix Schindler (2019)
-//   René Fritze     (2019)
-//   Tobias Leibner  (2019 - 2020)
-
-#include "config.h"
-
-#include <string>
-#include <vector>
-
-#include <dune/common/parallel/mpihelper.hh>
-
-#include <dune/pybindxi/pybind11.h>
-#include <dune/pybindxi/stl.h>
-
-#include <python/dune/xt/common/bindings.hh>
-#include <python/dune/xt/common/exceptions.bindings.hh>
-#include <python/dune/xt/grid/grids.bindings.hh>
-
-#include "gridfunction-interface.hh"
-
-
-template <class Tuple = Dune::XT::Grid::Available1dGridTypes>
-void bind_all_1d_grids(pybind11::module& m)
-{
-  Dune::XT::Common::bindings::guarded_bind([&]() { //  different grids but same entity
-    Dune::XT::Functions::bindings::addbind_GridFunctionInterface_all_dims<typename Tuple::head_type>(m);
-  });
-  bind_all_1d_grids<typename Tuple::tail_type>(m);
-}
-
-template <>
-void bind_all_1d_grids<boost::tuples::null_type>(pybind11::module&)
-{}
-
-
-PYBIND11_MODULE(_functions_gridfunction_interface_1d, m)
-{
-  namespace py = pybind11;
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions._functions_function_interface_1d");
-
-  bind_all_1d_grids(m);
-} // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/gridfunction-interface-2d.cc b/python/dune/xt/functions/gridfunction-interface-2d.cc
deleted file mode 100644
index 0d003986e08d1d1ace735b8a62d49e187bd61b82..0000000000000000000000000000000000000000
--- a/python/dune/xt/functions/gridfunction-interface-2d.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// This file is part of the dune-xt project:
-//   https://github.com/dune-community/dune-xt
-// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
-// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
-//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
-//          with "runtime exception" (http://www.dune-project.org/license.html)
-// Authors:
-//   Felix Schindler (2019)
-//   René Fritze     (2019)
-//   Tobias Leibner  (2019 - 2020)
-
-#include "config.h"
-
-#include <string>
-#include <vector>
-
-#include <dune/common/parallel/mpihelper.hh>
-
-#include <dune/pybindxi/pybind11.h>
-#include <dune/pybindxi/stl.h>
-
-#include <python/dune/xt/common/bindings.hh>
-#include <python/dune/xt/common/exceptions.bindings.hh>
-#include <python/dune/xt/grid/grids.bindings.hh>
-
-#include "gridfunction-interface.hh"
-
-
-template <class Tuple = Dune::XT::Grid::Available2dGridTypes>
-void bind_all_2d_grids(pybind11::module& m)
-{
-  Dune::XT::Common::bindings::guarded_bind([&]() { //  different grids but same entity
-    Dune::XT::Functions::bindings::addbind_GridFunctionInterface_all_dims<typename Tuple::head_type>(m);
-  });
-  bind_all_2d_grids<typename Tuple::tail_type>(m);
-}
-
-template <>
-void bind_all_2d_grids<boost::tuples::null_type>(pybind11::module&)
-{}
-
-
-PYBIND11_MODULE(_functions_gridfunction_interface_2d, m)
-{
-  namespace py = pybind11;
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions._functions_function_interface_2d");
-
-  bind_all_2d_grids(m);
-} // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/gridfunction-interface-3d.cc b/python/dune/xt/functions/gridfunction-interface-3d.cc
deleted file mode 100644
index db8793bba427a1606733ade8a007b8466eefde55..0000000000000000000000000000000000000000
--- a/python/dune/xt/functions/gridfunction-interface-3d.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// This file is part of the dune-xt project:
-//   https://github.com/dune-community/dune-xt
-// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
-// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
-//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
-//          with "runtime exception" (http://www.dune-project.org/license.html)
-// Authors:
-//   Felix Schindler (2019)
-//   René Fritze     (2019)
-//   Tobias Leibner  (2019 - 2020)
-
-#include "config.h"
-
-#include <string>
-#include <vector>
-
-#include <dune/common/parallel/mpihelper.hh>
-
-#include <dune/pybindxi/pybind11.h>
-#include <dune/pybindxi/stl.h>
-
-#include <python/dune/xt/common/bindings.hh>
-#include <python/dune/xt/common/exceptions.bindings.hh>
-#include <python/dune/xt/grid/grids.bindings.hh>
-
-#include "gridfunction-interface.hh"
-
-
-template <class Tuple = Dune::XT::Grid::Available3dGridTypes>
-void bind_all_3d_grids(pybind11::module& m)
-{
-  Dune::XT::Common::bindings::guarded_bind([&]() { //  different grids but same entity
-    Dune::XT::Functions::bindings::addbind_GridFunctionInterface_all_dims<typename Tuple::head_type>(m);
-  });
-  bind_all_3d_grids<typename Tuple::tail_type>(m);
-}
-
-template <>
-void bind_all_3d_grids<boost::tuples::null_type>(pybind11::module&)
-{}
-
-
-PYBIND11_MODULE(_functions_gridfunction_interface_3d, m)
-{
-  namespace py = pybind11;
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions._functions_function_interface_3d");
-
-  bind_all_3d_grids(m);
-} // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/gridfunction-interface.hh b/python/dune/xt/functions/gridfunction-interface.hh
deleted file mode 100644
index 88797b70b51618de172f9a06783860898d68e340..0000000000000000000000000000000000000000
--- a/python/dune/xt/functions/gridfunction-interface.hh
+++ /dev/null
@@ -1,461 +0,0 @@
-// This file is part of the dune-xt project:
-//   https://github.com/dune-community/dune-xt
-// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
-// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
-//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
-//          with "runtime exception" (http://www.dune-project.org/license.html)
-// Authors:
-//   Felix Schindler (2016 - 2019)
-//   René Fritze     (2018 - 2019)
-//   Tim Keil        (2018)
-//   Tobias Leibner  (2018, 2020)
-
-#ifndef DUNE_XT_FUNCTIONS_INTERFACE_PBH
-#define DUNE_XT_FUNCTIONS_INTERFACE_PBH
-
-#include <dune/pybindxi/pybind11.h>
-#include <dune/pybindxi/operators.h>
-
-#include <python/dune/xt/common/bindings.hh>
-#include <python/dune/xt/grid/grids.bindings.hh>
-#include <dune/xt/grid/gridprovider/provider.hh>
-
-#include <dune/xt/functions/interfaces/grid-function.hh>
-#include <dune/xt/functions/interfaces/function.hh>
-
-namespace Dune {
-namespace XT {
-namespace Functions {
-namespace bindings {
-namespace internal {
-
-
-template <class L, class R, CombinationType comb>
-struct get_combined
-{}; // struct get_combined
-
-template <class L, class R>
-struct get_combined<L, R, CombinationType::difference>
-{
-  typedef DifferenceFunction<L, R> type;
-
-  static std::string id()
-  {
-    return "DifferenceFunction";
-  }
-
-  static std::string doc()
-  {
-    return "difference";
-  }
-
-  static std::string op()
-  {
-    return "__sub__";
-  }
-
-  static auto call(const L& l, const R& r) -> decltype(l - r)
-  {
-    return l - r;
-  }
-}; // struct get_combined
-
-template <class L, class R>
-struct get_combined<L, R, CombinationType::sum>
-{
-  typedef SumFunction<L, R> type;
-
-  static std::string id()
-  {
-    return "SumFunction";
-  }
-
-  static std::string doc()
-  {
-    return "sum";
-  }
-
-  static std::string op()
-  {
-    return "__add__";
-  }
-
-  static auto call(const L& l, const R& r) -> decltype(l + r)
-  {
-    return l + r;
-  }
-}; // struct get_combined
-
-template <class L, class R>
-struct get_combined<L, R, CombinationType::product>
-{
-  typedef ProductFunction<L, R> type;
-
-  static std::string id()
-  {
-    return "ProductFunction";
-  }
-
-  static std::string doc()
-  {
-    return "product";
-  }
-
-  static std::string op()
-  {
-    return "__mul__";
-  }
-
-  static auto call(const L& l, const R& r) -> decltype(l * r)
-  {
-    return l * r;
-  }
-}; // struct get_combined
-
-
-/**
- * grid_combined
- */
-
-template <class L, class R, CombinationType comb>
-struct get_grid_combined
-{}; // struct get_grid_combined
-
-template <class L, class R>
-struct get_grid_combined<L, R, CombinationType::difference>
-{
-  typedef DifferenceGridFunction<L, R> type;
-
-  static std::string id()
-  {
-    return "DifferenceGridFunction";
-  }
-
-  static std::string doc()
-  {
-    return "difference";
-  }
-
-  static std::string op()
-  {
-    return "__sub__";
-  }
-
-  static auto call(const L& l, const R& r) -> decltype(l - r)
-  {
-    return l - r;
-  }
-}; // struct get_grid_combined
-
-template <class L, class R>
-struct get_grid_combined<L, R, CombinationType::sum>
-{
-  typedef SumGridFunction<L, R> type;
-
-  static std::string id()
-  {
-    return "SumGridFunction";
-  }
-
-  static std::string doc()
-  {
-    return "sum";
-  }
-
-  static std::string op()
-  {
-    return "__add__";
-  }
-
-  static auto call(const L& l, const R& r) -> decltype(l + r)
-  {
-    return l + r;
-  }
-}; // struct get_grid_combined
-
-template <class L, class R>
-struct get_grid_combined<L, R, CombinationType::product>
-{
-  typedef ProductGridFunction<L, R> type;
-
-  static std::string id()
-  {
-    return "ProductGridFunction";
-  }
-
-  static std::string doc()
-  {
-    return "product";
-  }
-
-  static std::string op()
-  {
-    return "__mul__";
-  }
-
-  static auto call(const L& l, const R& r) -> decltype(l * r)
-  {
-    return l * r;
-  }
-}; // struct get_grid_combined
-
-// template <class G>
-// struct Divergence
-//{
-//  template <size_t d, size_t r, size_t rC, bool dims_match = (d == r) && (rC == 1)>
-//  struct helper
-//  {
-//    template <class E, class R>
-//    static void addbind(pybind11::module& m, pybind11::class_<GridFunctionInterface<E, d, 1, R>>& c)
-//    {
-//      namespace py = pybind11;
-//      using namespace pybind11::literals;
-//      using Common::to_string;
-
-//      try { // guard since we might not be the first to do bind this combination
-//        py::class_<DivergenceFunction<GridFunctionInterface<E, d, 1, R>>,
-//                   GridFunctionInterface<E, d, 1, R>>(
-//            m,
-//            Common::to_camel_case(
-//                "divergence_of_function_from_" + Grid::bindings::grid_name<G>::value() + "_to_" + to_string(r) + "x"
-//                + to_string(rC))
-//                .c_str(),
-//            "DivergenceFunction");
-//      } catch (std::runtime_error&) {
-//      }
-
-//      c.def("divergence",
-//            [](const GridFunctionInterface<E, d, 1, R>& self, const std::string& name) {
-//              return new DivergenceFunction<GridFunctionInterface<E, d, 1, R>>(self, name);
-//            },
-//            "name"_a = "",
-//            py::keep_alive<0, 1>());
-//    } // ... addbind(...)
-//  }; // struct helper<..., true>
-
-//  template <size_t d, size_t r, size_t rC>
-//  struct helper<d, r, rC, false>
-//  {
-//    template <class E, class R>
-//    static void addbind(pybind11::module& /*m*/, pybind11::class_<GridFunctionInterface<E, r, rC, R>>& /*c*/)
-//    {
-//    }
-//  }; // struct helper<..., false>
-
-//  template <class E, size_t d, class R, size_t r, size_t rC>
-//  static void addbind(pybind11::module& m, pybind11::class_<GridFunctionInterface<E, r, rC, R>>& c)
-//  {
-//    helper<d, r, rC>::addbind(m, c);
-//  } // ... addbind(...)
-//}; // struct Divergence
-
-
-} // namespace internal
-
-
-/**
- * \note We would like to drop the d template paremter and use either of
-\code
-static constexpr           size_t d = G::dimension;
-static constexpr size_t d = G::dimension;
-\endcode
- *       but this triggers a bug in gcc-4.9, see e.g.: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59937
- */
-template <class G,
-          size_t d,
-          CombinationType comb,
-          size_t lr,
-          size_t lrC,
-          size_t rr,
-          size_t rrC,
-          class C = typename internal::get_grid_combined<
-              GridFunctionInterface<typename G::template Codim<0>::Entity, lr, lrC, double>,
-              GridFunctionInterface<typename G::template Codim<0>::Entity, rr, rrC, double>,
-              comb>::type>
-pybind11::class_<C,
-                 GridFunctionInterface<typename G::template Codim<0>::Entity, C::range_dim, C::range_dim_cols, double>>
-bind_combined_GridFunction(pybind11::module& m, const std::string& grid_id)
-{
-  namespace py = pybind11;
-
-  typedef typename G::template Codim<0>::Entity E;
-  typedef double R;
-  typedef GridFunctionInterface<E, lr, lrC, R> Left;
-  typedef GridFunctionInterface<E, rr, rrC, R> Right;
-  static constexpr size_t r = C::range_dim;
-  static constexpr size_t rC = C::range_dim_cols;
-  const std::string id = internal::get_grid_combined<Left, Right, comb>::id();
-  const std::string op = internal::get_grid_combined<Left, Right, comb>::doc();
-  const std::string class_name = id + "__" + grid_id + "_to_" + Common::to_string(r) + "x" + Common::to_string(rC);
-  const std::string doc = class_name + " (as a " + op + " of functions of dimensions " + Common::to_string(lr) + "x"
-                          + Common::to_string(lrC) + " and " + Common::to_string(rr) + "x" + Common::to_string(rrC)
-                          + ")";
-
-  py::class_<C, GridFunctionInterface<E, r, rC, R>> c(m, std::string(class_name).c_str(), doc.c_str());
-
-  c.def_property_readonly("static_id", [](const C& /*self*/) { return C::static_id(); });
-
-  return c;
-} // ... bind_combined_GridFunction(...)
-
-
-/**
- * \note We would like to drop the d template paremter and use either of
-\code
-static constexpr           size_t d = G::dimension;
-static constexpr size_t d = G::dimension;
-\endcode
- *       but this triggers a bug in gcc-4.9, see e.g.: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59937
- */
-template <class G, size_t d, CombinationType comb, size_t r, size_t rC, size_t oR, size_t orC, class C>
-void addbind_GridFunctionInterface_combined_op(C& c)
-{
-  namespace py = pybind11;
-
-  typedef typename G::template Codim<0>::Entity E;
-  typedef GridFunctionInterface<E, r, rC, double> S;
-  typedef GridFunctionInterface<E, oR, orC, double> O;
-
-  c.def(
-      internal::get_grid_combined<S, O, comb>::op().c_str(),
-      [](const S& self, const O& other) { return internal::get_grid_combined<S, O, comb>::call(self, other); },
-      py::keep_alive<0, 1>(),
-      py::keep_alive<0, 2>());
-} // ... addbind_GridFunctionInterface_combined_op(...)
-
-
-template <class G, size_t r, size_t rC>
-pybind11::class_<GridFunctionInterface<typename G::template Codim<0>::Entity, r, rC, double>>
-bind_GridFunctionInterface(pybind11::module& m, const std::string& grid_id)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  typedef GridFunctionInterface<typename G::template Codim<0>::Entity, r, rC, double> C;
-
-  py::class_<C> c(
-      m,
-      std::string("GridFunctionInterface__" + grid_id + "_to_" + Common::to_string(r) + "x" + Common::to_string(rC))
-          .c_str(),
-      std::string("GridFunctionInterface__" + grid_id + "_to_" + Common::to_string(r) + "x" + Common::to_string(rC))
-          .c_str());
-
-  c.def_property_readonly("static_id", [](const C& /*self*/) { return C::static_id(); });
-  c.def_property_readonly("name", [](const C& self) { return self.name(); });
-
-  c.def(
-      "visualize",
-      [](const C& self,
-         const Grid::GridProvider<G>& grid_provider,
-         const std::string& layer,
-         const ssize_t lvl,
-         const std::string& path,
-         const bool subsampling) {
-        const auto level = XT::Common::numeric_cast<int>(lvl);
-        if (layer == "leaf")
-          self.visualize(grid_provider.leaf_view(), path, subsampling);
-        else if (layer == "level")
-          self.visualize(grid_provider.template layer<XT::Grid::Layers::level, XT::Grid::Backends::view>(level),
-                         path,
-                         subsampling);
-        else
-          DUNE_THROW(XT::Common::Exceptions::wrong_input_given,
-                     "Given layer has to be one of ('leaf', 'level'), is '" << layer << "'!");
-      },
-      "grid_provider"_a,
-      "layer"_a = "leaf",
-      "level"_a = -1,
-      "path"_a,
-      "subsampling"_a = true);
-
-  // internal::Divergence<G>::addbind(m, c);
-
-  return c;
-} // ... bind_GridFunctionInterface(...)
-
-
-template <class G>
-void addbind_GridFunctionInterface_all_dims(pybind11::module& m)
-{
-  using namespace Dune::XT::Functions;
-  const auto grid_id = Dune::XT::Grid::bindings::grid_name<G>::value();
-  constexpr const auto diff = CombinationType::difference;
-  constexpr const auto sum = CombinationType::sum;
-  constexpr const auto prod = CombinationType::product;
-  constexpr const auto g_dim = G::dimension;
-
-  auto i_1_1 = bind_GridFunctionInterface<G, 1, 1>(m, grid_id);
-  auto i_1_2 = bind_GridFunctionInterface<G, 1, 2>(m, grid_id);
-  auto i_1_3 = bind_GridFunctionInterface<G, 1, 3>(m, grid_id);
-  auto i_2_1 = bind_GridFunctionInterface<G, 2, 1>(m, grid_id);
-  auto i_2_2 = bind_GridFunctionInterface<G, 2, 2>(m, grid_id);
-  auto i_2_3 = bind_GridFunctionInterface<G, 2, 3>(m, grid_id);
-  auto i_3_1 = bind_GridFunctionInterface<G, 3, 1>(m, grid_id);
-  auto i_3_2 = bind_GridFunctionInterface<G, 3, 2>(m, grid_id);
-  auto i_3_3 = bind_GridFunctionInterface<G, 3, 3>(m, grid_id);
-
-  bind_combined_GridFunction<G, g_dim, diff, 1, 1, 1, 1>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, diff, 1, 1, 1, 1>(i_1_1);
-  bind_combined_GridFunction<G, g_dim, diff, 1, 2, 1, 2>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, diff, 1, 2, 1, 2>(i_1_2);
-  bind_combined_GridFunction<G, g_dim, diff, 1, 3, 1, 3>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, diff, 1, 3, 1, 3>(i_1_3);
-  bind_combined_GridFunction<G, g_dim, diff, 2, 1, 2, 1>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, diff, 2, 1, 2, 1>(i_2_1);
-  bind_combined_GridFunction<G, g_dim, diff, 2, 2, 2, 2>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, diff, 2, 2, 2, 2>(i_2_2);
-  bind_combined_GridFunction<G, g_dim, diff, 2, 3, 2, 3>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, diff, 2, 3, 2, 3>(i_2_3);
-  bind_combined_GridFunction<G, g_dim, diff, 3, 1, 3, 1>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, diff, 3, 1, 3, 1>(i_3_1);
-  bind_combined_GridFunction<G, g_dim, diff, 3, 2, 3, 2>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, diff, 3, 2, 3, 2>(i_3_2);
-  bind_combined_GridFunction<G, g_dim, diff, 3, 3, 3, 3>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, diff, 3, 3, 3, 3>(i_3_3);
-
-  bind_combined_GridFunction<G, g_dim, sum, 1, 1, 1, 1>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, sum, 1, 1, 1, 1>(i_1_1);
-  bind_combined_GridFunction<G, g_dim, sum, 1, 2, 1, 2>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, sum, 1, 2, 1, 2>(i_1_2);
-  bind_combined_GridFunction<G, g_dim, sum, 1, 3, 1, 3>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, sum, 1, 3, 1, 3>(i_1_3);
-  bind_combined_GridFunction<G, g_dim, sum, 2, 1, 2, 1>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, sum, 2, 1, 2, 1>(i_2_1);
-  bind_combined_GridFunction<G, g_dim, sum, 2, 2, 2, 2>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, sum, 2, 2, 2, 2>(i_2_2);
-  bind_combined_GridFunction<G, g_dim, sum, 2, 3, 2, 3>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, sum, 2, 3, 2, 3>(i_2_3);
-  bind_combined_GridFunction<G, g_dim, sum, 3, 1, 3, 1>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, sum, 3, 1, 3, 1>(i_3_1);
-  bind_combined_GridFunction<G, g_dim, sum, 3, 2, 3, 2>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, sum, 3, 2, 3, 2>(i_3_2);
-  bind_combined_GridFunction<G, g_dim, sum, 3, 3, 3, 3>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, sum, 3, 3, 3, 3>(i_3_3);
-
-  bind_combined_GridFunction<G, g_dim, prod, 1, 1, 1, 1>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, prod, 1, 1, 1, 1>(i_1_1);
-  bind_combined_GridFunction<G, g_dim, prod, 1, 1, 1, 2>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, prod, 1, 1, 1, 2>(i_1_1);
-  bind_combined_GridFunction<G, g_dim, prod, 1, 1, 1, 3>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, prod, 1, 1, 1, 3>(i_1_1);
-  bind_combined_GridFunction<G, g_dim, prod, 1, 1, 2, 1>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, prod, 1, 1, 2, 1>(i_1_1);
-  bind_combined_GridFunction<G, g_dim, prod, 1, 1, 2, 2>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, prod, 1, 1, 2, 2>(i_1_1);
-  bind_combined_GridFunction<G, g_dim, prod, 1, 1, 2, 3>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, prod, 1, 1, 2, 3>(i_1_1);
-  bind_combined_GridFunction<G, g_dim, prod, 1, 1, 3, 1>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, prod, 1, 1, 3, 1>(i_1_1);
-  bind_combined_GridFunction<G, g_dim, prod, 1, 1, 3, 2>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, prod, 1, 1, 3, 2>(i_1_1);
-  bind_combined_GridFunction<G, g_dim, prod, 1, 1, 3, 3>(m, grid_id);
-  addbind_GridFunctionInterface_combined_op<G, g_dim, prod, 1, 1, 3, 3>(i_1_1);
-} // ... addbind_GridFunctionInterface_all_dims(...)
-
-
-} // namespace bindings
-} // namespace Functions
-} // namespace XT
-} // namespace Dune
-
-#endif // DUNE_XT_FUNCTIONS_INTERFACE_PBH
diff --git a/python/dune/xt/functions/gridfunction.cc b/python/dune/xt/functions/gridfunction.cc
index df6a82d39ffc3e3007dbd3b431aea66d71723eec..6ab69fb96f8c69bf8964cea98d1c5afcc5fea2e5 100644
--- a/python/dune/xt/functions/gridfunction.cc
+++ b/python/dune/xt/functions/gridfunction.cc
@@ -57,9 +57,16 @@ private:
       namespace py = pybind11;
       using namespace pybind11::literals;
 
-      c.def(py::init<const typename RangeTypeSelector<R, r, rC>::type&>(), "constant_matrix"_a);
-      c.def(py::init<const FunctionInterface<d, r, rC, R>&>(), "matrix_function"_a, py::keep_alive<1, 2>());
-      c.def(py::init<const GridFunctionInterface<E, r, rC, R>&>(), "matrix_grid_function"_a, py::keep_alive<1, 2>());
+      c.def(py::init<const typename RangeTypeSelector<R, r, rC>::type&, const std::string&, const std::string&>(),
+            "constant_matrix"_a,
+            "name"_a = "GridFunction",
+            "logging_prefix"_a = "");
+      c.def(py::init<const FunctionInterface<d, r, rC, R>&, const std::string&>(),
+            "matrix_function"_a,
+            "logging_prefix"_a = "");
+      c.def(py::init<const GridFunctionInterface<E, r, rC, R>&, const std::string&>(),
+            "matrix_grid_function"_a,
+            "logging_prefix"_a = "");
     } // ... init(...)
 
     static void factory(pybind11::module& m, const std::string& FactoryName)
@@ -70,63 +77,70 @@ private:
       // without dimRange
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const typename RangeTypeSelector<R, r, rC>::type& constant_matrix) {
-            return type(constant_matrix);
-          },
+          [](const GP&,
+             const typename RangeTypeSelector<R, r, rC>::type& constant_matrix,
+             const std::string& name,
+             const std::string& logging_prefix) { return new type(constant_matrix, name, logging_prefix); },
           "grid"_a,
-          "constant_matrix"_a);
+          "constant_matrix"_a,
+          "name"_a = "GridFunction",
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const FunctionInterface<d, r, rC, R>& matrix_function) { return type(matrix_function); },
+          [](const GP&, const FunctionInterface<d, r, rC, R>& matrix_function, const std::string& logging_prefix) {
+            return new type(matrix_function, logging_prefix);
+          },
           "grid"_a,
           "matrix_function"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const GridFunctionInterface<E, r, rC, R>& matrix_grid_function) {
-            return type(matrix_grid_function);
-          },
+          [](const GP&,
+             const GridFunctionInterface<E, r, rC, R>& matrix_grid_function,
+             const std::string& logging_prefix) { return new type(matrix_grid_function, logging_prefix); },
           "grid"_a,
           "matrix_grid_function"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GridFunctionInterface<E, r, rC, R>& matrix_grid_function) { return type(matrix_grid_function); },
+          [](const GridFunctionInterface<E, r, rC, R>& matrix_grid_function, const std::string& logging_prefix) {
+            return new type(matrix_grid_function, logging_prefix);
+          },
           "matrix_grid_function"_a,
-          py::keep_alive<0, 1>());
+          "logging_prefix"_a = "");
       // and with dimRange
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const typename RangeTypeSelector<R, r, rC>::type& constant_matrix,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&) {
-            return type(constant_matrix);
-          },
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&,
+             const std::string& name,
+             const std::string& logging_prefix) { return new type(constant_matrix, name, logging_prefix); },
           "grid"_a,
           "constant_matrix"_a,
-          "dim_range"_a);
+          "dim_range"_a,
+          "name"_a = "GridFunction",
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const FunctionInterface<d, r, rC, R>& matrix_function,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&) {
-            return type(matrix_function);
-          },
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&,
+             const std::string& logging_prefix) { return new type(matrix_function, logging_prefix); },
           "grid"_a,
           "matrix_function"_a,
           "dim_range"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const GridFunctionInterface<E, r, rC, R>& matrix_grid_function,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&) {
-            return type(matrix_grid_function);
-          },
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&,
+             const std::string& logging_prefix) { return new type(matrix_grid_function, logging_prefix); },
           "grid"_a,
           "matrix_grid_function"_a,
           "dim_range"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
     } // ... factory (...)
   }; // struct dim_dependent<r, rC, anything>, the general case, neither scalar nor square matrix
 
@@ -138,10 +152,20 @@ private:
       namespace py = pybind11;
       using namespace pybind11::literals;
 
-      c.def(py::init<const R&>(), "constant_scalar"_a);
-      c.def(py::init<const FieldVector<R, 1>&>(), "constant_vector_of_length_one"_a);
-      c.def(py::init<const FunctionInterface<d, 1, 1, R>&>(), "scalar_function"_a, py::keep_alive<1, 2>());
-      c.def(py::init<const GridFunctionInterface<E, 1, 1, R>&>(), "scalar_grid_function"_a, py::keep_alive<1, 2>());
+      c.def(py::init<const R&, const std::string&, const std::string&>(),
+            "constant_scalar"_a,
+            "name"_a = "GridFunction",
+            "logging_prefix"_a = "");
+      c.def(py::init<const FieldVector<R, 1>&, const std::string&, const std::string&>(),
+            "constant_vector_of_length_one"_a,
+            "name"_a = "GridFunction",
+            "logging_prefix"_a = "");
+      c.def(py::init<const FunctionInterface<d, 1, 1, R>&, const std::string&>(),
+            "scalar_function"_a,
+            "logging_prefix"_a = "");
+      c.def(py::init<const GridFunctionInterface<E, 1, 1, R>&, const std::string&>(),
+            "scalar_grid_function"_a,
+            "logging_prefix"_a = "");
     } // ... init(...)
 
     static void factory(pybind11::module& m, const std::string& FactoryName)
@@ -152,113 +176,142 @@ private:
       // without dimRange
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const R& constant_scalar) { return type(constant_scalar); },
+          [](const GP&, const R& constant_scalar, const std::string& name, const std::string& logging_prefix) {
+            return new type(constant_scalar, name, logging_prefix);
+          },
           "grid"_a,
-          "constant_scalar"_a);
+          "constant_scalar"_a,
+          "name"_a = "GridFunction",
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const FieldVector<R, 1>& constant_vector_of_length_one) {
-            return type(constant_vector_of_length_one);
+          [](const GP&,
+             const FieldVector<R, 1>& constant_vector_of_length_one,
+             const std::string& name,
+             const std::string& logging_prefix) {
+            return new type(constant_vector_of_length_one, name, logging_prefix);
           },
           "grid"_a,
-          "constant_vector_of_length_one"_a);
+          "constant_vector_of_length_one"_a,
+          "name"_a = "GridFunction",
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const FunctionInterface<d, 1, 1, R>& scalar_function) { return type(scalar_function); },
+          [](const GP&, const FunctionInterface<d, 1, 1, R>& scalar_function, const std::string& logging_prefix) {
+            return new type(scalar_function, logging_prefix);
+          },
           "grid"_a,
           "scalar_function"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const GridFunctionInterface<E, 1, 1, R>& scalar_grid_function) {
-            return type(scalar_grid_function);
-          },
+          [](const GP&,
+             const GridFunctionInterface<E, 1, 1, R>& scalar_grid_function,
+             const std::string& logging_prefix) { return new type(scalar_grid_function, logging_prefix); },
           "grid"_a,
           "scalar_grid_function"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GridFunctionInterface<E, 1, 1, R>& scalar_grid_function) { return type(scalar_grid_function); },
+          [](const GridFunctionInterface<E, 1, 1, R>& scalar_grid_function, const std::string& logging_prefix) {
+            return new type(scalar_grid_function, logging_prefix);
+          },
           "scalar_grid_function"_a,
-          py::keep_alive<0, 1>());
+          "logging_prefix"_a = "");
       // and with dimRange, to distinguish from the square matrix case
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const R& constant_scalar, const Grid::bindings::Dimension<1>&) {
-            return type(constant_scalar);
-          },
+          [](const GP&,
+             const R& constant_scalar,
+             const Grid::bindings::Dimension<1>&,
+             const std::string& name,
+             const std::string& logging_prefix) { return new type(constant_scalar, name, logging_prefix); },
           "grid"_a,
           "constant_scalar"_a,
-          "dim_range"_a);
+          "dim_range"_a,
+          "name"_a = "GridFunction",
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const FieldVector<R, 1>& constant_vector_of_length_one, const Grid::bindings::Dimension<1>&) {
-            return type(constant_vector_of_length_one);
+          [](const GP&,
+             const FieldVector<R, 1>& constant_vector_of_length_one,
+             const Grid::bindings::Dimension<1>&,
+             const std::string& name,
+             const std::string& logging_prefix) {
+            return new type(constant_vector_of_length_one, name, logging_prefix);
           },
           "grid"_a,
           "constant_vector_of_length_one"_a,
-          "dim_range"_a);
+          "dim_range"_a,
+          "name"_a = "GridFunction",
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const FunctionInterface<d, 1, 1, R>& scalar_function, const Grid::bindings::Dimension<1>&) {
-            return type(scalar_function);
-          },
+          [](const GP&,
+             const FunctionInterface<d, 1, 1, R>& scalar_function,
+             const Grid::bindings::Dimension<1>&,
+             const std::string& logging_prefix) { return new type(scalar_function, logging_prefix); },
           "grid"_a,
           "scalar_function"_a,
           "dim_range"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const GridFunctionInterface<E, 1, 1, R>& scalar_grid_function,
-             const Grid::bindings::Dimension<1>&) { return type(scalar_grid_function); },
+             const Grid::bindings::Dimension<1>&,
+             const std::string& logging_prefix) { return new type(scalar_grid_function, logging_prefix); },
           "grid"_a,
           "scalar_grid_function"_a,
           "dim_range"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       // and with dimRange x dimRangeCols
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const R& constant_scalar,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&) {
-            return type(constant_scalar);
-          },
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&,
+             const std::string& name,
+             const std::string& logging_prefix) { return new type(constant_scalar, name, logging_prefix); },
           "grid"_a,
           "constant_scalar"_a,
-          "dim_range"_a);
+          "dim_range"_a,
+          "name"_a = "GridFunction",
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const FieldVector<R, 1>& constant_vector_of_length_one,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&) {
-            return type(constant_vector_of_length_one);
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&,
+             const std::string& name,
+             const std::string& logging_prefix) {
+            return new type(constant_vector_of_length_one, name, logging_prefix);
           },
           "grid"_a,
           "constant_vector_of_length_one"_a,
-          "dim_range"_a);
+          "dim_range"_a,
+          "name"_a = "GridFunction",
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const FunctionInterface<d, 1, 1, R>& scalar_function,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&) {
-            return type(scalar_function);
-          },
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&,
+             const std::string& logging_prefix) { return new type(scalar_function, logging_prefix); },
           "grid"_a,
           "scalar_function"_a,
           "dim_range"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const GridFunctionInterface<E, 1, 1, R>& scalar_grid_function,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&) {
-            return type(scalar_grid_function);
-          },
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&,
+             const std::string& logging_prefix) { return new type(scalar_grid_function, logging_prefix); },
           "grid"_a,
           "scalar_grid_function"_a,
           "dim_range"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
     } // ... factory (...)
   }; // struct dim_dependent<1, 1, anything> the scalar case
 
@@ -270,9 +323,16 @@ private:
       namespace py = pybind11;
       using namespace pybind11::literals;
 
-      c.def(py::init<const typename RangeTypeSelector<R, r, rC>::type&>(), "constant_vector"_a);
-      c.def(py::init<const FunctionInterface<d, r, rC, R>&>(), "vector_function"_a, py::keep_alive<1, 2>());
-      c.def(py::init<const GridFunctionInterface<E, r, rC, R>&>(), "vector_grid_function"_a, py::keep_alive<1, 2>());
+      c.def(py::init<const typename RangeTypeSelector<R, r, rC>::type&, const std::string&, const std::string&>(),
+            "constant_vector"_a,
+            "name"_a = "GridFunction",
+            "logging_prefix"_a = "");
+      c.def(py::init<const FunctionInterface<d, r, rC, R>&, const std::string&>(),
+            "vector_function"_a,
+            "logging_prefix"_a = "");
+      c.def(py::init<const GridFunctionInterface<E, r, rC, R>&, const std::string&>(),
+            "vector_grid_function"_a,
+            "logging_prefix"_a = "");
     } // ... init(...)
 
     static void factory(pybind11::module& m, const std::string& FactoryName)
@@ -283,90 +343,103 @@ private:
       // without dimRange
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const typename RangeTypeSelector<R, r, rC>::type& constant_vector) {
-            return type(constant_vector);
-          },
+          [](const GP&,
+             const typename RangeTypeSelector<R, r, rC>::type& constant_vector,
+             const std::string& name,
+             const std::string& logging_prefix) { return new type(constant_vector, name, logging_prefix); },
           "grid"_a,
-          "constant_vector"_a);
+          "constant_vector"_a,
+          "name"_a = "GridFunction",
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const FunctionInterface<d, r, rC, R>& vector_function) { return type(vector_function); },
+          [](const GP&, const FunctionInterface<d, r, rC, R>& vector_function, const std::string& logging_prefix) {
+            return new type(vector_function, logging_prefix);
+          },
           "grid"_a,
           "vector_function"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const GridFunctionInterface<E, r, rC, R>& vector_grid_function) {
-            return type(vector_grid_function);
-          },
+          [](const GP&,
+             const GridFunctionInterface<E, r, rC, R>& vector_grid_function,
+             const std::string& logging_prefix) { return new type(vector_grid_function, logging_prefix); },
           "grid"_a,
           "vector_grid_function"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GridFunctionInterface<E, r, rC, R>& vector_grid_function) { return type(vector_grid_function); },
+          [](const GridFunctionInterface<E, r, rC, R>& vector_grid_function, const std::string& logging_prefix) {
+            return new type(vector_grid_function, logging_prefix);
+          },
           "vector_grid_function"_a,
-          py::keep_alive<0, 1>());
+          "logging_prefix"_a = "");
       // with dimRange
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const typename RangeTypeSelector<R, r, rC>::type& constant_vector,
-             const Grid::bindings::Dimension<r>&) { return type(constant_vector); },
+             const Grid::bindings::Dimension<r>&,
+             const std::string& name,
+             const std::string& logging_prefix) { return new type(constant_vector, name, logging_prefix); },
           "grid"_a,
           "constant_vector"_a,
-          "dim_range"_a);
+          "dim_range"_a,
+          "name"_a = "GridFunction",
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const FunctionInterface<d, r, rC, R>& vector_function, const Grid::bindings::Dimension<r>&) {
-            return type(vector_function);
-          },
+          [](const GP&,
+             const FunctionInterface<d, r, rC, R>& vector_function,
+             const Grid::bindings::Dimension<r>&,
+             const std::string& logging_prefix) { return new type(vector_function, logging_prefix); },
           "grid"_a,
           "vector_function"_a,
           "dim_range"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const GridFunctionInterface<E, r, rC, R>& vector_grid_function,
-             const Grid::bindings::Dimension<r>&) { return type(vector_grid_function); },
+             const Grid::bindings::Dimension<r>&,
+             const std::string& logging_prefix) { return new type(vector_grid_function, logging_prefix); },
           "grid"_a,
           "vector_grid_function"_a,
           "dim_range"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       // with dimRange x 1
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const typename RangeTypeSelector<R, r, rC>::type& constant_vector,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<1>>&) {
-            return type(constant_vector);
-          },
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<1>>&,
+             const std::string& name,
+             const std::string& logging_prefix) { return new type(constant_vector, name, logging_prefix); },
           "grid"_a,
           "constant_vector"_a,
-          "dim_range"_a);
+          "dim_range"_a,
+          "name"_a = "GridFunction",
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const FunctionInterface<d, r, rC, R>& vector_function,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<1>>&) {
-            return type(vector_function);
-          },
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<1>>&,
+             const std::string& logging_prefix) { return new type(vector_function, logging_prefix); },
           "grid"_a,
           "vector_function"_a,
           "dim_range"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const GridFunctionInterface<E, r, rC, R>& vector_grid_function,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<1>>&) {
-            return type(vector_grid_function);
-          },
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<1>>&,
+             const std::string& logging_prefix) { return new type(vector_grid_function, logging_prefix); },
           "grid"_a,
           "vector_grid_function"_a,
           "dim_range"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
     } // ... factory (...)
   }; // struct dim_dependent<r, 1, anything>, the non scalar vector case
 
@@ -382,11 +455,16 @@ private:
       dim_dependent<1, 1>::init(c);
 
       // .. and the matrix ones
-      c.def(py::init<const FieldMatrix<R, r, rC>&>(), "constant_square_matrix"_a);
-      c.def(py::init<const FunctionInterface<d, r, rC, R>&>(), "square_matrix_function"_a, py::keep_alive<1, 2>());
-      c.def(py::init<const GridFunctionInterface<E, r, rC, R>&>(),
+      c.def(py::init<const FieldMatrix<R, r, rC>&, const std::string&, const std::string&>(),
+            "constant_square_matrix"_a,
+            "name"_a = "GridFunction",
+            "logging_prefix"_a = "");
+      c.def(py::init<const FunctionInterface<d, r, rC, R>&, const std::string&>(),
+            "square_matrix_function"_a,
+            "logging_prefix"_a = "");
+      c.def(py::init<const GridFunctionInterface<E, r, rC, R>&, const std::string&>(),
             "square_matrix_grid_function"_a,
-            py::keep_alive<1, 2>());
+            "logging_prefix"_a = "");
     } // ... init(...)
 
     static void factory(pybind11::module& m, const std::string& FactoryName)
@@ -400,71 +478,76 @@ private:
       // .. and the matrix ones, without dimRange
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const FieldMatrix<R, r, rC>& constant_square_matrix) { return type(constant_square_matrix); },
+          [](const GP&,
+             const FieldMatrix<R, r, rC>& constant_square_matrix,
+             const std::string& name,
+             const std::string& logging_prefix) { return new type(constant_square_matrix, name, logging_prefix); },
           "grid"_a,
-          "constant_square_matrix"_a);
+          "constant_square_matrix"_a,
+          "name"_a = "GridFunction",
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const FunctionInterface<d, r, rC, R>& square_matrix_function) {
-            return type(square_matrix_function);
-          },
+          [](const GP&,
+             const FunctionInterface<d, r, rC, R>& square_matrix_function,
+             const std::string& logging_prefix) { return new type(square_matrix_function, logging_prefix); },
           "grid"_a,
           "square_matrix_function"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GP&, const GridFunctionInterface<E, r, rC, R>& square_matrix_grid_function) {
-            return type(square_matrix_grid_function);
-          },
+          [](const GP&,
+             const GridFunctionInterface<E, r, rC, R>& square_matrix_grid_function,
+             const std::string& logging_prefix) { return new type(square_matrix_grid_function, logging_prefix); },
           "grid"_a,
           "square_matrix_grid_function"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
-          [](const GridFunctionInterface<E, r, rC, R>& square_matrix_grid_function) {
-            return type(square_matrix_grid_function);
+          [](const GridFunctionInterface<E, r, rC, R>& square_matrix_grid_function, const std::string& logging_prefix) {
+            return new type(square_matrix_grid_function, logging_prefix);
           },
           "square_matrix_grid_function"_a,
-          py::keep_alive<0, 1>());
+          "logging_prefix"_a = "");
       // .. and with dimRange
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const FieldMatrix<R, r, rC>& constant_square_matrix,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&) {
-            return type(constant_square_matrix);
-          },
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&,
+             const std::string& name,
+             const std::string& logging_prefix) { return new type(constant_square_matrix, name, logging_prefix); },
           "grid"_a,
           "constant_square_matrix"_a,
-          "dim_range"_a);
+          "dim_range"_a,
+          "name"_a = "GridFunction",
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const FunctionInterface<d, r, rC, R>& square_matrix_function,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&) {
-            return type(square_matrix_function);
-          },
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&,
+             const std::string& logging_prefix) { return new type(square_matrix_function, logging_prefix); },
           "grid"_a,
           "square_matrix_function"_a,
           "dim_range"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              const GridFunctionInterface<E, r, rC, R>& square_matrix_grid_function,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&) {
-            return type(square_matrix_grid_function);
-          },
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&,
+             const std::string& logging_prefix) { return new type(square_matrix_grid_function, logging_prefix); },
           "grid"_a,
           "square_matrix_grid_function"_a,
           "dim_range"_a,
-          py::keep_alive<0, 2>());
+          "logging_prefix"_a = "");
     } // ... factory (...)
   }; // struct dim_dependent<r_, r_, anything> the square matrix (but not scalar) case
 
 public:
   static bound_type bind(pybind11::module& m,
-                         const std::string& grid_id = Grid::bindings::grid_name<G>::value(),
+                         const std::string& grid_id,
                          const std::string& layer_id = "",
                          const std::string& class_id = "grid_function")
   {
@@ -483,76 +566,87 @@ public:
       class_name += "_" + Common::Typename<R>::value(/*fail_wo_typeid=*/true);
     const auto ClassName = Common::to_camel_case(class_name);
     bound_type c(m, ClassName.c_str(), Common::to_camel_case(class_id).c_str());
-    c.def(py::init([](int order, typename GFT::GenericEvaluateFunctionType evaluate) {
-            return new type({order, evaluate});
+    c.def(py::init([](int order, typename GFT::GenericEvaluateFunctionType evaluate, const std::string& name) {
+            return new type({order, evaluate, name});
           }),
           "order"_a,
-          "evaluate_lambda"_a);
+          "evaluate_lambda"_a,
+          "name"_a = "GridFunction");
     c.def(py::init([](int order,
                       typename GFT::GenericEvaluateFunctionType evaluate,
-                      typename GFT::GenericJacobianFunctionType jacobian) {
-            return new type({order, evaluate, jacobian});
+                      typename GFT::GenericJacobianFunctionType jacobian,
+                      const std::string& name) {
+            return new type({order, evaluate, jacobian, name});
           }),
           "order"_a,
           "evaluate_lambda"_a,
-          "jacobian_lambda"_a);
+          "jacobian_lambda"_a,
+          "name"_a = "GridFunction");
     dim_dependent<>::init(c);
 
     const auto FactoryName = Common::to_camel_case(class_id);
-    if (rC == 1) { // two variants opposed to four above
+    if (rC == 1) { // two variants here opposed to four above
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              int order,
              typename GFT::GenericEvaluateFunctionType evaluate,
-             const Grid::bindings::Dimension<r>&) {
-            return new type({order, evaluate});
+             const Grid::bindings::Dimension<r>&,
+             const std::string& name) {
+            return new type({order, evaluate, name});
           },
           "grid"_a,
           "order"_a,
           "evaluate_lambda"_a,
-          "dim_range"_a);
+          "dim_range"_a,
+          "name"_a = "GridFunction");
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              int order,
              typename GFT::GenericEvaluateFunctionType evaluate,
              typename GFT::GenericJacobianFunctionType jacobian,
-             const Grid::bindings::Dimension<r>&) {
-            return new type({order, evaluate, jacobian});
+             const Grid::bindings::Dimension<r>&,
+             const std::string& name) {
+            return new type({order, evaluate, jacobian, name});
           },
           "grid"_a,
           "order"_a,
           "evaluate_lambda"_a,
           "jacobian_lambda"_a,
-          "dim_range"_a);
+          "dim_range"_a,
+          "name"_a = "GridFunction");
     } else {
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              int order,
              typename GFT::GenericEvaluateFunctionType evaluate,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&) {
-            return new type({order, evaluate});
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&,
+             const std::string& name) {
+            return new type({order, evaluate, name});
           },
           "grid"_a,
           "order"_a,
           "evaluate_lambda"_a,
-          "dim_range"_a);
+          "dim_range"_a,
+          "name"_a = "GridFunction");
       m.def(
           FactoryName.c_str(),
           [](const GP&,
              int order,
              typename GFT::GenericEvaluateFunctionType evaluate,
              typename GFT::GenericJacobianFunctionType jacobian,
-             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&) {
-            return new type({order, evaluate, jacobian});
+             const std::pair<Grid::bindings::Dimension<r>, Grid::bindings::Dimension<rC>>&,
+             const std::string& name) {
+            return new type({order, evaluate, jacobian, name});
           },
           "grid"_a,
           "order"_a,
           "evaluate_lambda"_a,
           "jacobian_lambda"_a,
-          "dim_range"_a);
+          "dim_range"_a,
+          "name"_a = "GridFunction");
     }
     dim_dependent<>::factory(m, FactoryName);
     return c;
@@ -579,15 +673,15 @@ struct GridFunction_for_all_grids
     using Dune::XT::Functions::bindings::GridFunction;
     using Dune::XT::Grid::bindings::grid_name;
 
-    GridFunction<G, E, 1, 1>::bind(m);
-    GridFunction<G, E, 1, 2>::bind(m);
-    GridFunction<G, E, 1, 3>::bind(m);
-    GridFunction<G, E, 2, 1>::bind(m);
-    GridFunction<G, E, 2, 2>::bind(m);
-    GridFunction<G, E, 2, 3>::bind(m);
-    GridFunction<G, E, 3, 1>::bind(m);
-    GridFunction<G, E, 3, 2>::bind(m);
-    GridFunction<G, E, 3, 3>::bind(m);
+    GridFunction<G, E, 1, 1>::bind(m, grid_name<G>::value());
+    GridFunction<G, E, 1, 2>::bind(m, grid_name<G>::value());
+    GridFunction<G, E, 1, 3>::bind(m, grid_name<G>::value());
+    GridFunction<G, E, 2, 1>::bind(m, grid_name<G>::value());
+    GridFunction<G, E, 2, 2>::bind(m, grid_name<G>::value());
+    GridFunction<G, E, 2, 3>::bind(m, grid_name<G>::value());
+    GridFunction<G, E, 3, 1>::bind(m, grid_name<G>::value());
+    GridFunction<G, E, 3, 2>::bind(m, grid_name<G>::value());
+    GridFunction<G, E, 3, 3>::bind(m, grid_name<G>::value());
 
     GridFunction_for_all_grids<typename GridTypes::tail_type>::bind(m);
   }
@@ -607,9 +701,9 @@ PYBIND11_MODULE(_functions_gridfunction, m)
   py::module::import("dune.xt.common");
   py::module::import("dune.xt.grid");
   py::module::import("dune.xt.la");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_1d");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_2d");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_3d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_1d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_2d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_3d");
 
   GridFunction_for_all_grids<>::bind(m);
 } // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/indicator.cc b/python/dune/xt/functions/indicator.cc
index f866bfe89611430854c5ff8f1bbf270b9eda31cb..8c81752cbcef9fa71c0d0ad63289c7abfdf4d17e 100644
--- a/python/dune/xt/functions/indicator.cc
+++ b/python/dune/xt/functions/indicator.cc
@@ -57,9 +57,9 @@ PYBIND11_MODULE(_functions_indicator, m)
   py::module::import("dune.xt.common");
   py::module::import("dune.xt.la");
   py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_1d");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_2d");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_3d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_1d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_2d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_3d");
 
   all_grids(m);
 
diff --git a/python/dune/xt/functions/interfaces/grid-function.hh b/python/dune/xt/functions/interfaces/grid-function.hh
new file mode 100644
index 0000000000000000000000000000000000000000..e8e7d752997e74216d76d68f984cecdb06464a68
--- /dev/null
+++ b/python/dune/xt/functions/interfaces/grid-function.hh
@@ -0,0 +1,202 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2018 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License
+// (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+#ifndef PYTHON_DUNE_XT_FUNCTIONS_INTERFACES_GRID_FUNCTION_HH
+#define PYTHON_DUNE_XT_FUNCTIONS_INTERFACES_GRID_FUNCTION_HH
+
+#include <dune/pybindxi/pybind11.h>
+
+#include <dune/xt/common/string.hh>
+#include <dune/xt/functions/interfaces/grid-function.hh>
+#include <dune/xt/grid/gridprovider/provider.hh>
+
+#include <python/dune/xt/common/parameter.hh>
+
+namespace Dune {
+namespace XT {
+namespace Functions {
+namespace bindings {
+
+
+template <class G, class E, size_t r = 1, size_t rC = 1, class R = double>
+class GridFunctionInterface
+{
+  using GP = XT::Grid::GridProvider<G>;
+  static const constexpr size_t d = G::dimension;
+
+  template <bool vector = (r != 1 && rC == 1), bool matrix = (rC != 1), bool anything = false>
+  struct product_helper // <true, false, ...>
+  {
+    template <class T, typename... options>
+    static void addbind(pybind11::class_<T, options...>& c)
+    {
+      namespace py = pybind11;
+
+      c.def(
+          "__mul__",
+          [](const T& self, const Functions::GridFunctionInterface<E, r, 1, R>& other) {
+            return std::make_unique<decltype(self * other)>(self * other);
+          },
+          py::is_operator());
+    }
+  };
+
+  template <bool anything>
+  struct product_helper<false, true, anything>
+  {
+    template <class T, typename... options>
+    static void addbind(pybind11::class_<T, options...>& c)
+    {
+      namespace py = pybind11;
+
+      c.def(
+          "__mul__",
+          [](const T& self, const Functions::GridFunctionInterface<E, rC, 1, R>& other) {
+            return std::make_unique<decltype(self * other)>(self * other);
+          },
+          py::is_operator());
+      c.def(
+          "__mul__",
+          [](const T& self, const Functions::GridFunctionInterface<E, rC, 2, R>& other) {
+            return std::make_unique<decltype(self * other)>(self * other);
+          },
+          py::is_operator());
+      c.def(
+          "__mul__",
+          [](const T& self, const Functions::GridFunctionInterface<E, rC, 3, R>& other) {
+            return std::make_unique<decltype(self * other)>(self * other);
+          },
+          py::is_operator());
+    }
+  };
+
+  template <bool scalar = (r == 1 && rC == 1), bool anything = false>
+  struct fraction_helper // <true, ...>
+  {
+    template <class T, typename... options>
+    static void addbind(pybind11::class_<T, options...>& c)
+    {
+      namespace py = pybind11;
+
+      c.def(
+          "__truediv__",
+          [](const T& self, const type& other) { return std::make_unique<decltype(other / self)>(other / self); },
+          py::is_operator());
+    }
+  };
+
+  template <bool anything>
+  struct fraction_helper<false, anything>
+  {
+    template <class T, typename... options>
+    static void addbind(pybind11::class_<T, options...>& /*c*/)
+    {}
+  };
+
+public:
+  using type = Functions::GridFunctionInterface<E, r, rC, R>;
+  using bound_type = pybind11::class_<type>;
+
+  static std::string class_name(const std::string& grid_id, const std::string& layer_id, const std::string& class_id)
+  {
+    std::string ret = class_id;
+    ret += "_" + grid_id;
+    if (!layer_id.empty())
+      ret += "_" + layer_id;
+    ret += "_to_" + Common::to_string(r);
+    if (rC > 1)
+      ret += "x" + Common::to_string(rC);
+    ret += "d";
+    if (!std::is_same<R, double>::value)
+      ret += "_" + Common::Typename<R>::value(/*fail_wo_typeid=*/true);
+    return ret;
+  } // ... class_name(...)
+
+  template <class T, typename... options>
+  static void addbind_methods(pybind11::class_<T, options...>& c)
+  {
+    namespace py = pybind11;
+    using namespace pybind11::literals;
+
+    // our methods
+    c.def(
+        "visualize",
+        [](const T& self, const GP& grid_provider, const std::string& filename, const bool subsampling) {
+          self.visualize(grid_provider.leaf_view(), filename, subsampling);
+        },
+        "grid"_a,
+        "filename"_a,
+        "subsampling"_a = true);
+    // our operators
+    c.def(
+        "__add__",
+        [](const T& self, const type& other) { return std::make_unique<decltype(self + other)>(self + other); },
+        py::is_operator());
+    c.def(
+        "__sub__",
+        [](const T& self, const type& other) { return std::make_unique<decltype(self - other)>(self - other); },
+        py::is_operator());
+    // we can always multiply with a scalar from the right ...
+    c.def(
+        "__mul__",
+        [](const T& self, const Functions::GridFunctionInterface<E, 1, 1, R>& other) {
+          return std::make_unique<decltype(self * other)>(self * other);
+        },
+        py::is_operator());
+    // .. and with lots of other dims
+    product_helper<>::addbind(c);
+    fraction_helper<>::addbind(c);
+
+    if constexpr (r == 1 && rC == 1)
+      c.def(
+          "__pow__",
+          [](const T& self) { return std::make_unique<decltype(self * self)>(self * self); },
+          py::is_operator());
+
+    // ParametricInterface methods
+    c.def(
+        "parse_parameter", [](const T& self, const Common::Parameter& mu) { return self.parse_parameter(mu); }, "mu"_a);
+  } // ... addbind_methods(...)
+
+  static bound_type bind(pybind11::module& m,
+                         const std::string& grid_id,
+                         const std::string& layer_id = "",
+                         const std::string& class_id = "grid_function_interface")
+  {
+    namespace py = pybind11;
+    using namespace pybind11::literals;
+
+    const auto ClassName = Common::to_camel_case(class_name(grid_id, layer_id, class_id));
+    bound_type c(m, ClassName.c_str(), Common::to_camel_case(class_id).c_str());
+
+    // our properties
+    c.def_property_readonly("dim_domain", [](type&) { return size_t(d); });
+    if (rC == 1)
+      c.def_property_readonly("dim_range", [](type&) { return size_t(r); });
+    else
+      c.def_property_readonly("dim_range", [](type&) { return std::make_pair(size_t(r), size_t(rC)); });
+    c.def_property_readonly("name", [](type& self) { return self.name(); });
+    // ParametricInterface properties
+    c.def_property_readonly("is_parametric", [](type& self) { return self.is_parametric(); });
+    c.def_property_readonly("parameter_type", [](type& self) { return self.parameter_type(); });
+
+    addbind_methods(c);
+
+    return c;
+  }
+}; // class GridFunctionInterface
+
+
+} // namespace bindings
+} // namespace Functions
+} // namespace XT
+} // namespace Dune
+
+#endif // PYTHON_DUNE_XT_FUNCTIONS_INTERFACES_GRID_FUNCTION_HH
diff --git a/python/dune/xt/functions/interfaces/grid-function_1d.cc b/python/dune/xt/functions/interfaces/grid-function_1d.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a1048fb91ea661b3bf96a201a4ab205d7a0f6596
--- /dev/null
+++ b/python/dune/xt/functions/interfaces/grid-function_1d.cc
@@ -0,0 +1,29 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2018 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+#include "config.h"
+
+#include <dune/xt/grid/grids.hh>
+
+#include "grid-function_for_all_grids.hh"
+
+
+PYBIND11_MODULE(_functions_interfaces_grid_function_1d, m)
+{
+  namespace py = pybind11;
+
+  py::module::import("dune.xt.common");
+  py::module::import("dune.xt.grid");
+  py::module::import("dune.xt.la");
+
+  // All of these need to be there ...
+  GridFunctionInterface_for_all_grids<boost::tuple<ONED_1D>>::bind_interface(m);
+  // ... before we start binding those.
+  GridFunctionInterface_for_all_grids<boost::tuple<ONED_1D>>::bind_combined(m);
+} // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/interfaces/grid-function_2d.cc b/python/dune/xt/functions/interfaces/grid-function_2d.cc
new file mode 100644
index 0000000000000000000000000000000000000000..400780e7750aa43e0bc0b2b2b9929082cdcbf53f
--- /dev/null
+++ b/python/dune/xt/functions/interfaces/grid-function_2d.cc
@@ -0,0 +1,39 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2018 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+#include "config.h"
+
+#include <dune/xt/grid/grids.hh>
+
+#include "grid-function_for_all_grids.hh"
+
+
+PYBIND11_MODULE(_functions_interfaces_grid_function_2d, m)
+{
+  namespace py = pybind11;
+
+  py::module::import("dune.xt.common");
+  py::module::import("dune.xt.grid");
+  py::module::import("dune.xt.la");
+
+  // All of these need to be there ...
+  GridFunctionInterface_for_all_grids<boost::tuple<YASP_2D_EQUIDISTANT_OFFSET
+#if HAVE_DUNE_ALUGRID
+                                                   ,
+                                                   ALU_2D_SIMPLEX_CONFORMING
+#endif
+                                                   >>::bind_interface(m);
+  // ... before we start binding those.
+  GridFunctionInterface_for_all_grids<boost::tuple<YASP_2D_EQUIDISTANT_OFFSET
+#if HAVE_DUNE_ALUGRID
+                                                   ,
+                                                   ALU_2D_SIMPLEX_CONFORMING
+#endif
+                                                   >>::bind_combined(m);
+} // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/interfaces/grid-function_3d.cc b/python/dune/xt/functions/interfaces/grid-function_3d.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8660b8d549b82c218df58822598180164dca7917
--- /dev/null
+++ b/python/dune/xt/functions/interfaces/grid-function_3d.cc
@@ -0,0 +1,39 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2018 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+#include "config.h"
+
+#include <dune/xt/grid/grids.hh>
+
+#include "grid-function_for_all_grids.hh"
+
+
+PYBIND11_MODULE(_functions_interfaces_grid_function_3d, m)
+{
+  namespace py = pybind11;
+
+  py::module::import("dune.xt.common");
+  py::module::import("dune.xt.grid");
+  py::module::import("dune.xt.la");
+
+  // All of these need to be there ...
+  GridFunctionInterface_for_all_grids<boost::tuple<YASP_3D_EQUIDISTANT_OFFSET
+#if HAVE_DUNE_ALUGRID
+                                                   ,
+                                                   ALU_3D_SIMPLEX_CONFORMING
+#endif
+                                                   >>::bind_interface(m);
+  // ... before we start binding those.
+  GridFunctionInterface_for_all_grids<boost::tuple<YASP_3D_EQUIDISTANT_OFFSET
+#if HAVE_DUNE_ALUGRID
+                                                   ,
+                                                   ALU_3D_SIMPLEX_CONFORMING
+#endif
+                                                   >>::bind_combined(m);
+} // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/interfaces/grid-function_for_all_grids.hh b/python/dune/xt/functions/interfaces/grid-function_for_all_grids.hh
new file mode 100644
index 0000000000000000000000000000000000000000..9d35efff5d83a6fb1161c3f9617d493f54356421
--- /dev/null
+++ b/python/dune/xt/functions/interfaces/grid-function_for_all_grids.hh
@@ -0,0 +1,173 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2018 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+#ifndef PYTHON_DUNE_XT_FUNCTIONS_INTERFACES_GRID_FUNCTION_FOR_ALL_GRIDS_HH
+#define PYTHON_DUNE_XT_FUNCTIONS_INTERFACES_GRID_FUNCTION_FOR_ALL_GRIDS_HH
+
+#include <dune/xt/grid/type_traits.hh>
+
+#include <python/dune/xt/functions/base/combined-grid-function.hh>
+
+#include "grid-function.hh"
+
+
+template <size_t ii>
+struct Int
+{
+  static const constexpr size_t value = ii;
+};
+
+
+template <class GridTypes>
+struct GridFunctionInterface_for_all_grids
+{
+  using G = typename GridTypes::head_type;
+  using GV = typename G::LeafGridView;
+  using E = Dune::XT::Grid::extract_entity_t<GV>;
+  static const constexpr size_t d = G::dimension;
+
+  template <size_t r, class Dims = boost::tuple<Int<1>, Int<2>, Int<3>>>
+  struct for_all_rC
+  {
+    static const constexpr size_t rC = Dims::head_type::value;
+
+    template <bool vector = (r != 1 && rC == 1), bool matrix = (rC != 1), bool anything = false>
+    struct product_helper // <true, false, ...>
+    {
+      static void addbind(pybind11::module& m)
+      {
+        using Dune::XT::Functions::bindings::ProductGridFunction;
+        using Dune::XT::Grid::bindings::grid_name;
+
+        // special case: vector * vector
+        ProductGridFunction<G, E, r, 1, r, 1>::bind(m, grid_name<G>::value());
+      }
+    };
+
+    template <bool anything>
+    struct product_helper<false, true, anything>
+    {
+      static void addbind(pybind11::module& m)
+      {
+        using Dune::XT::Functions::bindings::ProductGridFunction;
+        using Dune::XT::Grid::bindings::grid_name;
+
+        // general case: matrix * matrix or vector
+        ProductGridFunction<G, E, r, rC, rC, 1>::bind(m, grid_name<G>::value());
+        ProductGridFunction<G, E, r, rC, rC, 2>::bind(m, grid_name<G>::value());
+        ProductGridFunction<G, E, r, rC, rC, 3>::bind(m, grid_name<G>::value());
+      }
+    };
+
+    template <bool scalar = (r == 1 && rC == 1), bool anything = true>
+    struct fraction_helper
+    {
+      static void addbind(pybind11::module& m)
+      {
+        using Dune::XT::Functions::bindings::FractionGridFunction;
+        using Dune::XT::Grid::bindings::grid_name;
+
+        FractionGridFunction<G, E>::bind(m, grid_name<G>::value());
+      }
+    };
+
+    template <bool a>
+    struct fraction_helper<false, a>
+    {
+      static void addbind(pybind11::module& /*m*/) {}
+    };
+
+    static void bind_interface(pybind11::module& m)
+    {
+      using Dune::XT::Functions::bindings::GridFunctionInterface;
+      using Dune::XT::Grid::bindings::grid_name;
+
+      GridFunctionInterface<G, E, r, rC>::bind(m, grid_name<G>::value());
+
+      for_all_rC<r, typename Dims::tail_type>::bind_interface(m);
+    }
+
+    static void bind_combined(pybind11::module& m)
+    {
+      using Dune::XT::Functions::bindings::DifferenceGridFunction;
+      using Dune::XT::Functions::bindings::ProductGridFunction;
+      using Dune::XT::Functions::bindings::SumGridFunction;
+      using Dune::XT::Grid::bindings::grid_name;
+
+      DifferenceGridFunction<G, E, r, rC>::bind(m, grid_name<G>::value());
+      SumGridFunction<G, E, r, rC>::bind(m, grid_name<G>::value());
+      product_helper<>::addbind(m);
+      fraction_helper<>::addbind(m);
+
+      for_all_rC<r, typename Dims::tail_type>::bind_combined(m);
+    }
+  };
+
+  template <size_t r>
+  struct for_all_rC<r, boost::tuples::null_type>
+  {
+    static void bind_interface(pybind11::module& /*m*/) {}
+
+    static void bind_combined(pybind11::module& /*m*/) {}
+  };
+
+
+  template <class Dims = boost::tuple<Int<1>, Int<2>, Int<3>>, bool anything = false>
+  struct for_all_r_and_rC
+  {
+    static const constexpr size_t r = Dims::head_type::value;
+
+    static void bind_interface(pybind11::module& m)
+    {
+      for_all_rC<r>::bind_interface(m);
+
+      for_all_r_and_rC<typename Dims::tail_type>::bind_interface(m);
+    }
+
+    static void bind_combined(pybind11::module& m)
+    {
+      for_all_rC<r>::bind_combined(m);
+
+      for_all_r_and_rC<typename Dims::tail_type>::bind_combined(m);
+    }
+  };
+
+  template <bool a>
+  struct for_all_r_and_rC<boost::tuples::null_type, a>
+  {
+    static void bind_interface(pybind11::module& /*m*/) {}
+
+    static void bind_combined(pybind11::module& /*m*/) {}
+  };
+
+  static void bind_interface(pybind11::module& m)
+  {
+    for_all_r_and_rC<>::bind_interface(m);
+
+    GridFunctionInterface_for_all_grids<typename GridTypes::tail_type>::bind_interface(m);
+  }
+
+  static void bind_combined(pybind11::module& m)
+  {
+    for_all_r_and_rC<>::bind_combined(m);
+
+    GridFunctionInterface_for_all_grids<typename GridTypes::tail_type>::bind_combined(m);
+  }
+};
+
+template <>
+struct GridFunctionInterface_for_all_grids<boost::tuples::null_type>
+{
+  static void bind_interface(pybind11::module& /*m*/) {}
+
+  static void bind_combined(pybind11::module& /*m*/) {}
+};
+
+
+#endif // PYTHON_DUNE_XT_FUNCTIONS_INTERFACES_GRID_FUNCTION_FOR_ALL_GRIDS_HH
diff --git a/python/dune/xt/functions/inverse.cc b/python/dune/xt/functions/inverse.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8ae670831efd5e21a9a3ff0c7b18416f58902380
--- /dev/null
+++ b/python/dune/xt/functions/inverse.cc
@@ -0,0 +1,140 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2018 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2019)
+
+#include "config.h"
+
+#include <string>
+#include <vector>
+
+#include <dune/common/parallel/mpihelper.hh>
+
+#include <dune/pybindxi/pybind11.h>
+#include <dune/pybindxi/functional.h>
+#include <dune/pybindxi/stl.h>
+
+#include <dune/xt/common/string.hh>
+#include <dune/xt/grid/gridprovider/provider.hh>
+#include <dune/xt/grid/type_traits.hh>
+#include <dune/xt/functions/grid-function.hh>
+#include <dune/xt/functions/inverse.hh>
+
+#include <python/dune/xt/common/parameter.hh>
+#include <python/dune/xt/common/fvector.hh>
+#include <python/dune/xt/common/fmatrix.hh>
+#include <python/dune/xt/common/bindings.hh>
+#include <python/dune/xt/grid/traits.hh>
+#include <python/dune/xt/common/exceptions.bindings.hh>
+
+namespace Dune {
+namespace XT {
+namespace Functions {
+namespace bindings {
+
+
+template <class G, class E, size_t r = 1, size_t rC = 1>
+class InverseGridFunction
+{
+  using GP = XT::Grid::GridProvider<G>;
+  static const size_t d = G::dimension;
+  using GF = Functions::GridFunction<E, r, rC>;
+
+public:
+  using type = Functions::InverseGridFunction<GF>;
+  using base_type = typename type::BaseType;
+  using bound_type = pybind11::class_<type, base_type>;
+
+public:
+  static bound_type bind(pybind11::module& m,
+                         const std::string& grid_id = Grid::bindings::grid_name<G>::value(),
+                         const std::string& layer_id = "",
+                         const std::string& class_id = "inverse_grid_function")
+  {
+    namespace py = pybind11;
+    using namespace pybind11::literals;
+
+    std::string class_name = class_id;
+    class_name += "_" + grid_id;
+    if (!layer_id.empty())
+      class_name += "_" + layer_id;
+    class_name += "_to_" + Common::to_string(r);
+    if (rC > 1)
+      class_name += "x" + Common::to_string(rC);
+    class_name += "d";
+    const auto ClassName = Common::to_camel_case(class_name);
+    bound_type c(m, ClassName.c_str(), Common::to_camel_case(class_id).c_str());
+    c.def(
+        py::init<GF, const int, const std::string&>(), "grid_function"_a, "order"_a, "name"_a = "InverseGridFunction");
+
+    m.def(
+        "inverse",
+        [](const GridFunctionInterface<E, r, rC>& grid_function, const int order, const std::string& name) {
+          return new type(grid_function, order, name);
+        },
+        "grid_function"_a,
+        "order"_a,
+        "name"_a = "InverseGridFunction");
+    m.def(
+        "inverse",
+        [](GF grid_function, const int order, const std::string& name) { return new type(grid_function, order, name); },
+        "grid_function"_a,
+        "order"_a,
+        "name"_a = "InverseGridFunction");
+
+    return c;
+  }
+}; // class GridFunction
+
+
+} // namespace bindings
+} // namespace Functions
+} // namespace XT
+} // namespace Dune
+
+
+template <class GridTypes = Dune::XT::Grid::AvailableGridTypes>
+struct InverseGridFunction_for_all_grids
+{
+  using G = typename GridTypes::head_type;
+  using GV = typename G::LeafGridView;
+  using E = Dune::XT::Grid::extract_entity_t<GV>;
+
+  static void bind(pybind11::module& m)
+  {
+    using Dune::XT::Functions::bindings::InverseGridFunction;
+    using Dune::XT::Grid::bindings::grid_name;
+
+    InverseGridFunction<G, E, 1, 1>::bind(m, grid_name<G>::value());
+    InverseGridFunction<G, E, 2, 2>::bind(m, grid_name<G>::value());
+    InverseGridFunction<G, E, 3, 3>::bind(m, grid_name<G>::value());
+
+    InverseGridFunction_for_all_grids<typename GridTypes::tail_type>::bind(m);
+  }
+};
+
+template <>
+struct InverseGridFunction_for_all_grids<boost::tuples::null_type>
+{
+  static void bind(pybind11::module& /*m*/) {}
+};
+
+
+PYBIND11_MODULE(_functions_inverse, m)
+{
+  namespace py = pybind11;
+
+  py::module::import("dune.xt.common");
+  py::module::import("dune.xt.grid");
+  py::module::import("dune.xt.la");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_1d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_2d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_3d");
+  py::module::import("dune.xt.functions._functions_gridfunction");
+
+  InverseGridFunction_for_all_grids<>::bind(m);
+} // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/parametric-expression.cc b/python/dune/xt/functions/parametric-expression.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2622420aa54fac7c63d64ab9bdd535c7ee6b251b
--- /dev/null
+++ b/python/dune/xt/functions/parametric-expression.cc
@@ -0,0 +1,118 @@
+// This file is part of the dune-xt project:
+//   https://github.com/dune-community/dune-xt
+// Copyright 2009-2020 dune-xt developers and contributors. All rights reserved.
+// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
+//          with "runtime exception" (http://www.dune-project.org/license.html)
+// Authors:
+//   Felix Schindler (2020)
+
+#include "config.h"
+
+#include <string>
+#include <vector>
+
+#include <dune/common/parallel/mpihelper.hh>
+
+#include <dune/pybindxi/pybind11.h>
+#include <dune/pybindxi/stl.h>
+
+#include <dune/xt/common/string.hh>
+#include <dune/xt/grid/gridprovider/provider.hh>
+#include <dune/xt/grid/type_traits.hh>
+#include <dune/xt/functions/expression/parametric.hh>
+
+#include <python/dune/xt/common/fvector.hh>
+#include <python/dune/xt/common/fmatrix.hh>
+#include <python/dune/xt/common/bindings.hh>
+#include <python/dune/xt/common/parameter.hh>
+#include <python/dune/xt/grid/traits.hh>
+#include <python/dune/xt/common/exceptions.bindings.hh>
+
+namespace Dune {
+namespace XT {
+namespace Functions {
+namespace bindings {
+
+
+template <size_t d, size_t r = 1, class R = double>
+class ParametricExpressionFunction
+{
+  static const constexpr size_t rC = 1;
+
+  using type = Functions::ParametricExpressionFunction<d, r, rC, R>;
+  using base_type = Functions::FunctionInterface<d, r, rC, R>;
+  using bound_type = pybind11::class_<type, base_type>;
+
+public:
+  static bound_type bind(pybind11::module& m, const std::string& class_id = "parametric_expression_function")
+  {
+    namespace py = pybind11;
+    using namespace pybind11::literals;
+
+    std::string class_name = class_id + "_" + Common::to_string(d) + "d_to_" + Common::to_string(r);
+    if (rC > 1)
+      class_name += "x" + Common::to_string(rC);
+    class_name += "d";
+    const auto ClassName = Common::to_camel_case(class_name);
+    bound_type c(m, ClassName.c_str(), XT::Common::to_camel_case(class_id).c_str());
+    c.def(py::init<const std::string&,
+                   const Common::ParameterType&,
+                   const Common::FieldVector<std::string, r>&,
+                   const size_t,
+                   const std::string>(),
+          "variable"_a,
+          "param_type"_a,
+          "expressions"_a,
+          "order"_a,
+          "name"_a = type::static_id());
+
+    m.def(
+        Common::to_camel_case(class_id).c_str(),
+        [](Grid::bindings::Dimension<d> /*dim_domain*/,
+           const std::string& variable,
+           const Common::ParameterType& param_type,
+           const Common::FieldVector<std::string, r>& expressions,
+           const size_t order,
+           const std::string& name) { return new type(variable, param_type, expressions, order, name); },
+        "dim_domain"_a,
+        "variable"_a,
+        "param_type"_a,
+        "expressions"_a,
+        "order"_a,
+        "name"_a = type::static_id());
+
+    return c;
+  }
+}; // class ParametricExpressionFunction
+
+
+} // namespace bindings
+} // namespace Functions
+} // namespace XT
+} // namespace Dune
+
+
+PYBIND11_MODULE(_functions_parametric_expression, m)
+{
+  namespace py = pybind11;
+
+  py::module::import("dune.xt.common");
+  py::module::import("dune.xt.grid");
+  py::module::import("dune.xt.la");
+  py::module::import("dune.xt.functions._functions_function_interface_1d");
+  py::module::import("dune.xt.functions._functions_function_interface_2d");
+  py::module::import("dune.xt.functions._functions_function_interface_3d");
+
+  Dune::XT::Functions::bindings::ParametricExpressionFunction<1, 1>::bind(m);
+  Dune::XT::Functions::bindings::ParametricExpressionFunction<1, 2>::bind(m);
+  Dune::XT::Functions::bindings::ParametricExpressionFunction<1, 3>::bind(m);
+
+  Dune::XT::Functions::bindings::ParametricExpressionFunction<2, 1>::bind(m);
+  Dune::XT::Functions::bindings::ParametricExpressionFunction<2, 2>::bind(m);
+  Dune::XT::Functions::bindings::ParametricExpressionFunction<2, 3>::bind(m);
+
+  Dune::XT::Functions::bindings::ParametricExpressionFunction<3, 1>::bind(m);
+  Dune::XT::Functions::bindings::ParametricExpressionFunction<3, 2>::bind(m);
+  Dune::XT::Functions::bindings::ParametricExpressionFunction<3, 3>::bind(m);
+} // PYBIND11_MODULE(...)
diff --git a/python/dune/xt/functions/spe10.cc b/python/dune/xt/functions/spe10.cc
index e451c4881bb7368356a84267ee67a8ab51b27429..f83009d4ac5501ff126d1162b154df26f0406320 100644
--- a/python/dune/xt/functions/spe10.cc
+++ b/python/dune/xt/functions/spe10.cc
@@ -60,9 +60,9 @@ PYBIND11_MODULE(_functions_spe10, m)
   py::module::import("dune.xt.common");
   py::module::import("dune.xt.la");
   py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_1d");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_2d");
-  py::module::import("dune.xt.functions._functions_gridfunction_interface_3d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_1d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_2d");
+  py::module::import("dune.xt.functions._functions_interfaces_grid_function_3d");
 
   all_grids(m);
 }
diff --git a/python/dune/xt/functions/spe10.hh b/python/dune/xt/functions/spe10.hh
index 000ed9d96d59668516fabb479594edacf44a1ff4..ad5c2d0a3298e2b74529124b0247813e9023915d 100644
--- a/python/dune/xt/functions/spe10.hh
+++ b/python/dune/xt/functions/spe10.hh
@@ -104,6 +104,8 @@ auto bind_Spe10Model1Function_2D(pybind11::module& m, const std::string& grid_id
       "min"_a = Spe10::internal::model1_min_value,
       "max"_a = Spe10::internal::model1_max_value,
       "name"_a = C::static_id());
+
+  return c;
 } // ... bind_Spe10Model1Function(...)
 
 template <class G, size_t d, size_t r, size_t rC>
diff --git a/python/dune/xt/grid/CMakeLists.txt b/python/dune/xt/grid/CMakeLists.txt
index def3baa246d50e5ec5a731e3de3b708eabbbb1ce..f0f914f99f6ff482a3bb5de3b82f00218af01dc4 100644
--- a/python/dune/xt/grid/CMakeLists.txt
+++ b/python/dune/xt/grid/CMakeLists.txt
@@ -20,7 +20,6 @@ dune_pybindxi_add_module(_grid_boundaryinfo_normalbased EXCLUDE_FROM_ALL boundar
 dune_pybindxi_add_module(_grid_boundaryinfo_types EXCLUDE_FROM_ALL boundaryinfo/types.cc)
 dune_pybindxi_add_module(_grid_filters_base EXCLUDE_FROM_ALL filters/base.cc)
 dune_pybindxi_add_module(_grid_filters_element EXCLUDE_FROM_ALL filters/element.cc)
-dune_pybindxi_add_module(_grid_filters_intersection EXCLUDE_FROM_ALL filters/intersection.cc)
 dune_pybindxi_add_module(_grid_functors_boundary_detector EXCLUDE_FROM_ALL functors/boundary-detector.cc)
 # dune_pybindxi_add_module(_grid_functors_bounding_box EXCLUDE_FROM_ALL functors/bounding-box.cc)
 dune_pybindxi_add_module(_grid_functors_interfaces EXCLUDE_FROM_ALL functors/interfaces.cc)
diff --git a/python/dune/xt/grid/__init__.py b/python/dune/xt/grid/__init__.py
index 4cac944ead617b50f22455ee8cb7e04274ed29bf..fb8c1542fa91c161e4c0e6e0102582ced726f9ba 100644
--- a/python/dune/xt/grid/__init__.py
+++ b/python/dune/xt/grid/__init__.py
@@ -12,8 +12,10 @@
 # ~~~
 
 from numbers import Number
+from tempfile import NamedTemporaryFile
 
 from dune.xt import guarded_import
+from dune.xt.common.vtk.plot import plot
 
 for mod_name in (
         '_grid_boundaryinfo_alldirichlet',
@@ -24,7 +26,6 @@ for mod_name in (
         '_grid_boundaryinfo_types',
         '_grid_filters_base',
         '_grid_filters_element',
-        '_grid_filters_intersection',
         '_grid_functors_boundary_detector',
      # '_grid_functors_bounding_box',
         '_grid_functors_interfaces',
@@ -44,3 +45,10 @@ def Dim(d):
     if f'Dimension{d}' not in globals():
         raise RuntimeError(f'Dimension {d} not available, extend <python/dune/xt/grid/traits.cc>!')
     return globals()[f'Dimension{d}']()
+
+
+def visualize_grid(grid):
+    tmpfile = NamedTemporaryFile(mode='wb', delete=False, suffix='.vtu').name
+    grid.visualize(tmpfile[:-4])
+    return plot(
+        tmpfile, color_attribute_name='Element index')     # see visualize in python/dune/xt/grid/gridprovider.hh
diff --git a/python/dune/xt/grid/filters/element.hh b/python/dune/xt/grid/filters/element.hh
index c36c574d6eb7f8aecf8fcab689bf9c535c704ac7..a6e565aa87110c0edd82ef86bbcf8d6663380b4d 100644
--- a/python/dune/xt/grid/filters/element.hh
+++ b/python/dune/xt/grid/filters/element.hh
@@ -46,7 +46,7 @@ public:
     c.def(py::init([]() { return std::make_unique<type>(); }));
     c.def("__repr__", [ClassId](type&) { return ClassId + "()"; });
 
-    m.def(ClassId.c_str(), [](const Grid::GridProvider<G>&) { return type(); });
+    m.def(ClassId.c_str(), [](const Grid::GridProvider<G>&) { return new type(); });
 
     return c;
   } // ... bind(...)
diff --git a/python/dune/xt/grid/filters/intersection.cc b/python/dune/xt/grid/filters/intersection.cc
deleted file mode 100644
index bd1f8cc54d922a6c3a3389afeb5c842da57ef257..0000000000000000000000000000000000000000
--- a/python/dune/xt/grid/filters/intersection.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// This file is part of the dune-xt project:
-//   https://github.com/dune-community/dune-xt
-// Copyright 2009-2018 dune-xt developers and contributors. All rights reserved.
-// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
-//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
-//          with "runtime exception" (http://www.dune-project.org/license.html)
-// Authors:
-//   Felix Schindler (2020)
-
-#include "config.h"
-
-#include <dune/xt/grid/grids.hh>
-
-#include <python/dune/xt/grid/filters/intersection.hh>
-
-
-template <template <class> class Filter, class GridTypes = Dune::XT::Grid::AvailableGridTypes>
-struct InitlessIntersectionFilter_for_all_grids
-{
-  static void bind(pybind11::module& m, const std::string& class_id)
-  {
-    Dune::XT::Grid::bindings::InitlessIntersectionFilter<Filter, typename GridTypes::head_type>::bind(m, class_id);
-    InitlessIntersectionFilter_for_all_grids<Filter, typename GridTypes::tail_type>::bind(m, class_id);
-  }
-};
-
-template <template <class> class Filter>
-struct InitlessIntersectionFilter_for_all_grids<Filter, boost::tuples::null_type>
-{
-  static void bind(pybind11::module& /*m*/, const std::string& /*class_id*/) {}
-};
-
-
-template <class GridTypes = Dune::XT::Grid::AvailableGridTypes>
-struct CustomBoundaryIntersectionFilter_for_all_grids
-{
-  static void bind(pybind11::module& m)
-  {
-    Dune::XT::Grid::bindings::CustomBoundaryIntersectionsFilter<typename GridTypes::head_type>::bind(m);
-    CustomBoundaryIntersectionFilter_for_all_grids<typename GridTypes::tail_type>::bind(m);
-  }
-};
-
-template <>
-struct CustomBoundaryIntersectionFilter_for_all_grids<boost::tuples::null_type>
-{
-  static void bind(pybind11::module& /*m*/) {}
-};
-
-
-PYBIND11_MODULE(_grid_filters_intersection, m)
-{
-  namespace py = pybind11;
-  using namespace Dune::XT::Grid;
-
-  py::module::import("dune.xt.common.timedlogging");
-  py::module::import("dune.xt.grid._grid_boundaryinfo_interfaces");
-  py::module::import("dune.xt.grid._grid_boundaryinfo_types");
-  py::module::import("dune.xt.grid._grid_filters_base");
-  py::module::import("dune.xt.grid._grid_gridprovider_provider");
-
-#define BIND_(NAME) InitlessIntersectionFilter_for_all_grids<ApplyOn::NAME>::bind(m, std::string("ApplyOn") + #NAME)
-
-  BIND_(AllIntersections);
-  BIND_(AllIntersectionsOnce);
-  BIND_(NoIntersections);
-  BIND_(InnerIntersections);
-  BIND_(InnerIntersectionsOnce);
-  //  BIND_(PartitionSetInnerIntersectionsOnce); <- requires partition set as template argument
-  BIND_(BoundaryIntersections);
-  BIND_(NonPeriodicBoundaryIntersections);
-  BIND_(PeriodicBoundaryIntersections);
-  BIND_(PeriodicBoundaryIntersectionsOnce);
-  //  BIND_(GenericFilteredIntersections); <- requires lambda in init
-  //  BIND_(CustomBoundaryAndProcessIntersections); <- requires boundary type and info in init
-  BIND_(ProcessIntersections);
-
-#undef BIND_
-
-  CustomBoundaryIntersectionFilter_for_all_grids<>::bind(m);
-}
diff --git a/python/dune/xt/grid/filters/intersection.hh b/python/dune/xt/grid/filters/intersection.hh
index f9ebba17b89863904f51d143861e099a2f8a682d..ca8071aa46007f58b63d7ca48e990ca05ea6687c 100644
--- a/python/dune/xt/grid/filters/intersection.hh
+++ b/python/dune/xt/grid/filters/intersection.hh
@@ -47,7 +47,7 @@ public:
     c.def(py::init([]() { return std::make_unique<type>(); }));
     c.def("__repr__", [ClassId](type&) { return ClassId + "()"; });
 
-    m.def(ClassId.c_str(), [](const Grid::GridProvider<G>&) { return type(); });
+    m.def(ClassId.c_str(), [](const Grid::GridProvider<G>&) { return new type(); });
 
     return c;
   } // ... bind(...)
@@ -79,7 +79,7 @@ public:
     c.def(py::init([](const BoundaryInfo<I>& boundary_info,
                       const BoundaryType& boundary_type,
                       const std::string& logging_prefix) {
-            return type(boundary_info, boundary_type.copy(), logging_prefix);
+            return new type(boundary_info, boundary_type.copy(), logging_prefix);
           }),
           "boundary_info"_a,
           "boundary_type"_a,
@@ -92,7 +92,7 @@ public:
         [](const Grid::GridProvider<G>&,
            const BoundaryInfo<I>& boundary_info,
            const BoundaryType& boundary_type,
-           const std::string& logging_prefix) { return type(boundary_info, boundary_type.copy(), logging_prefix); },
+           const std::string& logging_prefix) { return new type(boundary_info, boundary_type.copy(), logging_prefix); },
         "grid_provider"_a,
         "boundary_info"_a,
         "boundary_type"_a,
diff --git a/python/dune/xt/grid/functors/refinement.cc b/python/dune/xt/grid/functors/refinement.cc
index f454327becd7bce701cd51bfa18066342750fc7e..fe4d242e30707547be1ed346a7a6f3e1dfd4a52b 100644
--- a/python/dune/xt/grid/functors/refinement.cc
+++ b/python/dune/xt/grid/functors/refinement.cc
@@ -11,6 +11,8 @@
 
 #include <dune/pybindxi/pybind11.h>
 #include <dune/xt/grid/functors/refinement.hh>
+#include <dune/xt/grid/grids.hh>
+#include <dune/xt/grid/type_traits.hh>
 
 #include "interfaces.hh"
 
@@ -22,14 +24,14 @@ namespace bindings {
 
 
 template <class G>
-class MaximumEntityVolumeRefineFunctorFunctor
+class MaximumEntityVolumeRefineFunctor
 {
   static_assert(is_grid<G>::value, "");
   using GV = typename G::LeafGridView;
   using I = extract_intersection_t<GV>;
 
 public:
-  using type = Grid::MaximumEntityVolumeRefineFunctorFunctor<GV>;
+  using type = Grid::MaximumEntityVolumeRefineFunctor<GV>;
   using base_type = Grid::ElementFunctor<GV>;
   using bound_type = pybind11::class_<type, base_type>;
 
@@ -42,7 +44,8 @@ public:
 
     auto ClassId = Common::to_camel_case(class_id);
     auto ClassName = Common::to_camel_case(class_id + "_" + grid_id);
-    bound_type c(m, ClassName.c_str(), std::string(ClassId + "( " + grid_id + " variant)").c_str());
+    const std::string doc{ClassId + "( " + grid_id + " variant)"};
+    bound_type c(m, ClassName.c_str(), doc.c_str());
     c.def(py::init([](GridProvider<G>& grid_provider, const double& volume) {
             return std::make_unique<type>(grid_provider.grid(), volume, 1.);
           }),
@@ -50,7 +53,8 @@ public:
           "volume"_a,
           py::keep_alive<0, 1>());
     c.def("__repr__", [ClassId](type&) { return ClassId + "(grid_provider=\?\?\?, volume=\?\?\?)"; });
-    c.def_property_readonly("result", [](const type& self) { return self.result(); });
+    // there's no result member in the functor??
+    // c.def_property_readonly("result", [](const type& self) { return self.result(); });
 
     m.def(
         ClassId.c_str(),
@@ -63,7 +67,7 @@ public:
 
     return c;
   } // ... bind(...)
-}; // class MaximumEntityVolumeRefineFunctorFunctor
+}; // class MaximumEntityVolumeRefineFunctor
 
 
 } // namespace bindings
@@ -73,17 +77,17 @@ public:
 
 
 template <class GridTypes = Dune::XT::Grid::AvailableGridTypes>
-struct MaximumEntityVolumeRefineFunctorFunctor_for_all_grids
+struct MaximumEntityVolumeRefineFunctor_for_all_grids
 {
   static void bind(pybind11::module& m)
   {
-    Dune::XT::Grid::bindings::MaximumEntityVolumeRefineFunctorFunctor<typename GridTypes::head_type>::bind(m);
-    MaximumEntityVolumeRefineFunctorFunctor_for_all_grids<typename GridTypes::tail_type>::bind(m);
+    Dune::XT::Grid::bindings::MaximumEntityVolumeRefineFunctor<typename GridTypes::head_type>::bind(m);
+    MaximumEntityVolumeRefineFunctor_for_all_grids<typename GridTypes::tail_type>::bind(m);
   }
 };
 
 template <>
-struct MaximumEntityVolumeRefineFunctorFunctor_for_all_grids<boost::tuples::null_type>
+struct MaximumEntityVolumeRefineFunctor_for_all_grids<boost::tuples::null_type>
 {
   static void bind(pybind11::module& /*m*/) {}
 };
@@ -98,5 +102,5 @@ PYBIND11_MODULE(_grid_functors_refinement, m)
   py::module::import("dune.xt.grid._grid_gridprovider_provider");
   py::module::import("dune.xt.grid._grid_functors_interfaces");
 
-  MaximumEntityVolumeRefineFunctorFunctor_for_all_grids<>::bind(m);
+  MaximumEntityVolumeRefineFunctor_for_all_grids<>::bind(m);
 }
diff --git a/python/dune/xt/grid/gridprovider.hh b/python/dune/xt/grid/gridprovider.hh
index 538f0738166fc74055e2a9b377a92af78a43583c..ed407707c1e3c1ea9a989d9b04d2f6a0e284ef23 100644
--- a/python/dune/xt/grid/gridprovider.hh
+++ b/python/dune/xt/grid/gridprovider.hh
@@ -12,17 +12,24 @@
 #ifndef PYTHON_DUNE_XT_GRID_GRIDPROVIDER_HH
 #define PYTHON_DUNE_XT_GRID_GRIDPROVIDER_HH
 
+#include <algorithm>
+
 #include <dune/geometry/type.hh>
 #include <dune/grid/common/mcmgmapper.hh>
 
 #include <dune/pybindxi/pybind11.h>
 #include <dune/pybindxi/stl.h>
-#include <dune/xt/common/parallel/mpi_comm_wrapper.hh>
 #include <dune/xt/common/numeric_cast.hh>
+#include <dune/xt/common/parallel/mpi_comm_wrapper.hh>
+#include <dune/xt/common/ranges.hh>
+#include <dune/xt/la/container/common/vector/dense.hh>
 #include <dune/xt/grid/entity.hh>
+#include <dune/xt/grid/element.hh>
 #include <dune/xt/grid/exceptions.hh>
 #include <dune/xt/grid/gridprovider/dgf.hh>
 #include <dune/xt/grid/gridprovider/provider.hh>
+#include <dune/xt/grid/filters/intersection.hh>
+#include <dune/xt/grid/mapper.hh>
 #include <dune/xt/functions/generic/grid-function.hh>
 
 #include <python/dune/xt/common/configuration.hh>
@@ -42,71 +49,186 @@ public:
   using type = Grid::GridProvider<G>;
   using bound_type = pybind11::class_<type>;
 
+  using GV = typename type::LeafGridViewType;
+
   static bound_type bind(pybind11::module& m,
                          const std::string& class_id = "grid_provider",
                          const std::string& grid_id = grid_name<G>::value())
   {
     namespace py = pybind11;
     using namespace pybind11::literals;
-    const int dim = type::GridType::dimension;
+    constexpr const int dim = type::GridType::dimension;
 
     const std::string class_name = class_id + "_" + grid_id;
     const auto ClassName = XT::Common::to_camel_case(class_name);
     bound_type c(m, ClassName.c_str(), (XT::Common::to_camel_case(class_id) + " (" + grid_id + " variant)").c_str());
-    c.def_property_readonly("dimension", [dim](type&) { return dim; });
+    c.def_property_readonly("dimension", [](type&) { return dim; });
     c.def_property_readonly("max_level", &type::max_level);
     c.def(
         "size",
-        [dim](type& self, const int codim) {
+        [](type& self, const int codim) {
           DUNE_THROW_IF(
               codim < 0 || codim > dim, Exceptions::wrong_codimension, "dim = " << dim << "\n   codim = " << codim);
-          auto grid_view = self.leaf_view();
-          MultipleCodimMultipleGeomTypeMapper<decltype(grid_view)> mapper(
-              grid_view,
-              [codim](GeometryType gt, int dimgrid) { return dimgrid - Common::numeric_cast<int>(gt.dim()) == codim; });
+          DUNE_THROW_IF(codim != dim && codim != 0 && !G::LeafGridView::conforming,
+                        XT::Common::Exceptions::requirements_not_met,
+                        "This is not yet implemented for non-conforming grids and codim " << codim << "!");
+          const LeafMultipleCodimMultipleGeomTypeMapper<G> mapper(self.grid(), [codim](GeometryType gt, int dimgrid) {
+            return dimgrid - Common::numeric_cast<int>(gt.dim()) == codim;
+          });
           return mapper.size();
         },
         "codim"_a);
     c.def(
         "centers",
-        [dim](type& self, const int codim) {
-          DUNE_THROW_IF(codim != 0, NotImplemented, "Only for codim 0 at the moment!");
+        [](type& self, const int codim) {
           DUNE_THROW_IF(
               codim < 0 || codim > dim, Exceptions::wrong_codimension, "dim = " << dim << "\n   codim = " << codim);
+          DUNE_THROW_IF(codim != dim && codim != 0 && !G::LeafGridView::conforming,
+                        XT::Common::Exceptions::requirements_not_met,
+                        "This is not yet implemented for non-conforming grids and codim " << codim << "!");
           auto grid_view = self.leaf_view();
-          MultipleCodimMultipleGeomTypeMapper<decltype(grid_view)> mapper(
-              grid_view,
-              [codim](GeometryType gt, int dimgrid) { return dimgrid - Common::numeric_cast<int>(gt.dim()) == codim; });
-          XT::LA::CommonDenseMatrix<double> centers(mapper.size(), Common::numeric_cast<size_t>(dim), 0.);
+          const LeafMultipleCodimMultipleGeomTypeMapper<G> mapper(self.grid(), [codim](GeometryType gt, int dimgrid) {
+            return dimgrid - Common::numeric_cast<int>(gt.dim()) == codim;
+          });
+          auto centers =
+              std::make_unique<XT::LA::CommonDenseMatrix<double>>(mapper.size(), Common::numeric_cast<size_t>(dim), 0.);
           for (auto&& element : elements(grid_view)) {
-            auto index = mapper.index(element);
-            auto center = element.geometry().center();
-            for (size_t jj = 0; jj < Common::numeric_cast<size_t>(dim); ++jj)
-              centers.set_entry(index, jj, center[jj]);
+            for (auto&& ii : Common::value_range(element.subEntities(codim))) {
+              auto index = sub_entity_index(mapper, element, codim, ii);
+              auto center = sub_entity_center(element, codim, ii);
+              for (size_t jj = 0; jj < Common::numeric_cast<size_t>(dim); ++jj)
+                centers->set_entry(index, jj, center[jj]);
+            }
           }
           return centers;
         },
         "codim"_a = 0,
         py::call_guard<py::gil_scoped_release>());
     c.def(
-        "visualize",
-        [](type& self, const std::string& filename, const std::string& layer) {
-          DUNE_THROW_IF(layer != "leaf", NotImplemented, "Visualization of level views not implemented yet!");
+        "inner_intersection_indices",
+        [](type& self) {
+          DUNE_THROW_IF(!G::LeafGridView::conforming,
+                        XT::Common::Exceptions::requirements_not_met,
+                        "This is not yet implemented for non-conforming grids!");
+          auto grid_view = self.leaf_view();
+          const LeafMultipleCodimMultipleGeomTypeMapper<G> mapper(self.grid(), mcmgLayout(Codim<1>()));
+          std::set<size_t> global_indices;
+          Grid::ApplyOn::InnerIntersections<GV> filter;
+          for (auto&& element : elements(grid_view)) {
+            for (auto&& intersection : intersections(grid_view, element)) {
+              if (filter.contains(grid_view, intersection)) {
+                const auto intersection_entity = element.template subEntity<1>(intersection.indexInInside());
+                global_indices.insert(Common::numeric_cast<size_t>(mapper.index(intersection_entity)));
+              }
+            }
+          }
+          LA::CommonDenseVector<size_t> ret(global_indices.size());
+          size_t ii = 0;
+          for (const auto& index : global_indices) {
+            ret[ii] = index;
+            ++ii;
+          }
+          return ret;
+        },
+        py::call_guard<py::gil_scoped_release>());
+    c.def(
+        "inside_element_indices",
+        [](type& self) {
+          DUNE_THROW_IF(!G::LeafGridView::conforming,
+                        XT::Common::Exceptions::requirements_not_met,
+                        "This is not yet implemented for non-conforming grids!");
+          auto grid_view = self.leaf_view();
+          const LeafMultipleCodimMultipleGeomTypeMapper<G> element_mapper(self.grid(), mcmgElementLayout());
+          const LeafMultipleCodimMultipleGeomTypeMapper<G> intersection_mapper(self.grid(), mcmgLayout(Codim<1>()));
+          LA::CommonDenseVector<size_t> element_indices(intersection_mapper.size(), std::numeric_limits<size_t>::max());
+          Grid::ApplyOn::AllIntersectionsOnce<GV> filter;
+          for (auto&& element : elements(grid_view)) {
+            const auto element_index = Common::numeric_cast<size_t>(element_mapper.index(element));
+            for (auto&& intersection : intersections(grid_view, element)) {
+              if (filter.contains(grid_view, intersection)) {
+                const auto intersection_entity = element.template subEntity<1>(intersection.indexInInside());
+                const auto intersection_index =
+                    Common::numeric_cast<size_t>(intersection_mapper.index(intersection_entity));
+                element_indices[intersection_index] = element_index;
+              }
+            }
+          }
+          return element_indices;
+        },
+        py::call_guard<py::gil_scoped_release>());
+    c.def(
+        "outside_element_indices",
+        [](type& self) {
+          DUNE_THROW_IF(!G::LeafGridView::conforming,
+                        XT::Common::Exceptions::requirements_not_met,
+                        "This is not yet implemented for non-conforming grids!");
+          auto grid_view = self.leaf_view();
+          const LeafMultipleCodimMultipleGeomTypeMapper<G> element_mapper(self.grid(), mcmgElementLayout());
+          const LeafMultipleCodimMultipleGeomTypeMapper<G> intersection_mapper(self.grid(), mcmgLayout(Codim<1>()));
+          LA::CommonDenseVector<size_t> element_indices(intersection_mapper.size(), std::numeric_limits<size_t>::max());
+          Grid::ApplyOn::InnerIntersectionsOnce<GV> filter;
+          for (auto&& element : elements(grid_view)) {
+            for (auto&& intersection : intersections(grid_view, element)) {
+              if (filter.contains(grid_view, intersection)) {
+                const auto intersection_entity = element.template subEntity<1>(intersection.indexInInside());
+                const auto intersection_index =
+                    Common::numeric_cast<size_t>(intersection_mapper.index(intersection_entity));
+                const auto outside_element_index =
+                    Common::numeric_cast<size_t>(element_mapper.index(intersection.outside()));
+                element_indices[intersection_index] = outside_element_index;
+              }
+            }
+          }
+          return element_indices;
+        },
+        py::call_guard<py::gil_scoped_release>());
+    c.def(
+        "boundary_intersection_indices",
+        [](type& self, const Grid::IntersectionFilter<GV>& filter) {
+          DUNE_THROW_IF(!G::LeafGridView::conforming,
+                        XT::Common::Exceptions::requirements_not_met,
+                        "This is not yet implemented for non-conforming grids!");
           auto grid_view = self.leaf_view();
-          using GV = decltype(grid_view);
-          const MultipleCodimMultipleGeomTypeMapper<GV> mapper(
-              grid_view, [](GeometryType gt, int dimgrid) { return dimgrid == Common::numeric_cast<int>(gt.dim()); });
-          double element_index = 0;
-          Functions::GenericGridFunction<extract_entity_t<GV>> element_index_function(
+          const LeafMultipleCodimMultipleGeomTypeMapper<G> mapper(self.grid(), mcmgLayout(Codim<1>()));
+          std::set<size_t> global_indices;
+          for (auto&& element : elements(grid_view)) {
+            for (auto&& intersection : intersections(grid_view, element)) {
+              if (filter.contains(grid_view, intersection)) {
+                const auto intersection_entity = element.template subEntity<1>(intersection.indexInInside());
+                global_indices.insert(Common::numeric_cast<size_t>(mapper.index(intersection_entity)));
+              }
+            }
+          }
+          LA::CommonDenseVector<size_t> ret(global_indices.size());
+          size_t ii = 0;
+          for (const auto& index : global_indices) {
+            ret[ii] = index;
+            ++ii;
+          }
+          return ret;
+        },
+        "intersection_filter"_a = Grid::ApplyOn::BoundaryIntersections<GV>(),
+        py::call_guard<py::gil_scoped_release>());
+    c.def(
+        "visualize",
+        [](type& self, const std::string& filename) {
+          const LeafMultipleCodimMultipleGeomTypeMapper<G> mapper(self.grid(), [](GeometryType gt, int dimgrid) {
+            return dimgrid - Common::numeric_cast<int>(gt.dim()) == 0;
+          });
+          double element_index = 0; // not thread safe!
+          Functions::GenericGridFunction<extract_entity_t<typename G::LeafGridView>> element_index_function(
               /*order=*/[](const auto&) { return 0; },
-              /*post_bind=*/[&mapper, &element_index](const auto& element) { element_index = mapper.index(element); },
-              /*evaluate=*/[&element_index](const auto&, const auto&) { return element_index; },
+              /*post_bind=*/
+              [&mapper, &element_index](const auto& element) { element_index = mapper.index(element); },
+              /*evaluate=*/
+              [&element_index](const auto&, const auto&) { return element_index; },
               /*param_type=*/{},
               /*name=*/"Element index");
-          element_index_function.visualize(grid_view, filename, /*subsampling=*/false);
+          element_index_function.visualize(self.leaf_view(),
+                                           filename,
+                                           /*subsampling=*/false);
         },
         "filename"_a,
-        "layer"_a = "leaf",
         py::call_guard<py::gil_scoped_release>());
     c.def(
         "global_refine",
diff --git a/python/dune/xt/grid/gridprovider/provider.cc b/python/dune/xt/grid/gridprovider/provider.cc
index 3d7ea98c308d8eb2ebcdde0c799f533b60400987..13786789760e1e5cc39e5cba871e5566a15095f9 100644
--- a/python/dune/xt/grid/gridprovider/provider.cc
+++ b/python/dune/xt/grid/gridprovider/provider.cc
@@ -14,6 +14,41 @@
 #include <dune/xt/grid/grids.hh>
 
 #include <python/dune/xt/grid/gridprovider.hh>
+#include <python/dune/xt/grid/filters/intersection.hh>
+
+
+template <template <class> class Filter, class GridTypes = Dune::XT::Grid::AvailableGridTypes>
+struct InitlessIntersectionFilter_for_all_grids
+{
+  static void bind(pybind11::module& m, const std::string& class_id)
+  {
+    Dune::XT::Grid::bindings::InitlessIntersectionFilter<Filter, typename GridTypes::head_type>::bind(m, class_id);
+    InitlessIntersectionFilter_for_all_grids<Filter, typename GridTypes::tail_type>::bind(m, class_id);
+  }
+};
+
+template <template <class> class Filter>
+struct InitlessIntersectionFilter_for_all_grids<Filter, boost::tuples::null_type>
+{
+  static void bind(pybind11::module& /*m*/, const std::string& /*class_id*/) {}
+};
+
+
+template <class GridTypes = Dune::XT::Grid::AvailableGridTypes>
+struct CustomBoundaryIntersectionFilter_for_all_grids
+{
+  static void bind(pybind11::module& m)
+  {
+    Dune::XT::Grid::bindings::CustomBoundaryIntersectionsFilter<typename GridTypes::head_type>::bind(m);
+    CustomBoundaryIntersectionFilter_for_all_grids<typename GridTypes::tail_type>::bind(m);
+  }
+};
+
+template <>
+struct CustomBoundaryIntersectionFilter_for_all_grids<boost::tuples::null_type>
+{
+  static void bind(pybind11::module& /*m*/) {}
+};
 
 
 template <class GridTypes = Dune::XT::Grid::AvailableGridTypes>
@@ -36,8 +71,33 @@ struct GridProvider_for_all_grids<boost::tuples::null_type>
 PYBIND11_MODULE(_grid_gridprovider_provider, m)
 {
   namespace py = pybind11;
+  using namespace Dune::XT::Grid;
 
   py::module::import("dune.xt.common");
+  py::module::import("dune.xt.la");
+  py::module::import("dune.xt.grid._grid_boundaryinfo_interfaces");
+  py::module::import("dune.xt.grid._grid_boundaryinfo_types");
+  py::module::import("dune.xt.grid._grid_filters_base");
+
+#define BIND_(NAME) InitlessIntersectionFilter_for_all_grids<ApplyOn::NAME>::bind(m, std::string("ApplyOn") + #NAME)
+
+  BIND_(AllIntersections);
+  BIND_(AllIntersectionsOnce);
+  BIND_(NoIntersections);
+  BIND_(InnerIntersections);
+  BIND_(InnerIntersectionsOnce);
+  //  BIND_(PartitionSetInnerIntersectionsOnce); <- requires partition set as template argument
+  BIND_(BoundaryIntersections);
+  BIND_(NonPeriodicBoundaryIntersections);
+  BIND_(PeriodicBoundaryIntersections);
+  BIND_(PeriodicBoundaryIntersectionsOnce);
+  //  BIND_(GenericFilteredIntersections); <- requires lambda in init
+  //  BIND_(CustomBoundaryAndProcessIntersections); <- requires boundary type and info in init
+  BIND_(ProcessIntersections);
+
+#undef BIND_
+
+  CustomBoundaryIntersectionFilter_for_all_grids<>::bind(m);
 
   GridProvider_for_all_grids<>::bind(m);
 }
diff --git a/python/dune/xt/grid/intersection.cc b/python/dune/xt/grid/intersection.cc
index c0c8af779a378da8e63febc35984930defd0abfa..5c6d3a88b89a958aabb00f6f5ba3efab38037977 100644
--- a/python/dune/xt/grid/intersection.cc
+++ b/python/dune/xt/grid/intersection.cc
@@ -18,7 +18,7 @@
 #include <dune/xt/grid/type_traits.hh>
 #include <dune/xt/grid/grids.hh>
 #include <dune/xt/grid/intersection.hh>
-#include <dune/xt/common/print.hh>
+#include <dune/xt/grid/print.hh>
 
 #include <python/dune/xt/common/fvector.hh>
 #include <python/dune/xt/grid/grids.bindings.hh>
@@ -72,12 +72,12 @@ public:
     // special methods/operators
     c.def("__str__", [](type& self) {
       std::stringstream ss;
-      ss << Common::print(self);
+      ss << print(self);
       return ss.str();
     });
     c.def("__repr__", [](type& self) {
       std::stringstream ss;
-      ss << Common::repr(self);
+      ss << repr(self);
       return ss.str();
     });
     c.def(
diff --git a/python/dune/xt/grid/walker.hh b/python/dune/xt/grid/walker.hh
index 4edb6091c56ff5d11d677d63bb45342f238ce8b8..69c2150c61e84639234947f4788c9503491f54e6 100644
--- a/python/dune/xt/grid/walker.hh
+++ b/python/dune/xt/grid/walker.hh
@@ -78,14 +78,15 @@ public:
 
     auto ClassName = Common::to_camel_case(class_id + "_" + grid_id);
     bound_type c(m, ClassName.c_str(), ClassName.c_str());
-    c.def(py::init([](GridProvider<G>& grid_provider) { return type(grid_provider.leaf_view()); }), "grid_provider"_a);
+    c.def(py::init([](GridProvider<G>& grid_provider) { return new type(grid_provider.leaf_view()); }),
+          "grid_provider"_a);
 
     addbind_methods(c);
 
     // factories
     m.def(
         Common::to_camel_case(class_id).c_str(),
-        [](GridProvider<G>& grid_provider) { return type(grid_provider.leaf_view()); },
+        [](GridProvider<G>& grid_provider) { return new type(grid_provider.leaf_view()); },
         "grid_provider"_a);
 
     return c;
diff --git a/python/dune/xt/grid/walker/apply-on.bindings.hh b/python/dune/xt/grid/walker/apply-on.bindings.hh
index c030f11c5f1c796c218245c45ce087e58111e245..c067b7a5d146ffd8253e6160ac1d71114cd0c70c 100644
--- a/python/dune/xt/grid/walker/apply-on.bindings.hh
+++ b/python/dune/xt/grid/walker/apply-on.bindings.hh
@@ -59,13 +59,13 @@ private:
       m.def(
           makename(class_name, layer_name).c_str(),
           [](const BoundaryInfoType& boundary_info, XT::Grid::BoundaryType*&& boundary_type) {
-            return type(boundary_info, std::move(boundary_type));
+            return new type(boundary_info, std::move(boundary_type));
           },
           "boundary_info"_a,
           "boundary_type"_a);
     } else {
       c.def(pybind11::init<>());
-      m.def(makename(class_name, layer_name).c_str(), []() { return type(); });
+      m.def(makename(class_name, layer_name).c_str(), []() { return new type(); });
     }
   }
 
diff --git a/python/dune/xt/la/container/matrix-interface.hh b/python/dune/xt/la/container/matrix-interface.hh
index 210a1630ae2e1be6ab65d63156b04fbbb57c25a1..eb167b05e12b63afa2aefd9fd6301a786d39316d 100644
--- a/python/dune/xt/la/container/matrix-interface.hh
+++ b/python/dune/xt/la/container/matrix-interface.hh
@@ -208,6 +208,55 @@ auto bind_Matrix(pybind11::module& m)
       "min_cols"_a = -1,
       "mode"_a = "ascii");
 
+  c.def(
+      "__add__", [](const C& self, const C& other) { return std::make_unique<C>(self + other); }, py::is_operator());
+  c.def("__iadd__", // function ptr signature required for the right return type
+        (C & (C::*)(const C&)) & C::operator+=,
+        py::is_operator());
+  c.def(
+      "__sub__", [](const C& self, const C& other) { return std::make_unique<C>(self - other); }, py::is_operator());
+  c.def("__isub__", // function ptr signature required for the right return type
+        (C & (C::*)(const C&)) & C::operator-=,
+        py::is_operator());
+  c.def(
+      "__mul__",
+      [](const C& self, const S& alpha) {
+        auto ret = std::make_unique<C>(self.copy());
+        (*ret) *= alpha;
+        return ret;
+      },
+      py::is_operator());
+  c.def(
+      "__rmul__",
+      [](const C& self, const S& alpha) {
+        auto ret = std::make_unique<C>(self.copy());
+        (*ret) *= alpha;
+        return ret;
+      },
+      py::is_operator());
+  c.def("__imul__", // function ptr signature required for the right return type
+        (C & (C::*)(const C&)) & C::operator*=,
+        py::is_operator());
+  c.def(
+      "__truediv__",
+      [](const C& self, const S& alpha) {
+        auto ret = std::make_unique<C>(self.copy());
+        (*ret) /= alpha;
+        return ret;
+      },
+      py::is_operator());
+  c.def("__itruediv__", // function ptr signature required for the right return type
+        (C & (C::*)(const C&)) & C::operator/=,
+        py::is_operator());
+  c.def(
+      "neg",
+      [](const C& self) {
+        auto ret = std::make_unique<C>(self.copy());
+        (*ret) *= -1;
+        return ret;
+      },
+      py::is_operator());
+
   addbind_ContainerInterface(c);
 
   return c;
@@ -221,11 +270,25 @@ void addbind_Matrix_Vector_interaction(pybind11::class_<M>& mat, pybind11::class
 
   mat.def(
       "mv", [](const M& self, const V& xx, V& yy) { self.mv(xx, yy); }, "source", "range");
+  mat.def(
+      "dot",
+      [](const M& self, const V& xx) {
+        V yy(self.rows());
+        self.mv(xx, yy);
+        return yy;
+      },
+      "source");
+  mat.def(
+      "__matmul__",
+      [](const M& self, const V& xx) {
+        V yy(self.rows());
+        self.mv(xx, yy);
+        return yy;
+      },
+      "source");
   mat.def(
       "mtv", [](const M& self, const V& xx, V& yy) { self.mtv(xx, yy); }, "source", "range");
 
-  mat.def(py::self * V());
-
   mat.def("vector_type", [vec](M& /*self*/) { return vec; });
   vec.def("matrix_type", [mat](V& /*self*/) { return mat; });
 } // ... addbind_Matrix_Vector_interaction(...)
diff --git a/python/dune/xt/la/container/vector-interface.hh b/python/dune/xt/la/container/vector-interface.hh
index 8eadd4d9f8bcaa92950f72367279f33d62ed2813..8a7d7141f0085ac43d6cf90538bfc9b5813032df 100644
--- a/python/dune/xt/la/container/vector-interface.hh
+++ b/python/dune/xt/la/container/vector-interface.hh
@@ -112,14 +112,54 @@ auto bind_Vector(pybind11::module& m)
       },
       pybind11::keep_alive<0, 1>() /*Essential: keep object alive while iterator exists!*/);
 
-  c.def(py::self == py::self);
-  c.def(py::self != py::self);
-  c.def(py::self + py::self);
-  c.def(py::self += py::self);
-  c.def(py::self -= py::self);
-  c.def(py::self * py::self);
-  c.def(py::self *= R());
-  c.def(py::self /= R());
+  c.def(
+      "__add__", [](const C& self, const C& other) { return std::make_unique<C>(self + other); }, py::is_operator());
+  c.def("__iadd__", // function ptr signature required for the right return type
+        (C & (C::*)(const C&)) & C::operator+=,
+        py::is_operator());
+  c.def(
+      "__sub__", [](const C& self, const C& other) { return std::make_unique<C>(self - other); }, py::is_operator());
+  c.def("__isub__", // function ptr signature required for the right return type
+        (C & (C::*)(const C&)) & C::operator-=,
+        py::is_operator());
+  c.def(
+      "__mul__",
+      [](const C& self, const R& alpha) {
+        auto ret = std::make_unique<C>(self.copy());
+        (*ret) *= alpha;
+        return ret;
+      },
+      py::is_operator());
+  c.def(
+      "__rmul__",
+      [](const C& self, const R& alpha) {
+        auto ret = std::make_unique<C>(self.copy());
+        (*ret) *= alpha;
+        return ret;
+      },
+      py::is_operator());
+  c.def("__imul__", // function ptr signature required for the right return type
+        (C & (C::*)(const C&)) & C::operator*=,
+        py::is_operator());
+  c.def(
+      "__truediv__",
+      [](const C& self, const R& alpha) {
+        auto ret = std::make_unique<C>(self.copy());
+        (*ret) /= alpha;
+        return ret;
+      },
+      py::is_operator());
+  c.def("__itruediv__", // function ptr signature required for the right return type
+        (C & (C::*)(const C&)) & C::operator/=,
+        py::is_operator());
+  c.def(
+      "neg",
+      [](const C& self) {
+        auto ret = std::make_unique<C>(self.copy());
+        (*ret) *= -1;
+        return ret;
+      },
+      py::is_operator());
 
   c.def_property_readonly("size", [](const C& self) { return self.size(); });
   c.def(
diff --git a/python/setup.py.in b/python/setup.py.in
index dcfd0b0e3b28063d928592965be86295f10e2c14..36c831b851ea87f5e199bd61e7186a5bd065062f 100644
--- a/python/setup.py.in
+++ b/python/setup.py.in
@@ -17,7 +17,7 @@ from setuptools import setup, find_packages
 
 requires=['binpacking==1.3', 'cython', 'jinja2', 'docopt', 'pylicense3>=0.4.1',
                         'ipython', 'pytest', 'pytest-cov', 'cmake_format==0.4.1',
-                        'codecov', 'yapf==0.25', 'loguru', 'numpy', 'scipy',
+                        'codecov', 'yapf==0.25', 'loguru', 'numpy', 'scipy', 'matplotlib',
                         'k3d==2.6.6', 'vtk', 'ipywidgets', 'lxml', 'xmljson']
 extras_require = []
 if '${HAVE_MPI}' == 'TRUE':