diff --git a/.ci/make_env_file.py b/.ci/make_env_file.py
deleted file mode 100755
index 450d3b7a7fa0fcd7cc2325ecc2882b71651d6e77..0000000000000000000000000000000000000000
--- a/.ci/make_env_file.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env python3
-#
-# ~~~
-# This file is part of the dune-gdt project:
-#   https://github.com/dune-community/dune-gdt
-# Copyright 2010-2018 dune-gdt 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:
-#   René Fritze (2018)
-# ~~~
-
-import os
-from os.path import expanduser
-from shlex import quote
-home = expanduser("~")
-
-prefixes = os.environ.get('ENV_PREFIXES', 'TRAVIS DRONE GITLAB CODECOV CI encrypt TOKEN TESTS').split(' ')
-blacklist = ['TRAVIS_COMMIT_MESSAGE']
-env_file = os.environ.get('ENV_FILE', os.path.join(home, 'env'))
-with open(env_file, 'wt') as env:
-    for k, v in os.environ.items():
-        for pref in prefixes:
-            if k.startswith(pref) and k not in blacklist:
-                env.write('{}={}\n'.format(k, quote(v)))
diff --git a/.ci/shared b/.ci/shared
index f108b7fd0ac9fefcab197eb331be48da6da3ff57..cc324c2a418c2e10cca465da7aeb81bd9201a462 160000
--- a/.ci/shared
+++ b/.ci/shared
@@ -1 +1 @@
-Subproject commit f108b7fd0ac9fefcab197eb331be48da6da3ff57
+Subproject commit cc324c2a418c2e10cca465da7aeb81bd9201a462
diff --git a/.ci/travis/add_swap.bash b/.ci/travis/add_swap.bash
deleted file mode 100755
index cd8ccf409302adb8508f32be200aa272b7c157d4..0000000000000000000000000000000000000000
--- a/.ci/travis/add_swap.bash
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-#
-# ~~~
-# This file is part of the dune-gdt project:
-#   https://github.com/dune-community/dune-gdt
-# Copyright 2010-2018 dune-gdt 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 - 2018)
-#   René Fritze     (2016 - 2018)
-#   Tobias Leibner  (2016 - 2018)
-# ~~~
-
-# ~~~
-# This file is part of the dune-gdt project:
-#   https://github.com/dune-community/dune-gdt
-# Copyright 2010-2018 dune-gdt 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:
-#   Rene Milk (2017 - 2018)
-# ~~~
-
-set -e
-SWAP=$HOME/swap.img
-MB_SIZE=${1:-4000}
-sudo -E dd if=/dev/zero of=${SWAP} bs=1M count=${MB_SIZE}
-sudo -E chown root:root ${SWAP}
-sudo -E chmod 0600 ${SWAP}
-sudo -E mkswap ${SWAP}
-sudo -E swapon ${SWAP}
-echo "echo 3 > /proc/sys/vm/drop_caches" | sudo -E sh
-
diff --git a/.ci/travis/after_script.bash b/.ci/travis/after_script.bash
deleted file mode 100755
index 066fb95fd459d4c80c1724ab69b7f2717d653015..0000000000000000000000000000000000000000
--- a/.ci/travis/after_script.bash
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# ~~~
-# This file is part of the dune-gdt project:
-#   https://github.com/dune-community/dune-gdt
-# Copyright 2010-2018 dune-gdt 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 (2017)
-#   René Fritze     (2016, 2018)
-#   Tobias Leibner  (2018)
-# ~~~
-
-git config --global hooks.clangformat ${CLANG_FORMAT}
-CHECK_DIR=${SUPERDIR}/${MY_MODULE}
-PYTHONPATH=${SUPERDIR}/scripts/python/ python3 -c "import travis_report as tp; tp.clang_format_status(\"${CHECK_DIR}\")"
-${SRC_DCTRL} ${BLD} --only=${MY_MODULE} configure
-${SRC_DCTRL} ${BLD} --only=${MY_MODULE} make doc
-if [ "X${TRAVIS_PULL_REQUEST}" != "Xfalse" ] ; then
-        ${SUPERDIR}/.ci/init_sshkey.sh ${encrypted_95fb78800815_key} ${encrypted_95fb78800815_iv} keys/dune-community/dune-community.github.io
-        ${SUPERDIR}/.ci/deploy_docs.sh ${MY_MODULE} "${DUNE_BUILD_DIR}"
-fi
diff --git a/.ci/travis/script.bash b/.ci/travis/script.bash
deleted file mode 100755
index d0d43f0f52726deb1d3ad9a7af9f66f9ddda189b..0000000000000000000000000000000000000000
--- a/.ci/travis/script.bash
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/bin/bash
-#
-# ~~~
-# This file is part of the dune-gdt project:
-#   https://github.com/dune-community/dune-gdt
-# Copyright 2010-2018 dune-gdt 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:
-#   René Fritze    (2018)
-#   Tobias Leibner (2018)
-# ~~~
-
-set -ex
-
-WAIT="${SUPERDIR}/scripts/bash/travis_wait_new.bash 45"
-source ${SUPERDIR}/scripts/bash/retry_command.bash
-
-${SRC_DCTRL} ${BLD} --only=${MY_MODULE} configure
-${SRC_DCTRL} ${BLD} --only=${MY_MODULE} make
-
-free -h
-
-if [ x"${TESTS}" == x ] ; then
-    ${WAIT} ${SRC_DCTRL} ${BLD} --only=${MY_MODULE} bexec ninja -v test_binaries
-    ${WAIT} ${SRC_DCTRL} ${BLD} --only=${MY_MODULE} bexec ctest -V -j 2
-else
-    ${WAIT} ${SRC_DCTRL} ${BLD} --only=${MY_MODULE} bexec ninja -v -j 1 test_binaries_builder_${TESTS}
-    ${WAIT} ${SRC_DCTRL} ${BLD} --only=${MY_MODULE} bexec ctest -V -j 2 -L "^builder_${TESTS}$"
-fi
-if [ "X${TRAVIS_PULL_REQUEST}" != "Xfalse" ] ; then
-        ${SUPERDIR}/.ci/init_sshkey.sh ${encrypted_95fb78800815_key} ${encrypted_95fb78800815_iv} keys/dune-community/dune-gdt-testlogs
-        retry_command ${SUPERDIR}/scripts/bash/travis_upload_test_logs.bash ${DUNE_BUILD_DIR}/${MY_MODULE}/dune/gdt/test/
-fi
-
-# clang coverage currently disabled for being to mem hungry
-if [[ ${CC} == *"clang"* ]] ; then
-    exit 0
-fi
-
-pushd ${DUNE_BUILD_DIR}/${MY_MODULE}
-COVERAGE_INFO=${PWD}/coverage.info
-lcov --directory . --output-file ${COVERAGE_INFO} -c
-for d in "dune-common" "dune-pybindxi" "dune-geometry"  "dune-istl"  "dune-grid" "dune-alugrid"  "dune-uggrid"  "dune-localfunctions" \
-         "dune-xt-common" "dune-xt-functions" "dune-xt-la" "dune-xt-grid" ; do
-    lcov --directory . --output-file ${COVERAGE_INFO} -r ${COVERAGE_INFO} "${SUPERDIR}/${d}/*"
-done
-lcov --directory . --output-file ${COVERAGE_INFO} -r ${COVERAGE_INFO} "${SUPERDIR}/${MY_MODULE}/dune/xt/*/test/*"
-cd ${SUPERDIR}/${MY_MODULE}
-${OLDPWD}/run-in-dune-env pip install codecov
-${OLDPWD}/run-in-dune-env codecov -v -X gcov -X coveragepy -F ctest -f ${COVERAGE_INFO} -t ${CODECOV_TOKEN}
-popd
diff --git a/.gitignore b/.gitignore
index 197a93be4ec62814daac4395fd2872dfb58bd8b3..1701849adf8b3da45bffd6322cf43ef748e47749 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,5 @@ demos-*
 build-*
 *.pyc
 .idea
+tags
+.vscode
diff --git a/.gitsuper b/.gitsuper
deleted file mode 100644
index f6402be6a07f302cb78213a1cb5fe66f910c04d2..0000000000000000000000000000000000000000
--- a/.gitsuper
+++ /dev/null
@@ -1,118 +0,0 @@
-[supermodule]
-remote = git@github.com:dune-community/dune-gdt-super.git
-status = 1a3bcab04b011a5d6e44f9983cae6ff89fa695e8 bin (heads/master)
-	 28c9ce81c14c878a71e907ab05b9bb72df77883e config.opts (heads/master)
-	 f308c6637edd65dcb83c4c1a46feaf05b958130e dune-alugrid (v2.6.0-7-gf308c663)
-	 76d7f0c9886a061571cb8dc66dd45a4ef86e7a58 dune-common (v2.2.1-2269-g76d7f0c9)
-	+36634986b34d08cd8f3805e2e607a7bba86ff25d dune-gdt (heads/saddlepoint)
-	 5235397bc16d24c759a1672fed7b8cfde4852e52 dune-geometry (v2.2.0-834-g5235397)
-	 af5766f0df47e3d0b62ea486efb9cdbf8e1cfc52 dune-grid (v2.2.0-2671-gaf5766f0d)
-	 1369ae9329d0928480d6b18ed772fc77e1abf752 dune-grid-glue (v2.4.0-161-g1369ae9)
-	 ef68ae0ec40f9d369e4ea9b31e560af6af545bf6 dune-istl (v2.6.0-4-gef68ae0e)
-	 5a1f77d7a0a41c2d065b29f00dda0871ec70337b dune-localfunctions (v2.6.0-2-g5a1f77d)
-	 6d2a4680493a2483d53f9dd05a19dd6b5f436572 dune-pybindxi (v2.2.1-30-g6d2a468)
-	 58bd932e2311a288e0163d041f836b50f19111cb dune-testtools (remotes/origin/testname_listing_hack2.6)
-	 07f9700459c616186737a9a34277f2edee76f475 dune-uggrid (v2.6.0-1-g07f97004)
-	 27a8010443c03eebddc3a910bdbef63ccf1f3e5d dune-xt-common (heads/master)
-	 376e88d14fd23ea12c3e23befc3fc967d4833751 dune-xt-data (heads/master)
-	 2da2612032ce64337f5af5cfec68d3c6d639e178 dune-xt-functions (heads/master)
-	 654c823f698418b67a696618342b54324f2cc151 dune-xt-grid (heads/master)
-	 d1ffc46178cb9d93308294c5144afb4d7d0f0d82 dune-xt-la (heads/master)
-	 09d0378f616b94d68bcdd9fc6114813181849ec0 scripts (heads/master)
-commit = ee1fda4fe42ee5f7c744cf529f84adf689251563
-
-[submodule.bin]
-remote = git@github.com:dune-community/local-bin.git
-status = 
-commit = 1a3bcab04b011a5d6e44f9983cae6ff89fa695e8
-
-[submodule.config.opts]
-remote = git@github.com:dune-community/config.opts.git
-status = 
-commit = 28c9ce81c14c878a71e907ab05b9bb72df77883e
-
-[submodule.dune-alugrid]
-remote = https://github.com/dune-mirrors/dune-alugrid.git
-status = 
-commit = f308c6637edd65dcb83c4c1a46feaf05b958130e
-
-[submodule.dune-common]
-remote = git@github.com:dune-community/dune-common.git
-status = 
-commit = 76d7f0c9886a061571cb8dc66dd45a4ef86e7a58
-
-[submodule.dune-gdt]
-remote = git@github.com:dune-community/dune-gdt.git
-status = c0b1735fab0ecbd4bb4f1eaa27cb65fe813e98f0 .vcsetup (heads/master)
-commit = 36634986b34d08cd8f3805e2e607a7bba86ff25d
-
-[submodule.dune-geometry]
-remote = git@github.com:dune-community/dune-geometry.git
-status = 
-commit = 5235397bc16d24c759a1672fed7b8cfde4852e52
-
-[submodule.dune-grid]
-remote = git@github.com:dune-community/dune-grid.git
-status = 
-commit = af5766f0df47e3d0b62ea486efb9cdbf8e1cfc52
-
-[submodule.dune-grid-glue]
-remote = https://github.com/dune-mirrors/dune-grid-glue.git
-status = 
-commit = 1369ae9329d0928480d6b18ed772fc77e1abf752
-
-[submodule.dune-istl]
-remote = https://github.com/dune-mirrors/dune-istl.git
-status = 
-commit = ef68ae0ec40f9d369e4ea9b31e560af6af545bf6
-
-[submodule.dune-localfunctions]
-remote = https://github.com/dune-mirrors/dune-localfunctions.git
-status = 
-commit = 5a1f77d7a0a41c2d065b29f00dda0871ec70337b
-
-[submodule.dune-pybindxi]
-remote = git@github.com:dune-community/dune-pybindxi.git
-status = c0b1735fab0ecbd4bb4f1eaa27cb65fe813e98f0 .vcsetup (heads/master)
-commit = 6d2a4680493a2483d53f9dd05a19dd6b5f436572
-
-[submodule.dune-testtools]
-remote = git@github.com:dune-community/dune-testtools.git
-status = 
-commit = 58bd932e2311a288e0163d041f836b50f19111cb
-
-[submodule.dune-uggrid]
-remote = https://github.com/dune-mirrors/dune-uggrid.git
-status = 
-commit = 07f9700459c616186737a9a34277f2edee76f475
-
-[submodule.dune-xt-common]
-remote = git@github.com:dune-community/dune-xt-common.git
-status = cc1bbdac283f4b9323c64345030f1b8f634b88d5 .vcsetup (heads/master)
-commit = 27a8010443c03eebddc3a910bdbef63ccf1f3e5d
-
-[submodule.dune-xt-data]
-remote = git@github.com:dune-community/dune-xt-data
-status = cc1bbdac283f4b9323c64345030f1b8f634b88d5 .vcsetup (heads/master)
-commit = 376e88d14fd23ea12c3e23befc3fc967d4833751
-
-[submodule.dune-xt-functions]
-remote = git@github.com:dune-community/dune-xt-functions.git
-status = cc1bbdac283f4b9323c64345030f1b8f634b88d5 .vcsetup (heads/master)
-commit = 2da2612032ce64337f5af5cfec68d3c6d639e178
-
-[submodule.dune-xt-grid]
-remote = git@github.com:dune-community/dune-xt-grid.git
-status = cc1bbdac283f4b9323c64345030f1b8f634b88d5 .vcsetup (heads/master)
-commit = 654c823f698418b67a696618342b54324f2cc151
-
-[submodule.dune-xt-la]
-remote = git@github.com:dune-community/dune-xt-la.git
-status = cc1bbdac283f4b9323c64345030f1b8f634b88d5 .vcsetup (heads/master)
-commit = d1ffc46178cb9d93308294c5144afb4d7d0f0d82
-
-[submodule.scripts]
-remote = https://github.com/wwu-numerik/scripts.git
-status = fb5ebc10e647d637c69497af2ec2560847eb2112 python/pylicense (v0.2.0~10)
-commit = 09d0378f616b94d68bcdd9fc6114813181849ec0
-
diff --git a/.pylicense-other.py b/.pylicense-other.py
index 9466c53ec881f5c7b485e504e0e27ce8b4893b61..57d6fabcd0e3fa210f9e5636a58eee4c02eb4799 100644
--- a/.pylicense-other.py
+++ b/.pylicense-other.py
@@ -24,4 +24,4 @@ include_patterns = ('*.txt', '*.cmake', '*.py', '*.sh', '*.bash', '*.dgf', '*.ms
                     '*.gitignore', '*.mailmap', '*.gitattributes', '*gitignore-*', '*stamp-vc', '*dune.module',
                     '*Doxylocal', '*.clang-format', '*COPYING-CMAKE-SCRIPTS', '*README', '*LICENSE', '*mainpage',
                     '*switch-build_dir', '*dune-xt-common.pc.in', '*CMakeLists.txt')
-exclude_patterns = ('*config.h.cmake', '*.vcsetup*', '*builder_definitions.cmake', '*.ci/shared/*)
+exclude_patterns = ('*config.h.cmake', '*.vcsetup*', '*builder_definitions.cmake', '*.ci/shared/*')
diff --git a/.vcsetup b/.vcsetup
index 0fe2d7bb19a198ee8d099ff308b9208af0914eb4..1f967c99ec990e557ad7b39a25c0148886019b79 160000
--- a/.vcsetup
+++ b/.vcsetup
@@ -1 +1 @@
-Subproject commit 0fe2d7bb19a198ee8d099ff308b9208af0914eb4
+Subproject commit 1f967c99ec990e557ad7b39a25c0148886019b79
diff --git a/dune/gdt/discretefunction/default-datahandle.hh b/dune/gdt/discretefunction/default-datahandle.hh
new file mode 100644
index 0000000000000000000000000000000000000000..187c13ddb76fd61f93418a92e34358ba7de0ae4d
--- /dev/null
+++ b/dune/gdt/discretefunction/default-datahandle.hh
@@ -0,0 +1,111 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_DISCRETEFUNCTION_DATAHANDLE_HH
+#define DUNE_GDT_DISCRETEFUNCTION_DATAHANDLE_HH
+
+#include <dune/grid/common/datahandleif.hh>
+
+#include <dune/xt/common/unused.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+template <class DiscreteFunctionType>
+class DiscreteFunctionDataHandle
+  : public Dune::CommDataHandleIF<DiscreteFunctionDataHandle<DiscreteFunctionType>,
+                                  typename DiscreteFunctionType::SpaceType::R>
+{
+public:
+  DiscreteFunctionDataHandle(DiscreteFunctionType& discrete_function, bool fixed_size = true)
+    : discrete_function_(discrete_function)
+    , mapper_(discrete_function_.space().mapper())
+    , vector_(discrete_function_.dofs().vector())
+    , fixed_size_(fixed_size)
+  {}
+
+  //! export type of data for message buffer
+  typedef typename DiscreteFunctionType::SpaceType::D DataType;
+
+  //! returns true if data for this codim should be communicated
+  bool contains(int /*dim*/, int codim) const
+  {
+    return (codim == 0);
+  }
+
+  //! returns true if size per entity of given dim and codim is a constant
+  bool fixedsize(int /*dim*/, int /*codim*/) const
+  {
+    return fixed_size_;
+  }
+
+  /*! how many objects of type DataType have to be sent for a given entity
+
+     Note: Only the sender side needs to know this size.
+   */
+  template <class EntityType>
+  std::enable_if_t<EntityType::codimension != 0, size_t> size(const EntityType& /*entity*/) const
+  {
+    return 0;
+  }
+
+  /*! how many objects of type DataType have to be sent for a given entity
+     Note: Only the sender side needs to know this size.
+   */
+  template <class EntityType>
+  std::enable_if_t<EntityType::codimension == 0, size_t> size(const EntityType& entity) const
+  {
+    return discrete_function_.space().mapper().local_size(entity);
+  }
+
+  //! pack data from user to message buffer
+  template <class MessageBuffer, class EntityType>
+  std::enable_if_t<EntityType::codimension != 0> gather(MessageBuffer& /*buff*/, const EntityType& /*entity*/) const
+  {}
+
+  template <class MessageBuffer, class EntityType>
+  std::enable_if_t<EntityType::codimension == 0> gather(MessageBuffer& buff, const EntityType& entity) const
+  {
+    const auto global_indices = mapper_.global_indices(entity);
+    for (const auto& index : global_indices)
+      buff.write(vector_.get_entry(index));
+  }
+
+  /*! unpack data from message buffer to user
+     n is the number of objects sent by the sender
+   */
+  template <class MessageBuffer, class EntityType>
+  std::enable_if_t<EntityType::codimension != 0>
+  scatter(MessageBuffer& /*buff*/, const EntityType& /*entity*/, size_t /*n*/)
+  {}
+
+  template <class MessageBuffer, class EntityType>
+  std::enable_if_t<EntityType::codimension == 0>
+  scatter(MessageBuffer& buff, const EntityType& entity, size_t DXTC_DEBUG_ONLY(n))
+  {
+    assert(mapper_.local_size(entity) == n);
+    const auto global_indices = mapper_.global_indices(entity);
+    for (const auto& index : global_indices)
+      buff.read(vector_[index]);
+  }
+
+private:
+  DiscreteFunctionType& discrete_function_;
+  const typename DiscreteFunctionType::SpaceType::MapperType& mapper_;
+  typename DiscreteFunctionType::VectorType& vector_;
+  const bool fixed_size_;
+};
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_DISCRETEFUNCTION_DATAHANDLE_HH
diff --git a/dune/gdt/discretefunction/default.hh b/dune/gdt/discretefunction/default.hh
index 1901ae741d374cc7858b134a1abf2215bd41ea53..0a6e7954529c174b456c993e714b02f32a2c0f21 100644
--- a/dune/gdt/discretefunction/default.hh
+++ b/dune/gdt/discretefunction/default.hh
@@ -56,7 +56,7 @@ class ConstDiscreteFunction
       range_dim,
       range_dim_cols,
       RangeField>;
-  using ThisType = ConstDiscreteFunction<Vector, GridView, range_dim, range_dim_cols, RangeField>;
+  using ThisType = ConstDiscreteFunction;
 
 public:
   using ConstDofVectorType = ConstDofVector<Vector, GridView>;
@@ -124,6 +124,7 @@ public:
    */
 
   using BaseType::visualize;
+  using BaseType::visualize_gradient;
 
   /**
    * \brief Visualizes the function using Dune::XT::Functions::GridFunctionInterface::visualize on the grid view
@@ -135,9 +136,27 @@ public:
                  const VTK::OutputType vtk_output_type = VTK::appendedraw,
                  const XT::Common::Parameter& param = {}) const
   {
-    this->visualize(space_.grid_view(), filename, space_.max_polorder() > 1, vtk_output_type, param);
+    const bool subsampling =
+        param.has_key("subsampling") ? static_cast<bool>(param.get("subsampling")[0]) : (space_.max_polorder() > 1);
+    this->visualize(space_.grid_view(), filename, subsampling, vtk_output_type, param);
   }
 
+  /**
+   * \brief Visualizes the function using Dune::XT::Functions::GridFunctionInterface::visualize on the grid view
+   *        associated with the space.
+   * \sa    Dune::XT::Functions::GridFunctionInterface::visualize
+   * \note  Subsampling is enabled by default for functions of order greater than one.
+   */
+  void visualize_gradient(const std::string filename,
+                          const VTK::OutputType vtk_output_type = VTK::appendedraw,
+                          const XT::Common::Parameter& param = {}) const
+  {
+    const bool subsampling =
+        param.has_key("subsampling") ? static_cast<bool>(param.get("subsampling")[0]) : (space_.max_polorder() > 1);
+    this->visualize_gradient(space_.grid_view(), filename, subsampling, vtk_output_type, param);
+  }
+
+
 protected:
   const SpaceType& space_;
 
@@ -163,7 +182,7 @@ class DiscreteFunction
   : XT::Common::StorageProvider<Vector>
   , public ConstDiscreteFunction<Vector, GridView, range_dim, range_dim_cols, RangeField>
 {
-  using ThisType = DiscreteFunction<Vector, GridView, range_dim, range_dim_cols, RangeField>;
+  using ThisType = DiscreteFunction;
   using VectorStorage = XT::Common::StorageProvider<Vector>;
   using BaseType = ConstDiscreteFunction<Vector, GridView, range_dim, range_dim_cols, RangeField>;
 
@@ -188,12 +207,17 @@ public:
   {}
 
   DiscreteFunction(const SpaceType& spc, const std::string nm = "dune.gdt.discretefunction")
-    : VectorStorage(new VectorType(spc.mapper().size(), 0))
+    : VectorStorage(new VectorType(spc.mapper().size(), 0.))
     , BaseType(spc, VectorStorage::access(), nm)
     , dofs_(space_.mapper(), VectorStorage::access())
   {}
 
-  DiscreteFunction(const ThisType&) = default;
+  DiscreteFunction(const ThisType& other)
+    : VectorStorage(new VectorType(other.access()))
+    , BaseType(other.space(), VectorStorage::access(), other.name())
+    , dofs_(other.space().mapper(), VectorStorage::access())
+  {}
+
   DiscreteFunction(ThisType&&) = default;
 
   ThisType& operator=(const ThisType&) = delete;
@@ -281,4 +305,6 @@ make_discrete_function(const SpaceInterface<GV, r, rC, R>& space, const std::str
 } // namespace GDT
 } // namespace Dune
 
+#include "default-datahandle.hh"
+
 #endif // DUNE_GDT_DISCRETEFUNCTION_DEFAULT_HH
diff --git a/dune/gdt/discretefunction/dof-vector.hh b/dune/gdt/discretefunction/dof-vector.hh
index 5bf37949b3c3da427dca892622651824963b4d58..5cb1739ad79121773a51e5e47aa090ef26cf8549 100644
--- a/dune/gdt/discretefunction/dof-vector.hh
+++ b/dune/gdt/discretefunction/dof-vector.hh
@@ -26,7 +26,7 @@ class ConstDofVector
 {
   static_assert(XT::LA::is_vector<Vector>::value, "");
 
-  using ThisType = ConstDofVector<Vector, GridView>;
+  using ThisType = ConstDofVector;
 
 public:
   using VectorType = Vector;
@@ -67,7 +67,7 @@ class DofVector : public ConstDofVector<Vector, GridView>
 {
   static_assert(XT::LA::is_vector<Vector>::value, "");
 
-  using ThisType = DofVector<Vector, GridView>;
+  using ThisType = DofVector;
   using BaseType = ConstDofVector<Vector, GridView>;
 
 public:
diff --git a/dune/gdt/functionals/l2.hh b/dune/gdt/functionals/l2.hh
index 72d8f5c8a481a2208eafceb09bd4f850873c32c2..2f3faafea297032bc11fab19e5b0b4e2e4e27fc3 100644
--- a/dune/gdt/functionals/l2.hh
+++ b/dune/gdt/functionals/l2.hh
@@ -40,7 +40,7 @@ namespace GDT {
 template <class V, class GV, size_t r = 1, size_t rC = 1, class F = double, class AssemblyGridView = GV>
 class L2VolumeVectorFunctional : public VectorBasedFunctional<V, GV, r, rC, F, AssemblyGridView>
 {
-  using ThisType = L2VolumeVectorFunctional<V, GV, r, rC, F, AssemblyGridView>;
+  using ThisType = L2VolumeVectorFunctional;
   using BaseType = VectorBasedFunctional<V, GV, r, rC, F, AssemblyGridView>;
 
 public:
diff --git a/dune/gdt/functionals/localizable-functional.hh b/dune/gdt/functionals/localizable-functional.hh
index a4105dfc8623ad8e12983160353bcc66faf56c2d..90c2224642df3580cd9432ad0d83403c23ecfbe7 100644
--- a/dune/gdt/functionals/localizable-functional.hh
+++ b/dune/gdt/functionals/localizable-functional.hh
@@ -31,7 +31,7 @@ class LocalizableFunctionalBase : public XT::Grid::Walker<GridView>
 {
   static_assert(XT::Grid::is_view<GridView>::value, "");
 
-  using ThisType = LocalizableFunctionalBase<GridView, range_dim, range_dim_cols, RangeField>;
+  using ThisType = LocalizableFunctionalBase;
   using BaseType = XT::Grid::Walker<GridView>;
 
 public:
diff --git a/dune/gdt/functionals/vector-based.hh b/dune/gdt/functionals/vector-based.hh
index e82925e9fbe3ee8671d0753255790f179289152e..0841e094fbbd5199754265a3ffc1414eb94f46c7 100644
--- a/dune/gdt/functionals/vector-based.hh
+++ b/dune/gdt/functionals/vector-based.hh
@@ -25,8 +25,6 @@
 
 #include "interfaces.hh"
 
-// clang-format off
-// see https://github.com/dune-community/dune-gdt/issues/142
 namespace Dune {
 namespace GDT {
 
@@ -45,7 +43,7 @@ class ConstVectorBasedFunctional : public FunctionalInterface<V, GV, r, rC, F>
   // All other types are checked elsewhere.
   static_assert(XT::LA::is_vector<V>::value, "");
 
-  using ThisType = ConstVectorBasedFunctional<V, GV, r, rC, F>;
+  using ThisType = ConstVectorBasedFunctional;
   using FunctionalBaseType = FunctionalInterface<V, GV, r, rC, F>;
 
 public:
@@ -145,7 +143,7 @@ class VectorBasedFunctional
   static_assert(std::is_same<XT::Grid::extract_entity_t<GV>, XT::Grid::extract_entity_t<AssemblyGridView>>::value,
                 "We cannot handle different element types!");
 
-  using ThisType = VectorBasedFunctional<V, GV, r, rC, F, AssemblyGridView>;
+  using ThisType = VectorBasedFunctional;
   using VectorStorage = XT::Common::StorageProvider<V>;
   using FunctionalBaseType = ConstVectorBasedFunctional<V, GV, r, rC, F>;
   using WalkerBaseType = XT::Grid::Walker<AssemblyGridView>;
@@ -277,6 +275,4 @@ make_vector_functional(const SpaceInterface<GV, r, rC, F>& space)
 } // namespace GDT
 } // namespace Dune
 
-// clang-format on
-
 #endif // DUNE_GDT_FUNCTIONALS_VECTOR_BASED_HH
diff --git a/dune/gdt/interpolations/default.hh b/dune/gdt/interpolations/default.hh
index b9715b16a34e8aad15d3290e48b067daed34f938..1ce16f3f4d133fc660801ce9e62820ba03339cb3 100644
--- a/dune/gdt/interpolations/default.hh
+++ b/dune/gdt/interpolations/default.hh
@@ -17,19 +17,75 @@
 #include <dune/grid/common/rangegenerators.hh>
 
 #include <dune/xt/grid/type_traits.hh>
+#include <dune/xt/grid/functors/interfaces.hh>
+#include <dune/xt/grid/walker.hh>
+
 #include <dune/xt/functions/interfaces/grid-function.hh>
 #include <dune/xt/functions/interfaces/function.hh>
 #include <dune/xt/functions/generic/function.hh>
 
 #include <dune/gdt/discretefunction/default.hh>
 
-
 namespace Dune {
 namespace GDT {
 
 
 // ### Variants for GridFunctionInterface
+template <class GV, size_t r, size_t rC, class R, class V, class IGV>
+class DefaultInterpolationElementFunctor : public XT::Grid::ElementFunctor<IGV>
+{
+  using BaseType = typename XT::Grid::ElementFunctor<IGV>;
+
+public:
+  using typename BaseType::E;
+  using SourceType = XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GV>, r, rC, R>;
+  using LocalSourceType = typename SourceType::LocalFunctionType;
+  using TargetType = DiscreteFunction<V, GV, r, rC, R>;
+  using LocalDofVectorType = typename TargetType::DofVectorType::LocalDofVectorType;
+  using TargetBasisType = typename TargetType::SpaceType::GlobalBasisType::LocalizedType;
+
+  DefaultInterpolationElementFunctor(const SourceType& source, TargetType& target)
+    : source_(source)
+    , target_(target)
+    , local_dof_vector_(target.dofs().localize())
+    , local_source_(source_.local_function())
+    , target_basis_(target.space().basis().localize())
+  {
+    DUNE_THROW_IF(target_.space().type() == SpaceType::raviart_thomas,
+                  Exceptions::interpolation_error,
+                  "Use the correct one from interpolations/raviart-thomas.hh instead!");
+  }
 
+  DefaultInterpolationElementFunctor(const DefaultInterpolationElementFunctor& other)
+    : BaseType(other)
+    , source_(other.source_)
+    , target_(other.target_)
+    , local_dof_vector_(target_.dofs().localize())
+    , local_source_(source_.local_function())
+    , target_basis_(target_.space().basis().localize())
+  {}
+
+  XT::Grid::ElementFunctor<IGV>* copy() override final
+  {
+    return new DefaultInterpolationElementFunctor(*this);
+  }
+
+  void apply_local(const E& element) override final
+  {
+    local_source_->bind(element);
+    local_dof_vector_.bind(element);
+    target_basis_->bind(element);
+    target_basis_->interpolate(
+        [&](const auto& xx) { return local_source_->evaluate(xx); }, local_source_->order(), local_dof_vector_);
+  }
+
+private:
+  const SourceType& source_;
+  TargetType& target_;
+  LocalDofVectorType local_dof_vector_;
+  std::unique_ptr<LocalSourceType> local_source_;
+  std::unique_ptr<TargetBasisType> target_basis_;
+};
 
 /**
  * \brief Interpolates a localizable function within a given space [most general variant].
@@ -46,26 +102,18 @@ namespace GDT {
  *
  * \note This might not be correct for all spaces, in particular if source is not continuous.
  */
-template <class GV, size_t r, size_t rC, class R, class V, class IGV>
-std::enable_if_t<std::is_same<XT::Grid::extract_entity_t<GV>, typename IGV::Grid::template Codim<0>::Entity>::value,
+template <class GV, size_t r, size_t rC, class R, class V, class IGVT>
+std::enable_if_t<std::is_same<XT::Grid::extract_entity_t<GV>, typename IGVT::Grid::template Codim<0>::Entity>::value,
                  void>
 default_interpolation(const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GV>, r, rC, R>& source,
                       DiscreteFunction<V, GV, r, rC, R>& target,
-                      const GridView<IGV>& interpolation_grid_view)
+                      const GridView<IGVT>& interpolation_grid_view)
 {
-  DUNE_THROW_IF(target.space().type() == SpaceType::raviart_thomas,
-                Exceptions::interpolation_error,
-                "Use the correct one from interpolations/raviart-thomas.hh instead!");
-  auto local_dof_vector = target.dofs().localize();
-  auto local_source = source.local_function();
-  auto target_basis = target.space().basis().localize();
-  for (auto&& element : elements(interpolation_grid_view)) {
-    local_source->bind(element);
-    local_dof_vector.bind(element);
-    target_basis->bind(element);
-    target_basis->interpolate(
-        [&](const auto& xx) { return local_source->evaluate(xx); }, local_source->order(), local_dof_vector);
-  }
+  DefaultInterpolationElementFunctor<GV, r, rC, R, V, GridView<IGVT>> functor(source, target);
+  auto walker = XT::Grid::Walker<GridView<IGVT>>(interpolation_grid_view);
+  walker.append(functor);
+  // Basis functions other than FV do not seem to be thread safe. TODO: fix
+  walker.walk(target.space().type() == SpaceType::finite_volume);
 } // ... default_interpolation(...)
 
 
@@ -84,14 +132,14 @@ void default_interpolation(const XT::Functions::GridFunctionInterface<XT::Grid::
 /**
  * \brief Interpolates a localizable function within a given space [creates a suitable target function].
  **/
-template <class VectorType, class GV, size_t r, size_t rC, class R, class IGV>
+template <class VectorType, class GV, size_t r, size_t rC, class R, class IGVT>
 std::enable_if_t<
     XT::LA::is_vector<VectorType>::value
-        && std::is_same<XT::Grid::extract_entity_t<GV>, typename IGV::Grid::template Codim<0>::Entity>::value,
+        && std::is_same<XT::Grid::extract_entity_t<GV>, typename IGVT::Grid::template Codim<0>::Entity>::value,
     DiscreteFunction<VectorType, GV, r, rC, R>>
 default_interpolation(const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GV>, r, rC, R>& source,
                       const SpaceInterface<GV, r, rC, R>& target_space,
-                      const GridView<IGV>& interpolation_grid_view)
+                      const GridView<IGVT>& interpolation_grid_view)
 {
   auto target_function = make_discrete_function<VectorType>(target_space);
   default_interpolation(source, target_function, interpolation_grid_view);
@@ -122,12 +170,12 @@ default_interpolation(const XT::Functions::GridFunctionInterface<XT::Grid::extra
  *
  * Simply calls as_grid_function<>() and redirects to the appropriate default_interpolation() function.
  */
-template <class GV, size_t r, size_t rC, class R, class V, class IGV>
-std::enable_if_t<std::is_same<XT::Grid::extract_entity_t<GV>, typename IGV::Grid::template Codim<0>::Entity>::value,
+template <class GV, size_t r, size_t rC, class R, class V, class IGVT>
+std::enable_if_t<std::is_same<XT::Grid::extract_entity_t<GV>, typename IGVT::Grid::template Codim<0>::Entity>::value,
                  void>
-default_interpolation(const XT::Functions::FunctionInterface<GridView<IGV>::dimension, r, rC, R>& source,
+default_interpolation(const XT::Functions::FunctionInterface<GridView<IGVT>::dimension, r, rC, R>& source,
                       DiscreteFunction<V, GV, r, rC, R>& target,
-                      const GridView<IGV>& interpolation_grid_view)
+                      const GridView<IGVT>& interpolation_grid_view)
 {
   default_interpolation(source.as_grid_function(interpolation_grid_view), target, interpolation_grid_view);
 }
@@ -148,14 +196,14 @@ void default_interpolation(const XT::Functions::FunctionInterface<GV::dimension,
 /**
  * \brief Interpolates a function within a given space [creates a suitable target function].
  **/
-template <class VectorType, class GV, size_t r, size_t rC, class R, class IGV>
+template <class VectorType, class GV, size_t r, size_t rC, class R, class IGVT>
 std::enable_if_t<
     XT::LA::is_vector<VectorType>::value
-        && std::is_same<XT::Grid::extract_entity_t<GV>, typename IGV::Grid::template Codim<0>::Entity>::value,
+        && std::is_same<XT::Grid::extract_entity_t<GV>, typename IGVT::Grid::template Codim<0>::Entity>::value,
     DiscreteFunction<VectorType, GV, r, rC, R>>
-default_interpolation(const XT::Functions::FunctionInterface<GridView<IGV>::dimension, r, rC, R>& source,
+default_interpolation(const XT::Functions::FunctionInterface<GridView<IGVT>::dimension, r, rC, R>& source,
                       const SpaceInterface<GV, r, rC, R>& target_space,
-                      const GridView<IGV>& interpolation_grid_view)
+                      const GridView<IGVT>& interpolation_grid_view)
 {
   return default_interpolation<VectorType>(
       source.as_grid_function(interpolation_grid_view), target_space, interpolation_grid_view);
@@ -183,19 +231,19 @@ default_interpolation(const XT::Functions::FunctionInterface<GV::dimension, r, r
  *
  * Simply creates a XT::Functions::GenericFunction and redirects to the appropriate default_interpolation() function.
  */
-template <class GV, size_t r, size_t rC, class R, class V, class IGV>
-std::enable_if_t<std::is_same<XT::Grid::extract_entity_t<GV>, typename IGV::Grid::template Codim<0>::Entity>::value,
+template <class GV, size_t r, size_t rC, class R, class V, class IGVT>
+std::enable_if_t<std::is_same<XT::Grid::extract_entity_t<GV>, typename IGVT::Grid::template Codim<0>::Entity>::value,
                  void>
 default_interpolation(
     const int source_order,
-    const std::function<typename XT::Functions::GenericFunction<GridView<IGV>::dimension, r, rC, R>::RangeReturnType(
-        const typename XT::Functions::GenericFunction<GridView<IGV>::dimension, r, rC, R>::DomainType&,
+    const std::function<typename XT::Functions::GenericFunction<GridView<IGVT>::dimension, r, rC, R>::RangeReturnType(
+        const typename XT::Functions::GenericFunction<GridView<IGVT>::dimension, r, rC, R>::DomainType&,
         const XT::Common::Parameter&)> source_evaluate_lambda,
     DiscreteFunction<V, GV, r, rC, R>& target,
-    const GridView<IGV>& interpolation_grid_view)
+    const GridView<IGVT>& interpolation_grid_view)
 {
   default_interpolation(
-      XT::Functions::GenericFunction<GridView<IGV>::dimension, r, rC, R>(source_order, source_evaluate_lambda),
+      XT::Functions::GenericFunction<GridView<IGVT>::dimension, r, rC, R>(source_order, source_evaluate_lambda),
       target,
       interpolation_grid_view);
 }
@@ -222,21 +270,21 @@ void default_interpolation(
  * \brief Interpolates a function given as a lambda expression within a given space [creates a suitable target
  *        function].
  **/
-template <class VectorType, class GV, size_t r, size_t rC, class R, class IGV>
+template <class VectorType, class GV, size_t r, size_t rC, class R, class IGVT>
 std::enable_if_t<
     XT::LA::is_vector<VectorType>::value
-        && std::is_same<XT::Grid::extract_entity_t<GV>, typename IGV::Grid::template Codim<0>::Entity>::value,
+        && std::is_same<XT::Grid::extract_entity_t<GV>, typename IGVT::Grid::template Codim<0>::Entity>::value,
     DiscreteFunction<VectorType, GV, r, rC, R>>
 default_interpolation(
     const int source_order,
-    const std::function<typename XT::Functions::GenericFunction<GridView<IGV>::dimension, r, rC, R>::RangeReturnType(
-        const typename XT::Functions::GenericFunction<GridView<IGV>::dimension, r, rC, R>::DomainType&,
+    const std::function<typename XT::Functions::GenericFunction<GridView<IGVT>::dimension, r, rC, R>::RangeReturnType(
+        const typename XT::Functions::GenericFunction<GridView<IGVT>::dimension, r, rC, R>::DomainType&,
         const XT::Common::Parameter&)> source_evaluate_lambda,
     const SpaceInterface<GV, r, rC, R>& target_space,
-    const GridView<IGV>& interpolation_grid_view)
+    const GridView<IGVT>& interpolation_grid_view)
 {
   return default_interpolation<VectorType>(
-      XT::Functions::GenericFunction<GridView<IGV>::dimension, r, rC, R>(source_order, source_evaluate_lambda),
+      XT::Functions::GenericFunction<GridView<IGVT>::dimension, r, rC, R>(source_order, source_evaluate_lambda),
       target_space,
       interpolation_grid_view);
 }
diff --git a/dune/gdt/local/assembler/bilinear-form-accumulators.hh b/dune/gdt/local/assembler/bilinear-form-accumulators.hh
index 14c0b36d7a0b4d34ae972be5a11638c5e824210c..e300e65690cf0093edf1594298e4547756360be0 100644
--- a/dune/gdt/local/assembler/bilinear-form-accumulators.hh
+++ b/dune/gdt/local/assembler/bilinear-form-accumulators.hh
@@ -48,7 +48,7 @@ class LocalElementBilinearFormAccumulator
 {
   static_assert(XT::Grid::is_view<GV>::value, "");
 
-  using ThisType = LocalElementBilinearFormAccumulator<GV, s_r, s_rC, SF, R, r_r, r_rC, RF>;
+  using ThisType = LocalElementBilinearFormAccumulator;
   using BaseType = XT::Grid::ElementFunctor<GV>;
   using Propagator =
       XT::Common::ThreadResultPropagator<LocalElementBilinearFormAccumulator<GV, s_r, s_rC, SF, R, r_r, r_rC, RF>, R>;
diff --git a/dune/gdt/local/assembler/bilinear-form-assemblers.hh b/dune/gdt/local/assembler/bilinear-form-assemblers.hh
index 526bb127a5d9416f6cabe63470602f14cd43da90..089ce82d541a26c837330c7be23deba1bf2b7ab5 100644
--- a/dune/gdt/local/assembler/bilinear-form-assemblers.hh
+++ b/dune/gdt/local/assembler/bilinear-form-assemblers.hh
@@ -36,7 +36,7 @@ class LocalElementBilinearFormAssembler : public XT::Grid::ElementFunctor<GridVi
   static_assert(XT::LA::is_matrix<Matrix>::value, "");
   static_assert(XT::Grid::is_view<GridView>::value, "");
 
-  using ThisType = LocalElementBilinearFormAssembler<Matrix, GridView, t_r, t_rC, TR, TGV, AGV, a_r, a_rC, AR>;
+  using ThisType = LocalElementBilinearFormAssembler;
   using BaseType = XT::Grid::ElementFunctor<GridView>;
 
 public:
@@ -53,41 +53,41 @@ public:
                                     MatrixType& global_matrix,
                                     const XT::Common::Parameter& param = {})
     : BaseType()
-    , test_space_(test_space)
-    , ansatz_space_(ansatz_space)
+    , test_space_(test_space.copy())
+    , ansatz_space_(ansatz_space.copy())
     , local_bilinear_form_(local_two_form.copy())
     , global_matrix_(global_matrix)
     , param_(param)
     , scaling_(param_.has_key("matrixoperator.scaling") ? param_.get("matrixoperator.scaling").at(0) : 1.)
-    , local_matrix_(test_space_.mapper().max_local_size(), ansatz_space_.mapper().max_local_size())
-    , global_test_indices_(test_space_.mapper().max_local_size())
-    , global_ansatz_indices_(ansatz_space_.mapper().max_local_size())
-    , test_basis_(test_space_.basis().localize())
-    , ansatz_basis_(ansatz_space_.basis().localize())
+    , local_matrix_(test_space_->mapper().max_local_size(), ansatz_space_->mapper().max_local_size())
+    , global_test_indices_(test_space_->mapper().max_local_size())
+    , global_ansatz_indices_(ansatz_space_->mapper().max_local_size())
+    , test_basis_(test_space_->basis().localize())
+    , ansatz_basis_(ansatz_space_->basis().localize())
   {
-    DUNE_THROW_IF(global_matrix_.rows() != test_space_.mapper().size(),
+    DUNE_THROW_IF(global_matrix_.rows() != test_space_->mapper().size(),
                   XT::Common::Exceptions::shapes_do_not_match,
                   "global_matrix_.rows() = " << global_matrix_.rows() << "\n  "
-                                             << "test_space_.mapper().size()" << test_space_.mapper().size());
-    DUNE_THROW_IF(global_matrix_.cols() != ansatz_space_.mapper().size(),
+                                             << "test_space_->mapper().size()" << test_space_->mapper().size());
+    DUNE_THROW_IF(global_matrix_.cols() != ansatz_space_->mapper().size(),
                   XT::Common::Exceptions::shapes_do_not_match,
                   "global_matrix_.cols() = " << global_matrix_.cols() << "\n  "
-                                             << "ansatz_space_.mapper().size()" << ansatz_space_.mapper().size());
+                                             << "ansatz_space_->mapper().size()" << ansatz_space_->mapper().size());
   }
 
   LocalElementBilinearFormAssembler(const ThisType& other)
     : BaseType()
-    , test_space_(other.test_space_)
-    , ansatz_space_(other.ansatz_space_)
+    , test_space_(other.test_space_->copy())
+    , ansatz_space_(other.ansatz_space_->copy())
     , local_bilinear_form_(other.local_bilinear_form_->copy())
     , global_matrix_(other.global_matrix_)
     , param_(other.param_)
     , scaling_(other.scaling_)
-    , local_matrix_(test_space_.mapper().max_local_size(), ansatz_space_.mapper().max_local_size())
-    , global_test_indices_(test_space_.mapper().max_local_size())
-    , global_ansatz_indices_(ansatz_space_.mapper().max_local_size())
-    , test_basis_(test_space_.basis().localize())
-    , ansatz_basis_(ansatz_space_.basis().localize())
+    , local_matrix_(test_space_->mapper().max_local_size(), ansatz_space_->mapper().max_local_size())
+    , global_test_indices_(test_space_->mapper().max_local_size())
+    , global_ansatz_indices_(ansatz_space_->mapper().max_local_size())
+    , test_basis_(test_space_->basis().localize())
+    , ansatz_basis_(ansatz_space_->basis().localize())
   {}
 
   LocalElementBilinearFormAssembler(ThisType&& source) = default;
@@ -104,8 +104,8 @@ public:
     ansatz_basis_->bind(element);
     local_bilinear_form_->apply2(*test_basis_, *ansatz_basis_, local_matrix_, param_);
     // copy local matrix to global matrix
-    test_space_.mapper().global_indices(element, global_test_indices_);
-    ansatz_space_.mapper().global_indices(element, global_ansatz_indices_);
+    test_space_->mapper().global_indices(element, global_test_indices_);
+    ansatz_space_->mapper().global_indices(element, global_ansatz_indices_);
     for (size_t ii = 0; ii < test_basis_->size(param_); ++ii)
       for (size_t jj = 0; jj < ansatz_basis_->size(param_); ++jj)
         global_matrix_.add_to_entry(
@@ -113,8 +113,8 @@ public:
   } // ... apply_local(...)
 
 private:
-  const TestSpaceType& test_space_;
-  const AnsatzSpaceType& ansatz_space_;
+  const std::unique_ptr<TestSpaceType> test_space_;
+  const std::unique_ptr<AnsatzSpaceType> ansatz_space_;
   const std::unique_ptr<LocalBilinearFormType> local_bilinear_form_;
   MatrixType& global_matrix_;
   XT::Common::Parameter param_;
@@ -142,7 +142,7 @@ class LocalIntersectionBilinearFormAssembler : public XT::Grid::IntersectionFunc
   static_assert(XT::LA::is_matrix<Matrix>::value, "");
   static_assert(XT::Grid::is_view<GridView>::value, "");
 
-  using ThisType = LocalIntersectionBilinearFormAssembler<Matrix, GridView, t_r, t_rC, TR, TGV, AGV, a_r, a_rC, AR>;
+  using ThisType = LocalIntersectionBilinearFormAssembler;
   using BaseType = XT::Grid::IntersectionFunctor<GridView>;
 
 public:
@@ -161,55 +161,55 @@ public:
                                          MatrixType& global_matrix,
                                          const XT::Common::Parameter& param = {})
     : BaseType()
-    , test_space_(test_space)
-    , ansatz_space_(ansatz_space)
+    , test_space_(test_space.copy())
+    , ansatz_space_(ansatz_space.copy())
     , local_bilinear_form_(local_two_form.copy())
     , global_matrix_(global_matrix)
     , param_(param)
     , scaling_(param_.has_key("matrixoperator.scaling") ? param_.get("matrixoperator.scaling").at(0) : 1.)
-    , local_matrix_in_in_(test_space_.mapper().max_local_size(), ansatz_space_.mapper().max_local_size())
-    , local_matrix_in_out_(test_space_.mapper().max_local_size(), ansatz_space_.mapper().max_local_size())
-    , local_matrix_out_in_(test_space_.mapper().max_local_size(), ansatz_space_.mapper().max_local_size())
-    , local_matrix_out_out_(test_space_.mapper().max_local_size(), ansatz_space_.mapper().max_local_size())
-    , global_test_indices_in_(test_space_.mapper().max_local_size())
-    , global_test_indices_out_(test_space_.mapper().max_local_size())
-    , global_ansatz_indices_in_(ansatz_space_.mapper().max_local_size())
-    , global_ansatz_indices_out_(ansatz_space_.mapper().max_local_size())
-    , test_basis_inside_(test_space_.basis().localize())
-    , test_basis_outside_(test_space_.basis().localize())
-    , ansatz_basis_inside_(ansatz_space_.basis().localize())
-    , ansatz_basis_outside_(ansatz_space_.basis().localize())
+    , local_matrix_in_in_(test_space_->mapper().max_local_size(), ansatz_space_->mapper().max_local_size())
+    , local_matrix_in_out_(test_space_->mapper().max_local_size(), ansatz_space_->mapper().max_local_size())
+    , local_matrix_out_in_(test_space_->mapper().max_local_size(), ansatz_space_->mapper().max_local_size())
+    , local_matrix_out_out_(test_space_->mapper().max_local_size(), ansatz_space_->mapper().max_local_size())
+    , global_test_indices_in_(test_space_->mapper().max_local_size())
+    , global_test_indices_out_(test_space_->mapper().max_local_size())
+    , global_ansatz_indices_in_(ansatz_space_->mapper().max_local_size())
+    , global_ansatz_indices_out_(ansatz_space_->mapper().max_local_size())
+    , test_basis_inside_(test_space_->basis().localize())
+    , test_basis_outside_(test_space_->basis().localize())
+    , ansatz_basis_inside_(ansatz_space_->basis().localize())
+    , ansatz_basis_outside_(ansatz_space_->basis().localize())
   {
-    DUNE_THROW_IF(global_matrix_.rows() != test_space_.mapper().size(),
+    DUNE_THROW_IF(global_matrix_.rows() != test_space_->mapper().size(),
                   XT::Common::Exceptions::shapes_do_not_match,
                   "global_matrix_.rows() = " << global_matrix_.rows() << "\n  "
-                                             << "test_space_.mapper().size()" << test_space_.mapper().size());
-    DUNE_THROW_IF(global_matrix_.cols() != ansatz_space_.mapper().size(),
+                                             << "test_space_->mapper().size()" << test_space_->mapper().size());
+    DUNE_THROW_IF(global_matrix_.cols() != ansatz_space_->mapper().size(),
                   XT::Common::Exceptions::shapes_do_not_match,
                   "global_matrix_.cols() = " << global_matrix_.cols() << "\n  "
-                                             << "ansatz_space_.mapper().size()" << ansatz_space_.mapper().size());
+                                             << "ansatz_space_->mapper().size()" << ansatz_space_->mapper().size());
   }
 
   LocalIntersectionBilinearFormAssembler(const ThisType& other)
     : BaseType()
-    , test_space_(other.test_space_)
-    , ansatz_space_(other.ansatz_space_)
+    , test_space_(other.test_space_->copy())
+    , ansatz_space_(other.ansatz_space_->copy())
     , local_bilinear_form_(other.local_bilinear_form_->copy())
     , global_matrix_(other.global_matrix_)
     , param_(other.param_)
     , scaling_(other.scaling_)
-    , local_matrix_in_in_(test_space_.mapper().max_local_size(), ansatz_space_.mapper().max_local_size())
-    , local_matrix_in_out_(test_space_.mapper().max_local_size(), ansatz_space_.mapper().max_local_size())
-    , local_matrix_out_in_(test_space_.mapper().max_local_size(), ansatz_space_.mapper().max_local_size())
-    , local_matrix_out_out_(test_space_.mapper().max_local_size(), ansatz_space_.mapper().max_local_size())
-    , global_test_indices_in_(test_space_.mapper().max_local_size())
-    , global_test_indices_out_(test_space_.mapper().max_local_size())
-    , global_ansatz_indices_in_(ansatz_space_.mapper().max_local_size())
-    , global_ansatz_indices_out_(ansatz_space_.mapper().max_local_size())
-    , test_basis_inside_(test_space_.basis().localize())
-    , test_basis_outside_(test_space_.basis().localize())
-    , ansatz_basis_inside_(ansatz_space_.basis().localize())
-    , ansatz_basis_outside_(ansatz_space_.basis().localize())
+    , local_matrix_in_in_(test_space_->mapper().max_local_size(), ansatz_space_->mapper().max_local_size())
+    , local_matrix_in_out_(test_space_->mapper().max_local_size(), ansatz_space_->mapper().max_local_size())
+    , local_matrix_out_in_(test_space_->mapper().max_local_size(), ansatz_space_->mapper().max_local_size())
+    , local_matrix_out_out_(test_space_->mapper().max_local_size(), ansatz_space_->mapper().max_local_size())
+    , global_test_indices_in_(test_space_->mapper().max_local_size())
+    , global_test_indices_out_(test_space_->mapper().max_local_size())
+    , global_ansatz_indices_in_(ansatz_space_->mapper().max_local_size())
+    , global_ansatz_indices_out_(ansatz_space_->mapper().max_local_size())
+    , test_basis_inside_(test_space_->basis().localize())
+    , test_basis_outside_(test_space_->basis().localize())
+    , ansatz_basis_inside_(ansatz_space_->basis().localize())
+    , ansatz_basis_outside_(ansatz_space_->basis().localize())
   {}
 
   LocalIntersectionBilinearFormAssembler(ThisType&& source) = default;
@@ -239,10 +239,10 @@ public:
                                  local_matrix_out_out_,
                                  param_);
     // copy local matrices to global matrix
-    test_space_.mapper().global_indices(inside_element, global_test_indices_in_);
-    test_space_.mapper().global_indices(outside_element, global_test_indices_out_);
-    ansatz_space_.mapper().global_indices(inside_element, global_ansatz_indices_in_);
-    ansatz_space_.mapper().global_indices(outside_element, global_ansatz_indices_out_);
+    test_space_->mapper().global_indices(inside_element, global_test_indices_in_);
+    test_space_->mapper().global_indices(outside_element, global_test_indices_out_);
+    ansatz_space_->mapper().global_indices(inside_element, global_ansatz_indices_in_);
+    ansatz_space_->mapper().global_indices(outside_element, global_ansatz_indices_out_);
     for (size_t ii = 0; ii < test_basis_inside_->size(param_); ++ii) {
       for (size_t jj = 0; jj < ansatz_basis_inside_->size(param_); ++jj)
         global_matrix_.add_to_entry(
@@ -262,8 +262,8 @@ public:
   } // ... apply_local(...)
 
 private:
-  const TestSpaceType& test_space_;
-  const AnsatzSpaceType& ansatz_space_;
+  const std::unique_ptr<TestSpaceType> test_space_;
+  const std::unique_ptr<AnsatzSpaceType> ansatz_space_;
   const std::unique_ptr<LocalBilinearFormType> local_bilinear_form_;
   MatrixType& global_matrix_;
   XT::Common::Parameter param_;
diff --git a/dune/gdt/local/assembler/functional-accumulators.hh b/dune/gdt/local/assembler/functional-accumulators.hh
index 0d6d7809289b339d858fa71ca11363a256026c31..c152f0103ae4405deadeccaa9bc30631298d0807 100644
--- a/dune/gdt/local/assembler/functional-accumulators.hh
+++ b/dune/gdt/local/assembler/functional-accumulators.hh
@@ -40,7 +40,7 @@ class LocalElementFunctionalAccumulator
 {
   static_assert(XT::Grid::is_view<GridView>::value, "");
 
-  using ThisType = LocalElementFunctionalAccumulator<GridView, r, rC, R, F>;
+  using ThisType = LocalElementFunctionalAccumulator;
   using BaseType = XT::Grid::ElementFunctor<GridView>;
   using Propagator = XT::Common::ThreadResultPropagator<LocalElementFunctionalAccumulator<GridView, r, rC, R, F>, F>;
   friend Propagator;
diff --git a/dune/gdt/local/assembler/functional-assemblers.hh b/dune/gdt/local/assembler/functional-assemblers.hh
index 56eadff3b33ea9ae1d65d19b6e741352ce93884d..841fb6adece82dd916ec50d0492f5398f48997b5 100644
--- a/dune/gdt/local/assembler/functional-assemblers.hh
+++ b/dune/gdt/local/assembler/functional-assemblers.hh
@@ -28,7 +28,7 @@ class LocalElementFunctionalAssembler : public XT::Grid::ElementFunctor<GridView
   static_assert(XT::Grid::is_view<GridView>::value, "");
   static_assert(XT::Grid::is_view<SpaceGridView>::value, "");
 
-  using ThisType = LocalElementFunctionalAssembler<Vector, GridView, r, rC, R, SpaceGridView>;
+  using ThisType = LocalElementFunctionalAssembler;
   using BaseType = XT::Grid::ElementFunctor<GridView>;
 
 public:
@@ -42,29 +42,29 @@ public:
                                   const LocalFunctionalType& local_functional,
                                   VectorType& global_vector,
                                   const XT::Common::Parameter& param = {})
-    : space_(space)
+    : space_(space.copy())
     , local_functional_(local_functional.copy())
     , global_vector_(global_vector)
     , param_(param)
-    , local_vector_(space_.mapper().max_local_size())
-    , global_indices_(space_.mapper().max_local_size())
-    , basis_(space_.basis().localize())
+    , local_vector_(space_->mapper().max_local_size())
+    , global_indices_(space_->mapper().max_local_size())
+    , basis_(space_->basis().localize())
   {
-    DUNE_THROW_IF(global_vector_.size() != space_.mapper().size(),
+    DUNE_THROW_IF(global_vector_.size() != space_->mapper().size(),
                   XT::Common::Exceptions::shapes_do_not_match,
                   "global_vector.size() = " << global_vector_.size() << "\n  "
-                                            << "space.mapper().size()" << space_.mapper().size());
+                                            << "space.mapper().size()" << space_->mapper().size());
   }
 
   LocalElementFunctionalAssembler(const ThisType& other)
     : BaseType()
-    , space_(other.space_)
+    , space_(other.space_->copy())
     , local_functional_(other.local_functional_->copy())
     , global_vector_(other.global_vector_)
     , param_(other.param_)
     , local_vector_(other.local_vector_)
     , global_indices_(other.global_indices_)
-    , basis_(space_.basis().localize())
+    , basis_(space_->basis().localize())
   {}
 
   LocalElementFunctionalAssembler(ThisType&& source) = default;
@@ -80,13 +80,13 @@ public:
     basis_->bind(element);
     local_functional_->apply(*basis_, local_vector_, param_);
     // copy local vector to global
-    space_.mapper().global_indices(element, global_indices_);
+    space_->mapper().global_indices(element, global_indices_);
     for (size_t jj = 0; jj < basis_->size(param_); ++jj)
       global_vector_.add_to_entry(global_indices_[jj], local_vector_[jj]);
   }
 
 private:
-  const SpaceType& space_;
+  std::unique_ptr<const SpaceType> space_;
   std::unique_ptr<LocalFunctionalType> local_functional_;
   VectorType& global_vector_;
   XT::Common::Parameter param_;
diff --git a/dune/gdt/local/assembler/operator-applicators.hh b/dune/gdt/local/assembler/operator-applicators.hh
index afadc887836ec544c1b4994255e93cc1d0c24cf0..fdfd45a27b22a1315e8b9e44d31ae5ea3ab74db9 100644
--- a/dune/gdt/local/assembler/operator-applicators.hh
+++ b/dune/gdt/local/assembler/operator-applicators.hh
@@ -51,7 +51,7 @@ class LocalElementOperatorApplicator : public XT::Grid::ElementFunctor<AssemblyG
   static_assert(std::is_same<XT::Grid::extract_entity_t<AssemblyGridView>, XT::Grid::extract_entity_t<SGV>>::value, "");
   static_assert(std::is_same<XT::Grid::extract_entity_t<AssemblyGridView>, XT::Grid::extract_entity_t<RGV>>::value, "");
 
-  using ThisType = LocalElementOperatorApplicator<AssemblyGridView, SV, s_r, s_rC, SF, SGV, r_r, r_rC, RF, RGV, RV>;
+  using ThisType = LocalElementOperatorApplicator;
   using BaseType = XT::Grid::ElementFunctor<AssemblyGridView>;
 
 public:
@@ -62,18 +62,16 @@ public:
   using LocalOperatorType = LocalElementOperatorInterface<SV, SGV, s_r, s_rC, SF, r_r, r_rC, RF, RGV, RV>;
 
   LocalElementOperatorApplicator(const LocalOperatorType& local_operator,
-                                 const SourceType& source,
                                  RangeType& range,
                                  const XT::Common::Parameter& param = {})
     : local_operator_(local_operator.copy())
-    , source_(source)
     , range_(range)
     , param_(param)
     , local_range_(range_.local_discrete_function())
   {}
 
   LocalElementOperatorApplicator(const ThisType& other)
-    : LocalElementOperatorApplicator(*other.local_operator_, other.source_, other.range_, other.param_)
+    : LocalElementOperatorApplicator(*other.local_operator_, other.range_, other.param_)
   {}
 
   BaseType* copy() override final
@@ -84,12 +82,11 @@ public:
   void apply_local(const ElementType& element) override final
   {
     local_range_->bind(element);
-    local_operator_->apply(source_, *local_range_, param_);
+    local_operator_->apply(*local_range_, param_);
   }
 
 private:
   const std::unique_ptr<LocalOperatorType> local_operator_;
-  const SourceType& source_;
   RangeType& range_;
   const XT::Common::Parameter param_;
   std::unique_ptr<typename RangeType::LocalDiscreteFunctionType> local_range_;
@@ -103,12 +100,11 @@ template <class SV, class GV, size_t s_r, size_t s_rC, class SF, size_t r_r, siz
 std::unique_ptr<LocalElementOperatorApplicator<GV, SV, s_r, s_rC, SF, GV, r_r, r_rC, RF, GV, RV>>
 make_local_element_operator_applicator(
     const LocalElementOperatorInterface<SV, GV, s_r, s_rC, SF, r_r, r_rC, RF, GV, RV>& local_operator,
-    const ConstDiscreteFunction<SV, GV, s_r, s_rC, SF>& source,
     DiscreteFunction<RV, GV, r_r, r_rC, RF>& range,
     const XT::Common::Parameter& param = {})
 {
   return std::make_unique<LocalElementOperatorApplicator<GV, SV, s_r, s_rC, SF, GV, r_r, r_rC, RF, GV, RV>>(
-      local_operator, source, range, param);
+      local_operator, range, param);
 }
 
 /**
@@ -128,13 +124,12 @@ template <class AssemblyGridView,
 std::unique_ptr<LocalElementOperatorApplicator<AssemblyGridView, SV, s_r, s_rC, SF, SGV, r_r, r_rC, RF, RGV, RV>>
 make_local_element_operator_applicator(
     const LocalElementOperatorInterface<SV, SGV, s_r, s_rC, SF, r_r, r_rC, RF, RGV, RV>& local_operator,
-    const ConstDiscreteFunction<SV, SGV, s_r, s_rC, SF>& source,
     DiscreteFunction<RV, RGV, r_r, r_rC, RF>& range,
     const XT::Common::Parameter& param = {})
 {
   return std::make_unique<
       LocalElementOperatorApplicator<AssemblyGridView, SV, s_r, s_rC, SF, SGV, r_r, r_rC, RF, RGV, RV>>(
-      local_operator, source, range, param);
+      local_operator, range, param);
 }
 
 
@@ -199,12 +194,10 @@ public:
       LocalIntersectionOperatorInterface<IntersectionType, SV, SGV, s_r, s_rC, SF, r_r, r_rC, RF, IRGV, IRV, ORGV, ORV>;
 
   LocalIntersectionOperatorApplicator(const LocalOperatorType& local_operator,
-                                      const SourceType& source,
                                       InsideRangeType& range_inside,
                                       OutsideRangeType& range_outside,
                                       const XT::Common::Parameter& param = {})
     : local_operator_(local_operator.copy())
-    , source_(source)
     , range_inside_(range_inside)
     , range_outside_(range_outside)
     , param_(param)
@@ -214,7 +207,7 @@ public:
 
   LocalIntersectionOperatorApplicator(const ThisType& other)
     : LocalIntersectionOperatorApplicator(
-          *other.local_operator_, other.source_, other.range_inside_, other.range_outside_, other.param_)
+          *other.local_operator_, other.range_inside_, other.range_outside_, other.param_)
   {}
 
   BaseType* copy() override final
@@ -228,12 +221,12 @@ public:
   {
     local_range_inside_->bind(inside_element);
     local_range_outside_->bind(outside_element);
-    local_operator_->apply(source_, intersection, *local_range_inside_, *local_range_outside_, param_);
+    local_operator_->bind(intersection);
+    local_operator_->apply(intersection, *local_range_inside_, *local_range_outside_, param_);
   }
 
 private:
   const std::unique_ptr<LocalOperatorType> local_operator_;
-  const SourceType& source_;
   InsideRangeType& range_inside_;
   OutsideRangeType& range_outside_;
   const XT::Common::Parameter param_;
@@ -263,14 +256,13 @@ std::enable_if_t<
     std::unique_ptr<LocalIntersectionOperatorApplicator<GV, SV, s_r, s_rC, SF, GV, r_r, r_rC, RF, GV, IRV, GV, ORV>>>
 make_local_intersection_operator_applicator(
     const LocalIntersectionOperatorInterface<I, SV, GV, s_r, s_rC, SF, r_r, r_rC, RF, GV, IRV, GV, ORV>& local_operator,
-    const ConstDiscreteFunction<SV, GV, s_r, s_rC, SF>& source,
     DiscreteFunction<IRV, GV, r_r, r_rC, RF>& range_inside,
     DiscreteFunction<ORV, GV, r_r, r_rC, RF>& range_outside,
     const XT::Common::Parameter& param = {})
 {
   return std::make_unique<
       LocalIntersectionOperatorApplicator<GV, SV, s_r, s_rC, SF, GV, r_r, r_rC, RF, GV, IRV, GV, ORV>>(
-      local_operator, source, range_inside, range_outside, param);
+      local_operator, range_inside, range_outside, param);
 } // ... make_local_intersection_operator_applicator(...)
 
 template <class I, class SV, class GV, size_t s_r, size_t s_rC, class SF, size_t r_r, size_t r_rC, class RF, class RV>
@@ -278,12 +270,11 @@ std::enable_if_t<std::is_same<XT::Grid::extract_intersection_t<GV>, I>::value,
                  std::unique_ptr<LocalIntersectionOperatorApplicator<GV, SV, s_r, s_rC, SF, GV, r_r, r_rC, RF, GV, RV>>>
 make_local_intersection_operator_applicator(
     const LocalIntersectionOperatorInterface<I, SV, GV, s_r, s_rC, SF, r_r, r_rC, RF, GV, RV>& local_operator,
-    const ConstDiscreteFunction<SV, GV, s_r, s_rC, SF>& source,
     DiscreteFunction<RV, GV, r_r, r_rC, RF>& range,
     const XT::Common::Parameter& param = {})
 {
   return std::make_unique<LocalIntersectionOperatorApplicator<GV, SV, s_r, s_rC, SF, GV, r_r, r_rC, RF, GV, RV>>(
-      local_operator, source, range, range, param);
+      local_operator, range, range, param);
 }
 
 /**
@@ -322,7 +313,6 @@ std::enable_if_t<std::is_same<XT::Grid::extract_intersection_t<AssemblyGridView>
 make_local_intersection_operator_applicator(
     const LocalIntersectionOperatorInterface<I, SV, SGV, s_r, s_rC, SF, r_r, r_rC, RF, IRGV, IRV, ORGV, ORV>&
         local_operator,
-    const ConstDiscreteFunction<SV, SGV, s_r, s_rC, SF>& source,
     DiscreteFunction<IRV, IRGV, r_r, r_rC, RF>& range_inside,
     DiscreteFunction<ORV, ORGV, r_r, r_rC, RF>& range_outside,
     const XT::Common::Parameter& param = {})
@@ -339,8 +329,7 @@ make_local_intersection_operator_applicator(
                                                               IRGV,
                                                               IRV,
                                                               ORGV,
-                                                              ORV>>(
-      local_operator, source, range_inside, range_outside, param);
+                                                              ORV>>(local_operator, range_inside, range_outside, param);
 } // ... make_local_intersection_operator_applicator(...)
 
 template <class AssemblyGridView,
@@ -361,13 +350,12 @@ std::enable_if_t<
         LocalIntersectionOperatorApplicator<AssemblyGridView, SV, s_r, s_rC, SF, SGV, r_r, r_rC, RF, RGV, RV>>>
 make_local_intersection_operator_applicator(
     const LocalIntersectionOperatorInterface<I, SV, SGV, s_r, s_rC, SF, r_r, r_rC, RF, RGV, RV>& local_operator,
-    const ConstDiscreteFunction<SV, SGV, s_r, s_rC, SF>& source,
     DiscreteFunction<RV, RGV, r_r, r_rC, RF>& range,
     const XT::Common::Parameter& param = {})
 {
   return std::make_unique<
       LocalIntersectionOperatorApplicator<AssemblyGridView, SV, s_r, s_rC, SF, SGV, r_r, r_rC, RF, RGV, RV>>(
-      local_operator, source, range, range, param);
+      local_operator, range, range, param);
 }
 
 /// \}
diff --git a/dune/gdt/local/assembler/operator-fd-jacobian-assemblers.hh b/dune/gdt/local/assembler/operator-fd-jacobian-assemblers.hh
index bc0c6ba940a221dcf593581192b4b77aad59f84c..3c5714b4ddfd65b802c76347f4ee460c1df46ff5 100644
--- a/dune/gdt/local/assembler/operator-fd-jacobian-assemblers.hh
+++ b/dune/gdt/local/assembler/operator-fd-jacobian-assemblers.hh
@@ -59,7 +59,7 @@ class LocalElementOperatorFiniteDifferenceJacobianAssembler : public XT::Grid::E
   static_assert(std::is_same<XT::Grid::extract_entity_t<SGV>, XT::Grid::extract_entity_t<RGV>>::value, "");
 
   using BaseType = XT::Grid::ElementFunctor<SGV>;
-  using ThisType = LocalElementOperatorFiniteDifferenceJacobianAssembler<M, SGV, s_r, s_rC, F, r_r, r_rC, RGV>;
+  using ThisType = LocalElementOperatorFiniteDifferenceJacobianAssembler;
 
 public:
   using typename BaseType::ElementType;
@@ -69,7 +69,7 @@ public:
   using V = VectorType;
 
   using LocalElementOperatorType = LocalElementOperatorInterface<V, SGV, s_r, s_rC, F, r_r, r_rC, F, RGV>;
-  using SourceSpaceType = typename LocalElementOperatorType::SourceType::SpaceType;
+  using SourceSpaceType = typename LocalElementOperatorType::DiscreteSourceType::SpaceType;
   using RangeSpaceType = typename LocalElementOperatorType::LocalRangeType::SpaceType;
 
   LocalElementOperatorFiniteDifferenceJacobianAssembler(const SourceSpaceType& source_space,
@@ -79,37 +79,37 @@ public:
                                                         const LocalElementOperatorType& local_operator,
                                                         const XT::Common::Parameter& param = {})
     : BaseType()
-    , source_space_(source_space)
-    , range_space_(range_space)
+    , source_space_(source_space.copy())
+    , range_space_(range_space.copy())
     , matrix_(matrix)
     , source_vector_(source_vector)
-    , local_op_(local_operator.copy())
     , param_(param)
     , scaling_(param_.has_key("matrixoperator.scaling") ? param_.get("matrixoperator.scaling").at(0) : 1.)
     , eps_(param_.has_key("finite-difference-jacobians.eps") ? param_.get("finite-difference-jacobians.eps").at(0)
                                                              : 1e-7)
-    , source_(source_space_) // This is a full vector, and intended!
-    , range_(range_space_) // This is a full vector, and intended!
+    , source_(*source_space_) // This is a full vector, and intended!
+    , range_(*range_space_) // This is a full vector, and intended!
     , local_source_(source_.local_discrete_function())
     , local_range_(range_.local_discrete_function())
+    , local_op_(local_operator.with_source(source_))
   {
     source_.dofs().vector() = source_vector_;
   }
 
   LocalElementOperatorFiniteDifferenceJacobianAssembler(const ThisType& other)
     : BaseType(other)
-    , source_space_(other.source_space_)
-    , range_space_(other.range_space_)
+    , source_space_(other.source_space_->copy())
+    , range_space_(other.range_space_->copy())
     , matrix_(other.matrix_)
     , source_vector_(other.source_vector_)
-    , local_op_(other.local_op_->copy())
     , param_(other.param_)
     , scaling_(other.scaling_)
     , eps_(other.eps_)
-    , source_(source_space_) // This is a full vector, and intended!
-    , range_(range_space_) // This is a full vector, and intended!
+    , source_(*source_space_) // This is a full vector, and intended!
+    , range_(*range_space_) // This is a full vector, and intended!
     , local_source_(source_.local_discrete_function())
     , local_range_(range_.local_discrete_function())
+    , local_op_(other.local_op_->with_source(source_))
   {
     source_.dofs().vector() = source_vector_;
   }
@@ -124,15 +124,15 @@ public:
     // some preparations
     local_source_->bind(element);
     local_range_->bind(element);
-    source_space_.mapper().global_indices(element, global_source_indices_);
-    range_space_.mapper().global_indices(element, global_range_indices_);
-    const size_t local_source_size = source_space_.mapper().local_size(element);
-    const size_t local_range_size = range_space_.mapper().local_size(element);
+    source_space_->mapper().global_indices(element, global_source_indices_);
+    range_space_->mapper().global_indices(element, global_range_indices_);
+    const size_t local_source_size = source_space_->mapper().local_size(element);
+    const size_t local_range_size = range_space_->mapper().local_size(element);
     if (range_DoFs_.size() < local_range_size)
       range_DoFs_.resize(local_range_size, 0);
     local_range_->dofs().set_all(0);
     // apply op as is, keep the result, clear local range
-    local_op_->apply(source_, *local_range_, param_);
+    local_op_->apply(*local_range_, param_);
     for (size_t ii = 0; ii < local_range_size; ++ii)
       range_DoFs_[ii] = local_range_->dofs()[ii];
     local_range_->dofs().set_all(0);
@@ -143,7 +143,7 @@ public:
       const auto eps = eps_ * (1. + std::abs(jjth_source_DoF));
       local_source_->dofs()[jj] += eps;
       // apply op with perturbed source DoF
-      local_op_->apply(source_, *local_range_, param_);
+      local_op_->apply(*local_range_, param_);
       // observe perturbation in range DoFs
       for (size_t ii = 0; ii < local_range_size; ++ii) {
         auto derivative = (local_range_->dofs()[ii] - range_DoFs_[ii]) / eps;
@@ -157,11 +157,10 @@ public:
   } // ... apply_local(...)
 
 private:
-  const SourceSpaceType& source_space_;
-  const RangeSpaceType& range_space_;
+  std::unique_ptr<const SourceSpaceType> source_space_;
+  std::unique_ptr<const RangeSpaceType> range_space_;
   MatrixType& matrix_;
   const VectorType& source_vector_;
-  const std::unique_ptr<LocalElementOperatorType> local_op_;
   const XT::Common::Parameter param_;
   const double scaling_;
   const real_t<F> eps_;
@@ -172,6 +171,7 @@ private:
   DynamicVector<size_t> global_source_indices_;
   DynamicVector<size_t> global_range_indices_;
   DynamicVector<F> range_DoFs_;
+  const std::unique_ptr<LocalElementOperatorType> local_op_;
 }; // class LocalElementOperatorFiniteDifferenceJacobianAssembler
 
 
@@ -205,7 +205,7 @@ class LocalIntersectionOperatorFiniteDifferenceJacobianAssembler : public XT::Gr
   static_assert(std::is_same<XT::Grid::extract_entity_t<SGV>, XT::Grid::extract_entity_t<RGV>>::value, "");
 
   using BaseType = XT::Grid::IntersectionFunctor<SGV>;
-  using ThisType = LocalIntersectionOperatorFiniteDifferenceJacobianAssembler<M, SGV, s_r, s_rC, F, r_r, r_rC, RGV>;
+  using ThisType = LocalIntersectionOperatorFiniteDifferenceJacobianAssembler;
 
 public:
   using typename BaseType::ElementType;
@@ -217,7 +217,7 @@ public:
   using V = VectorType;
 
   using LocalIntersectionOperatorType = LocalIntersectionOperatorInterface<I, V, SGV, s_r, s_rC, F, r_r, r_rC, F, RGV>;
-  using SourceSpaceType = typename LocalIntersectionOperatorType::SourceType::SpaceType;
+  using SourceSpaceType = typename LocalIntersectionOperatorType::DiscreteSourceType::SpaceType;
   using RangeSpaceType = typename LocalIntersectionOperatorType::LocalInsideRangeType::SpaceType;
 
   LocalIntersectionOperatorFiniteDifferenceJacobianAssembler(const SourceSpaceType& source_space,
@@ -227,40 +227,40 @@ public:
                                                              const LocalIntersectionOperatorType& local_operator,
                                                              const XT::Common::Parameter& param = {},
                                                              const real_t<F> eps = 1e-7)
-    : source_space_(source_space)
-    , range_space_(range_space)
+    : source_space_(source_space.copy())
+    , range_space_(range_space.copy())
     , matrix_(matrix)
     , source_vector_(source_vector)
-    , local_op_(local_operator.copy())
     , param_(param)
     , scaling_(param_.has_key("matrixoperator.scaling") ? param_.get("matrixoperator.scaling").at(0) : 1.)
     , eps_(eps)
-    , source_(source_space_) // This is a full vector, and intended!
-    , range_(range_space_) // This is a full vector, and intended!
+    , source_(*source_space_) // This is a full vector, and intended!
+    , range_(*range_space_) // This is a full vector, and intended!
     , local_source_inside_(source_.local_discrete_function())
     , local_source_outside_(source_.local_discrete_function())
     , local_range_inside_(range_.local_discrete_function())
     , local_range_outside_(range_.local_discrete_function())
+    , local_op_(local_operator.with_source(source_))
   {
     source_.dofs().vector() = source_vector_;
   }
 
   LocalIntersectionOperatorFiniteDifferenceJacobianAssembler(const ThisType& other)
     : BaseType(other)
-    , source_space_(other.source_space_)
-    , range_space_(other.range_space_)
+    , source_space_(other.source_space_->copy())
+    , range_space_(other.range_space_->copy())
     , matrix_(other.matrix_)
     , source_vector_(other.source_vector_)
-    , local_op_(other.local_op_->copy())
     , param_(other.param_)
     , scaling_(other.scaling_)
     , eps_(other.eps_)
-    , source_(source_space_) // This is a full vector, and intended!
-    , range_(range_space_) // This is a full vector, and intended!
+    , source_(*source_space_) // This is a full vector, and intended!
+    , range_(*range_space_) // This is a full vector, and intended!
     , local_source_inside_(source_.local_discrete_function())
     , local_source_outside_(source_.local_discrete_function())
     , local_range_inside_(range_.local_discrete_function())
     , local_range_outside_(range_.local_discrete_function())
+    , local_op_(other.local_op_->with_source(source_))
   {
     source_.dofs().vector() = source_vector_;
   }
@@ -276,28 +276,29 @@ public:
   {
     const bool treat_outside = intersection.neighbor();
     // some preparations
+    local_op_->bind(intersection);
     local_source_inside_->bind(inside_element);
     local_range_inside_->bind(inside_element);
-    source_space_.mapper().global_indices(inside_element, global_source_indices_inside_);
-    range_space_.mapper().global_indices(inside_element, global_range_indices_inside_);
-    const size_t local_source_inside_size = source_space_.mapper().local_size(inside_element);
-    const size_t local_source_outside_size = treat_outside ? source_space_.mapper().local_size(outside_element) : 0;
-    const size_t local_range_inside_size = range_space_.mapper().local_size(inside_element);
-    const size_t local_range_outside_size = treat_outside ? range_space_.mapper().local_size(outside_element) : 0;
+    source_space_->mapper().global_indices(inside_element, global_source_indices_inside_);
+    range_space_->mapper().global_indices(inside_element, global_range_indices_inside_);
+    const size_t local_source_inside_size = source_space_->mapper().local_size(inside_element);
+    const size_t local_source_outside_size = treat_outside ? source_space_->mapper().local_size(outside_element) : 0;
+    const size_t local_range_inside_size = range_space_->mapper().local_size(inside_element);
+    const size_t local_range_outside_size = treat_outside ? range_space_->mapper().local_size(outside_element) : 0;
     if (range_DoFs_inside_.size() < local_range_inside_size)
       range_DoFs_inside_.resize(local_range_inside_size, 0);
     local_range_inside_->dofs().set_all(0);
     if (treat_outside) {
       local_source_outside_->bind(outside_element);
       local_range_outside_->bind(outside_element);
-      source_space_.mapper().global_indices(outside_element, global_source_indices_outside_);
-      range_space_.mapper().global_indices(outside_element, global_range_indices_outside_);
+      source_space_->mapper().global_indices(outside_element, global_source_indices_outside_);
+      range_space_->mapper().global_indices(outside_element, global_range_indices_outside_);
       if (range_DoFs_outside_.size() < local_range_outside_size)
         range_DoFs_outside_.resize(local_range_outside_size, 0);
       local_range_outside_->dofs().set_all(0);
     }
     // apply op as is, keep the result, clear local range
-    local_op_->apply(source_, intersection, *local_range_inside_, *local_range_outside_, param_);
+    local_op_->apply(*local_range_inside_, *local_range_outside_, param_);
     for (size_t ii = 0; ii < local_range_inside_size; ++ii)
       range_DoFs_inside_[ii] = local_range_inside_->dofs()[ii];
     local_range_inside_->dofs().set_all(0);
@@ -313,7 +314,7 @@ public:
       const auto eps = eps_ * (1. + std::abs(jjth_source_DoF));
       local_source_inside_->dofs()[jj] += eps;
       // apply op with perturbed source DoF
-      local_op_->apply(source_, intersection, *local_range_inside_, *local_range_outside_, param_);
+      local_op_->apply(*local_range_inside_, *local_range_outside_, param_);
       // observe perturbation in inside range DoFs
       for (size_t ii = 0; ii < local_range_inside_size; ++ii) {
         auto derivative = (local_range_inside_->dofs()[ii] - range_DoFs_inside_[ii]) / eps;
@@ -346,7 +347,7 @@ public:
         const auto eps = eps_ * (1. + std::abs(jjth_source_DoF));
         local_source_outside_->dofs()[jj] += eps;
         // apply op with perturbed source DoF
-        local_op_->apply(source_, intersection, *local_range_inside_, *local_range_outside_, param_);
+        local_op_->apply(*local_range_inside_, *local_range_outside_, param_);
         // observe perturbation in inside range DoFs
         for (size_t ii = 0; ii < local_range_inside_size; ++ii) {
           auto derivative = (local_range_inside_->dofs()[ii] - range_DoFs_inside_[ii]) / eps;
@@ -370,11 +371,10 @@ public:
   } // ... apply_local(...)
 
 private:
-  const SourceSpaceType& source_space_;
-  const RangeSpaceType& range_space_;
+  std::unique_ptr<const SourceSpaceType> source_space_;
+  std::unique_ptr<const RangeSpaceType> range_space_;
   MatrixType& matrix_;
   const VectorType& source_vector_;
-  const std::unique_ptr<LocalIntersectionOperatorType> local_op_;
   const XT::Common::Parameter param_;
   const double scaling_;
   const real_t<F> eps_;
@@ -390,6 +390,7 @@ private:
   DynamicVector<size_t> global_range_indices_outside_;
   DynamicVector<F> range_DoFs_inside_;
   DynamicVector<F> range_DoFs_outside_;
+  const std::unique_ptr<LocalIntersectionOperatorType> local_op_;
 }; // class LocalIntersectionOperatorFiniteDifferenceJacobianAssembler
 
 
diff --git a/dune/gdt/local/bilinear-forms/generic.hh b/dune/gdt/local/bilinear-forms/generic.hh
index d75d9cf3179e2e8c9c45b7452c438b7739e643c7..79723b95686950b4871172f26d5bb8d289b78b4b 100644
--- a/dune/gdt/local/bilinear-forms/generic.hh
+++ b/dune/gdt/local/bilinear-forms/generic.hh
@@ -34,7 +34,7 @@ template <class E,
           class AR = TR>
 class GenericLocalElementBilinearForm : public LocalElementBilinearFormInterface<E, t_r, t_rC, TR, F, a_r, a_rC, AR>
 {
-  using ThisType = GenericLocalElementBilinearForm<E, t_r, t_rC, TR, F, a_r, a_rC, AR>;
+  using ThisType = GenericLocalElementBilinearForm;
   using BaseType = LocalElementBilinearFormInterface<E, t_r, t_rC, TR, F, a_r, a_rC, AR>;
 
 public:
@@ -94,7 +94,7 @@ template <class I,
 class GenericLocalIntersectionBilinearForm
   : public LocalIntersectionBilinearFormInterface<I, t_r, t_rC, TR, F, a_r, a_rC, AR>
 {
-  using ThisType = GenericLocalIntersectionBilinearForm<I, t_r, t_rC, TR, F, a_r, a_rC, AR>;
+  using ThisType = GenericLocalIntersectionBilinearForm;
   using BaseType = LocalIntersectionBilinearFormInterface<I, t_r, t_rC, TR, F, a_r, a_rC, AR>;
 
 public:
diff --git a/dune/gdt/local/bilinear-forms/integrals.hh b/dune/gdt/local/bilinear-forms/integrals.hh
index 2bf67f25342bf21d85fb8a529971d78f40d3662a..72ad281eaa7d91c9bb638da5e0cca3f7cecc5954 100644
--- a/dune/gdt/local/bilinear-forms/integrals.hh
+++ b/dune/gdt/local/bilinear-forms/integrals.hh
@@ -36,7 +36,7 @@ template <class E,
           class AR = TR>
 class LocalElementIntegralBilinearForm : public LocalElementBilinearFormInterface<E, t_r, t_rC, TR, F, a_r, a_rC, AR>
 {
-  using ThisType = LocalElementIntegralBilinearForm<E, t_r, t_rC, TR, F, a_r, a_rC, AR>;
+  using ThisType = LocalElementIntegralBilinearForm;
   using BaseType = LocalElementBilinearFormInterface<E, t_r, t_rC, TR, F, a_r, a_rC, AR>;
 
 public:
@@ -55,7 +55,7 @@ public:
   {}
 
   LocalElementIntegralBilinearForm(typename GenericIntegrand::GenericOrderFunctionType order_function,
-                                   typename GenericIntegrand::GenericEvalauteFunctionType evaluate_function,
+                                   typename GenericIntegrand::GenericEvaluateFunctionType evaluate_function,
                                    const XT::Common::ParameterType& param_type = {},
                                    const int over_integrate = 0)
     : BaseType(param_type)
@@ -132,7 +132,7 @@ template <class I,
 class LocalIntersectionIntegralBilinearForm
   : public LocalIntersectionBilinearFormInterface<I, t_r, t_rC, TR, F, a_r, a_rC, AR>
 {
-  using ThisType = LocalIntersectionIntegralBilinearForm<I, t_r, t_rC, TR, F, a_r, a_rC, AR>;
+  using ThisType = LocalIntersectionIntegralBilinearForm;
   using BaseType = LocalIntersectionBilinearFormInterface<I, t_r, t_rC, TR, F, a_r, a_rC, AR>;
 
 public:
diff --git a/dune/gdt/local/bilinear-forms/interfaces.hh b/dune/gdt/local/bilinear-forms/interfaces.hh
index 18f0b90a6939ac2a194d98d4b9be52f9f2146241..b65572225f3593a8734d147ed13f0c36507f9bda 100644
--- a/dune/gdt/local/bilinear-forms/interfaces.hh
+++ b/dune/gdt/local/bilinear-forms/interfaces.hh
@@ -172,6 +172,27 @@ public:
                       DynamicMatrix<F>& result_out_out,
                       const XT::Common::Parameter& param = {}) const = 0;
 
+  /**
+   * Variant which consideres the intersection only from the inside.
+   */
+  virtual void apply2(const IntersectionType& intersection,
+                      const LocalTestBasisType& test_basis,
+                      const LocalAnsatzBasisType& ansatz_basis,
+                      DynamicMatrix<F>& result,
+                      const XT::Common::Parameter& param = {}) const
+  {
+    this->apply2(intersection,
+                 test_basis,
+                 ansatz_basis,
+                 test_basis,
+                 ansatz_basis,
+                 result,
+                 unused_result_,
+                 unused_result_,
+                 unused_result_,
+                 param);
+  }
+
   /**
    * This method is provided for convenience and should not be used within library code.
    */
@@ -197,7 +218,23 @@ public:
                  result_out_out,
                  param);
     return {result_in_in, result_in_out, result_out_in, result_out_out};
-  } // ... apply(...)
+  } // ... apply2(...)
+
+  /**
+   * This method is provided for convenience and should not be used within library code.
+   */
+  DynamicMatrix<F> apply2(const IntersectionType& intersection,
+                          const LocalTestBasisType& test_basis,
+                          const LocalAnsatzBasisType& ansatz_basis,
+                          const XT::Common::Parameter& param = {}) const
+  {
+    DynamicMatrix<F> result(test_basis.size(param), ansatz_basis.size(param), 0);
+    this->apply2(intersection.test_basis, ansatz_basis, param);
+    return result;
+  }
+
+protected:
+  mutable DynamicMatrix<F> unused_result_;
 }; // class LocalIntersectionBilinearFormInterface
 
 
diff --git a/dune/gdt/local/discretefunction.hh b/dune/gdt/local/discretefunction.hh
index efdf28230764bfe8d08fc981e7be8b733882f204..08653f544ac01a3114072d48234f1b94648bdbe8 100644
--- a/dune/gdt/local/discretefunction.hh
+++ b/dune/gdt/local/discretefunction.hh
@@ -34,7 +34,7 @@ class ConstLocalDiscreteFunction
   static_assert(XT::LA::is_vector<Vector>::value, "");
   static_assert(range_dim_cols == 1, "The matrix-valued case requires updates to evaluate/jacobian/derivative!");
 
-  using ThisType = ConstLocalDiscreteFunction<Vector, GridView, range_dim, range_dim_cols, RangeField>;
+  using ThisType = ConstLocalDiscreteFunction;
   using BaseType = XT::Functions::
       ElementFunctionInterface<XT::Grid::extract_entity_t<GridView>, range_dim, range_dim_cols, RangeField>;
 
@@ -63,19 +63,19 @@ public:
 
   ConstLocalDiscreteFunction(const SpaceType& spc, const ConstDofVectorType& dof_vector)
     : BaseType()
-    , space_(spc)
+    , space_(spc.copy())
     , dof_vector_(dof_vector.localize())
-    , basis_(space_.basis().localize())
-    , basis_values_(space_.mapper().max_local_size())
-    , dynamic_basis_values_(space_.mapper().max_local_size())
-    , basis_derivatives_(space_.mapper().max_local_size())
-    , dynamic_basis_derivatives_(space_.mapper().max_local_size())
+    , basis_(space_->basis().localize())
+    , basis_values_(space_->mapper().max_local_size())
+    , dynamic_basis_values_(space_->mapper().max_local_size())
+    , basis_derivatives_(space_->mapper().max_local_size())
+    , dynamic_basis_derivatives_(space_->mapper().max_local_size())
   {}
 
   virtual ~ConstLocalDiscreteFunction() = default;
 
 protected:
-  virtual void post_bind(const ElementType& ent) override
+  void post_bind(const ElementType& ent) override
   {
     basis_->bind(ent);
     dof_vector_.bind(ent);
@@ -90,7 +90,7 @@ public:
 
   const SpaceType& space() const
   {
-    return space_;
+    return *space_;
   }
 
   const LocalBasisType& basis() const
@@ -119,7 +119,7 @@ public:
   {
     DUNE_THROW_IF(!this->is_bound_, Exceptions::not_bound_to_an_element_yet, "");
     RangeReturnType result(0);
-    if (space_.type() == GDT::SpaceType::finite_volume) {
+    if (space_->type() == GDT::SpaceType::finite_volume) {
       for (size_t ii = 0; ii < r; ++ii)
         result[ii] = dof_vector_[ii];
     } else {
@@ -135,7 +135,7 @@ public:
   {
     DUNE_THROW_IF(!this->is_bound_, Exceptions::not_bound_to_an_element_yet, "");
     DerivativeRangeReturnType result(0);
-    if (space_.type() == GDT::SpaceType::finite_volume) {
+    if (space_->type() == GDT::SpaceType::finite_volume) {
       return result;
     } else {
       basis_->jacobians(point_in_reference_element, basis_derivatives_, param);
@@ -150,7 +150,7 @@ public:
                                        const XT::Common::Parameter& /*param*/ = {}) const override final
   {
     DUNE_THROW_IF(!this->is_bound_, Exceptions::not_bound_to_an_element_yet, "");
-    DUNE_THROW_IF(space_.type() != GDT::SpaceType::finite_volume,
+    DUNE_THROW_IF(space_->type() != GDT::SpaceType::finite_volume,
                   Exceptions::discrete_function_error,
                   "arbitrary derivatives are not supported by the local finite elements!\n\n"
                       << "alpha = " << alpha << "\n"
@@ -183,7 +183,7 @@ public:
     DUNE_THROW_IF(!this->is_bound_, Exceptions::not_bound_to_an_element_yet, "");
     RangeSelector::ensure_size(result);
     result *= 0;
-    if (space_.type() == GDT::SpaceType::finite_volume) {
+    if (space_->type() == GDT::SpaceType::finite_volume) {
       for (size_t ii = 0; ii < r; ++ii)
         result[ii] = dof_vector_[ii];
     } else {
@@ -200,7 +200,7 @@ public:
     DUNE_THROW_IF(!this->is_bound_, Exceptions::not_bound_to_an_element_yet, "");
     DerivativeRangeSelector::ensure_size(result);
     result *= 0;
-    if (space_.type() == GDT::SpaceType::finite_volume) {
+    if (space_->type() == GDT::SpaceType::finite_volume) {
       return;
     } else {
       basis_->jacobians(point_in_reference_element, dynamic_basis_derivatives_, param);
@@ -215,7 +215,7 @@ public:
                   const XT::Common::Parameter& /*param*/ = {}) const override final
   {
     DUNE_THROW_IF(!this->is_bound_, Exceptions::not_bound_to_an_element_yet, "");
-    DUNE_THROW_IF(space_.type() != GDT::SpaceType::finite_volume,
+    DUNE_THROW_IF(space_->type() != GDT::SpaceType::finite_volume,
                   Exceptions::discrete_function_error,
                   "arbitrary derivatives are not supported by the local finite elements!\n\n"
                       << "alpha = " << alpha << "\n"
@@ -248,7 +248,7 @@ public:
   {
     DUNE_THROW_IF(!this->is_bound_, Exceptions::not_bound_to_an_element_yet, "");
     this->assert_correct_dims(row, col, "evaluate");
-    if (space_.type() == GDT::SpaceType::finite_volume) {
+    if (space_->type() == GDT::SpaceType::finite_volume) {
       return dof_vector_[row];
     } else {
       R result(0);
@@ -266,7 +266,7 @@ public:
   {
     DUNE_THROW_IF(!this->is_bound_, Exceptions::not_bound_to_an_element_yet, "");
     this->assert_correct_dims(row, col, "jacobian");
-    if (space_.type() == GDT::SpaceType::finite_volume) {
+    if (space_->type() == GDT::SpaceType::finite_volume) {
       return 0;
     } else {
       SingleDerivativeRangeReturnType result(0);
@@ -280,7 +280,7 @@ public:
   /// \}
 
 private:
-  const SpaceType& space_;
+  std::unique_ptr<const SpaceType> space_;
   ConstLocalDofVectorType dof_vector_;
   std::unique_ptr<LocalBasisType> basis_;
   mutable std::vector<RangeType> basis_values_;
@@ -293,7 +293,7 @@ private:
 template <class V, class GV, size_t r = 1, size_t rC = 1, class R = double>
 class LocalDiscreteFunction : public ConstLocalDiscreteFunction<V, GV, r, rC, R>
 {
-  using ThisType = LocalDiscreteFunction<V, GV, r, rC, R>;
+  using ThisType = LocalDiscreteFunction;
   using BaseType = ConstLocalDiscreteFunction<V, GV, r, rC, R>;
 
 public:
diff --git a/dune/gdt/local/dof-vector.hh b/dune/gdt/local/dof-vector.hh
index e442cd65043da1c5a90d35c18393a8515fda0f3e..7b396b89b4eaa63a955df933a138a391120a4127 100644
--- a/dune/gdt/local/dof-vector.hh
+++ b/dune/gdt/local/dof-vector.hh
@@ -88,7 +88,7 @@ class ConstLocalDofVector
   , public XT::Grid::ElementBoundObject<XT::Grid::extract_entity_t<GridView>>
 {
 
-  using ThisType = ConstLocalDofVector<Vector, GridView, Traits>;
+  using ThisType = ConstLocalDofVector;
   using BaseType = XT::LA::VectorInterface<Traits>;
 
 public:
@@ -196,7 +196,7 @@ template <class Vector, class GridView>
 class LocalDofVector : public ConstLocalDofVector<Vector, GridView, internal::LocalDofVectorTraits<Vector, GridView>>
 {
 
-  using ThisType = LocalDofVector<Vector, GridView>;
+  using ThisType = LocalDofVector;
   using BaseType = ConstLocalDofVector<Vector, GridView, internal::LocalDofVectorTraits<Vector, GridView>>;
 
 public:
diff --git a/dune/gdt/local/finite-elements/0d.hh b/dune/gdt/local/finite-elements/0d.hh
index 1ed5e95c97721ee76fd34178b0e1776c56437813..4cb52a01451a50ce3c53e4580098d203d5fad1d4 100644
--- a/dune/gdt/local/finite-elements/0d.hh
+++ b/dune/gdt/local/finite-elements/0d.hh
@@ -26,17 +26,19 @@ namespace GDT {
  * \sa LocalFiniteElementBasisInterface
  * \sa Local0dFiniteElement
  */
-template <class D, class R>
-class Local0dFiniteElementBasis : public LocalFiniteElementBasisInterface<D, 0, R, 1>
+template <class D, class R, size_t r = 1>
+class Local0dFiniteElementBasis : public LocalFiniteElementBasisInterface<D, 0, R, r>
 {
-  using ThisType = Local0dFiniteElementBasis<D, R>;
-  using BaseType = LocalFiniteElementBasisInterface<D, 0, R, 1>;
+  using ThisType = Local0dFiniteElementBasis;
+  using BaseType = LocalFiniteElementBasisInterface<D, 0, R, r>;
 
 public:
   using typename BaseType::DerivativeRangeType;
   using typename BaseType::DomainType;
   using typename BaseType::RangeType;
 
+  static_assert(r == 1, "Not yet implemented for r > 1");
+
   Local0dFiniteElementBasis()
     : geometry_type_(GeometryTypes::simplex(0))
   {}
@@ -93,16 +95,18 @@ private:
  * \sa LocalFiniteElementInterpolationInterface
  * \sa Local0dFiniteElement
  */
-template <class D, class R>
-class Local0dFiniteElementInterpolation : public LocalFiniteElementInterpolationInterface<D, 0, R, 1>
+template <class D, class R, size_t r = 1>
+class Local0dFiniteElementInterpolation : public LocalFiniteElementInterpolationInterface<D, 0, R, r>
 {
-  using ThisType = Local0dFiniteElementInterpolation<D, R>;
-  using BaseType = LocalFiniteElementInterpolationInterface<D, 0, R, 1>;
+  using ThisType = Local0dFiniteElementInterpolation;
+  using BaseType = LocalFiniteElementInterpolationInterface<D, 0, R, r>;
 
 public:
   using typename BaseType::DomainType;
   using typename BaseType::RangeType;
 
+  static_assert(r == 1, "Not yet implemented for r > 1");
+
   Local0dFiniteElementInterpolation()
     : geometry_type_(GeometryTypes::simplex(0))
   {}
@@ -149,14 +153,17 @@ private:
 template <class D>
 class Local0dFiniteElementCoefficients : public LocalFiniteElementCoefficientsInterface<D, 0>
 {
-  using ThisType = Local0dFiniteElementCoefficients<D>;
+  using ThisType = Local0dFiniteElementCoefficients;
   using BaseType = LocalFiniteElementCoefficientsInterface<D, 0>;
 
 public:
-  Local0dFiniteElementCoefficients()
+  Local0dFiniteElementCoefficients(const size_t r = 1)
     : geometry_type_(GeometryTypes::simplex(0))
-    , local_key_(0, 0, 0)
-  {}
+    , local_keys_(r)
+  {
+    for (unsigned int ii = 0; ii < r; ++ii)
+      local_keys_[ii] = LocalKey(0, 0, ii);
+  }
 
   Local0dFiniteElementCoefficients(const ThisType& other) = default;
 
@@ -172,18 +179,20 @@ public:
 
   size_t size() const override final
   {
-    return 1;
+    return local_keys_.size();
   }
 
   const LocalKey& local_key(const size_t ii) const override final
   {
-    DUNE_THROW_IF(ii > 0, Exceptions::finite_element_error, "ii = " << ii << "\n   size() = 1");
-    return local_key_;
+    DUNE_THROW_IF(ii > local_keys_.size(),
+                  Exceptions::finite_element_error,
+                  "ii = " << ii << "\n   size() = " << local_keys_.size());
+    return local_keys_[ii];
   }
 
 private:
   const GeometryType geometry_type_;
-  const LocalKey local_key_;
+  std::vector<LocalKey> local_keys_;
 }; // class Local0dFiniteElementCoefficients
 
 
@@ -197,17 +206,17 @@ private:
  * \sa Local0dFiniteElementInterpolation
  * \sa Local0dFiniteElementCoefficients
  */
-template <class D, class R>
-class Local0dFiniteElement : public LocalFiniteElementDefault<D, 0, R, 1>
+template <class D, class R, size_t r = 1>
+class Local0dFiniteElement : public LocalFiniteElementDefault<D, 0, R, r>
 {
-  using BaseType = LocalFiniteElementDefault<D, 0, R, 1>;
+  using BaseType = LocalFiniteElementDefault<D, 0, R, r>;
 
 public:
   Local0dFiniteElement()
     : BaseType(0,
-               new Local0dFiniteElementBasis<D, R>(),
-               new Local0dFiniteElementCoefficients<D>(),
-               new Local0dFiniteElementInterpolation<D, R>(),
+               new Local0dFiniteElementBasis<D, R, r>(),
+               new Local0dFiniteElementCoefficients<D>(r),
+               new Local0dFiniteElementInterpolation<D, R, r>(),
                {ReferenceElements<D, 0>::simplex().position(0, 0)})
   {}
 }; // class Local0dFiniteElement
diff --git a/dune/gdt/local/finite-elements/default.hh b/dune/gdt/local/finite-elements/default.hh
index ed2b1ae5f6402341b7d807114436876223256746..afcc6eabddb5926f50eb887cc97e92fd189adc58 100644
--- a/dune/gdt/local/finite-elements/default.hh
+++ b/dune/gdt/local/finite-elements/default.hh
@@ -11,7 +11,10 @@
 #ifndef DUNE_GDT_LOCAL_FINITE_ELEMENTS_DEFAULT_HH
 #define DUNE_GDT_LOCAL_FINITE_ELEMENTS_DEFAULT_HH
 
+#include <dune/geometry/quadraturerules.hh>
+
 #include <dune/xt/common/memory.hh>
+#include <dune/xt/grid/integrals.hh>
 
 #include "interfaces.hh"
 
@@ -25,7 +28,7 @@ namespace GDT {
 template <class D, size_t d, class R, size_t r, size_t rC = 1>
 class LocalFiniteElementDefault : public LocalFiniteElementInterface<D, d, R, r, rC>
 {
-  using ThisType = LocalFiniteElementDefault<D, d, R, r, rC>;
+  using ThisType = LocalFiniteElementDefault;
   using BaseType = LocalFiniteElementInterface<D, d, R, r, rC>;
 
 public:
@@ -145,26 +148,26 @@ private:
 
 
 template <class D, size_t d, class R = double, size_t r = 1, size_t rC = 1>
-class ThreadSafeDefaultLocalLagrangeFiniteElementFamily : public LocalFiniteElementFamilyInterface<D, d, R, r, rC>
+class ThreadSafeDefaultLocalFiniteElementFamily : public LocalFiniteElementFamilyInterface<D, d, R, r, rC>
 {
-  using ThisType = ThreadSafeDefaultLocalLagrangeFiniteElementFamily<D, d, R, r, rC>;
+  using ThisType = ThreadSafeDefaultLocalFiniteElementFamily;
   using BaseType = LocalFiniteElementFamilyInterface<D, d, R, r, rC>;
 
 public:
   using typename BaseType::LocalFiniteElementType;
 
-  ThreadSafeDefaultLocalLagrangeFiniteElementFamily(
+  ThreadSafeDefaultLocalFiniteElementFamily(
       std::function<std::unique_ptr<LocalFiniteElementType>(const GeometryType&, const int&)> factory)
     : factory_(factory)
   {}
 
-  ThreadSafeDefaultLocalLagrangeFiniteElementFamily(const ThisType& other)
+  ThreadSafeDefaultLocalFiniteElementFamily(const ThisType& other)
     : factory_(other.factory_)
   {
     // we do not even try to create the FEs in a thread safe way, they will just be recreated when required
   }
 
-  ThreadSafeDefaultLocalLagrangeFiniteElementFamily(ThisType&& source)
+  ThreadSafeDefaultLocalFiniteElementFamily(ThisType&& source)
     : factory_(std::move(source.factory_))
     , fes_(std::move(source.fes_))
   {}
@@ -179,19 +182,80 @@ public:
       // the FE needs to be created, we need to lock
       std::lock_guard<std::mutex> DXTC_UNUSED(guard)(mutex_);
       // and to check again if someone else created the FE while we were waiting to acquire the lock
-      if (fes_.count(key) == 0) {
-        auto dings = factory_(geometry_type, order);
-        fes_.insert(std::make_pair(key, std::shared_ptr<LocalFiniteElementType>(dings.release())));
-      }
+      if (fes_.count(key) == 0)
+        fes_[key] = factory_(geometry_type, order);
     }
     return *fes_.at(key);
   } // ... get(...)
 
 private:
   const std::function<std::unique_ptr<LocalFiniteElementType>(const GeometryType&, const int&)> factory_;
-  mutable std::map<std::pair<GeometryType, int>, std::shared_ptr<LocalFiniteElementType>> fes_;
+  mutable std::map<std::pair<GeometryType, int>, std::unique_ptr<LocalFiniteElementType>> fes_;
   mutable std::mutex mutex_;
-}; // class ThreadSafeDefaultLocalLagrangeFiniteElementFamily
+}; // class ThreadSafeDefaultLocalFiniteElementFamily
+
+
+template <class D, size_t d, class R, size_t r = 1>
+class LocalL2FiniteElementInterpolation : public LocalFiniteElementInterpolationInterface<D, d, R, r, 1>
+{
+  using ThisType = LocalL2FiniteElementInterpolation;
+  using BaseType = LocalFiniteElementInterpolationInterface<D, d, R, r, 1>;
+
+public:
+  using typename BaseType::DomainType;
+  using typename BaseType::RangeType;
+
+  using LocalBasisType = LocalFiniteElementBasisInterface<D, d, R, r, 1>;
+
+  LocalL2FiniteElementInterpolation(const LocalBasisType& local_basis)
+    : local_basis_(local_basis.copy())
+  {}
+
+  LocalL2FiniteElementInterpolation(const ThisType& other)
+    : local_basis_(other.local_basis_->copy())
+  {}
+
+  LocalL2FiniteElementInterpolation(ThisType& source) = default;
+
+  ThisType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  const GeometryType& geometry_type() const override final
+  {
+    return local_basis_->geometry_type();
+  }
+
+  size_t size() const override final
+  {
+    return local_basis_->size();
+  }
+
+  using BaseType::interpolate;
+
+  void interpolate(const std::function<RangeType(DomainType)>& local_function,
+                   const int order,
+                   DynamicVector<R>& dofs) const override final
+  {
+    if (dofs.size() < this->size())
+      dofs.resize(this->size());
+    dofs *= 0.;
+    std::vector<RangeType> basis_values(local_basis_->size());
+    RangeType function_value;
+    for (auto&& quadrature_point : QuadratureRules<D, d>::rule(this->geometry_type(), order + local_basis_->order())) {
+      const auto point_in_reference_element = quadrature_point.position();
+      const auto quadrature_weight = quadrature_point.weight();
+      local_basis_->evaluate(point_in_reference_element, basis_values);
+      function_value = local_function(point_in_reference_element);
+      for (size_t ii = 0; ii < local_basis_->size(); ++ii)
+        dofs[ii] = (basis_values[ii] * function_value) * quadrature_weight;
+    }
+  } // ... interpolate(...)
+
+private:
+  std::unique_ptr<LocalBasisType> local_basis_;
+}; // class LocalL2FiniteElementInterpolation
 
 
 } // namespace GDT
diff --git a/dune/gdt/local/finite-elements/flattop.hh b/dune/gdt/local/finite-elements/flattop.hh
new file mode 100644
index 0000000000000000000000000000000000000000..8d7723d54a12c54235a3fae01acaca722a3c188c
--- /dev/null
+++ b/dune/gdt/local/finite-elements/flattop.hh
@@ -0,0 +1,297 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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)
+
+#ifndef DUNE_GDT_LOCAL_FINITE_ELEMENTS_FLATTOP_HH
+#define DUNE_GDT_LOCAL_FINITE_ELEMENTS_FLATTOP_HH
+
+#include <functional>
+
+#include <dune/gdt/exceptions.hh>
+
+#include "default.hh"
+#include "interfaces.hh"
+#include "lagrange.hh"
+#include "power.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+/**
+ * \sa LocalFlatTop2dCubeFiniteElement
+ * \sa LocalFlatTopFiniteElementFactory
+ */
+template <class D = double, class R = double>
+class LocalFlatTop2dCubeFiniteElementBasis : public LocalFiniteElementBasisInterface<D, 2, R, 1, 1>
+{
+  using ThisType = LocalFlatTop2dCubeFiniteElementBasis;
+  using BaseType = LocalFiniteElementBasisInterface<D, 2, R, 1, 1>;
+
+public:
+  using typename BaseType::DerivativeRangeType;
+  using typename BaseType::DomainType;
+  using typename BaseType::RangeType;
+
+  LocalFlatTop2dCubeFiniteElementBasis(const double& overlap = 0.5)
+    : geometry_type_(Dune::GeometryTypes::cube(2))
+  {
+    // we cannot let L_ and R_ be members and define phi_L_ and phi_R_ in the ctor initializer list, as they will
+    // copy/reference broken/empty/default L_ and R_
+    const auto L_ = (1. - overlap) / 2.;
+    const auto R_ = (1. + overlap) / 2.;
+    phi_L_ = [=](const D& x) {
+      if (x < L_)
+        return 1.;
+      else if (x > R_)
+        return 0.;
+      else
+        return -1. * (x / overlap) + R_ / overlap;
+    };
+    grad_phi_L_ = [=](const D& x) {
+      if (x < L_)
+        return 0.;
+      else if (x > R_)
+        return 0.;
+      else
+        return -1. / overlap;
+    };
+    phi_R_ = [=](const D& x) {
+      if (x < L_)
+        return 0.;
+      else if (x > R_)
+        return 1.;
+      else
+        return (x / overlap) - L_ / overlap;
+    };
+    grad_phi_R_ = [=](const D& x) {
+      if (x < L_)
+        return 0.;
+      else if (x > R_)
+        return 0.;
+      else
+        return (1. / overlap);
+    };
+  } // LocalFlatTop2dCubeFiniteElementBasis(...)
+
+  LocalFlatTop2dCubeFiniteElementBasis(const ThisType& other) = default;
+  LocalFlatTop2dCubeFiniteElementBasis(ThisType&& source) = default;
+
+  ThisType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  const GeometryType& geometry_type() const override final
+  {
+    return geometry_type_;
+  }
+
+  int order() const override final
+  {
+    return 2;
+  }
+
+  size_t size() const override final
+  {
+    return 4;
+  }
+
+  using BaseType::evaluate;
+
+  void evaluate(const DomainType& point_in_reference_element, std::vector<RangeType>& result) const override final
+  {
+    if (result.size() < 4)
+      result.resize(4);
+    const auto& x = point_in_reference_element[0];
+    const auto& y = point_in_reference_element[1];
+    // each shape function is associated with one corner, the positions of which are known on the reference element
+    // * shape function 0: bottom left corner
+    result[0] = phi_L_(x) * phi_L_(y);
+    // * shape function 1: bottom right corner
+    result[1] = phi_R_(x) * phi_L_(y);
+    // * shape function 2: top left corner
+    result[2] = phi_L_(x) * phi_R_(y);
+    // * shape function 3: top right corner
+    result[3] = phi_R_(x) * phi_R_(y);
+  } // ... evaluate(...)
+
+  using BaseType::jacobian;
+
+  void jacobian(const DomainType& point_in_reference_element,
+                std::vector<DerivativeRangeType>& result) const override final
+  {
+    if (result.size() < 4)
+      result.resize(4);
+    const auto& x = point_in_reference_element[0];
+    const auto& y = point_in_reference_element[1];
+    // * shape function 0
+    result[0][0][0] = grad_phi_L_(x) * phi_L_(y);
+    result[0][0][1] = phi_L_(x) * grad_phi_L_(y);
+    // * shape function 1
+    result[1][0][0] = grad_phi_R_(x) * phi_L_(y);
+    result[1][0][1] = phi_R_(x) * grad_phi_L_(y);
+    // * shape function 2
+    result[2][0][0] = grad_phi_L_(x) * phi_R_(y);
+    result[2][0][1] = phi_L_(x) * grad_phi_R_(y);
+    // * shape function 3
+    result[3][0][0] = grad_phi_R_(x) * phi_R_(y);
+    result[3][0][1] = phi_R_(x) * grad_phi_R_(y);
+  } // ... jacobian(...)
+
+private:
+  const GeometryType geometry_type_;
+  std::function<R(const D&)> phi_L_;
+  std::function<R(const D&)> phi_R_;
+  std::function<R(const D&)> grad_phi_L_;
+  std::function<R(const D&)> grad_phi_R_;
+}; // class LocalFlatTop2dCubeFiniteElementBasis
+
+
+template <class D = double, class R = double>
+class LocalFlatTop2dCubeFiniteElement : public LocalFiniteElementDefault<D, 2, R, 1>
+{
+  using ThisType = LocalFlatTop2dCubeFiniteElement;
+  using BaseType = LocalFiniteElementDefault<D, 2, R, 1>;
+
+public:
+  LocalFlatTop2dCubeFiniteElement(const double& overlap = 0.5)
+    : BaseType(
+          1,
+          LocalFlatTop2dCubeFiniteElementBasis<D, R>(overlap).copy(),
+          LocalLagrangeFiniteElementFactory<D, 2, R, 1>::create(Dune::GeometryTypes::cube(2), 1)->coefficients().copy(),
+          LocalL2FiniteElementInterpolation<D, 2, R, 1>(LocalFlatTop2dCubeFiniteElementBasis<D, R>(overlap)).copy(),
+          {})
+  {}
+}; // class LocalFlatTop2dCubeFiniteElement
+
+
+template <class D, size_t d, class R, size_t r = 1>
+class LocalFlatTopFiniteElementFactory
+{
+  using ScalarLocalFiniteElementType = LocalFiniteElementInterface<D, d, R, 1>;
+
+public:
+  using LocalFiniteElementType = LocalFiniteElementInterface<D, d, R, r>;
+
+private:
+  static std::string order_error(const GeometryType& geometry_type, const int order)
+  {
+    std::stringstream ss;
+    ss << "when creating a local FlatTop finite element: the FlatTopLocalFiniteElement is known to fail in " << d
+       << "d on a " << geometry_type << " reference element for order " << order
+       << " (if you think it is working, update this check)!";
+    return ss.str();
+  }
+
+  static std::string geometry_error(const GeometryType& geometry_type, const int order)
+  {
+    std::stringstream ss;
+    ss << "when creating a local FlatTop finite element: this is untested!\n"
+       << "Please update this check if you believe that FlatTopLocalFiniteElement is available for\n- dimension: " << d
+       << "\n- geometry_type: " << geometry_type << "\n- order: " << order;
+    return ss.str();
+  }
+
+  // Fist we create the scalar FE ...
+
+  template <size_t d_ = d, bool anything = true>
+  struct scalar_helper
+  {
+    static std::unique_ptr<ScalarLocalFiniteElementType>
+    create(const GeometryType& geometry_type, const int& order, const D& overlap = 0.5)
+    {
+      DUNE_THROW_IF(geometry_type.dim() != d,
+                    Exceptions::finite_element_error,
+                    "geometry_type = " << geometry_type << "\nd = " << d);
+      // checks
+      if (d == 2) {
+        if (geometry_type == GeometryTypes::cube(2))
+          DUNE_THROW_IF(order != 1, Exceptions::finite_element_error, order_error(geometry_type, order));
+        else
+          DUNE_THROW(Exceptions::finite_element_error, geometry_error(geometry_type, order));
+      } else
+        DUNE_THROW(Exceptions::finite_element_error, geometry_error(geometry_type, order));
+      // the actual finite element
+      return std::unique_ptr<LocalFiniteElementInterface<D, d, R, 1>>(
+          new LocalFlatTop2dCubeFiniteElement<D, R>(overlap));
+    }
+  }; // helper<...>
+
+  template <bool anything>
+  struct scalar_helper<0, anything>
+  {
+    static std::unique_ptr<ScalarLocalFiniteElementType>
+    create(const GeometryType& geometry_type, const int& /*order*/, const D& /*overlap*/ = 0.5)
+    {
+      // If we need this, and geometry_type.dim() == 0, we must simply implement the corresponding ctors of the 0d FE!
+      DUNE_THROW_IF(
+          geometry_type.dim() != 0 || !geometry_type.isSimplex(),
+          Exceptions::finite_element_error,
+          "when creating a local 0d orthonomal finite element: not available for geometry_type = " << geometry_type);
+      return std::make_unique<Local0dFiniteElement<D, R>>();
+    }
+  }; // helper<...>
+
+  // ... then we wrap this in a power FE, if required.
+
+  template <size_t d_ = d, size_t r_ = r>
+  struct helper // r != 1
+  {
+    static std::unique_ptr<LocalFiniteElementType>
+    create(const GeometryType& geometry_type, const int& order, const D& overlap = 0.5)
+    {
+      return make_local_powered_finite_element<r>(*scalar_helper<>::create(geometry_type, order, overlap));
+    }
+  };
+
+  template <size_t d_>
+  struct helper<d_, 1>
+  {
+    static std::unique_ptr<LocalFiniteElementType>
+    create(const GeometryType& geometry_type, const int& order, const D& overlap = 0.5)
+    {
+      return scalar_helper<>::create(geometry_type, order, overlap);
+    }
+  };
+
+public:
+  static std::unique_ptr<LocalFiniteElementInterface<D, d, R, r>>
+  create(const GeometryType& geometry_type, const int& order, const D& overlap = 0.5)
+  {
+    return helper<>::create(geometry_type, order, overlap);
+  }
+}; // class LocalFlatTopFiniteElementFactory
+
+
+template <class D, size_t d, class R, size_t r = 1>
+std::unique_ptr<LocalFiniteElementInterface<D, d, R, r>>
+make_local_flattop_finite_element(const GeometryType& geometry_type, const int order, const D& overlap = 0.5)
+{
+  return LocalFlatTopFiniteElementFactory<D, d, R, r>::create(geometry_type, order, overlap);
+}
+
+
+template <class D, size_t d, class R, size_t r = 1>
+class LocalFlatTopFiniteElementFamily : public ThreadSafeDefaultLocalFiniteElementFamily<D, d, R, r>
+{
+  using BaseType = ThreadSafeDefaultLocalFiniteElementFamily<D, d, R, r>;
+
+public:
+  LocalFlatTopFiniteElementFamily(const D& overlap = 0.5)
+    : BaseType([=](const auto& geometry_type, const auto& order) {
+      return LocalFlatTopFiniteElementFactory<D, d, R, r>::create(geometry_type, order, overlap);
+    })
+  {}
+}; // ... LocalFlatTopFiniteElementFamily(...)
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_LOCAL_FINITE_ELEMENTS_FLATTOP_HH
diff --git a/dune/gdt/local/finite-elements/interfaces.hh b/dune/gdt/local/finite-elements/interfaces.hh
index fff008196bf0751bc3f19954775599282dc467ba..09a890c5d1e1135f2a32265e7e167a9c53983ed5 100644
--- a/dune/gdt/local/finite-elements/interfaces.hh
+++ b/dune/gdt/local/finite-elements/interfaces.hh
@@ -42,7 +42,7 @@ class LocalDofVector;
 template <class DomainField, size_t domain_dim, class RangeField, size_t range_dim, size_t range_dim_columns = 1>
 class LocalFiniteElementBasisInterface
 {
-  using ThisType = LocalFiniteElementBasisInterface<DomainField, domain_dim, RangeField, range_dim, range_dim_columns>;
+  using ThisType = LocalFiniteElementBasisInterface;
 
 public:
   using D = DomainField;
@@ -154,7 +154,7 @@ private:
 template <class DomainField, size_t domain_dim>
 class LocalFiniteElementCoefficientsInterface
 {
-  using ThisType = LocalFiniteElementCoefficientsInterface<DomainField, domain_dim>;
+  using ThisType = LocalFiniteElementCoefficientsInterface;
 
 public:
   using D = DomainField;
diff --git a/dune/gdt/local/finite-elements/lagrange.hh b/dune/gdt/local/finite-elements/lagrange.hh
index e575ddda9d24d78afbb4a31dd61d0d2f7f5c3ffe..3381f710564ebb3105c545f4b8d25f4c2886a775 100644
--- a/dune/gdt/local/finite-elements/lagrange.hh
+++ b/dune/gdt/local/finite-elements/lagrange.hh
@@ -36,16 +36,16 @@ namespace GDT {
 /**
  * Wraps the P0LocalFiniteElement from dune-localfunctions and adds Lagrange points.
  */
-template <class D, size_t d, class R>
+template <class D, size_t d, class R, size_t r = 1>
 class LocalZeroOrderLagrangeFiniteElement
-  : XT::Common::ConstStorageProvider<LocalFiniteElementWrapper<P0LocalFiniteElement<D, R, d>, D, d, R, 1>>
-  , public LocalFiniteElementDefault<D, d, R, 1>
+  : XT::Common::ConstStorageProvider<LocalFiniteElementWrapper<P0LocalFiniteElement<D, R, d>, D, d, R, r>>
+  , public LocalFiniteElementDefault<D, d, R, r>
 {
-  using ThisType = LocalZeroOrderLagrangeFiniteElement<D, d, R>;
+  using ThisType = LocalZeroOrderLagrangeFiniteElement;
   using Implementation = P0LocalFiniteElement<D, R, d>;
-  using Wrapper = LocalFiniteElementWrapper<Implementation, D, d, R, 1>;
+  using Wrapper = LocalFiniteElementWrapper<Implementation, D, d, R, r>;
   using Storage = XT::Common::ConstStorageProvider<Wrapper>;
-  using BaseType = LocalFiniteElementDefault<D, d, R, 1>;
+  using BaseType = LocalFiniteElementDefault<D, d, R, r>;
 
 public:
   LocalZeroOrderLagrangeFiniteElement(const GeometryType& geometry_type)
@@ -195,9 +195,9 @@ make_local_lagrange_finite_element(const GeometryType& geometry_type, const int
 
 
 template <class D, size_t d, class R, size_t r = 1>
-class LocalLagrangeFiniteElementFamily : public ThreadSafeDefaultLocalLagrangeFiniteElementFamily<D, d, R, r>
+class LocalLagrangeFiniteElementFamily : public ThreadSafeDefaultLocalFiniteElementFamily<D, d, R, r>
 {
-  using BaseType = ThreadSafeDefaultLocalLagrangeFiniteElementFamily<D, d, R, r>;
+  using BaseType = ThreadSafeDefaultLocalFiniteElementFamily<D, d, R, r>;
 
 public:
   LocalLagrangeFiniteElementFamily()
diff --git a/dune/gdt/local/finite-elements/power.hh b/dune/gdt/local/finite-elements/power.hh
index ed6d1bfa80f0eeef159d9624c7d1379e61c1db85..506415ad740a7b35ed57440caaad1698ecb6efb2 100644
--- a/dune/gdt/local/finite-elements/power.hh
+++ b/dune/gdt/local/finite-elements/power.hh
@@ -48,7 +48,7 @@ class LocalPowerFiniteElementBasis
 template <size_t power, class D, size_t d, class R, size_t r>
 class LocalPowerFiniteElementBasis<power, D, d, R, r, 1> : public LocalFiniteElementBasisInterface<D, d, R, power * r>
 {
-  using ThisType = LocalPowerFiniteElementBasis<power, D, d, R, r, 1>;
+  using ThisType = LocalPowerFiniteElementBasis;
   using BaseType = LocalFiniteElementBasisInterface<D, d, R, power * r>;
 
 public:
@@ -149,7 +149,7 @@ template <size_t power, class D, size_t d, class R, size_t r>
 class LocalPowerFiniteElementInterpolation<power, D, d, R, r, 1>
   : public LocalFiniteElementInterpolationInterface<D, d, R, power * r, 1>
 {
-  using ThisType = LocalPowerFiniteElementInterpolation<power, D, d, R, r, 1>;
+  using ThisType = LocalPowerFiniteElementInterpolation;
   using BaseType = LocalFiniteElementInterpolationInterface<D, d, R, power * r, 1>;
 
 public:
@@ -193,10 +193,17 @@ public:
     const size_t sz = this->size();
     if (dofs.size() < sz)
       dofs.resize(sz);
+    eval_cache_.clear();
     for (size_t pp = 0; pp < power; ++pp) {
       unpowered_->interpolate(
           [&](const auto& point_in_reference_element) {
-            const auto tmp = local_function(point_in_reference_element);
+            auto cache_it = eval_cache_.find(point_in_reference_element);
+            if (cache_it == eval_cache_.end())
+              cache_it =
+                  eval_cache_
+                      .insert(std::make_pair(point_in_reference_element, local_function(point_in_reference_element)))
+                      .first;
+            const RangeType& tmp = cache_it->second;
             FieldVector<R, r> ret;
             for (size_t rr = 0; rr < r; ++rr)
               ret[rr] = tmp[pp * r + rr];
@@ -213,6 +220,7 @@ public:
 private:
   const std::unique_ptr<const UnpoweredType> unpowered_;
   mutable DynamicVector<R> unpowered_dofs_;
+  mutable std::map<DomainType, RangeType, XT::Common::FieldVectorLess> eval_cache_;
 }; // class LocalPowerFiniteElementInterpolation
 
 
@@ -227,7 +235,7 @@ private:
 template <class D, size_t d>
 class LocalPowerFiniteElementCoefficients : public LocalFiniteElementCoefficientsInterface<D, d>
 {
-  using ThisType = LocalPowerFiniteElementCoefficients<D, d>;
+  using ThisType = LocalPowerFiniteElementCoefficients;
   using BaseType = LocalFiniteElementCoefficientsInterface<D, d>;
 
 public:
@@ -309,7 +317,7 @@ class LocalPowerFiniteElement
 template <class D, size_t d, class R, size_t r, size_t rC>
 class LocalPowerFiniteElement<1, D, d, R, r, rC> : public LocalFiniteElementDefault<D, d, R, r, rC>
 {
-  using ThisType = LocalPowerFiniteElement<1, D, d, R, r, rC>;
+  using ThisType = LocalPowerFiniteElement;
   using BaseType = LocalFiniteElementDefault<D, d, R, r, rC>;
 
 public:
@@ -331,7 +339,7 @@ public:
 template <size_t power, class D, size_t d, class R, size_t r>
 class LocalPowerFiniteElement<power, D, d, R, r, 1> : public LocalFiniteElementDefault<D, d, R, power * r>
 {
-  using ThisType = LocalPowerFiniteElement<power, D, d, R, r, 1>;
+  using ThisType = LocalPowerFiniteElement;
   using BaseType = LocalFiniteElementDefault<D, d, R, power * r>;
 
 public:
diff --git a/dune/gdt/local/finite-elements/raviart-thomas.hh b/dune/gdt/local/finite-elements/raviart-thomas.hh
index 244a502b616e77e41a8e87caabba91445a28d389..fbd8179c0ef00e5ec5c1f2e784a81e47b6f53f42 100644
--- a/dune/gdt/local/finite-elements/raviart-thomas.hh
+++ b/dune/gdt/local/finite-elements/raviart-thomas.hh
@@ -41,7 +41,7 @@ namespace GDT {
 template <class D, size_t d, class R>
 class LocalRaviartThomasInterpolation : public LocalFiniteElementInterpolationInterface<D, d, R, d, 1>
 {
-  using ThisType = LocalRaviartThomasInterpolation<D, d, R>;
+  using ThisType = LocalRaviartThomasInterpolation;
   using BaseType = LocalFiniteElementInterpolationInterface<D, d, R, d, 1>;
 
 public:
@@ -353,13 +353,16 @@ make_local_raviart_thomas_finite_element(const GeometryType& geometry_type, cons
 
 
 template <class D, size_t d, class R>
-class LocalRaviartThomasFiniteElementFamily : public ThreadSafeDefaultLocalLagrangeFiniteElementFamily<D, d, R, d, 1>
+class LocalRaviartThomasFiniteElementFamily : public ThreadSafeDefaultLocalFiniteElementFamily<D, d, R, d, 1>
 {
-  using BaseType = ThreadSafeDefaultLocalLagrangeFiniteElementFamily<D, d, R, d, 1>;
+  using BaseType = ThreadSafeDefaultLocalFiniteElementFamily<D, d, R, d, 1>;
 
 public:
   LocalRaviartThomasFiniteElementFamily()
     : BaseType([](const auto& geometry_type, const auto& order) {
+      // Can't figure out why this lock is needed, but for some reasons without it we are not thread-safe
+      static std::mutex mutex;
+      std::lock_guard<std::mutex> DXTC_UNUSED(guard)(mutex);
       return LocalRaviartThomasFiniteElementFactory<D, d, R>::create(geometry_type, order);
     })
   {}
diff --git a/dune/gdt/local/finite-elements/wrapper.hh b/dune/gdt/local/finite-elements/wrapper.hh
index 5f0abfee1dcaa157de6a1fc9bdfe47af0b5f8986..72415f1a7a885ab37cf7796ba4bfcc79bacc1d62 100644
--- a/dune/gdt/local/finite-elements/wrapper.hh
+++ b/dune/gdt/local/finite-elements/wrapper.hh
@@ -81,7 +81,7 @@ public:
 template <class Implementation, class D, size_t d, class R, size_t r, size_t rC = 1>
 class LocalFiniteElementBasisWrapper : public LocalFiniteElementBasisInterface<D, d, R, r, rC>
 {
-  using ThisType = LocalFiniteElementBasisWrapper<Implementation, D, d, R, r, rC>;
+  using ThisType = LocalFiniteElementBasisWrapper;
   using BaseType = LocalFiniteElementBasisInterface<D, d, R, r, rC>;
 
 public:
@@ -164,7 +164,7 @@ private:
 template <class Implementation, class D, size_t d, class R, size_t r, size_t rC = 1>
 class LocalFiniteElementInterpolationWrapper : public LocalFiniteElementInterpolationInterface<D, d, R, r, rC>
 {
-  using ThisType = LocalFiniteElementInterpolationWrapper<Implementation, D, d, R, r, rC>;
+  using ThisType = LocalFiniteElementInterpolationWrapper;
   using BaseType = LocalFiniteElementInterpolationInterface<D, d, R, r, rC>;
 
   template <class R_ = R, size_t r_ = r, size_t rC_ = rC>
@@ -244,7 +244,8 @@ public:
 
   size_t size() const override final
   {
-    return XT::Common::numeric_cast<size_t>(imp_->size());
+    assert(imp_->size() >= 0 && imp_->size() < std::numeric_limits<size_t>::max());
+    return static_cast<size_t>(imp_->size());
   }
 
   using BaseType::interpolate;
@@ -279,7 +280,7 @@ private:
 template <class Implementation, class D, size_t d>
 class LocalFiniteElementCoefficientsWrapper : public LocalFiniteElementCoefficientsInterface<D, d>
 {
-  using ThisType = LocalFiniteElementCoefficientsWrapper<Implementation, D, d>;
+  using ThisType = LocalFiniteElementCoefficientsWrapper;
   using BaseType = LocalFiniteElementCoefficientsInterface<D, d>;
 
 public:
@@ -352,7 +353,7 @@ class LocalFiniteElementWrapper
   : XT::Common::ConstSharedStorageProvider<Implementation>
   , public LocalFiniteElementDefault<D, d, R, r, rC>
 {
-  using ThisType = LocalFiniteElementWrapper<Implementation, D, d, R, r, rC>;
+  using ThisType = LocalFiniteElementWrapper;
   using ImplementationProvider = XT::Common::ConstSharedStorageProvider<Implementation>;
   using BaseType = LocalFiniteElementDefault<D, d, R, r, rC>;
 
diff --git a/dune/gdt/local/functionals/integrals.hh b/dune/gdt/local/functionals/integrals.hh
index 2d528c62c032b44695f425db880db73caaad1160..95b1e4cc3ee24a50a52f5bff74be04d28495f68c 100644
--- a/dune/gdt/local/functionals/integrals.hh
+++ b/dune/gdt/local/functionals/integrals.hh
@@ -26,7 +26,7 @@ namespace GDT {
 template <class E, size_t r = 1, size_t rC = 1, class R = double, class F = R>
 class LocalElementIntegralFunctional : public LocalElementFunctionalInterface<E, r, rC, R, F>
 {
-  using ThisType = LocalElementIntegralFunctional<E, r, rC, R, F>;
+  using ThisType = LocalElementIntegralFunctional;
   using BaseType = LocalElementFunctionalInterface<E, r, rC, R, F>;
 
 public:
@@ -43,11 +43,13 @@ public:
   {}
 
   LocalElementIntegralFunctional(typename GenericIntegrand::GenericOrderFunctionType order_function,
-                                 typename GenericIntegrand::GenericEvalauteFunctionType evaluate_function,
+                                 typename GenericIntegrand::GenericEvaluateFunctionType evaluate_function,
+                                 typename GenericIntegrand::GenericPostBindFunctionType post_bind_function =
+                                     [](const E&) {},
                                  const XT::Common::ParameterType& param_type = {},
                                  const int over_integrate = 0)
     : BaseType(param_type)
-    , integrand_(GenericIntegrand(order_function, evaluate_function).copy())
+    , integrand_(GenericIntegrand(order_function, evaluate_function, post_bind_function).copy())
     , over_integrate_(over_integrate)
   {}
 
@@ -104,7 +106,7 @@ private:
 template <class I, size_t r = 1, size_t rC = 1, class R = double, class F = R>
 class LocalIntersectionIntegralFunctional : public LocalIntersectionFunctionalInterface<I, r, rC, R, F>
 {
-  using ThisType = LocalIntersectionIntegralFunctional<I, r, rC, R, F>;
+  using ThisType = LocalIntersectionIntegralFunctional;
   using BaseType = LocalIntersectionFunctionalInterface<I, r, rC, R, F>;
 
 public:
@@ -122,11 +124,13 @@ public:
   {}
 
   LocalIntersectionIntegralFunctional(typename GenericIntegrand::GenericOrderFunctionType order_function,
-                                      typename GenericIntegrand::GenericEvalauteFunctionType evaluate_function,
+                                      typename GenericIntegrand::GenericEvaluateFunctionType evaluate_function,
+                                      typename GenericIntegrand::GenericPostBindFunctionType post_bind_function =
+                                          [](const I&) {},
                                       const XT::Common::ParameterType& param_type = {},
                                       const int over_integrate = 0)
     : BaseType(param_type)
-    , integrand_(GenericIntegrand(order_function, evaluate_function).copy())
+    , integrand_(GenericIntegrand(order_function, evaluate_function, post_bind_function).copy())
     , over_integrate_(over_integrate)
   {}
 
diff --git a/dune/gdt/local/functionals/interfaces.hh b/dune/gdt/local/functionals/interfaces.hh
index e01512b1a0050ebb29acb44cf679d3498acb6686..a9cb21a8202f916a25cc3fdebeb048d8a7902fc7 100644
--- a/dune/gdt/local/functionals/interfaces.hh
+++ b/dune/gdt/local/functionals/interfaces.hh
@@ -41,7 +41,7 @@ class LocalElementFunctionalInterface : public XT::Common::ParametricInterface
 {
   static_assert(XT::Grid::is_entity<Element>::value, "");
 
-  using ThisType = LocalElementFunctionalInterface<Element, range_dim, range_dim_cols, RangeField>;
+  using ThisType = LocalElementFunctionalInterface;
 
 public:
   using E = Element;
@@ -100,7 +100,7 @@ class LocalIntersectionFunctionalInterface : public XT::Common::ParametricInterf
 {
   static_assert(XT::Grid::is_intersection<Intersection>::value, "");
 
-  using ThisType = LocalIntersectionFunctionalInterface<Intersection, range_dim, range_dim_cols, RangeField>;
+  using ThisType = LocalIntersectionFunctionalInterface;
 
 public:
   using IntersectionType = Intersection;
diff --git a/dune/gdt/local/integrands/abs.hh b/dune/gdt/local/integrands/abs.hh
index 970c2c3ae41bbb4b944b0741df4658c53fe6bfac..e09794fb5cf37557f9879e35356c8ee54645b233 100644
--- a/dune/gdt/local/integrands/abs.hh
+++ b/dune/gdt/local/integrands/abs.hh
@@ -23,7 +23,7 @@ template <class E, size_t r = 1, size_t rC = 1, class R = double, class F = doub
 class LocalElementAbsIntegrand : public LocalUnaryElementIntegrandInterface<E, r, rC, R, F>
 {
   static_assert(rC == 1, "");
-  using ThisType = LocalElementAbsIntegrand<E, r, rC, R, F>;
+  using ThisType = LocalElementAbsIntegrand;
   using BaseType = LocalUnaryElementIntegrandInterface<E, r, rC, R, F>;
 
 public:
diff --git a/dune/gdt/local/integrands/combined.hh b/dune/gdt/local/integrands/combined.hh
new file mode 100644
index 0000000000000000000000000000000000000000..4d0ae1f71b44e5c9878f377a2bc95861ca88774b
--- /dev/null
+++ b/dune/gdt/local/integrands/combined.hh
@@ -0,0 +1,379 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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)
+
+#ifndef DUNE_GDT_LOCAL_INTEGRANDS_COMBINED_HH
+#define DUNE_GDT_LOCAL_INTEGRANDS_COMBINED_HH
+
+#include <dune/xt/common/memory.hh>
+
+#include "interfaces.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+/// \todo add LocalUnaryElementIntegrandSum
+/// \todo add operator+ to LocalUnaryElementIntegrandInterface
+/// \sa LocalQuaternaryIntersectionIntegrandSum
+// template <class E, size_t r, size_t rC, class R, class F>
+// class LocalUnaryElementIntegrandSum
+//  : public XT::Common::ParametricInterface
+//  , public XT::Grid::ElementBoundObject<Element>
+//{
+//  static_assert(XT::Grid::is_entity<Element>::value, "");
+
+//  using ThisType = LocalUnaryElementIntegrandInterface;
+
+// public:
+//  using E = Element;
+//  using D = typename Element::Geometry::ctype;
+//  static const constexpr size_t d = E::dimension;
+//  using F = Field;
+
+//  using R = RangeField;
+//  static const constexpr size_t r = range_dim;
+//  static const constexpr size_t rC = range_dim_cols;
+
+//  using typename XT::Grid::ElementBoundObject<Element>::ElementType;
+//  using DomainType = FieldVector<D, d>;
+//  using LocalBasisType = XT::Functions::ElementFunctionSetInterface<E, r, rC, R>;
+
+//  LocalUnaryElementIntegrandInterface(const XT::Common::ParameterType& param_type = {})
+//    : XT::Common::ParametricInterface(param_type)
+//  {}
+
+//  virtual ~LocalUnaryElementIntegrandInterface() = default;
+
+//  virtual std::unique_ptr<ThisType> copy() const = 0;
+
+// protected:
+//  virtual void post_bind(const IntersectionType& intrsctn) = 0;
+
+// public:
+
+//  /**
+//   * Returns the polynomial order of the integrand, given the basis.
+//   *
+//   * \note Will throw Exceptions::not_bound_to_an_element_yet error if not bound yet!
+//   **/
+//  virtual int order(const LocalBasisType& basis, const XT::Common::Parameter& param = {}) const = 0;
+
+//  /**
+//   * Computes the evaluation of this integrand at the given point for each function in the basis.
+//   *
+//   * \note Will throw Exceptions::not_bound_to_an_element_yet error if not bound yet!
+//   **/
+//  virtual void evaluate(const LocalBasisType& basis,
+//                        const DomainType& point_in_reference_element,
+//                        DynamicVector<F>& result,
+//                        const XT::Common::Parameter& param = {}) const = 0;
+
+//  /**
+//   * This method is provided for convenience and should not be used within library code.
+//   *
+//   * \note Will throw Exceptions::not_bound_to_an_element_yet error if not bound yet!
+//   **/
+//  virtual DynamicVector<F> evaluate(const LocalBasisType& basis,
+//                                    const DomainType& point_in_reference_element,
+//                                    const XT::Common::Parameter& param = {}) const
+//  {
+//    DynamicVector<F> result(basis.size(param));
+//    evaluate(basis, point_in_reference_element, result, param);
+//    return result;
+//  }
+//}; // class LocalUnaryElementIntegrandSum
+
+
+/// \todo add LocalBinaryElementIntegrandSum
+/// \todo add operator+ to LocalBinaryElementIntegrandInterface
+/// \sa LocalQuaternaryIntersectionIntegrandSum
+// template <class E, size_t t_r, size_t t_RC, class TF, class F, size_t a_r, size_t a_rC, class AF>
+// class LocalBinaryElementIntegrandSum
+//  : public XT::Common::ParametricInterface
+//  , public XT::Grid::ElementBoundObject<Element>
+//{
+//  static_assert(XT::Grid::is_entity<Element>::value, "");
+
+//  using ThisType = LocalBinaryElementIntegrandInterface<Element,
+//                                                        test_range_dim,
+//                                                        test_range_dim_cols,
+//                                                        TestRangeField,
+//                                                        Field,
+//                                                        ansatz_range_dim,
+//                                                        ansatz_range_dim_cols,
+//                                                        AnsatzRangeField>;
+
+// public:
+//  using E = Element;
+//  using D = typename Element::Geometry::ctype;
+//  static const constexpr size_t d = E::dimension;
+//  using F = Field;
+
+//  using TR = TestRangeField;
+//  static const constexpr size_t t_r = test_range_dim;
+//  static const constexpr size_t t_rC = test_range_dim_cols;
+
+//  using AR = AnsatzRangeField;
+//  static const constexpr size_t a_r = ansatz_range_dim;
+//  static const constexpr size_t a_rC = ansatz_range_dim_cols;
+
+//  using typename XT::Grid::ElementBoundObject<Element>::ElementType;
+//  using DomainType = FieldVector<D, d>;
+//  using LocalTestBasisType = XT::Functions::ElementFunctionSetInterface<E, t_r, t_rC, TR>;
+//  using LocalAnsatzBasisType = XT::Functions::ElementFunctionSetInterface<E, a_r, a_rC, AR>;
+
+//  LocalBinaryElementIntegrandInterface(const XT::Common::ParameterType& param_type = {})
+//    : XT::Common::ParametricInterface(param_type)
+//  {}
+
+//  virtual ~LocalBinaryElementIntegrandInterface() = default;
+
+//  virtual std::unique_ptr<ThisType> copy() const = 0;
+
+// protected:
+//  virtual void post_bind(const IntersectionType& intrsctn) = 0;
+
+// public:
+//  /**
+//   * Returns the polynomial order of the integrand, given the bases.
+//   *
+//   * \note Will throw Exceptions::not_bound_to_an_element_yet error if not bound yet!
+//   **/
+//  virtual int order(const LocalTestBasisType& test_basis,
+//                    const LocalAnsatzBasisType& ansatz_basis,
+//                    const XT::Common::Parameter& param = {}) const = 0;
+
+//  /**
+//   * Computes the evaluation of this integrand at the given point for each combination of functions from the two
+//   bases.
+//   *
+//   * \note Will throw Exceptions::not_bound_to_an_element_yet error if not bound yet!
+//   **/
+//  virtual void evaluate(const LocalTestBasisType& test_basis,
+//                        const LocalAnsatzBasisType& ansatz_basis,
+//                        const DomainType& point_in_reference_element,
+//                        DynamicMatrix<F>& result,
+//                        const XT::Common::Parameter& param = {}) const = 0;
+
+//  /**
+//   * This method is provided for convenience and should not be used within library code.
+//   *
+//   * \note Will throw Exceptions::not_bound_to_an_element_yet error if not bound yet!
+//   **/
+//  virtual DynamicMatrix<F> evaluate(const LocalTestBasisType& test_basis,
+//                                    const LocalAnsatzBasisType& ansatz_basis,
+//                                    const DomainType& point_in_reference_element,
+//                                    const XT::Common::Parameter& param = {}) const
+//  {
+//    DynamicMatrix<F> result(test_basis.size(param), ansatz_basis.size(param), 0);
+//    evaluate(test_basis, ansatz_basis, point_in_reference_element, result, param);
+//    return result;
+//  }
+//}; // class LocalBinaryElementIntegrandInterface
+
+
+/// \todo add LocalBinaryIntersectionIntegrandSum
+/// \todo add operator+ to LocalBinaryIntersectionIntegrandInterface
+/// \sa LocalQuaternaryIntersectionIntegrandSum
+// template <class I, size_t r, size_t rC, class R, class F>
+// class LocalBinaryIntersectionIntegrandSum
+//  : public XT::Common::ParametricInterface
+//  , public XT::Grid::IntersectionBoundObject<Intersection>
+//{
+//  static_assert(XT::Grid::is_intersection<Intersection>::value, "");
+
+//  using ThisType =
+//      LocalBinaryIntersectionIntegrandInterface<Intersection, range_dim, range_dim_cols, RangeField, Field>;
+
+// public:
+//  using typename XT::Grid::IntersectionBoundObject<Intersection>::IntersectionType;
+//  using ElementType = XT::Grid::extract_inside_element_t<Intersection>;
+
+//  using I = Intersection;
+//  using E = ElementType;
+//  using D = typename ElementType::Geometry::ctype;
+//  static const constexpr size_t d = E::dimension;
+//  using F = Field;
+
+//  using RF = RangeField;
+//  static const constexpr size_t r = range_dim;
+//  static const constexpr size_t rC = range_dim_cols;
+
+//  using DomainType = FieldVector<D, d - 1>;
+//  using LocalBasisType = XT::Functions::ElementFunctionSetInterface<E, r, rC, RF>;
+
+//  LocalBinaryIntersectionIntegrandInterface(const XT::Common::ParameterType& param_type = {})
+//    : XT::Common::ParametricInterface(param_type)
+//  {}
+
+//  virtual ~LocalBinaryIntersectionIntegrandInterface() = default;
+
+//  virtual std::unique_ptr<ThisType> copy() const = 0;
+
+// protected:
+//  virtual void post_bind(const IntersectionType& intrsctn) = 0;
+
+// public:
+//  /**
+//   * Returns the polynomial order of the integrand, given the bases.
+//   *
+//   * \note Will throw Exceptions::not_bound_to_an_element_yet error if not bound yet!
+//   **/
+//  virtual int order(const LocalBasisType& inside_basis,
+//                    const LocalBasisType& outside_basis,
+//                    const XT::Common::Parameter& param = {}) const = 0;
+
+//  /**
+//   * Computes the evaluation of this integrand at the given point for each combination of functions from the two
+//   bases.
+//   *
+//   * \note Will throw Exceptions::not_bound_to_an_element_yet error if not bound yet!
+//   **/
+//  virtual void evaluate(const LocalBasisType& inside_basis,
+//                        const LocalBasisType& outside_basis,
+//                        const DomainType& point_in_reference_element,
+//                        DynamicMatrix<F>& result,
+//                        const XT::Common::Parameter& param = {}) const = 0;
+
+//  /**
+//   * This method is provided for convenience and should not be used within library code.
+//   *
+//   * \note Will throw Exceptions::not_bound_to_an_element_yet error if not bound yet!
+//   **/
+//  virtual DynamicMatrix<F> evaluate(const LocalBasisType& inside_basis,
+//                                    const LocalBasisType& outside_basis,
+//                                    const DomainType& point_in_reference_element,
+//                                    const XT::Common::Parameter& param = {}) const
+//  {
+//    DynamicMatrix<F> result(inside_basis.size(param), outside_basis.size(param), 0);
+//    evaluate(inside_basis, inside_basis, point_in_reference_element, result, param);
+//    return result;
+//  }
+//}; // class LocalBinaryIntersectionIntegrandSum
+
+
+template <class I, size_t t_r, size_t t_rC, class TF, class F, size_t a_r, size_t a_rC, class AF>
+class LocalQuaternaryIntersectionIntegrandSum
+  : public LocalQuaternaryIntersectionIntegrandInterface<I, t_r, t_rC, TF, F, a_r, a_rC, AF>
+{
+  using BaseType = LocalQuaternaryIntersectionIntegrandInterface<I, t_r, t_rC, TF, F, a_r, a_rC, AF>;
+  using ThisType = LocalQuaternaryIntersectionIntegrandSum;
+
+public:
+  using typename BaseType::DomainType;
+  using typename BaseType::IntersectionType;
+  using typename BaseType::LocalAnsatzBasisType;
+  using typename BaseType::LocalTestBasisType;
+
+  LocalQuaternaryIntersectionIntegrandSum(const BaseType& left, const BaseType& right)
+    : BaseType(left.parameter_type() + right.parameter_type())
+    , left_(left.copy().release())
+    , right_(right.copy().release())
+  {}
+
+  LocalQuaternaryIntersectionIntegrandSum(const ThisType& other)
+    : BaseType(other)
+    , left_(other.left_.access().copy().release())
+    , right_(other.right_.access().copy().release())
+  {}
+
+  LocalQuaternaryIntersectionIntegrandSum(ThisType&& source) = default;
+
+  std::unique_ptr<BaseType> copy() const override final
+  {
+    return std::make_unique<ThisType>(*this);
+  }
+
+protected:
+  void post_bind(const IntersectionType& intrsctn) override final
+  {
+    left_.access().bind(intrsctn);
+    right_.access().bind(intrsctn);
+  }
+
+public:
+  int order(const LocalTestBasisType& test_basis_inside,
+            const LocalAnsatzBasisType& ansatz_basis_inside,
+            const LocalTestBasisType& test_basis_outside,
+            const LocalAnsatzBasisType& ansatz_basis_outside,
+            const XT::Common::Parameter& param = {}) const override final
+  {
+    return std::max(
+        left_.access().order(test_basis_inside, ansatz_basis_inside, test_basis_outside, ansatz_basis_outside, param),
+        right_.access().order(test_basis_inside, ansatz_basis_inside, test_basis_outside, ansatz_basis_outside, param));
+  }
+
+  using BaseType::evaluate;
+
+  void evaluate(const LocalTestBasisType& test_basis_inside,
+                const LocalAnsatzBasisType& ansatz_basis_inside,
+                const LocalTestBasisType& test_basis_outside,
+                const LocalAnsatzBasisType& ansatz_basis_outside,
+                const DomainType& point_in_reference_intersection,
+                DynamicMatrix<F>& result_in_in,
+                DynamicMatrix<F>& result_in_out,
+                DynamicMatrix<F>& result_out_in,
+                DynamicMatrix<F>& result_out_out,
+                const XT::Common::Parameter& param = {}) const override final
+  {
+    // Each integrand clears its storage, so we let the left one write into ...
+    left_.access().evaluate(test_basis_inside,
+                            ansatz_basis_inside,
+                            test_basis_outside,
+                            ansatz_basis_outside,
+                            point_in_reference_intersection,
+                            result_in_in,
+                            result_in_out,
+                            result_out_in,
+                            result_out_out,
+                            param);
+    // ..., the right one into ...
+    right_.access().evaluate(test_basis_inside,
+                             ansatz_basis_inside,
+                             test_basis_outside,
+                             ansatz_basis_outside,
+                             point_in_reference_intersection,
+                             result_in_in_,
+                             result_in_out_,
+                             result_out_in_,
+                             result_out_out_,
+                             param);
+    // ... and simply add them up (cannot use += here, matrices might be larger).
+    const size_t rows_in = test_basis_inside.size(param);
+    const size_t rows_out = test_basis_outside.size(param);
+    const size_t cols_in = ansatz_basis_inside.size(param);
+    const size_t cols_out = ansatz_basis_outside.size(param);
+    for (size_t ii = 0; ii < rows_in; ++ii)
+      for (size_t jj = 0; jj < cols_in; ++jj)
+        result_in_in[ii][jj] += result_in_in_[ii][jj];
+    for (size_t ii = 0; ii < rows_in; ++ii)
+      for (size_t jj = 0; jj < cols_out; ++jj)
+        result_in_out[ii][jj] += result_in_out_[ii][jj];
+    for (size_t ii = 0; ii < rows_out; ++ii)
+      for (size_t jj = 0; jj < cols_in; ++jj)
+        result_out_in[ii][jj] += result_out_in_[ii][jj];
+    for (size_t ii = 0; ii < rows_out; ++ii)
+      for (size_t jj = 0; jj < cols_out; ++jj)
+        result_out_out[ii][jj] += result_out_out_[ii][jj];
+  } // ... evaluate(...)
+
+private:
+  XT::Common::StorageProvider<BaseType> left_;
+  XT::Common::StorageProvider<BaseType> right_;
+  mutable DynamicMatrix<F> result_in_in_;
+  mutable DynamicMatrix<F> result_in_out_;
+  mutable DynamicMatrix<F> result_out_in_;
+  mutable DynamicMatrix<F> result_out_out_;
+}; // class LocalQuaternaryIntersectionIntegrandSum
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_LOCAL_INTEGRANDS_COMBINED_HH
diff --git a/dune/gdt/local/integrands/conversion.hh b/dune/gdt/local/integrands/conversion.hh
index 2f1ec208c52b3c86cc1ed0d44e1624a660a97020..65dc14cfeb6dddc6c7497d20eb766182fd4cd0d4 100644
--- a/dune/gdt/local/integrands/conversion.hh
+++ b/dune/gdt/local/integrands/conversion.hh
@@ -39,7 +39,7 @@ template <class E,
           class AF = TF>
 class LocalBinaryToUnaryElementIntegrand : public LocalUnaryElementIntegrandInterface<E, a_r, a_rC, AF, F>
 {
-  using ThisType = LocalBinaryToUnaryElementIntegrand<E, t_r, t_rC, TF, F, a_r, a_rC, AF>;
+  using ThisType = LocalBinaryToUnaryElementIntegrand;
   using BaseType = LocalUnaryElementIntegrandInterface<E, a_r, a_rC, AF, F>;
 
 public:
diff --git a/dune/gdt/local/integrands/div.hh b/dune/gdt/local/integrands/div.hh
index 8c1629f6cb65b7e578f88bfe5e4b1a7801a017e3..4732224ba2298ae30d346b2fbae87d776b9bcea9 100644
--- a/dune/gdt/local/integrands/div.hh
+++ b/dune/gdt/local/integrands/div.hh
@@ -68,8 +68,8 @@ protected:
 
 
 /**
- * Given an inducing function f, computes `f(x) * phi(x) * div(psi(x))` for all combinations of phi and psi in the
- * bases.
+ * Given an inducing function f, computes `f(x) * phi(x) * div(psi(x))` for all combinations of phi in the ansatz basis
+ * and psi in the test basis.
  *
  * \note Note that f can also be given as a scalar value or omitted.
  *
@@ -139,7 +139,7 @@ public:
             const LocalAnsatzBasisType& ansatz_basis,
             const XT::Common::Parameter& param = {}) const override final
   {
-    return std::max(local_function_->order(param) + test_basis.order(param) + ansatz_basis.order(param) - 1, 0);
+    return local_function_->order(param) + test_basis.order(param) + ansatz_basis.order(param);
   }
 
   using BaseType::evaluate;
@@ -169,7 +169,8 @@ private:
 
 
 /**
- * Given an inducing function f, computes `f(x) * div phi(x) * psi(x)` for all combinations of phi and psi in the bases.
+ * Given an inducing function f, computes `f(x) * div(phi(x)) * psi(x)` for all combinations of phi in the ansatz basis
+ * and psi in the test basis.
  *
  * \sa LocalElementAnsatzValueTestDivProductIntegrand
  */
@@ -234,7 +235,7 @@ public:
             const LocalAnsatzBasisType& ansatz_basis,
             const XT::Common::Parameter& param = {}) const override final
   {
-    return std::max(local_function_->order(param) + test_basis.order(param) + ansatz_basis.order(param) - 1 + 10, 0);
+    return local_function_->order(param) + test_basis.order(param) + ansatz_basis.order(param);
   }
 
   using BaseType::evaluate;
@@ -245,14 +246,15 @@ public:
                 DynamicMatrix<F>& result,
                 const XT::Common::Parameter& param = {}) const override final
   {
-    DivBaseType::evaluate(test_basis,
-                          ansatz_basis,
+    DivBaseType::evaluate(ansatz_basis,
+                          test_basis,
                           point_in_reference_element,
                           result,
                           param,
                           *local_function_,
                           ansatz_basis_jacobians_,
                           test_basis_values_);
+    result = XT::Common::transposed(result);
   } // ... evaluate(...)
 
 private:
diff --git a/dune/gdt/local/integrands/elliptic-ipdg.hh b/dune/gdt/local/integrands/elliptic-ipdg.hh
index b79d79fd9266a945dda5b558192eeeb5acfb4459..edb8d2948518d9d6017dfdc12ab4116b26a5781f 100644
--- a/dune/gdt/local/integrands/elliptic-ipdg.hh
+++ b/dune/gdt/local/integrands/elliptic-ipdg.hh
@@ -10,16 +10,328 @@
 //   René Milk       (2017)
 //   Tobias Leibner  (2014)
 
+#warning This header is deprecated, use and include <dune/gdt/local/integrands/laplace-ipdg.hh> instead!
+
 #ifndef DUNE_GDT_LOCAL_INTEGRANDS_ELLIPTIC_IPDG_HH
-#define DUNE_GDT_LOCAL_INTEGRANDS_ELLIPTIC_IPDG_HH
+#  define DUNE_GDT_LOCAL_INTEGRANDS_ELLIPTIC_IPDG_HH
 
-#include <dune/xt/functions/interfaces/grid-function.hh>
+#  include <dune/xt/common/deprecated.hh>
+#  include <dune/xt/functions/grid-function.hh>
+#  include <dune/xt/functions/interfaces/grid-function.hh>
 
-#include "interfaces.hh"
+#  include "interfaces.hh"
+#  include "ipdg.hh"
 
 namespace Dune {
 namespace GDT {
 
+/**
+ * \brief To replace the ones in LocalEllipticIpdgIntegrands at some point.
+ */
+namespace LocalEllipticIPDGIntegrands {
+
+
+/**
+ * \note The role of symmetry_prefactor:
+ *       * -1 => NIPDG
+ *       *  0 => IIPDG
+ *       *  1 => SIPDG
+ * \note The role of the weight:
+ *       * symmetry_prefactor = 1 && weight_function = 1 => SIPDG
+ *       * symmetry_prefactor = 1 && weight_function = diffusion => SWIPDG
+ */
+template <class I>
+class InnerCoupling : public LocalQuaternaryIntersectionIntegrandInterface<I>
+{
+  using BaseType = LocalQuaternaryIntersectionIntegrandInterface<I>;
+  using ThisType = InnerCoupling;
+
+public:
+  using BaseType::d;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::F;
+  using typename BaseType::IntersectionType;
+  using typename BaseType::LocalAnsatzBasisType;
+  using typename BaseType::LocalTestBasisType;
+
+  InnerCoupling(const double& symmetry_prefactor,
+                XT::Functions::GridFunction<E, d, d> diffusion,
+                XT::Functions::GridFunction<E, d, d> weight_function = {1.})
+    : BaseType(diffusion.parameter_type() + weight_function.parameter_type())
+    , symmetry_prefactor_(symmetry_prefactor)
+    , diffusion_(diffusion)
+    , weight_(weight_function)
+    , local_diffusion_in_(diffusion_.local_function())
+    , local_diffusion_out_(diffusion_.local_function())
+    , local_weight_in_(weight_.local_function())
+    , local_weight_out_(weight_.local_function())
+  {}
+
+  InnerCoupling(const ThisType& other)
+    : BaseType(other.parameter_type())
+    , symmetry_prefactor_(other.symmetry_prefactor_)
+    , diffusion_(other.diffusion_)
+    , weight_(other.weight_)
+    , local_diffusion_in_(diffusion_.local_function())
+    , local_diffusion_out_(diffusion_.local_function())
+    , local_weight_in_(weight_.local_function())
+    , local_weight_out_(weight_.local_function())
+  {}
+
+  InnerCoupling(ThisType&& source) = default;
+
+  std::unique_ptr<BaseType> copy() const override final
+  {
+    return std::make_unique<ThisType>(*this);
+  }
+
+protected:
+  void post_bind(const IntersectionType& intersection) override final
+  {
+    DUNE_THROW_IF(!intersection.neighbor(),
+                  Exceptions::integrand_error,
+                  "This integrand cannot be used on a boundary intersection!");
+    const auto inside_element = intersection.inside();
+    const auto outside_element = intersection.outside();
+    local_diffusion_in_->bind(inside_element);
+    local_weight_in_->bind(inside_element);
+    local_diffusion_out_->bind(outside_element);
+    local_weight_out_->bind(outside_element);
+  } // ... post_bind(...)
+
+public:
+  int order(const LocalTestBasisType& test_basis_inside,
+            const LocalAnsatzBasisType& ansatz_basis_inside,
+            const LocalTestBasisType& test_basis_outside,
+            const LocalAnsatzBasisType& ansatz_basis_outside,
+            const XT::Common::Parameter& param = {}) const override final
+  {
+    return std::max(local_diffusion_in_->order(param), local_diffusion_out_->order(param))
+           + std::max(local_weight_in_->order(), local_weight_out_->order(param))
+           + std::max(test_basis_inside.order(param), test_basis_outside.order(param))
+           + std::max(ansatz_basis_inside.order(param), ansatz_basis_outside.order(param));
+  }
+
+  void evaluate(const LocalTestBasisType& test_basis_inside,
+                const LocalAnsatzBasisType& ansatz_basis_inside,
+                const LocalTestBasisType& test_basis_outside,
+                const LocalAnsatzBasisType& ansatz_basis_outside,
+                const DomainType& point_in_reference_intersection,
+                DynamicMatrix<F>& result_in_in,
+                DynamicMatrix<F>& result_in_out,
+                DynamicMatrix<F>& result_out_in,
+                DynamicMatrix<F>& result_out_out,
+                const XT::Common::Parameter& param = {}) const override final
+  {
+    // Prepare sotrage, ...
+    this->ensure_size_and_clear_results(test_basis_inside,
+                                        ansatz_basis_inside,
+                                        test_basis_outside,
+                                        ansatz_basis_outside,
+                                        result_in_in,
+                                        result_in_out,
+                                        result_out_in,
+                                        result_out_out,
+                                        param);
+    // evaluate ...
+    const auto point_in_inside_reference_element =
+        this->intersection().geometryInInside().global(point_in_reference_intersection);
+    const auto point_in_outside_reference_element =
+        this->intersection().geometryInOutside().global(point_in_reference_intersection);
+    const auto normal = this->intersection().unitOuterNormal(point_in_reference_intersection);
+    // ... basis functions and ...
+    test_basis_inside.evaluate(point_in_inside_reference_element, test_basis_in_values_, param);
+    test_basis_inside.jacobians(point_in_inside_reference_element, test_basis_in_grads_, param);
+    test_basis_outside.evaluate(point_in_outside_reference_element, test_basis_out_values_, param);
+    test_basis_outside.jacobians(point_in_outside_reference_element, test_basis_out_grads_, param);
+    ansatz_basis_inside.evaluate(point_in_inside_reference_element, ansatz_basis_in_values_, param);
+    ansatz_basis_inside.jacobians(point_in_inside_reference_element, ansatz_basis_in_grads_, param);
+    ansatz_basis_outside.evaluate(point_in_outside_reference_element, ansatz_basis_out_values_, param);
+    ansatz_basis_outside.jacobians(point_in_outside_reference_element, ansatz_basis_out_grads_, param);
+    // ... data functions, ...
+    const auto diffusion_in = local_diffusion_in_->evaluate(point_in_inside_reference_element, param);
+    const auto diffusion_out = local_diffusion_out_->evaluate(point_in_outside_reference_element, param);
+    const auto weight_in = local_weight_in_->evaluate(point_in_inside_reference_element, param);
+    const auto weight_out = local_weight_out_->evaluate(point_in_outside_reference_element, param);
+    // compute the weighted mean ...
+    const auto delta_plus = normal * (weight_out * normal);
+    const auto delta_minus = normal * (weight_in * normal);
+    const auto weight_minus = delta_plus / (delta_plus + delta_minus);
+    const auto weight_plus = delta_minus / (delta_plus + delta_minus);
+    // ... and finally compute the integrand.
+    const size_t rows_in = test_basis_inside.size(param);
+    const size_t rows_out = test_basis_outside.size(param);
+    const size_t cols_in = ansatz_basis_inside.size(param);
+    const size_t cols_out = ansatz_basis_outside.size(param);
+    for (size_t ii = 0; ii < rows_in; ++ii) {
+      for (size_t jj = 0; jj < cols_in; ++jj) {
+        result_in_in[ii][jj] +=
+            -1.0 * weight_minus * ((diffusion_in * ansatz_basis_in_grads_[jj][0]) * normal) * test_basis_in_values_[ii];
+        result_in_in[ii][jj] += -1.0 * symmetry_prefactor_ * weight_minus * ansatz_basis_in_values_[jj]
+                                * ((diffusion_in * test_basis_in_grads_[ii][0]) * normal);
+      }
+      for (size_t jj = 0; jj < cols_out; ++jj) {
+        result_in_out[ii][jj] += -1.0 * weight_plus * ((diffusion_out * ansatz_basis_out_grads_[jj][0]) * normal)
+                                 * test_basis_in_values_[ii];
+        result_in_out[ii][jj] += symmetry_prefactor_ * weight_minus * ansatz_basis_out_values_[jj]
+                                 * ((diffusion_in * test_basis_in_grads_[ii][0]) * normal);
+      }
+    }
+    for (size_t ii = 0; ii < rows_out; ++ii) {
+      for (size_t jj = 0; jj < cols_in; ++jj) {
+        result_out_in[ii][jj] +=
+            weight_minus * ((diffusion_in * ansatz_basis_in_grads_[jj][0]) * normal) * test_basis_out_values_[ii];
+        result_out_in[ii][jj] += -1.0 * symmetry_prefactor_ * weight_plus * ansatz_basis_in_values_[jj]
+                                 * ((diffusion_out * test_basis_out_grads_[ii][0]) * normal);
+      }
+      for (size_t jj = 0; jj < cols_out; ++jj) {
+        result_out_out[ii][jj] +=
+            weight_plus * ((diffusion_out * ansatz_basis_out_grads_[jj][0]) * normal) * test_basis_out_values_[ii];
+        result_out_out[ii][jj] += symmetry_prefactor_ * weight_plus * ansatz_basis_out_values_[jj]
+                                  * ((diffusion_out * test_basis_out_grads_[ii][0]) * normal);
+      }
+    }
+  } // ... evaluate(...)
+
+private:
+  const double symmetry_prefactor_;
+  XT::Functions::GridFunction<E, d, d> diffusion_;
+  XT::Functions::GridFunction<E, d, d> weight_;
+  std::unique_ptr<typename XT::Functions::GridFunctionInterface<E, d, d>::LocalFunctionType> local_diffusion_in_;
+  std::unique_ptr<typename XT::Functions::GridFunctionInterface<E, d, d>::LocalFunctionType> local_diffusion_out_;
+  std::unique_ptr<typename XT::Functions::GridFunctionInterface<E, d, d>::LocalFunctionType> local_weight_in_;
+  std::unique_ptr<typename XT::Functions::GridFunctionInterface<E, d, d>::LocalFunctionType> local_weight_out_;
+  mutable std::vector<typename LocalTestBasisType::RangeType> test_basis_in_values_;
+  mutable std::vector<typename LocalTestBasisType::DerivativeRangeType> test_basis_in_grads_;
+  mutable std::vector<typename LocalTestBasisType::RangeType> test_basis_out_values_;
+  mutable std::vector<typename LocalTestBasisType::DerivativeRangeType> test_basis_out_grads_;
+  mutable std::vector<typename LocalAnsatzBasisType::RangeType> ansatz_basis_in_values_;
+  mutable std::vector<typename LocalAnsatzBasisType::DerivativeRangeType> ansatz_basis_in_grads_;
+  mutable std::vector<typename LocalAnsatzBasisType::RangeType> ansatz_basis_out_values_;
+  mutable std::vector<typename LocalAnsatzBasisType::DerivativeRangeType> ansatz_basis_out_grads_;
+}; // InnerCoupling
+
+
+/**
+ * \note The role of symmetry_prefactor:
+ *       * -1 => NIPDG
+ *       *  0 => IIPDG
+ *       *  1 => SIPDG
+ * \note The role of the weight:
+ *       * symmetry_prefactor = 1 && weight_function = 1 => SIPDG
+ *       * symmetry_prefactor = 1 && weight_function = diffusion => SWIPDG
+ */
+template <class I>
+class DirichletCoupling : public LocalQuaternaryIntersectionIntegrandInterface<I>
+{
+  using BaseType = LocalQuaternaryIntersectionIntegrandInterface<I>;
+  using ThisType = DirichletCoupling;
+
+public:
+  using BaseType::d;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::F;
+  using typename BaseType::IntersectionType;
+  using typename BaseType::LocalAnsatzBasisType;
+  using typename BaseType::LocalTestBasisType;
+
+  DirichletCoupling(const double& symmetry_prefactor, XT::Functions::GridFunction<E, d, d> diffusion)
+    : BaseType(diffusion.parameter_type())
+    , symmetry_prefactor_(symmetry_prefactor)
+    , diffusion_(diffusion)
+    , local_diffusion_(diffusion_.local_function())
+  {}
+
+  DirichletCoupling(const ThisType& other)
+    : BaseType(other.parameter_type())
+    , symmetry_prefactor_(other.symmetry_prefactor_)
+    , diffusion_(other.diffusion_)
+    , local_diffusion_(diffusion_.local_function())
+  {}
+
+  DirichletCoupling(ThisType&& source) = default;
+
+  std::unique_ptr<BaseType> copy() const override final
+  {
+    return std::make_unique<ThisType>(*this);
+  }
+
+protected:
+  void post_bind(const IntersectionType& intersection) override final
+  {
+    const auto inside_element = intersection.inside();
+    local_diffusion_->bind(inside_element);
+  }
+
+public:
+  int order(const LocalTestBasisType& test_basis_inside,
+            const LocalAnsatzBasisType& ansatz_basis_inside,
+            const LocalTestBasisType& /*test_basis_outside*/,
+            const LocalAnsatzBasisType& /*ansatz_basis_outside*/,
+            const XT::Common::Parameter& param = {}) const override final
+  {
+    return local_diffusion_->order(param) + test_basis_inside.order(param) + ansatz_basis_inside.order(param);
+  }
+
+  void evaluate(const LocalTestBasisType& test_basis_inside,
+                const LocalAnsatzBasisType& ansatz_basis_inside,
+                const LocalTestBasisType& test_basis_outside,
+                const LocalAnsatzBasisType& ansatz_basis_outside,
+                const DomainType& point_in_reference_intersection,
+                DynamicMatrix<F>& result_in_in,
+                DynamicMatrix<F>& result_in_out,
+                DynamicMatrix<F>& result_out_in,
+                DynamicMatrix<F>& result_out_out,
+                const XT::Common::Parameter& param = {}) const override final
+  {
+    // Prepare sotrage, ...
+    this->ensure_size_and_clear_results(test_basis_inside,
+                                        ansatz_basis_inside,
+                                        test_basis_outside,
+                                        ansatz_basis_outside,
+                                        result_in_in,
+                                        result_in_out,
+                                        result_out_in,
+                                        result_out_out,
+                                        param);
+    // evaluate ...
+    const auto point_in_inside_reference_element =
+        this->intersection().geometryInInside().global(point_in_reference_intersection);
+    const auto normal = this->intersection().unitOuterNormal(point_in_reference_intersection);
+    // ... basis functions and ...
+    test_basis_inside.evaluate(point_in_inside_reference_element, test_basis_values_, param);
+    test_basis_inside.jacobians(point_in_inside_reference_element, test_basis_grads_, param);
+    ansatz_basis_inside.evaluate(point_in_inside_reference_element, ansatz_basis_values_, param);
+    ansatz_basis_inside.jacobians(point_in_inside_reference_element, ansatz_basis_grads_, param);
+    // ... data functions, ...
+    const auto diffusion = local_diffusion_->evaluate(point_in_inside_reference_element, param);
+    // ... and finally compute the integrand.
+    const size_t rows = test_basis_inside.size(param);
+    const size_t cols = ansatz_basis_inside.size(param);
+    for (size_t ii = 0; ii < rows; ++ii)
+      for (size_t jj = 0; jj < cols; ++jj) {
+        result_in_in[ii][jj] += -1.0 * ((diffusion * ansatz_basis_grads_[jj][0]) * normal) * test_basis_values_[ii];
+        result_in_in[ii][jj] +=
+            -1.0 * symmetry_prefactor_ * ansatz_basis_values_[jj] * ((diffusion * test_basis_grads_[ii][0]) * normal);
+      }
+  } // ... evaluate(...)
+
+private:
+  const double symmetry_prefactor_;
+  XT::Functions::GridFunction<E, d, d> diffusion_;
+  std::unique_ptr<typename XT::Functions::GridFunctionInterface<E, d, d>::LocalFunctionType> local_diffusion_;
+  mutable std::vector<typename LocalTestBasisType::RangeType> test_basis_values_;
+  mutable std::vector<typename LocalTestBasisType::DerivativeRangeType> test_basis_grads_;
+  mutable std::vector<typename LocalAnsatzBasisType::RangeType> ansatz_basis_values_;
+  mutable std::vector<typename LocalAnsatzBasisType::DerivativeRangeType> ansatz_basis_grads_;
+}; // DirichletCoupling
+
+
+} // namespace LocalEllipticIPDGIntegrands
+
+
 /**
  *  \brief      Contains local integrands for the family of interior penalty discontinuous Galerkin (IPDG)
  *              discretization schemes.
@@ -32,7 +344,7 @@ namespace GDT {
 namespace LocalEllipticIpdgIntegrands {
 
 
-enum class Method
+enum class DXT_DEPRECATED_MSG("Use the LocalLaplaceIPDGIntegrands instead (10.08.2019)!") Method
 {
   ipdg,
   nipdg,
@@ -132,6 +444,7 @@ namespace internal {
 /**
  * \note see Epshteyn, Riviere, 2007
  */
+DXT_DEPRECATED_MSG("Use the LocalLaplaceIPDGIntegrands instead (10.08.2019)!")
 static inline double default_beta(const size_t d)
 {
   return 1.0 / (d - 1.0);
@@ -141,6 +454,7 @@ static inline double default_beta(const size_t d)
 /**
  * \note see Epshteyn, Riviere, 2007
  */
+DXT_DEPRECATED_MSG("Use the LocalLaplaceIPDGIntegrands instead (10.08.2019)!")
 static inline double inner_sigma(const size_t pol_order)
 {
   double sigma = 1.0;
@@ -151,14 +465,14 @@ static inline double inner_sigma(const size_t pol_order)
   else if (pol_order <= 3)
     sigma *= 38.0;
   else {
-#ifndef NDEBUG
-#  ifndef DUNE_GDT_DISABLE_WARNINGS
+#  ifndef NDEBUG
+#    ifndef DUNE_GDT_DISABLE_WARNINGS
     Dune::XT::Common::TimedLogger().get("gdt.local.integrands.elliptic-ipdg.inner").warn()
         << "a polynomial order of " << pol_order << " is untested!\n"
         << "  #define DUNE_GDT_DISABLE_WARNINGS to statically disable this warning\n"
         << "  or dynamically disable warnings of the TimedLogger() instance!" << std::endl;
+#    endif
 #  endif
-#endif
     sigma *= 50.0;
   }
   return sigma;
@@ -168,6 +482,7 @@ static inline double inner_sigma(const size_t pol_order)
 /**
  * \note see Epshteyn, Riviere, 2007
  */
+DXT_DEPRECATED_MSG("Use the LocalLaplaceIPDGIntegrands instead (10.08.2019)!")
 static inline double boundary_sigma(const size_t pol_order)
 {
   double sigma = 1.0;
@@ -178,14 +493,14 @@ static inline double boundary_sigma(const size_t pol_order)
   else if (pol_order <= 3)
     sigma *= 74.0;
   else {
-#ifndef NDEBUG
-#  ifndef DUNE_GDT_DISABLE_WARNINGS
+#  ifndef NDEBUG
+#    ifndef DUNE_GDT_DISABLE_WARNINGS
     Dune::XT::Common::TimedLogger().get("gdt.local.integrands.elliptic-ipdg.boundary").warn()
         << "a polynomial order of " << pol_order << " is untested!\n"
         << "  #define DUNE_GDT_DISABLE_WARNINGS to statically disable this warning\n"
         << "  or dynamically disable warnings of the TimedLogger() instance!" << std::endl;
+#    endif
 #  endif
-#endif
     sigma *= 100.0;
   }
   return sigma;
@@ -199,10 +514,12 @@ static inline double boundary_sigma(const size_t pol_order)
  * \sa [Epshteyn, Riviere, 2007] for the meaning of beta
  */
 template <class I, class F = double, Method method = default_method>
-class Inner : public LocalQuaternaryIntersectionIntegrandInterface<I, 1, 1, F, F, 1, 1, F>
+class DXT_DEPRECATED_MSG(
+    "Use LocalLaplaceIPDGIntegrands::InnerCoupling + LocalIPDGIntegrands::InnerPenalty} instead (10.08.2019)!") Inner
+  : public LocalQuaternaryIntersectionIntegrandInterface<I, 1, 1, F, F, 1, 1, F>
 {
   using BaseType = LocalQuaternaryIntersectionIntegrandInterface<I, 1, 1, F, F, 1, 1, F>;
-  using ThisType = Inner<I, F, method>;
+  using ThisType = Inner;
 
 public:
   using BaseType::d;
@@ -690,10 +1007,12 @@ private:
  * \sa [Epshteyn, Riviere, 2007] for the meaning of beta
  */
 template <class I, class F = double, Method method = default_method>
-class DirichletBoundaryLhs : public LocalQuaternaryIntersectionIntegrandInterface<I, 1, 1, F, F, 1, 1, F>
+class DXT_DEPRECATED_MSG(
+    "Use LocalLaplaceIPDGIntegrands::DirichletCoupling + LocalIPDGIntegrands::boundaryPenalty instead (10.08.2019)!")
+    DirichletBoundaryLhs : public LocalQuaternaryIntersectionIntegrandInterface<I, 1, 1, F, F, 1, 1, F>
 {
   using BaseType = LocalQuaternaryIntersectionIntegrandInterface<I, 1, 1, F, F, 1, 1, F>;
-  using ThisType = DirichletBoundaryLhs<I, F, method>;
+  using ThisType = DirichletBoundaryLhs;
 
 public:
   using BaseType::d;
@@ -882,7 +1201,7 @@ private:
 }; // DirichletBoundaryLhs
 
 
-#if 0
+#  if 0
 template <class DirichletImp, class DiffusionFactorImp, class DiffusionTensorImp, Method method>
 class BoundaryRHS : public LocalFaceIntegrandInterface<internal::BoundaryRHSTraits<DirichletImp,
                                                                                    DiffusionFactorImp,
@@ -1124,16 +1443,17 @@ public:
   const EllipticType elliptic_;
   const double beta_;
 }; // class BoundaryRHS
-#endif // 0
+#  endif // 0
 
 /**
  * \sa [Epshteyn, Riviere, 2007] for the meaning of beta
  */
 template <class I, class F = double, Method method = default_method>
-class InnerOnlyPenalty : public LocalQuaternaryIntersectionIntegrandInterface<I, 1, 1, F, F, 1, 1, F>
+class DXT_DEPRECATED_MSG("Use LocalIPDGIntegrands::InnerPenalty instead (05.08.2019)!") InnerOnlyPenalty
+  : public LocalQuaternaryIntersectionIntegrandInterface<I, 1, 1, F, F, 1, 1, F>
 {
   using BaseType = LocalQuaternaryIntersectionIntegrandInterface<I, 1, 1, F, F, 1, 1, F>;
-  using ThisType = InnerOnlyPenalty<I, F, method>;
+  using ThisType = InnerOnlyPenalty;
 
 public:
   using BaseType::d;
@@ -1576,14 +1896,16 @@ private:
   mutable std::vector<typename LocalAnsatzBasisType::RangeType> ansatz_basis_out_values_;
 }; //  InnerOnlyPenalty
 
+
 /**
  * \sa [Epshteyn, Riviere, 2007] for the meaning of beta
  */
 template <class I, class F = double, Method method = default_method>
-class DirichletBoundaryLhsOnlyPenalty : public LocalQuaternaryIntersectionIntegrandInterface<I, 1, 1, F, F, 1, 1, F>
+class DXT_DEPRECATED_MSG("Use LocalIPDGIntegrands::BoundaryPenalty instead (05.08.2019)!")
+    DirichletBoundaryLhsOnlyPenalty : public LocalQuaternaryIntersectionIntegrandInterface<I, 1, 1, F, F, 1, 1, F>
 {
   using BaseType = LocalQuaternaryIntersectionIntegrandInterface<I, 1, 1, F, F, 1, 1, F>;
-  using ThisType = DirichletBoundaryLhsOnlyPenalty<I, F, method>;
+  using ThisType = DirichletBoundaryLhsOnlyPenalty;
 
 public:
   using BaseType::d;
@@ -1764,6 +2086,7 @@ private:
   mutable std::vector<typename LocalAnsatzBasisType::DerivativeRangeType> ansatz_basis_grads_;
 }; // DirichletBoundaryLhsOnlyPenalty
 
+
 } // namespace LocalEllipticIpdgIntegrands
 } // namespace GDT
 } // namespace Dune
diff --git a/dune/gdt/local/integrands/elliptic.hh b/dune/gdt/local/integrands/elliptic.hh
index 7f383af70ad5fb4bd5f357c3f19c3a9dff4fc5e2..e793ca67cefeeb9b71afc563b699d7531fd8c52b 100644
--- a/dune/gdt/local/integrands/elliptic.hh
+++ b/dune/gdt/local/integrands/elliptic.hh
@@ -11,27 +11,35 @@
 //   René Milk       (2017)
 //   Tobias Leibner  (2014, 2016 - 2018)
 
+#warning This header is deprecated, use and include <dune/gdt/local/integrands/laplace.hh> instead!
+
 #ifndef DUNE_GDT_LOCAL_INTEGRANDS_ELLIPTIC_HH
-#define DUNE_GDT_LOCAL_INTEGRANDS_ELLIPTIC_HH
+#  define DUNE_GDT_LOCAL_INTEGRANDS_ELLIPTIC_HH
 
-#include <dune/xt/common/memory.hh>
-#include <dune/xt/la/container/eye-matrix.hh>
-#include <dune/xt/functions/base/function-as-grid-function.hh>
-#include <dune/xt/functions/constant.hh>
-#include <dune/xt/functions/interfaces/grid-function.hh>
+#  include <dune/xt/common/deprecated.hh>
+#  include <dune/xt/common/memory.hh>
+#  include <dune/xt/la/container/eye-matrix.hh>
+#  include <dune/xt/functions/base/function-as-grid-function.hh>
+#  include <dune/xt/functions/constant.hh>
+#  include <dune/xt/functions/interfaces/grid-function.hh>
 
-#include "interfaces.hh"
+#  include "interfaces.hh"
 
 namespace Dune {
 namespace GDT {
 
 
 /**
+ * This class is deprecated, use LocalLaplaceIntegrand instead (10.08.2019)!
  * Given an inducing scalar function lambda and an inducing matrix-valued function kappa, computes
- * `lambda(x) * {[kappa(x) \nabla phi(x)] * \nabla psi(x)}` for all combinations of phi and psi in the bases.
+ * `lambda(x) * {[kappa(x) \nabla phi(x)] * \nabla psi(x)}` for all combinations of phi in the ansatz basis and psi in
+ * the test basis.
+ * If phi and psi are vector-valued, \nabla phi is the jacobian matrix and we are actually computing
+ * `lambda(x) * {[kappa(x) (\nabla phi(x))^T] : (\nabla psi(x))^T}`, where ':' denotes the matrix scalar product.
  */
 template <class E, size_t r = 1, class F = double>
-class LocalEllipticIntegrand : public LocalBinaryElementIntegrandInterface<E, r, 1, F, F, r, 1, F>
+class DXT_DEPRECATED_MSG("Use LocalLaplaceIntegrand instead (10.08.2019)!") LocalEllipticIntegrand
+  : public LocalBinaryElementIntegrandInterface<E, r, 1, F, F, r, 1, F>
 {
   using BaseType = LocalBinaryElementIntegrandInterface<E, r, 1, F, F, r, 1, F>;
   using ThisType = LocalEllipticIntegrand;
@@ -93,6 +101,10 @@ public:
 protected:
   void post_bind(const ElementType& ele) override
   {
+#  ifndef NDEBUG
+    if (!ele.geometry().affine())
+      std::cerr << "Warning: integration order has to be increased for non-affine geometries!" << std::endl;
+#  endif
     local_diffusion_factor_->bind(ele);
     local_diffusion_tensor_->bind(ele);
   }
@@ -102,8 +114,8 @@ public:
             const LocalAnsatzBasisType& ansatz_basis,
             const XT::Common::Parameter& param = {}) const override final
   {
-    return local_diffusion_factor_->order(param) + local_diffusion_tensor_->order(param)
-           + std::max(test_basis.order(param) - 1, 0) + std::max(ansatz_basis.order(param) - 1, 0);
+    return local_diffusion_factor_->order(param) + local_diffusion_tensor_->order(param) + test_basis.order(param)
+           + ansatz_basis.order(param);
   }
 
   void evaluate(const LocalTestBasisType& test_basis,
diff --git a/dune/gdt/local/integrands/generic.hh b/dune/gdt/local/integrands/generic.hh
index d6597958aab7eeee26b6c7a86b10c85499c96963..06d901dbd9758451763fe9b6054a4489658dca7c 100644
--- a/dune/gdt/local/integrands/generic.hh
+++ b/dune/gdt/local/integrands/generic.hh
@@ -24,7 +24,7 @@ namespace GDT {
 template <class E, size_t r = 1, size_t rC = 1, class R = double, class F = double>
 class GenericLocalUnaryElementIntegrand : public LocalUnaryElementIntegrandInterface<E, r, rC, R, F>
 {
-  using ThisType = GenericLocalUnaryElementIntegrand<E, r, rC, R, F>;
+  using ThisType = GenericLocalUnaryElementIntegrand;
   using BaseType = LocalUnaryElementIntegrandInterface<E, r, rC, R, F>;
 
 public:
@@ -33,41 +33,50 @@ public:
 
   using GenericOrderFunctionType =
       std::function<int(const LocalBasisType& /*basis*/, const XT::Common::Parameter& /*param*/)>;
-  using GenericEvalauteFunctionType = std::function<void(const LocalBasisType& /*basis*/,
+  using GenericEvaluateFunctionType = std::function<void(const LocalBasisType& /*basis*/,
                                                          const DomainType& /*point_in_reference_element*/,
                                                          DynamicVector<F>& /*result*/,
                                                          const XT::Common::Parameter& /*param*/)>;
+  using GenericPostBindFunctionType = std::function<void(const E& /*ele*/)>;
 
   GenericLocalUnaryElementIntegrand(GenericOrderFunctionType order_function,
-                                    GenericEvalauteFunctionType evaluate_function,
+                                    GenericEvaluateFunctionType evaluate_function,
+                                    GenericPostBindFunctionType post_bind_function = [](const E&) {},
                                     const XT::Common::ParameterType& param_type = {})
     : BaseType(param_type)
     , order_(order_function)
     , evaluate_(evaluate_function)
+    , post_bind_(post_bind_function)
   {}
 
   GenericLocalUnaryElementIntegrand(const ThisType& other)
     : BaseType(other.parameter_type())
     , order_(other.order_)
     , evaluate_(other.evaluate_)
+    , post_bind_(other.post_bind_)
   {}
 
-  std::unique_ptr<BaseType> copy() const
+  std::unique_ptr<BaseType> copy() const override final
   {
     return std::make_unique<ThisType>(*this);
   }
 
-  int order(const LocalBasisType& basis, const XT::Common::Parameter& param = {}) const
+  void post_bind(const E& ele) override final
+  {
+    post_bind_(ele);
+  }
+
+  int order(const LocalBasisType& basis, const XT::Common::Parameter& param = {}) const override final
   {
     return order_(basis, this->parse_parameter(param));
   }
 
   using BaseType::evaluate;
 
-  void evaluate(const LocalBasisType& basis,
-                const DomainType& point_in_reference_element,
-                DynamicVector<F>& result,
-                const XT::Common::Parameter& param = {}) const
+  virtual void evaluate(const LocalBasisType& basis,
+                        const DomainType& point_in_reference_element,
+                        DynamicVector<F>& result,
+                        const XT::Common::Parameter& param = {}) const override final
   {
     // prepare storage
     const size_t size = basis.size(param);
@@ -83,7 +92,8 @@ public:
 
 private:
   const GenericOrderFunctionType order_;
-  const GenericEvalauteFunctionType evaluate_;
+  const GenericEvaluateFunctionType evaluate_;
+  const GenericPostBindFunctionType post_bind_;
 }; // class GenericLocalUnaryElementIntegrand
 
 
@@ -98,7 +108,7 @@ template <class E,
 class GenericLocalBinaryElementIntegrand
   : public LocalBinaryElementIntegrandInterface<E, t_r, t_rC, TF, F, a_r, a_rC, AF>
 {
-  using ThisType = GenericLocalBinaryElementIntegrand<E, t_r, t_rC, TF, F, a_r, a_rC, AF>;
+  using ThisType = GenericLocalBinaryElementIntegrand;
   using BaseType = LocalBinaryElementIntegrandInterface<E, t_r, t_rC, TF, F, a_r, a_rC, AF>;
 
 public:
@@ -109,45 +119,54 @@ public:
   using GenericOrderFunctionType = std::function<int(const LocalTestBasisType& /*test_basis*/,
                                                      const LocalAnsatzBasisType& /*ansatz_basis*/,
                                                      const XT::Common::Parameter& /*param*/)>;
-  using GenericEvalauteFunctionType = std::function<void(const LocalTestBasisType& /*test_basis*/,
+  using GenericEvaluateFunctionType = std::function<void(const LocalTestBasisType& /*test_basis*/,
                                                          const LocalAnsatzBasisType& /*ansatz_basis*/,
                                                          const DomainType& /*point_in_reference_element*/,
                                                          DynamicMatrix<F>& /*result*/,
                                                          const XT::Common::Parameter& /*param*/)>;
+  using GenericPostBindFunctionType = std::function<void(const E& /*ele*/)>;
 
   GenericLocalBinaryElementIntegrand(GenericOrderFunctionType order_function,
-                                     GenericEvalauteFunctionType evaluate_function,
+                                     GenericEvaluateFunctionType evaluate_function,
+                                     GenericPostBindFunctionType post_bind_function = [](const E&) {},
                                      const XT::Common::ParameterType& param_type = {})
     : BaseType(param_type)
     , order_(order_function)
     , evaluate_(evaluate_function)
+    , post_bind_(post_bind_function)
   {}
 
   GenericLocalBinaryElementIntegrand(const ThisType& other)
     : BaseType(other.parameter_type())
     , order_(other.order_)
     , evaluate_(other.evaluate_)
+    , post_bind_(other.post_bind_)
   {}
 
-  std::unique_ptr<BaseType> copy() const
+  std::unique_ptr<BaseType> copy() const override final
   {
     return std::make_unique<ThisType>(*this);
   }
 
-  int order(const LocalTestBasisType& test_basis,
-            const LocalAnsatzBasisType& ansatz_basis,
-            const XT::Common::Parameter& param = {}) const
+  void post_bind(const E& ele) override final
+  {
+    post_bind_(ele);
+  }
+
+  virtual int order(const LocalTestBasisType& test_basis,
+                    const LocalAnsatzBasisType& ansatz_basis,
+                    const XT::Common::Parameter& param = {}) const override final
   {
     return order_(test_basis, ansatz_basis, this->parse_parameter(param));
   }
 
   using BaseType::evaluate;
 
-  void evaluate(const LocalTestBasisType& test_basis,
-                const LocalAnsatzBasisType& ansatz_basis,
-                const DomainType& point_in_reference_element,
-                DynamicMatrix<F>& result,
-                const XT::Common::Parameter& param = {}) const
+  virtual void evaluate(const LocalTestBasisType& test_basis,
+                        const LocalAnsatzBasisType& ansatz_basis,
+                        const DomainType& point_in_reference_element,
+                        DynamicMatrix<F>& result,
+                        const XT::Common::Parameter& param = {}) const override final
   {
     // prepare storage
     const size_t rows = test_basis.size(param);
@@ -166,15 +185,16 @@ public:
 
 private:
   const GenericOrderFunctionType order_;
-  const GenericEvalauteFunctionType evaluate_;
+  const GenericEvaluateFunctionType evaluate_;
+  const GenericPostBindFunctionType post_bind_;
 }; // class GenericLocalBinaryElementIntegrand
 
 
 template <class I, size_t r = 1, size_t rC = 1, class RF = double, class F = double>
 class GenericLocalBinaryIntersectionIntegrand : public LocalBinaryIntersectionIntegrandInterface<I, r, rC, RF, F>
 {
+  using ThisType = GenericLocalBinaryIntersectionIntegrand;
   using BaseType = LocalBinaryIntersectionIntegrandInterface<I, r, rC, RF, F>;
-  using ThisType = GenericLocalBinaryIntersectionIntegrand<I, r, rC, RF, F>;
 
 public:
   using typename BaseType::DomainType;
@@ -183,24 +203,28 @@ public:
   using GenericOrderFunctionType = std::function<int(const LocalBasisType& /*inside_basis*/,
                                                      const LocalBasisType& /*outside_basis*/,
                                                      const XT::Common::Parameter& /*param*/)>;
-  using GenericEvalauteFunctionType = std::function<void(const LocalBasisType& /*inside_basis*/,
+  using GenericEvaluateFunctionType = std::function<void(const LocalBasisType& /*inside_basis*/,
                                                          const LocalBasisType& /*outside_basis*/,
                                                          const DomainType& /*point_in_reference_intersection*/,
                                                          DynamicMatrix<F>& /*result*/,
                                                          const XT::Common::Parameter& /*param*/)>;
+  using GenericPostBindFunctionType = std::function<void(const I& /*ele*/)>;
 
   GenericLocalBinaryIntersectionIntegrand(GenericOrderFunctionType order_function,
-                                          GenericEvalauteFunctionType evaluate_function,
+                                          GenericEvaluateFunctionType evaluate_function,
+                                          GenericPostBindFunctionType post_bind_function = [](const I&) {},
                                           const XT::Common::ParameterType& param_type = {})
     : BaseType(param_type)
     , order_(order_function)
     , evaluate_(evaluate_function)
+    , post_bind_(post_bind_function)
   {}
 
   GenericLocalBinaryIntersectionIntegrand(const ThisType& other)
     : BaseType(other.parameter_type())
     , order_(other.order_)
     , evaluate_(other.evaluate_)
+    , post_bind_(other.post_bind_)
   {}
 
   std::unique_ptr<BaseType> copy() const override final
@@ -208,20 +232,25 @@ public:
     return std::make_unique<ThisType>(*this);
   }
 
-  int order(const LocalBasisType& inside_basis,
-            const LocalBasisType& outside_basis,
-            const XT::Common::Parameter& param = {}) const override final
+  void post_bind(const I& intersect) override final
+  {
+    post_bind_(intersect);
+  }
+
+  virtual int order(const LocalBasisType& inside_basis,
+                    const LocalBasisType& outside_basis,
+                    const XT::Common::Parameter& param = {}) const override final
   {
     return order_(inside_basis, outside_basis, this->parse_parameter(param));
   }
 
   using BaseType::evaluate;
 
-  void evaluate(const LocalBasisType& inside_basis,
-                const LocalBasisType& outside_basis,
-                const DomainType& point_in_reference_intersection,
-                DynamicMatrix<F>& result,
-                const XT::Common::Parameter& param = {}) const override final
+  virtual void evaluate(const LocalBasisType& inside_basis,
+                        const LocalBasisType& outside_basis,
+                        const DomainType& point_in_reference_intersection,
+                        DynamicMatrix<F>& result,
+                        const XT::Common::Parameter& param = {}) const override final
   { // prepare storage
     const size_t rows = inside_basis.size(param);
     const size_t cols = outside_basis.size(param);
@@ -239,7 +268,8 @@ public:
 
 private:
   const GenericOrderFunctionType order_;
-  const GenericEvalauteFunctionType evaluate_;
+  const GenericEvaluateFunctionType evaluate_;
+  const GenericPostBindFunctionType post_bind_;
 }; // class GenericLocalBinaryIntersectionIntegrand
 
 
diff --git a/dune/gdt/local/integrands/gradient-value.hh b/dune/gdt/local/integrands/gradient-value.hh
new file mode 100644
index 0000000000000000000000000000000000000000..2d93e0ad8acc91fc64d753a8bf33175505fb7e0b
--- /dev/null
+++ b/dune/gdt/local/integrands/gradient-value.hh
@@ -0,0 +1,169 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner  (2019)
+
+#ifndef DUNE_GDT_LOCAL_INTEGRANDS_GRADIENT_VALUE_HH
+#define DUNE_GDT_LOCAL_INTEGRANDS_GRADIENT_VALUE_HH
+
+#include <dune/xt/common/memory.hh>
+#include <dune/xt/functions/constant.hh>
+#include <dune/xt/functions/grid-function.hh>
+
+#include "interfaces.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+/**
+ * Given an inducing (vector-valued) function u, computes `(\nabla phi(x) * u(x)) * psi(x)` for all combinations of phi
+ * in the ansatz basis and psi in the test basis. If use_test_gradient is set to true, ansatz and test basis are
+ * swapped.
+ *
+ * \sa local_binary_to_unary_element_integrand
+ */
+template <class E,
+          size_t r = 1,
+          size_t rC = 1,
+          class TR = double,
+          class F = double,
+          class AR = TR,
+          bool use_test_gradient = false>
+class LocalElementGradientValueIntegrand : public LocalBinaryElementIntegrandInterface<E, r, rC, TR, F, r, rC, AR>
+{
+  using BaseType = LocalBinaryElementIntegrandInterface<E, r, rC, TR, F, r, rC, AR>;
+  using ThisType = LocalElementGradientValueIntegrand;
+  static_assert(rC == 1, "Not tested for rC > 1!");
+
+public:
+  using BaseType::d;
+  using typename BaseType::DomainType;
+  using typename BaseType::ElementType;
+  using typename BaseType::LocalAnsatzBasisType;
+  using typename BaseType::LocalTestBasisType;
+  using BasisValues =
+      std::vector<typename std::conditional_t<use_test_gradient, LocalAnsatzBasisType, LocalTestBasisType>::RangeType>;
+  using BasisJacobians = std::vector<
+      typename std::conditional_t<use_test_gradient, LocalTestBasisType, LocalAnsatzBasisType>::DerivativeRangeType>;
+  static const size_t vector_size = d;
+
+  using VectorGridFunctionType = XT::Functions::GridFunction<E, vector_size, 1, F>;
+  using VectorValues = typename VectorGridFunctionType::LocalFunctionType::RangeReturnType;
+
+  LocalElementGradientValueIntegrand(const VectorGridFunctionType vector_in)
+    : BaseType()
+    , vector_(vector_in)
+    , local_function_(vector_.local_function())
+  {}
+
+  LocalElementGradientValueIntegrand(const ThisType& other)
+    : BaseType(other.parameter_type())
+    , vector_(other.vector_)
+    , local_function_(vector_.local_function())
+  {}
+
+  LocalElementGradientValueIntegrand(ThisType&& source) = default;
+
+  std::unique_ptr<BaseType> copy() const override final
+  {
+    return std::make_unique<ThisType>(*this);
+  }
+
+protected:
+  void post_bind(const ElementType& ele) override final
+  {
+    local_function_->bind(ele);
+  }
+
+public:
+  int order(const LocalTestBasisType& test_basis,
+            const LocalAnsatzBasisType& ansatz_basis,
+            const XT::Common::Parameter& param = {}) const override final
+  {
+    return local_function_->order(param) + test_basis.order(param) + ansatz_basis.order(param);
+  }
+
+  using BaseType::evaluate;
+
+  void evaluate(const LocalTestBasisType& test_basis,
+                const LocalAnsatzBasisType& ansatz_basis,
+                const DomainType& point_in_reference_element,
+                DynamicMatrix<F>& result,
+                const XT::Common::Parameter& param = {}) const override final
+  {
+    // prepare storage
+    const size_t rows = test_basis.size(param);
+    const size_t cols = ansatz_basis.size(param);
+    if (result.rows() < rows || result.cols() < cols)
+      result.resize(rows, cols);
+    vector_values_ = local_function_->evaluate(point_in_reference_element, param);
+    helper<>::evaluate(
+        test_basis, ansatz_basis, point_in_reference_element, vector_values_, values_, jacobians_, result, param);
+  } // ... evaluate(...)
+
+private:
+  template <bool use_test = use_test_gradient, bool anything = true>
+  struct helper
+  {
+    static void evaluate(const LocalTestBasisType& test_basis,
+                         const LocalAnsatzBasisType& ansatz_basis,
+                         const DomainType& point_in_reference_element,
+                         const VectorValues& vector_values,
+                         BasisValues& values,
+                         BasisJacobians& jacobians,
+                         DynamicMatrix<F>& result,
+                         const XT::Common::Parameter& param)
+    {
+      test_basis.evaluate(point_in_reference_element, values, param);
+      ansatz_basis.jacobians(point_in_reference_element, jacobians, param);
+
+      auto nabla_phi_times_u = values[0];
+      for (size_t jj = 0; jj < ansatz_basis.size(); ++jj) {
+        jacobians[jj].mv(vector_values, nabla_phi_times_u);
+        for (size_t ii = 0; ii < test_basis.size(); ++ii)
+          result[ii][jj] = nabla_phi_times_u * values[ii];
+      }
+    }
+  };
+
+  template <bool anything>
+  struct helper<true, anything>
+  {
+    static void evaluate(const LocalTestBasisType& test_basis,
+                         const LocalAnsatzBasisType& ansatz_basis,
+                         const DomainType& point_in_reference_element,
+                         const VectorValues& vector_values,
+                         BasisValues& values,
+                         BasisJacobians& jacobians,
+                         DynamicMatrix<F>& result,
+                         const XT::Common::Parameter& param)
+    {
+      ansatz_basis.evaluate(point_in_reference_element, values, param);
+      test_basis.jacobians(point_in_reference_element, jacobians, param);
+
+      auto nabla_psi_times_u = values[0];
+      for (size_t ii = 0; ii < test_basis.size(); ++ii) {
+        jacobians[ii].mv(vector_values, nabla_psi_times_u);
+        for (size_t jj = 0; jj < ansatz_basis.size(); ++jj)
+          result[ii][jj] = nabla_psi_times_u * values[jj];
+      }
+    }
+  };
+
+  const VectorGridFunctionType vector_;
+  std::unique_ptr<typename VectorGridFunctionType::LocalFunctionType> local_function_;
+  mutable VectorValues vector_values_;
+  mutable BasisValues values_;
+  mutable BasisJacobians jacobians_;
+}; // class LocalElementGradientValueIntegrand
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_LOCAL_INTEGRANDS_GRADIENT_VALUE_HH
diff --git a/dune/gdt/local/integrands/identity.hh b/dune/gdt/local/integrands/identity.hh
index 663822eba1e798fe79bab78508725e4c89d37d59..f93db08c6868957de490f652b2ece3d9b735d8ac 100644
--- a/dune/gdt/local/integrands/identity.hh
+++ b/dune/gdt/local/integrands/identity.hh
@@ -24,7 +24,7 @@ class LocalElementIdentityIntegrand : public LocalUnaryElementIntegrandInterface
 {
   static_assert(r == 1, "");
   static_assert(rC == 1, "");
-  using ThisType = LocalElementIdentityIntegrand<E, r, rC, R, F>;
+  using ThisType = LocalElementIdentityIntegrand;
   using BaseType = LocalUnaryElementIntegrandInterface<E, r, rC, R, F>;
 
 public:
diff --git a/dune/gdt/local/integrands/interfaces.hh b/dune/gdt/local/integrands/interfaces.hh
index cb6b15f872390708db877ec0a1b72514fd57ca11..25e377eabaa6f547213fe9b2fb9693d9bcbe1bc2 100644
--- a/dune/gdt/local/integrands/interfaces.hh
+++ b/dune/gdt/local/integrands/interfaces.hh
@@ -26,6 +26,20 @@ namespace Dune {
 namespace GDT {
 
 
+// forwards (required for operator+, include are below)
+template <class E, size_t r, size_t rC, class R, class F>
+class LocalUnaryElementIntegrandSum;
+
+template <class E, size_t t_r, size_t t_RC, class TF, class F, size_t a_r, size_t a_rC, class AF>
+class LocalBinaryElementIntegrandSum;
+
+template <class I, size_t r, size_t rC, class R, class F>
+class LocalBinaryIntersectionIntegrandSum;
+
+template <class I, size_t t_r, size_t t_rC, class TF, class F, size_t a_r, size_t a_rC, class AF>
+class LocalQuaternaryIntersectionIntegrandSum;
+
+
 /**
  * Interface for integrands in integrals over grid elements, which depend on one argument only (usually the test basis
  * in an integral-based functional).
@@ -45,7 +59,7 @@ class LocalUnaryElementIntegrandInterface
 {
   static_assert(XT::Grid::is_entity<Element>::value, "");
 
-  using ThisType = LocalUnaryElementIntegrandInterface<Element, range_dim, range_dim_cols, RangeField, Field>;
+  using ThisType = LocalUnaryElementIntegrandInterface;
 
 public:
   using E = Element;
@@ -124,14 +138,7 @@ class LocalBinaryElementIntegrandInterface
 {
   static_assert(XT::Grid::is_entity<Element>::value, "");
 
-  using ThisType = LocalBinaryElementIntegrandInterface<Element,
-                                                        test_range_dim,
-                                                        test_range_dim_cols,
-                                                        TestRangeField,
-                                                        Field,
-                                                        ansatz_range_dim,
-                                                        ansatz_range_dim_cols,
-                                                        AnsatzRangeField>;
+  using ThisType = LocalBinaryElementIntegrandInterface;
 
 public:
   using E = Element;
@@ -216,8 +223,7 @@ class LocalBinaryIntersectionIntegrandInterface
 {
   static_assert(XT::Grid::is_intersection<Intersection>::value, "");
 
-  using ThisType =
-      LocalBinaryIntersectionIntegrandInterface<Intersection, range_dim, range_dim_cols, RangeField, Field>;
+  using ThisType = LocalBinaryIntersectionIntegrandInterface;
 
 public:
   using typename XT::Grid::IntersectionBoundObject<Intersection>::IntersectionType;
@@ -295,14 +301,7 @@ class LocalQuaternaryIntersectionIntegrandInterface
 {
   static_assert(XT::Grid::is_intersection<Intersection>::value, "");
 
-  using ThisType = LocalQuaternaryIntersectionIntegrandInterface<Intersection,
-                                                                 test_range_dim,
-                                                                 test_range_dim_cols,
-                                                                 TestRangeField,
-                                                                 Field,
-                                                                 ansatz_range_dim,
-                                                                 ansatz_range_dim_cols,
-                                                                 AnsatzRangeField>;
+  using ThisType = LocalQuaternaryIntersectionIntegrandInterface;
 
 public:
   using typename XT::Grid::IntersectionBoundObject<Intersection>::IntersectionType;
@@ -334,6 +333,11 @@ public:
 
   virtual std::unique_ptr<ThisType> copy() const = 0;
 
+  LocalQuaternaryIntersectionIntegrandSum<I, t_r, t_rC, TR, F, a_r, a_rC, AR> operator+(const ThisType& other) const
+  {
+    return LocalQuaternaryIntersectionIntegrandSum<I, t_r, t_rC, TR, F, a_r, a_rC, AR>(*this, other);
+  }
+
   /**
    * Returns the polynomial order of the integrand, given the bases.
    *
@@ -390,12 +394,37 @@ public:
     return {result_in_in, result_in_out, result_out_in, result_out_out};
   } // ... apply(...)
 
-private:
-  std::unique_ptr<IntersectionType> intersection_;
+protected:
+  void ensure_size_and_clear_results(const LocalTestBasisType& test_basis_inside,
+                                     const LocalAnsatzBasisType& ansatz_basis_inside,
+                                     const LocalTestBasisType& test_basis_outside,
+                                     const LocalAnsatzBasisType& ansatz_basis_outside,
+                                     DynamicMatrix<F>& result_in_in,
+                                     DynamicMatrix<F>& result_in_out,
+                                     DynamicMatrix<F>& result_out_in,
+                                     DynamicMatrix<F>& result_out_out,
+                                     const XT::Common::Parameter& param) const
+  {
+    const size_t rows_in = test_basis_inside.size(param);
+    const size_t rows_out = test_basis_outside.size(param);
+    const size_t cols_in = ansatz_basis_inside.size(param);
+    const size_t cols_out = ansatz_basis_outside.size(param);
+    const auto ensure_size_and_clear = [](auto& m, const auto& r, const auto& c) {
+      if (m.rows() < r || m.cols() < c)
+        m.resize(r, c);
+      m *= 0;
+    };
+    ensure_size_and_clear(result_in_in, rows_in, cols_in);
+    ensure_size_and_clear(result_in_out, rows_in, cols_out);
+    ensure_size_and_clear(result_out_in, rows_out, cols_in);
+    ensure_size_and_clear(result_out_out, rows_out, cols_out);
+  } // ... ensure_size_and_clear_results(...)
 }; // class LocalQuaternaryIntersectionIntegrandInterface
 
 
 } // namespace GDT
 } // namespace Dune
 
+#include "combined.hh"
+
 #endif // DUNE_GDT_LOCAL_INTEGRANDS_INTERFACES_HH
diff --git a/dune/gdt/local/integrands/ipdg.hh b/dune/gdt/local/integrands/ipdg.hh
new file mode 100644
index 0000000000000000000000000000000000000000..8e6cba4a21ca8d4909ecdd6243d4402b1a536d4c
--- /dev/null
+++ b/dune/gdt/local/integrands/ipdg.hh
@@ -0,0 +1,307 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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)
+
+#ifndef DUNE_GDT_LOCAL_INTEGRANDS_IPDG_HH
+#define DUNE_GDT_LOCAL_INTEGRANDS_IPDG_HH
+
+#include <functional>
+
+#include <dune/xt/common/memory.hh>
+#include <dune/xt/functions/grid-function.hh>
+#include <dune/xt/grid/entity.hh>
+#include <dune/xt/grid/intersection.hh>
+
+#include "interfaces.hh"
+
+namespace Dune {
+namespace GDT {
+namespace LocalIPDGIntegrands {
+namespace internal {
+
+
+template <class Intersection>
+static std::function<double(const Intersection&)> default_inner_intersection_diameter()
+{
+  return [](const Intersection& intersection) {
+    if (Intersection::dimension == 1) {
+      if (intersection.neighbor())
+        return 0.5 * (XT::Grid::diameter(intersection.inside()) + XT::Grid::diameter(intersection.outside()));
+      else
+        return XT::Grid::diameter(intersection.inside());
+    } else
+      return XT::Grid::diameter(intersection);
+  };
+} // ... default_inner_intersection_diameter(...)
+
+
+template <class Intersection>
+static std::function<double(const Intersection&)> default_boundary_intersection_diameter()
+{
+  return [](const Intersection& intersection) {
+    if (Intersection::dimension == 1)
+      return XT::Grid::diameter(intersection.inside());
+    else
+      return XT::Grid::diameter(intersection);
+  };
+} // ... default_boundary_intersection_diameter(...)
+
+
+} // namespace internal
+
+
+template <class I>
+class InnerPenalty : public LocalQuaternaryIntersectionIntegrandInterface<I>
+{
+  using BaseType = LocalQuaternaryIntersectionIntegrandInterface<I>;
+  using ThisType = InnerPenalty;
+
+public:
+  using BaseType::d;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::F;
+  using typename BaseType::IntersectionType;
+  using typename BaseType::LocalAnsatzBasisType;
+  using typename BaseType::LocalTestBasisType;
+
+  InnerPenalty(
+      const double& penalty,
+      XT::Functions::GridFunction<E, d, d> weight_function = 1.,
+      const std::function<double(const I&)>& intersection_diameter = internal::default_inner_intersection_diameter<I>())
+    : BaseType(weight_function.parameter_type())
+    , penalty_(penalty)
+    , weight_(weight_function)
+    , intersection_diameter_(intersection_diameter)
+    , local_weight_in_(weight_.local_function())
+    , local_weight_out_(weight_.local_function())
+  {}
+
+  InnerPenalty(const ThisType& other)
+    : BaseType(other.parameter_type())
+    , penalty_(other.penalty_)
+    , weight_(other.weight_)
+    , intersection_diameter_(other.intersection_diameter_)
+    , local_weight_in_(weight_.local_function())
+    , local_weight_out_(weight_.local_function())
+  {}
+
+  InnerPenalty(ThisType&& source) = default;
+
+  std::unique_ptr<BaseType> copy() const override final
+  {
+    return std::make_unique<ThisType>(*this);
+  }
+
+protected:
+  void post_bind(const IntersectionType& intrsctn) override final
+  {
+    DUNE_THROW_IF(
+        !intrsctn.neighbor(), Exceptions::integrand_error, "This integrand cannot be used on a boundary intersection!");
+    local_weight_in_->bind(intrsctn.inside());
+    local_weight_out_->bind(intrsctn.outside());
+  }
+
+public:
+  int order(const LocalTestBasisType& test_basis_inside,
+            const LocalAnsatzBasisType& ansatz_basis_inside,
+            const LocalTestBasisType& test_basis_outside,
+            const LocalAnsatzBasisType& ansatz_basis_outside,
+            const XT::Common::Parameter& param = {}) const override final
+  {
+    return std::max(local_weight_in_->order(param), local_weight_out_->order(param))
+           + std::max(test_basis_inside.order(param), test_basis_outside.order(param))
+           + std::max(ansatz_basis_inside.order(param), ansatz_basis_outside.order(param));
+  }
+
+  void evaluate(const LocalTestBasisType& test_basis_inside,
+                const LocalAnsatzBasisType& ansatz_basis_inside,
+                const LocalTestBasisType& test_basis_outside,
+                const LocalAnsatzBasisType& ansatz_basis_outside,
+                const DomainType& point_in_reference_intersection,
+                DynamicMatrix<F>& result_in_in,
+                DynamicMatrix<F>& result_in_out,
+                DynamicMatrix<F>& result_out_in,
+                DynamicMatrix<F>& result_out_out,
+                const XT::Common::Parameter& param = {}) const override final
+  {
+    // Prepare sotrage, ...
+    this->ensure_size_and_clear_results(test_basis_inside,
+                                        ansatz_basis_inside,
+                                        test_basis_outside,
+                                        ansatz_basis_outside,
+                                        result_in_in,
+                                        result_in_out,
+                                        result_out_in,
+                                        result_out_out,
+                                        param);
+    // evaluate ...
+    const auto point_in_inside_reference_element =
+        this->intersection().geometryInInside().global(point_in_reference_intersection);
+    const auto point_in_outside_reference_element =
+        this->intersection().geometryInOutside().global(point_in_reference_intersection);
+    const auto normal = this->intersection().unitOuterNormal(point_in_reference_intersection);
+    // ... basis functions
+    test_basis_inside.evaluate(point_in_inside_reference_element, test_basis_in_values_, param);
+    test_basis_outside.evaluate(point_in_outside_reference_element, test_basis_out_values_, param);
+    ansatz_basis_inside.evaluate(point_in_inside_reference_element, ansatz_basis_in_values_, param);
+    ansatz_basis_outside.evaluate(point_in_outside_reference_element, ansatz_basis_out_values_, param);
+    // ... and data functions, ...
+    const auto weight_in = local_weight_in_->evaluate(point_in_inside_reference_element, param);
+    const auto weight_out = local_weight_out_->evaluate(point_in_outside_reference_element, param);
+    // compute the weighted penalty ...
+    const double delta_plus = normal * (weight_out * normal);
+    const double delta_minus = normal * (weight_in * normal);
+    const auto weight = (delta_plus * delta_minus) / (delta_plus + delta_minus); // half harmonic average
+    const auto h = intersection_diameter_(this->intersection());
+    const auto penalty = (penalty_ * weight) / h;
+    // and finally compute the integrand.
+    const size_t rows_in = test_basis_inside.size(param);
+    const size_t rows_out = test_basis_outside.size(param);
+    const size_t cols_in = ansatz_basis_inside.size(param);
+    const size_t cols_out = ansatz_basis_outside.size(param);
+    for (size_t ii = 0; ii < rows_in; ++ii) {
+      for (size_t jj = 0; jj < cols_in; ++jj)
+        result_in_in[ii][jj] += penalty * ansatz_basis_in_values_[jj] * test_basis_in_values_[ii];
+      for (size_t jj = 0; jj < cols_out; ++jj)
+        result_in_out[ii][jj] += -1.0 * penalty * ansatz_basis_out_values_[jj] * test_basis_in_values_[ii];
+    }
+    for (size_t ii = 0; ii < rows_out; ++ii) {
+      for (size_t jj = 0; jj < cols_in; ++jj)
+        result_out_in[ii][jj] += -1.0 * penalty * ansatz_basis_in_values_[jj] * test_basis_out_values_[ii];
+      for (size_t jj = 0; jj < cols_out; ++jj)
+        result_out_out[ii][jj] += penalty * ansatz_basis_out_values_[jj] * test_basis_out_values_[ii];
+    }
+  } // ... evaluate(...)
+
+private:
+  const double penalty_;
+  XT::Functions::GridFunction<E, d, d> weight_;
+  const std::function<double(const I&)> intersection_diameter_;
+  std::unique_ptr<typename XT::Functions::GridFunction<E, d, d>::LocalFunctionType> local_weight_in_;
+  std::unique_ptr<typename XT::Functions::GridFunction<E, d, d>::LocalFunctionType> local_weight_out_;
+  mutable std::vector<typename LocalTestBasisType::RangeType> test_basis_in_values_;
+  mutable std::vector<typename LocalTestBasisType::RangeType> test_basis_out_values_;
+  mutable std::vector<typename LocalAnsatzBasisType::RangeType> ansatz_basis_in_values_;
+  mutable std::vector<typename LocalAnsatzBasisType::RangeType> ansatz_basis_out_values_;
+}; // InnerPenalty
+
+
+template <class I>
+class BoundaryPenalty : public LocalQuaternaryIntersectionIntegrandInterface<I>
+{
+  using BaseType = LocalQuaternaryIntersectionIntegrandInterface<I>;
+  using ThisType = BoundaryPenalty;
+
+public:
+  using BaseType::d;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::F;
+  using typename BaseType::IntersectionType;
+  using typename BaseType::LocalAnsatzBasisType;
+  using typename BaseType::LocalTestBasisType;
+
+  BoundaryPenalty(const double& penalty,
+                  XT::Functions::GridFunction<E, d, d> weight_function = 1.,
+                  const std::function<double(const I&)>& intersection_diameter =
+                      internal::default_boundary_intersection_diameter<I>())
+    : BaseType()
+    , penalty_(penalty)
+    , weight_(weight_function)
+    , intersection_diameter_(intersection_diameter)
+    , local_weight_(weight_.local_function())
+  {}
+
+  BoundaryPenalty(const ThisType& other)
+    : BaseType(other.parameter_type())
+    , penalty_(other.penalty_)
+    , weight_(other.weight_)
+    , intersection_diameter_(other.intersection_diameter_)
+    , local_weight_(weight_.local_function())
+  {}
+
+  BoundaryPenalty(ThisType&& source) = default;
+
+  std::unique_ptr<BaseType> copy() const override final
+  {
+    return std::make_unique<ThisType>(*this);
+  }
+
+protected:
+  void post_bind(const IntersectionType& intersection) override final
+  {
+    local_weight_->bind(intersection.inside());
+  }
+
+public:
+  int order(const LocalTestBasisType& test_basis_inside,
+            const LocalAnsatzBasisType& ansatz_basis_inside,
+            const LocalTestBasisType& /*test_basis_outside*/,
+            const LocalAnsatzBasisType& /*ansatz_basis_outside*/,
+            const XT::Common::Parameter& param = {}) const override final
+  {
+    return local_weight_->order(param) + test_basis_inside.order(param) + ansatz_basis_inside.order(param);
+  }
+
+  void evaluate(const LocalTestBasisType& test_basis_inside,
+                const LocalAnsatzBasisType& ansatz_basis_inside,
+                const LocalTestBasisType& test_basis_outside,
+                const LocalAnsatzBasisType& ansatz_basis_outside,
+                const DomainType& point_in_reference_intersection,
+                DynamicMatrix<F>& result_in_in,
+                DynamicMatrix<F>& result_in_out,
+                DynamicMatrix<F>& result_out_in,
+                DynamicMatrix<F>& result_out_out,
+                const XT::Common::Parameter& param = {}) const override final
+  {
+    // Prepare sotrage, ...
+    this->ensure_size_and_clear_results(test_basis_inside,
+                                        ansatz_basis_inside,
+                                        test_basis_outside,
+                                        ansatz_basis_outside,
+                                        result_in_in,
+                                        result_in_out,
+                                        result_out_in,
+                                        result_out_out,
+                                        param);
+    // evaluate ...
+    const auto point_in_inside_reference_element =
+        this->intersection().geometryInInside().global(point_in_reference_intersection);
+    const auto normal = this->intersection().unitOuterNormal(point_in_reference_intersection);
+    // ... basis functions ...
+    test_basis_inside.evaluate(point_in_inside_reference_element, test_basis_values_, param);
+    ansatz_basis_inside.evaluate(point_in_inside_reference_element, ansatz_basis_values_, param);
+    // ... and data functions, ....
+    const auto weight = local_weight_->evaluate(point_in_inside_reference_element, param);
+    // compute the weighted penalty ...
+    const auto h = intersection_diameter_(this->intersection());
+    const auto penalty = (penalty_ * (normal * (weight * normal))) / h;
+    // and finally compute integrand.
+    const size_t rows = test_basis_inside.size(param);
+    const size_t cols = ansatz_basis_inside.size(param);
+    for (size_t ii = 0; ii < rows; ++ii)
+      for (size_t jj = 0; jj < cols; ++jj)
+        result_in_in[ii][jj] += penalty * ansatz_basis_values_[jj] * test_basis_values_[ii];
+  } // ... evaluate(...)
+
+private:
+  const double penalty_;
+  XT::Functions::GridFunction<E, d, d> weight_;
+  const std::function<double(const I&)> intersection_diameter_;
+  std::unique_ptr<typename XT::Functions::GridFunction<E, d, d>::LocalFunctionType> local_weight_;
+  mutable std::vector<typename LocalTestBasisType::RangeType> test_basis_values_;
+  mutable std::vector<typename LocalAnsatzBasisType::RangeType> ansatz_basis_values_;
+}; // BoundaryPenalty
+
+
+} // namespace LocalIPDGIntegrands
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_LOCAL_INTEGRANDS_IPDG_HH
diff --git a/dune/gdt/local/integrands/laplace-ipdg.hh b/dune/gdt/local/integrands/laplace-ipdg.hh
new file mode 100644
index 0000000000000000000000000000000000000000..319451421ebeeb84dabd5631d93c89c83dd05290
--- /dev/null
+++ b/dune/gdt/local/integrands/laplace-ipdg.hh
@@ -0,0 +1,324 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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)
+
+#ifndef DUNE_GDT_LOCAL_INTEGRANDS_LAPLACE_IPDG_HH
+#define DUNE_GDT_LOCAL_INTEGRANDS_LAPLACE_IPDG_HH
+
+#include <dune/xt/functions/grid-function.hh>
+
+#include "interfaces.hh"
+#include "ipdg.hh"
+
+namespace Dune {
+namespace GDT {
+namespace LocalLaplaceIPDGIntegrands {
+
+
+/**
+ * \note The role of symmetry_prefactor:
+ *       * -1 => NIPDG
+ *       *  0 => IIPDG
+ *       *  1 => SIPDG
+ * \note The role of the weight:
+ *       * symmetry_prefactor = 1 && weight_function = 1 => SIPDG
+ *       * symmetry_prefactor = 1 && weight_function = diffusion => SWIPDG
+ */
+template <class I>
+class InnerCoupling : public LocalQuaternaryIntersectionIntegrandInterface<I>
+{
+  using BaseType = LocalQuaternaryIntersectionIntegrandInterface<I>;
+  using ThisType = InnerCoupling;
+
+public:
+  using BaseType::d;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::F;
+  using typename BaseType::IntersectionType;
+  using typename BaseType::LocalAnsatzBasisType;
+  using typename BaseType::LocalTestBasisType;
+
+  InnerCoupling(const double& symmetry_prefactor,
+                XT::Functions::GridFunction<E, d, d> diffusion,
+                XT::Functions::GridFunction<E, d, d> weight_function = {1.})
+    : BaseType(diffusion.parameter_type() + weight_function.parameter_type())
+    , symmetry_prefactor_(symmetry_prefactor)
+    , diffusion_(diffusion)
+    , weight_(weight_function)
+    , local_diffusion_in_(diffusion_.local_function())
+    , local_diffusion_out_(diffusion_.local_function())
+    , local_weight_in_(weight_.local_function())
+    , local_weight_out_(weight_.local_function())
+  {}
+
+  InnerCoupling(const ThisType& other)
+    : BaseType(other.parameter_type())
+    , symmetry_prefactor_(other.symmetry_prefactor_)
+    , diffusion_(other.diffusion_)
+    , weight_(other.weight_)
+    , local_diffusion_in_(diffusion_.local_function())
+    , local_diffusion_out_(diffusion_.local_function())
+    , local_weight_in_(weight_.local_function())
+    , local_weight_out_(weight_.local_function())
+  {}
+
+  InnerCoupling(ThisType&& source) = default;
+
+  std::unique_ptr<BaseType> copy() const override final
+  {
+    return std::make_unique<ThisType>(*this);
+  }
+
+protected:
+  void post_bind(const IntersectionType& intrsctn) override final
+  {
+    DUNE_THROW_IF(
+        !intrsctn.neighbor(), Exceptions::integrand_error, "This integrand cannot be used on a boundary intersection!");
+    const auto inside_element = intrsctn.inside();
+    const auto outside_element = intrsctn.outside();
+    local_diffusion_in_->bind(inside_element);
+    local_weight_in_->bind(inside_element);
+    local_diffusion_out_->bind(outside_element);
+    local_weight_out_->bind(outside_element);
+  } // ... post_bind(...)
+
+public:
+  int order(const LocalTestBasisType& test_basis_inside,
+            const LocalAnsatzBasisType& ansatz_basis_inside,
+            const LocalTestBasisType& test_basis_outside,
+            const LocalAnsatzBasisType& ansatz_basis_outside,
+            const XT::Common::Parameter& param = {}) const override final
+  {
+    return std::max(local_diffusion_in_->order(param), local_diffusion_out_->order(param))
+           + std::max(local_weight_in_->order(), local_weight_out_->order(param))
+           + std::max(test_basis_inside.order(param), test_basis_outside.order(param))
+           + std::max(ansatz_basis_inside.order(param), ansatz_basis_outside.order(param));
+  }
+
+  void evaluate(const LocalTestBasisType& test_basis_inside,
+                const LocalAnsatzBasisType& ansatz_basis_inside,
+                const LocalTestBasisType& test_basis_outside,
+                const LocalAnsatzBasisType& ansatz_basis_outside,
+                const DomainType& point_in_reference_intersection,
+                DynamicMatrix<F>& result_in_in,
+                DynamicMatrix<F>& result_in_out,
+                DynamicMatrix<F>& result_out_in,
+                DynamicMatrix<F>& result_out_out,
+                const XT::Common::Parameter& param = {}) const override final
+  {
+    // Prepare sotrage, ...
+    this->ensure_size_and_clear_results(test_basis_inside,
+                                        ansatz_basis_inside,
+                                        test_basis_outside,
+                                        ansatz_basis_outside,
+                                        result_in_in,
+                                        result_in_out,
+                                        result_out_in,
+                                        result_out_out,
+                                        param);
+    // evaluate ...
+    const auto point_in_inside_reference_element =
+        this->intersection().geometryInInside().global(point_in_reference_intersection);
+    const auto point_in_outside_reference_element =
+        this->intersection().geometryInOutside().global(point_in_reference_intersection);
+    const auto normal = this->intersection().unitOuterNormal(point_in_reference_intersection);
+    // ... basis functions and ...
+    test_basis_inside.evaluate(point_in_inside_reference_element, test_basis_in_values_, param);
+    test_basis_inside.jacobians(point_in_inside_reference_element, test_basis_in_grads_, param);
+    test_basis_outside.evaluate(point_in_outside_reference_element, test_basis_out_values_, param);
+    test_basis_outside.jacobians(point_in_outside_reference_element, test_basis_out_grads_, param);
+    ansatz_basis_inside.evaluate(point_in_inside_reference_element, ansatz_basis_in_values_, param);
+    ansatz_basis_inside.jacobians(point_in_inside_reference_element, ansatz_basis_in_grads_, param);
+    ansatz_basis_outside.evaluate(point_in_outside_reference_element, ansatz_basis_out_values_, param);
+    ansatz_basis_outside.jacobians(point_in_outside_reference_element, ansatz_basis_out_grads_, param);
+    // ... data functions, ...
+    const auto diffusion_in = local_diffusion_in_->evaluate(point_in_inside_reference_element, param);
+    const auto diffusion_out = local_diffusion_out_->evaluate(point_in_outside_reference_element, param);
+    const auto weight_in = local_weight_in_->evaluate(point_in_inside_reference_element, param);
+    const auto weight_out = local_weight_out_->evaluate(point_in_outside_reference_element, param);
+    // compute the weighted mean ...
+    const auto delta_plus = normal * (weight_out * normal);
+    const auto delta_minus = normal * (weight_in * normal);
+    const auto weight_minus = delta_plus / (delta_plus + delta_minus);
+    const auto weight_plus = delta_minus / (delta_plus + delta_minus);
+    // ... and finally compute the integrand.
+    const size_t rows_in = test_basis_inside.size(param);
+    const size_t rows_out = test_basis_outside.size(param);
+    const size_t cols_in = ansatz_basis_inside.size(param);
+    const size_t cols_out = ansatz_basis_outside.size(param);
+    for (size_t ii = 0; ii < rows_in; ++ii) {
+      for (size_t jj = 0; jj < cols_in; ++jj) {
+        result_in_in[ii][jj] +=
+            -1.0 * weight_minus * ((diffusion_in * ansatz_basis_in_grads_[jj][0]) * normal) * test_basis_in_values_[ii];
+        result_in_in[ii][jj] += -1.0 * symmetry_prefactor_ * weight_minus * ansatz_basis_in_values_[jj]
+                                * ((diffusion_in * test_basis_in_grads_[ii][0]) * normal);
+      }
+      for (size_t jj = 0; jj < cols_out; ++jj) {
+        result_in_out[ii][jj] += -1.0 * weight_plus * ((diffusion_out * ansatz_basis_out_grads_[jj][0]) * normal)
+                                 * test_basis_in_values_[ii];
+        result_in_out[ii][jj] += symmetry_prefactor_ * weight_minus * ansatz_basis_out_values_[jj]
+                                 * ((diffusion_in * test_basis_in_grads_[ii][0]) * normal);
+      }
+    }
+    for (size_t ii = 0; ii < rows_out; ++ii) {
+      for (size_t jj = 0; jj < cols_in; ++jj) {
+        result_out_in[ii][jj] +=
+            weight_minus * ((diffusion_in * ansatz_basis_in_grads_[jj][0]) * normal) * test_basis_out_values_[ii];
+        result_out_in[ii][jj] += -1.0 * symmetry_prefactor_ * weight_plus * ansatz_basis_in_values_[jj]
+                                 * ((diffusion_out * test_basis_out_grads_[ii][0]) * normal);
+      }
+      for (size_t jj = 0; jj < cols_out; ++jj) {
+        result_out_out[ii][jj] +=
+            weight_plus * ((diffusion_out * ansatz_basis_out_grads_[jj][0]) * normal) * test_basis_out_values_[ii];
+        result_out_out[ii][jj] += symmetry_prefactor_ * weight_plus * ansatz_basis_out_values_[jj]
+                                  * ((diffusion_out * test_basis_out_grads_[ii][0]) * normal);
+      }
+    }
+  } // ... evaluate(...)
+
+private:
+  const double symmetry_prefactor_;
+  XT::Functions::GridFunction<E, d, d> diffusion_;
+  XT::Functions::GridFunction<E, d, d> weight_;
+  std::unique_ptr<typename XT::Functions::GridFunctionInterface<E, d, d>::LocalFunctionType> local_diffusion_in_;
+  std::unique_ptr<typename XT::Functions::GridFunctionInterface<E, d, d>::LocalFunctionType> local_diffusion_out_;
+  std::unique_ptr<typename XT::Functions::GridFunctionInterface<E, d, d>::LocalFunctionType> local_weight_in_;
+  std::unique_ptr<typename XT::Functions::GridFunctionInterface<E, d, d>::LocalFunctionType> local_weight_out_;
+  mutable std::vector<typename LocalTestBasisType::RangeType> test_basis_in_values_;
+  mutable std::vector<typename LocalTestBasisType::DerivativeRangeType> test_basis_in_grads_;
+  mutable std::vector<typename LocalTestBasisType::RangeType> test_basis_out_values_;
+  mutable std::vector<typename LocalTestBasisType::DerivativeRangeType> test_basis_out_grads_;
+  mutable std::vector<typename LocalAnsatzBasisType::RangeType> ansatz_basis_in_values_;
+  mutable std::vector<typename LocalAnsatzBasisType::DerivativeRangeType> ansatz_basis_in_grads_;
+  mutable std::vector<typename LocalAnsatzBasisType::RangeType> ansatz_basis_out_values_;
+  mutable std::vector<typename LocalAnsatzBasisType::DerivativeRangeType> ansatz_basis_out_grads_;
+}; // InnerCoupling
+
+
+/**
+ * \note The role of symmetry_prefactor:
+ *       * -1 => NIPDG
+ *       *  0 => IIPDG
+ *       *  1 => SIPDG
+ * \note The role of the weight:
+ *       * symmetry_prefactor = 1 && weight_function = 1 => SIPDG
+ *       * symmetry_prefactor = 1 && weight_function = diffusion => SWIPDG
+ */
+template <class I>
+class DirichletCoupling : public LocalQuaternaryIntersectionIntegrandInterface<I>
+{
+  using BaseType = LocalQuaternaryIntersectionIntegrandInterface<I>;
+  using ThisType = DirichletCoupling;
+
+public:
+  using BaseType::d;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::F;
+  using typename BaseType::IntersectionType;
+  using typename BaseType::LocalAnsatzBasisType;
+  using typename BaseType::LocalTestBasisType;
+
+  DirichletCoupling(const double& symmetry_prefactor, XT::Functions::GridFunction<E, d, d> diffusion)
+    : BaseType(diffusion.parameter_type())
+    , symmetry_prefactor_(symmetry_prefactor)
+    , diffusion_(diffusion)
+    , local_diffusion_(diffusion_.local_function())
+  {}
+
+  DirichletCoupling(const ThisType& other)
+    : BaseType(other.parameter_type())
+    , symmetry_prefactor_(other.symmetry_prefactor_)
+    , diffusion_(other.diffusion_)
+    , local_diffusion_(diffusion_.local_function())
+  {}
+
+  DirichletCoupling(ThisType&& source) = default;
+
+  std::unique_ptr<BaseType> copy() const override final
+  {
+    return std::make_unique<ThisType>(*this);
+  }
+
+protected:
+  void post_bind(const IntersectionType& intersection) override final
+  {
+    const auto inside_element = intersection.inside();
+    local_diffusion_->bind(inside_element);
+  }
+
+public:
+  int order(const LocalTestBasisType& test_basis_inside,
+            const LocalAnsatzBasisType& ansatz_basis_inside,
+            const LocalTestBasisType& /*test_basis_outside*/,
+            const LocalAnsatzBasisType& /*ansatz_basis_outside*/,
+            const XT::Common::Parameter& param = {}) const override final
+  {
+    return local_diffusion_->order(param) + test_basis_inside.order(param) + ansatz_basis_inside.order(param);
+  }
+
+  void evaluate(const LocalTestBasisType& test_basis_inside,
+                const LocalAnsatzBasisType& ansatz_basis_inside,
+                const LocalTestBasisType& test_basis_outside,
+                const LocalAnsatzBasisType& ansatz_basis_outside,
+                const DomainType& point_in_reference_intersection,
+                DynamicMatrix<F>& result_in_in,
+                DynamicMatrix<F>& result_in_out,
+                DynamicMatrix<F>& result_out_in,
+                DynamicMatrix<F>& result_out_out,
+                const XT::Common::Parameter& param = {}) const override final
+  {
+    // Prepare sotrage, ...
+    this->ensure_size_and_clear_results(test_basis_inside,
+                                        ansatz_basis_inside,
+                                        test_basis_outside,
+                                        ansatz_basis_outside,
+                                        result_in_in,
+                                        result_in_out,
+                                        result_out_in,
+                                        result_out_out,
+                                        param);
+    // evaluate ...
+    const auto point_in_inside_reference_element =
+        this->intersection().geometryInInside().global(point_in_reference_intersection);
+    const auto normal = this->intersection().unitOuterNormal(point_in_reference_intersection);
+    // ... basis functions and ...
+    test_basis_inside.evaluate(point_in_inside_reference_element, test_basis_values_, param);
+    test_basis_inside.jacobians(point_in_inside_reference_element, test_basis_grads_, param);
+    ansatz_basis_inside.evaluate(point_in_inside_reference_element, ansatz_basis_values_, param);
+    ansatz_basis_inside.jacobians(point_in_inside_reference_element, ansatz_basis_grads_, param);
+    // ... data functions, ...
+    const auto diffusion = local_diffusion_->evaluate(point_in_inside_reference_element, param);
+    // ... and finally compute the integrand.
+    const size_t rows = test_basis_inside.size(param);
+    const size_t cols = ansatz_basis_inside.size(param);
+    for (size_t ii = 0; ii < rows; ++ii)
+      for (size_t jj = 0; jj < cols; ++jj) {
+        result_in_in[ii][jj] += -1.0 * ((diffusion * ansatz_basis_grads_[jj][0]) * normal) * test_basis_values_[ii];
+        result_in_in[ii][jj] +=
+            -1.0 * symmetry_prefactor_ * ansatz_basis_values_[jj] * ((diffusion * test_basis_grads_[ii][0]) * normal);
+      }
+  } // ... evaluate(...)
+
+private:
+  const double symmetry_prefactor_;
+  XT::Functions::GridFunction<E, d, d> diffusion_;
+  std::unique_ptr<typename XT::Functions::GridFunctionInterface<E, d, d>::LocalFunctionType> local_diffusion_;
+  mutable std::vector<typename LocalTestBasisType::RangeType> test_basis_values_;
+  mutable std::vector<typename LocalTestBasisType::DerivativeRangeType> test_basis_grads_;
+  mutable std::vector<typename LocalAnsatzBasisType::RangeType> ansatz_basis_values_;
+  mutable std::vector<typename LocalAnsatzBasisType::DerivativeRangeType> ansatz_basis_grads_;
+}; // DirichletCoupling
+
+
+} // namespace LocalLaplaceIPDGIntegrands
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_LOCAL_INTEGRANDS_LAPLACE_IPDG_HH
diff --git a/dune/gdt/local/integrands/laplace.hh b/dune/gdt/local/integrands/laplace.hh
new file mode 100644
index 0000000000000000000000000000000000000000..d7b6556af2bcbae3d9cd7464290a2ff171174962
--- /dev/null
+++ b/dune/gdt/local/integrands/laplace.hh
@@ -0,0 +1,112 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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)
+
+#ifndef DUNE_GDT_LOCAL_INTEGRANDS_LAPLACE_HH
+#define DUNE_GDT_LOCAL_INTEGRANDS_LAPLACE_HH
+
+#include <dune/xt/common/memory.hh>
+#include <dune/xt/la/container/eye-matrix.hh>
+#include <dune/xt/functions/grid-function.hh>
+
+#include "interfaces.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+/**
+ * Given a weight function kappa, computes `{[kappa(x) \nabla phi(x)] * \nabla psi(x)}` for all combinations of phi and
+ * psi in the bases.
+ */
+template <class E, size_t r = 1, class F = double>
+class LocalLaplaceIntegrand : public LocalBinaryElementIntegrandInterface<E, r, 1, F, F, r, 1, F>
+{
+  using BaseType = LocalBinaryElementIntegrandInterface<E, r, 1, F, F, r, 1, F>;
+  using ThisType = LocalLaplaceIntegrand;
+
+public:
+  using BaseType::d;
+  using typename BaseType::DomainType;
+  using typename BaseType::ElementType;
+  using typename BaseType::LocalAnsatzBasisType;
+  using typename BaseType::LocalTestBasisType;
+
+  explicit LocalLaplaceIntegrand(
+      XT::Functions::GridFunction<E, d, d, F> diffusion = XT::LA::eye_matrix<FieldMatrix<F, d, d>>(d, d))
+    : BaseType()
+    , weight_(diffusion)
+    , local_weight_(weight_.local_function())
+  {}
+
+  LocalLaplaceIntegrand(const ThisType& other)
+    : BaseType(other.parameter_type())
+    , weight_(other.weight_)
+    , local_weight_(weight_.local_function())
+  {}
+
+  LocalLaplaceIntegrand(ThisType&& source) = default;
+
+  std::unique_ptr<BaseType> copy() const override final
+  {
+    return std::make_unique<ThisType>(*this);
+  }
+
+protected:
+  void post_bind(const ElementType& ele) override
+  {
+#ifndef NDEBUG
+    if (!ele.geometry().affine())
+      std::cerr << "Warning: integration order has to be increased for non-affine geometries!" << std::endl;
+#endif
+    local_weight_->bind(ele);
+  }
+
+public:
+  int order(const LocalTestBasisType& test_basis,
+            const LocalAnsatzBasisType& ansatz_basis,
+            const XT::Common::Parameter& param = {}) const override final
+  {
+    return local_weight_->order(param) + test_basis.order(param) + ansatz_basis.order(param);
+  }
+
+  void evaluate(const LocalTestBasisType& test_basis,
+                const LocalAnsatzBasisType& ansatz_basis,
+                const DomainType& point_in_reference_element,
+                DynamicMatrix<F>& result,
+                const XT::Common::Parameter& param = {}) const override final
+  {
+    // prepare storage
+    const size_t rows = test_basis.size(param);
+    const size_t cols = ansatz_basis.size(param);
+    if (result.rows() < rows || result.cols() < cols)
+      result.resize(rows, cols);
+    result *= 0;
+    // evaluate
+    test_basis.jacobians(point_in_reference_element, test_basis_grads_, param);
+    ansatz_basis.jacobians(point_in_reference_element, ansatz_basis_grads_, param);
+    const auto weight = local_weight_->evaluate(point_in_reference_element, param);
+    // compute elliptic evaluation
+    for (size_t ii = 0; ii < rows; ++ii)
+      for (size_t jj = 0; jj < cols; ++jj)
+        for (size_t rr = 0; rr < r; ++rr)
+          result[ii][jj] += (weight * ansatz_basis_grads_[jj][rr]) * test_basis_grads_[ii][rr];
+  } // ... evaluate(...)
+
+private:
+  XT::Functions::GridFunction<E, d, d, F> weight_;
+  std::unique_ptr<typename XT::Functions::GridFunction<E, d, d, F>::LocalFunctionType> local_weight_;
+  mutable std::vector<typename LocalTestBasisType::DerivativeRangeType> test_basis_grads_;
+  mutable std::vector<typename LocalAnsatzBasisType::DerivativeRangeType> ansatz_basis_grads_;
+}; // class LocalLaplaceIntegrand
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_LOCAL_INTEGRANDS_LAPLACE_HH
diff --git a/dune/gdt/local/integrands/product.hh b/dune/gdt/local/integrands/product.hh
index 8cb7c10970432005fcd9b45dc903b78996efd1d6..9507c4143c34cddb19be038e90e05a21193a8ebc 100644
--- a/dune/gdt/local/integrands/product.hh
+++ b/dune/gdt/local/integrands/product.hh
@@ -18,7 +18,7 @@
 #include <dune/xt/functions/constant.hh>
 #include <dune/xt/functions/base/combined-functions.hh>
 #include <dune/xt/functions/base/combined-grid-functions.hh>
-#include <dune/xt/functions/base/function-as-grid-function.hh>
+#include <dune/xt/functions/grid-function.hh>
 #include <dune/xt/functions/interfaces/grid-function.hh>
 
 #include "interfaces.hh"
@@ -28,16 +28,18 @@ namespace GDT {
 
 
 /**
- * Given an inducing function f, computes `f(x) * phi(x) * psi(x)` for all combinations of phi and psi in the bases.
+ * Given an inducing function f (may be matrix-valued), computes `(f(x) * psi(x)) * phi(x)` for all combinations of phi
+ * in the ansatz basis and psi in the test basis.
  *
  * \note Note that f can also be given as a scalar value or omitted.
+ * \note Applying f to the ansatz basis can be done by passing f^T (the transposed of f)
  *
  * \sa local_binary_to_unary_element_integrand
  */
 template <class E, size_t r = 1, class TR = double, class F = double, class AR = TR>
 class LocalElementProductIntegrand : public LocalBinaryElementIntegrandInterface<E, r, 1, TR, F, r, 1, AR>
 {
-  using ThisType = LocalElementProductIntegrand<E, r, TR, F, AR>;
+  using ThisType = LocalElementProductIntegrand;
   using BaseType = LocalBinaryElementIntegrandInterface<E, r, 1, TR, F, r, 1, AR>;
 
 public:
@@ -47,60 +49,16 @@ public:
   using typename BaseType::LocalAnsatzBasisType;
   using typename BaseType::LocalTestBasisType;
 
-  using GridFunctionType = XT::Functions::GridFunctionInterface<E, r, r, F>;
-
-  LocalElementProductIntegrand(const F& inducing_value = F(1))
-    : BaseType()
-    , inducing_function_(new XT::Functions::FunctionAsGridFunctionWrapper<E, r, r, F>(
-          new XT::Functions::ProductFunction<XT::Functions::ConstantFunction<d, 1, 1, F>,
-                                             XT::Functions::ConstantFunction<d, r, r, F>>(
-              new XT::Functions::ConstantFunction<d, 1, 1, F>(inducing_value),
-              new XT::Functions::ConstantFunction<d, r, r, F>(XT::LA::eye_matrix<FieldMatrix<F, r, r>>(r)))))
-    , local_function_(inducing_function_.access().local_function())
-    , test_basis_values_()
-    , ansatz_basis_values_()
-  {}
-
-  LocalElementProductIntegrand(const XT::Functions::FunctionInterface<d, 1, 1, F>& inducing_function)
-    : BaseType()
-    , inducing_function_(new XT::Functions::FunctionAsGridFunctionWrapper<E, r, r, F>(
-          new XT::Functions::ProductFunction<XT::Functions::FunctionInterface<d, 1, 1, F>,
-                                             XT::Functions::ConstantFunction<d, r, r, F>>(
-              inducing_function,
-              new XT::Functions::ConstantFunction<d, r, r, F>(XT::LA::eye_matrix<FieldMatrix<F, r, r>>(r)))))
-    , local_function_(inducing_function_.access().local_function())
-    , test_basis_values_()
-    , ansatz_basis_values_()
-  {}
-
-  LocalElementProductIntegrand(const XT::Functions::GridFunctionInterface<E, 1, 1, F>& inducing_function)
-    : BaseType()
-    , inducing_function_(new XT::Functions::FunctionAsGridFunctionWrapper<E, r, r, F>(
-          new XT::Functions::ProductGridFunction<XT::Functions::GridFunctionInterface<E, 1, 1, F>,
-                                                 XT::Functions::GridFunctionInterface<E, r, r, F>>(
-              inducing_function,
-              new XT::Functions::FunctionAsGridFunctionWrapper<E, r, r, F>(
-                  new XT::Functions::ConstantFunction<d, r, r, F>(XT::LA::eye_matrix<FieldMatrix<F, r, r>>(r))))))
-    , local_function_(inducing_function_.access().local_function())
-    , test_basis_values_()
-    , ansatz_basis_values_()
-  {}
-
-  template <class E_, typename = std::enable_if_t<std::is_same<E_, E>::value && r != 1, void>>
-  LocalElementProductIntegrand(const XT::Functions::GridFunctionInterface<E_, r, r, F>& inducing_function)
+  LocalElementProductIntegrand(XT::Functions::GridFunction<E, r, r, F> weight = {1.})
     : BaseType()
-    , inducing_function_(inducing_function)
-    , local_function_(inducing_function_.access().local_function())
-    , test_basis_values_()
-    , ansatz_basis_values_()
+    , weight_(weight)
+    , local_weight_(weight_.local_function())
   {}
 
   LocalElementProductIntegrand(const ThisType& other)
     : BaseType(other.parameter_type())
-    , inducing_function_(other.inducing_function_)
-    , local_function_(inducing_function_.access().local_function())
-    , test_basis_values_()
-    , ansatz_basis_values_()
+    , weight_(other.weight_)
+    , local_weight_(weight_.local_function())
   {}
 
   LocalElementProductIntegrand(ThisType&& source) = default;
@@ -113,7 +71,7 @@ public:
 protected:
   void post_bind(const ElementType& ele) override final
   {
-    local_function_->bind(ele);
+    local_weight_->bind(ele);
   }
 
 public:
@@ -121,7 +79,7 @@ public:
             const LocalAnsatzBasisType& ansatz_basis,
             const XT::Common::Parameter& param = {}) const override final
   {
-    return local_function_->order(param) + test_basis.order(param) + ansatz_basis.order(param);
+    return local_weight_->order(param) + test_basis.order(param) + ansatz_basis.order(param);
   }
 
   using BaseType::evaluate;
@@ -140,23 +98,150 @@ public:
     // evaluate
     test_basis.evaluate(point_in_reference_element, test_basis_values_, param);
     ansatz_basis.evaluate(point_in_reference_element, ansatz_basis_values_, param);
-    const auto function_value = local_function_->evaluate(point_in_reference_element, param);
+    const auto weight = local_weight_->evaluate(point_in_reference_element, param);
     // compute product
     for (size_t ii = 0; ii < rows; ++ii)
       for (size_t jj = 0; jj < cols; ++jj)
-        result[ii][jj] = (function_value * test_basis_values_[ii]) * ansatz_basis_values_[jj];
+        result[ii][jj] = (weight * test_basis_values_[ii]) * ansatz_basis_values_[jj];
   } // ... evaluate(...)
 
 private:
-  const XT::Common::ConstStorageProvider<GridFunctionType> inducing_function_;
-  std::unique_ptr<typename GridFunctionType::LocalFunctionType> local_function_;
+  XT::Functions::GridFunction<E, r, r, F> weight_;
+  std::unique_ptr<typename XT::Functions::GridFunction<E, r, r, F>::LocalFunctionType> local_weight_;
   mutable std::vector<typename LocalTestBasisType::RangeType> test_basis_values_;
   mutable std::vector<typename LocalAnsatzBasisType::RangeType> ansatz_basis_values_;
 }; // class LocalElementProductIntegrand
 
 
-/// \todo add LocalIntersectionProductIntegrand
-///
+/**
+ * Given an inducing function f, computes `<f> * phi * psi` for all combinations of phi and psi in the bases, where
+ * `<f>` denotes the average of f evaluated on the inside and evaluated on the outside.
+ *
+ * \note Note that f can also be given as a scalar value or omitted.
+ */
+template <class I, size_t r = 1, class TR = double, class F = double, class AR = TR>
+class LocalIntersectionProductIntegrand : public LocalQuaternaryIntersectionIntegrandInterface<I, r, 1, TR, F, r, 1, AR>
+{
+  using ThisType = LocalIntersectionProductIntegrand;
+  using BaseType = LocalQuaternaryIntersectionIntegrandInterface<I, r, 1, TR, F, r, 1, AR>;
+
+public:
+  using BaseType::d;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::IntersectionType;
+  using typename BaseType::LocalAnsatzBasisType;
+  using typename BaseType::LocalTestBasisType;
+
+  using GridFunctionType = XT::Functions::GridFunctionInterface<E, r, r, F>;
+
+  LocalIntersectionProductIntegrand(XT::Functions::GridFunction<E, r, r, F> weight = {1.})
+    : BaseType()
+    , weight_(weight)
+    , local_weight_in_(weight_.local_function())
+    , local_weight_out_(weight_.local_function())
+  {}
+
+  LocalIntersectionProductIntegrand(const ThisType& other)
+    : BaseType(other.parameter_type())
+    , weight_(other.weight_)
+    , local_weight_in_(weight_.local_function())
+    , local_weight_out_(weight_.local_function())
+  {}
+
+  LocalIntersectionProductIntegrand(ThisType&& source) = default;
+
+  std::unique_ptr<BaseType> copy() const override final
+  {
+    return std::make_unique<ThisType>(*this);
+  }
+
+protected:
+  void post_bind(const IntersectionType& intersct) override final
+  {
+    auto inside_element = intersct.inside();
+    local_weight_in_->bind(inside_element);
+    if (intersct.neighbor()) {
+      local_weight_out_->bind(intersct.outside());
+    } else
+      local_weight_out_->bind(intersct.inside());
+  } // ... post_bind(...)
+
+public:
+  int order(const LocalTestBasisType& test_basis_inside,
+            const LocalAnsatzBasisType& ansatz_basis_inside,
+            const LocalTestBasisType& test_basis_outside,
+            const LocalAnsatzBasisType& ansatz_basis_outside,
+            const XT::Common::Parameter& param = {}) const override final
+  {
+    return std::max(local_weight_in_->order(param), local_weight_out_->order(param))
+           + std::max(test_basis_inside.order(param), test_basis_outside.order(param))
+           + std::max(ansatz_basis_inside.order(param), ansatz_basis_outside.order(param));
+  }
+
+  using BaseType::evaluate;
+
+  void evaluate(const LocalTestBasisType& test_basis_inside,
+                const LocalAnsatzBasisType& ansatz_basis_inside,
+                const LocalTestBasisType& test_basis_outside,
+                const LocalAnsatzBasisType& ansatz_basis_outside,
+                const DomainType& point_in_reference_intersection,
+                DynamicMatrix<F>& result_in_in,
+                DynamicMatrix<F>& result_in_out,
+                DynamicMatrix<F>& result_out_in,
+                DynamicMatrix<F>& result_out_out,
+                const XT::Common::Parameter& param = {}) const override final
+  {
+    // prepare sotrage
+    const size_t rows_in = test_basis_inside.size(param);
+    const size_t rows_out = test_basis_outside.size(param);
+    const size_t cols_in = ansatz_basis_inside.size(param);
+    const size_t cols_out = ansatz_basis_outside.size(param);
+    const auto ensure_size = [](auto& m, const auto& rws, const auto& cls) {
+      if (m.rows() < rws || m.cols() < cls)
+        m.resize(rws, cls);
+    };
+    ensure_size(result_in_in, rows_in, cols_in);
+    ensure_size(result_in_out, rows_in, cols_out);
+    ensure_size(result_out_in, rows_out, cols_in);
+    ensure_size(result_out_out, rows_out, cols_out);
+    // evaluate
+    const auto point_in_inside_reference_element =
+        this->intersection().geometryInInside().global(point_in_reference_intersection);
+    test_basis_inside.evaluate(point_in_inside_reference_element, test_basis_in_values_, param);
+    ansatz_basis_inside.evaluate(point_in_inside_reference_element, ansatz_basis_in_values_, param);
+    const auto weight_in = local_weight_in_->evaluate(point_in_inside_reference_element, param);
+    const auto point_in_outside_reference_element =
+        this->intersection().geometryInOutside().global(point_in_reference_intersection);
+    test_basis_outside.evaluate(point_in_outside_reference_element, test_basis_out_values_, param);
+    ansatz_basis_outside.evaluate(point_in_outside_reference_element, ansatz_basis_out_values_, param);
+    const auto weight_out = local_weight_out_->evaluate(point_in_outside_reference_element, param);
+    // compute integrand
+    const auto average_function_value = (weight_in + weight_out) * 0.5;
+    for (size_t ii = 0; ii < rows_in; ++ii) {
+      for (size_t jj = 0; jj < cols_in; ++jj)
+        result_in_in[ii][jj] = (average_function_value * ansatz_basis_in_values_[jj]) * test_basis_in_values_[ii];
+      for (size_t jj = 0; jj < cols_out; ++jj)
+        result_in_out[ii][jj] = (average_function_value * ansatz_basis_out_values_[jj]) * test_basis_in_values_[ii];
+    }
+    for (size_t ii = 0; ii < rows_out; ++ii) {
+      for (size_t jj = 0; jj < cols_in; ++jj)
+        result_out_in[ii][jj] = (average_function_value * ansatz_basis_in_values_[jj]) * test_basis_out_values_[ii];
+      for (size_t jj = 0; jj < cols_out; ++jj)
+        result_out_out[ii][jj] = (average_function_value * ansatz_basis_out_values_[jj]) * test_basis_out_values_[ii];
+    }
+  } // ... evaluate(...)
+
+private:
+  XT::Functions::GridFunction<E, r, r, F> weight_;
+  std::unique_ptr<typename XT::Functions::GridFunction<E, r, r, F>::LocalFunctionType> local_weight_in_;
+  std::unique_ptr<typename XT::Functions::GridFunction<E, r, r, F>::LocalFunctionType> local_weight_out_;
+  mutable std::vector<typename LocalTestBasisType::RangeType> test_basis_in_values_;
+  mutable std::vector<typename LocalTestBasisType::RangeType> test_basis_out_values_;
+  mutable std::vector<typename LocalAnsatzBasisType::RangeType> ansatz_basis_in_values_;
+  mutable std::vector<typename LocalAnsatzBasisType::RangeType> ansatz_basis_out_values_;
+}; // class LocalIntersectionProductIntegrand
+
 
 } // namespace GDT
 } // namespace Dune
diff --git a/dune/gdt/local/integrands/symmetrized-laplace.hh b/dune/gdt/local/integrands/symmetrized-laplace.hh
new file mode 100644
index 0000000000000000000000000000000000000000..97792ac1f7762b09dbbebbc889481cd0374520f2
--- /dev/null
+++ b/dune/gdt/local/integrands/symmetrized-laplace.hh
@@ -0,0 +1,136 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner  (2014, 2016 - 2018)
+
+#ifndef DUNE_GDT_LOCAL_INTEGRANDS_SYMMETRIZED_LAPLACE_HH
+#define DUNE_GDT_LOCAL_INTEGRANDS_SYMMETRIZED_LAPLACE_HH
+
+#include <dune/xt/common/memory.hh>
+#include <dune/xt/la/container/eye-matrix.hh>
+#include <dune/xt/functions/base/function-as-grid-function.hh>
+#include <dune/xt/functions/constant.hh>
+#include <dune/xt/functions/interfaces/grid-function.hh>
+
+#include "interfaces.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+/**
+ * Given an inducing scalar function lambda computes
+ * `lambda(x) * 1/2*(\nabla phi(x) + (\nabla phi(x))^T) : \nabla psi(x)` for all combinations of phi in the ansatz and
+ * psi in the test basis. Here, ':' denotes the (matrix) scalar product.
+ */
+template <class E, class F = double>
+class LocalSymmetrizedLaplaceIntegrand
+  : public LocalBinaryElementIntegrandInterface<E, E::dimension, 1, F, F, E::dimension, 1, F>
+{
+
+  using BaseType = LocalBinaryElementIntegrandInterface<E, E::dimension, 1, F, F, E::dimension, 1, F>;
+  using ThisType = LocalSymmetrizedLaplaceIntegrand;
+
+public:
+  using BaseType::d;
+  static constexpr size_t r = d;
+  using typename BaseType::DomainType;
+  using typename BaseType::ElementType;
+  using typename BaseType::LocalAnsatzBasisType;
+  using typename BaseType::LocalTestBasisType;
+
+  using DiffusionFactorType = XT::Functions::GridFunctionInterface<E, 1, 1, F>;
+
+  LocalSymmetrizedLaplaceIntegrand(const F& diffusion_factor = F(1))
+    : BaseType()
+    , diffusion_factor_(new XT::Functions::FunctionAsGridFunctionWrapper<E, 1, 1, F>(
+          new XT::Functions::ConstantFunction<d, 1, 1, F>(diffusion_factor)))
+    , local_diffusion_factor_(diffusion_factor_.access().local_function())
+  {}
+
+  LocalSymmetrizedLaplaceIntegrand(const XT::Functions::FunctionInterface<d, 1, 1, F>& diffusion_factor)
+    : BaseType(diffusion_factor.parameter_type())
+    , diffusion_factor_(new XT::Functions::FunctionAsGridFunctionWrapper<E, 1, 1, F>(diffusion_factor))
+    , local_diffusion_factor_(diffusion_factor_.access().local_function())
+  {}
+
+  LocalSymmetrizedLaplaceIntegrand(const DiffusionFactorType& diffusion_factor)
+    : BaseType(diffusion_factor.parameter_type())
+    , diffusion_factor_(diffusion_factor)
+    , local_diffusion_factor_(diffusion_factor_.access().local_function())
+  {}
+
+  LocalSymmetrizedLaplaceIntegrand(const ThisType& other)
+    : BaseType(other.parameter_type())
+    , diffusion_factor_(other.diffusion_factor_)
+    , local_diffusion_factor_(diffusion_factor_.access().local_function())
+  {}
+
+  LocalSymmetrizedLaplaceIntegrand(ThisType&& source) = default;
+
+  std::unique_ptr<BaseType> copy() const override final
+  {
+    return std::make_unique<ThisType>(*this);
+  }
+
+protected:
+  void post_bind(const ElementType& ele) override
+  {
+    local_diffusion_factor_->bind(ele);
+  }
+
+public:
+  int order(const LocalTestBasisType& test_basis,
+            const LocalAnsatzBasisType& ansatz_basis,
+            const XT::Common::Parameter& param = {}) const override final
+  {
+    return local_diffusion_factor_->order(param) + test_basis.order(param) + ansatz_basis.order(param);
+  }
+
+  void evaluate(const LocalTestBasisType& test_basis,
+                const LocalAnsatzBasisType& ansatz_basis,
+                const DomainType& point_in_reference_element,
+                DynamicMatrix<F>& result,
+                const XT::Common::Parameter& param = {}) const override final
+  {
+    // prepare storage
+    const size_t rows = test_basis.size(param);
+    const size_t cols = ansatz_basis.size(param);
+    if (result.rows() < rows || result.cols() < cols)
+      result.resize(rows, cols);
+    result *= 0;
+    // evaluate
+    test_basis.jacobians(point_in_reference_element, test_basis_grads_, param);
+    ansatz_basis.jacobians(point_in_reference_element, ansatz_basis_grads_, param);
+    const auto diffusion = local_diffusion_factor_->evaluate(point_in_reference_element, param);
+    symmetric_ansatz_basis_grads_.resize(cols);
+    for (size_t jj = 0; jj < cols; ++jj)
+      for (size_t rr = 0; rr < r; ++rr)
+        for (size_t cc = 0; cc < d; ++cc)
+          symmetric_ansatz_basis_grads_[jj][rr][cc] =
+              0.5 * (ansatz_basis_grads_[jj][rr][cc] + ansatz_basis_grads_[jj][cc][rr]);
+    // compute elliptic evaluation
+    for (size_t ii = 0; ii < rows; ++ii)
+      for (size_t jj = 0; jj < cols; ++jj)
+        for (size_t rr = 0; rr < r; ++rr)
+          for (size_t cc = 0; cc < d; ++cc)
+            result[ii][jj] += symmetric_ansatz_basis_grads_[jj][rr][cc] * test_basis_grads_[ii][rr][cc] * diffusion;
+  } // ... evaluate(...)
+
+private:
+  const XT::Common::ConstStorageProvider<DiffusionFactorType> diffusion_factor_;
+  std::unique_ptr<typename DiffusionFactorType::LocalFunctionType> local_diffusion_factor_;
+  mutable std::vector<typename LocalTestBasisType::DerivativeRangeType> test_basis_grads_;
+  mutable std::vector<typename LocalAnsatzBasisType::DerivativeRangeType> ansatz_basis_grads_;
+  mutable std::vector<typename LocalAnsatzBasisType::DerivativeRangeType> symmetric_ansatz_basis_grads_;
+}; // class LocalSymmetrizedLaplaceIntegrand
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_LOCAL_INTEGRANDS_SYMMETRIZED_LAPLACE_HH
diff --git a/dune/gdt/local/numerical-fluxes/engquist-osher.hh b/dune/gdt/local/numerical-fluxes/engquist-osher.hh
index 29f5f18b0d11bb09fcf37e14fe78811d8be85df4..c308728f9db67f1a91b97daf29a50094b8226eba 100644
--- a/dune/gdt/local/numerical-fluxes/engquist-osher.hh
+++ b/dune/gdt/local/numerical-fluxes/engquist-osher.hh
@@ -23,32 +23,39 @@ namespace Dune {
 namespace GDT {
 
 
-template <size_t d, size_t m = 1, class R = double>
-class NumericalEngquistOsherFlux : public internal::ThisNumericalFluxIsNotAvailableForTheseDimensions<d, m, R>
+template <class I, size_t d, size_t m = 1, class R = double>
+class NumericalEngquistOsherFlux : public internal::ThisNumericalFluxIsNotAvailableForTheseDimensions<I, d, m, R>
 {
 public:
   template <class... Args>
   explicit NumericalEngquistOsherFlux(Args&&... /*args*/)
-    : internal::ThisNumericalFluxIsNotAvailableForTheseDimensions<d, m, R>()
+    : internal::ThisNumericalFluxIsNotAvailableForTheseDimensions<I, d, m, R>()
   {}
 };
 
-template <size_t d, class R>
-class NumericalEngquistOsherFlux<d, 1, R> : public NumericalFluxInterface<d, 1, R>
+template <class I, size_t d, class R>
+class NumericalEngquistOsherFlux<I, d, 1, R> : public NumericalFluxInterface<I, d, 1, R>
 {
   static const constexpr size_t m = 1;
-  using ThisType = NumericalEngquistOsherFlux<d, m, R>;
-  using BaseType = NumericalFluxInterface<d, m, R>;
+  using ThisType = NumericalEngquistOsherFlux;
+  using BaseType = NumericalFluxInterface<I, d, m, R>;
 
 public:
   using typename BaseType::FluxType;
+  using typename BaseType::LocalFluxType;
+  using typename BaseType::LocalIntersectionCoords;
   using typename BaseType::PhysicalDomainType;
-  using typename BaseType::StateRangeType;
+  using typename BaseType::StateType;
+  using typename BaseType::XIndependentFluxType;
 
   NumericalEngquistOsherFlux(const FluxType& flx)
     : BaseType(flx)
   {}
 
+  NumericalEngquistOsherFlux(const XIndependentFluxType& flx)
+    : BaseType(flx)
+  {}
+
   NumericalEngquistOsherFlux(const ThisType& other) = default;
 
   std::unique_ptr<BaseType> copy() const override final
@@ -58,38 +65,61 @@ public:
 
   using BaseType::apply;
 
-  StateRangeType apply(const StateRangeType& u,
-                       const StateRangeType& v,
-                       const PhysicalDomainType& n,
-                       const XT::Common::Parameter& param = {}) const override final
+  StateType apply(const LocalIntersectionCoords& x_in_intersection_coords,
+                  const StateType& u,
+                  const StateType& v,
+                  const PhysicalDomainType& n,
+                  const XT::Common::Parameter& param = {}) const override final
   {
-    auto integrate_f = [&](const auto& s, const std::function<double(const R&, const R&)>& min_max) {
+    this->compute_entity_coords(x_in_intersection_coords);
+    auto integrate_f = [&](const LocalFluxType& local_flux,
+                           const auto& x,
+                           const auto& s,
+                           const std::function<double(const R&, const R&)>& min_max) {
       if (!(s[0] > 0.))
         return 0.;
       double ret = 0.;
       const OneDGrid state_grid(1, 0., s[0]);
       const auto state_interval = *state_grid.leafGridView().template begin<0>();
-      for (const auto& quadrature_point :
-           QuadratureRules<R, 1>::rule(state_interval.type(), this->flux().order(param))) {
+      for (const auto& quadrature_point : QuadratureRules<R, 1>::rule(state_interval.type(), local_flux.order(param))) {
         const auto local_uu = quadrature_point.position();
         const auto uu = state_interval.geometry().global(local_uu);
-        const auto df = this->flux().jacobian(uu, param);
+        const auto df = local_flux.jacobian(x, uu, param);
         ret += state_interval.geometry().integrationElement(local_uu) * quadrature_point.weight() * min_max(n * df, 0.);
       }
       return ret;
     };
-    return (this->flux().evaluate(0., param) * n)
-           + integrate_f(u, [](const double& a, const double& b) { return std::max(a, b); })
-           + integrate_f(v, [](const double& a, const double& b) { return std::min(a, b); });
+    return (local_flux_inside_->evaluate(x_in_inside_coords_, 0., param) * n)
+           + integrate_f(*local_flux_inside_,
+                         x_in_inside_coords_,
+                         u,
+                         [](const double& a, const double& b) { return std::max(a, b); })
+           + integrate_f(this->intersection().neighbor() ? *local_flux_outside_ : *local_flux_inside_,
+                         x_in_outside_coords_,
+                         v,
+                         [](const double& a, const double& b) { return std::min(a, b); });
   }
+
+private:
+  using BaseType::local_flux_inside_;
+  using BaseType::local_flux_outside_;
+  using BaseType::x_in_inside_coords_;
+  using BaseType::x_in_outside_coords_;
 }; // class NumericalEngquistOsherFlux
 
 
-template <size_t d, size_t m, class R>
-NumericalEngquistOsherFlux<d, m, R>
+template <class I, size_t d, size_t m, class R>
+NumericalEngquistOsherFlux<I, d, m, R>
+make_numerical_engquist_osher_flux(const XT::Functions::FluxFunctionInterface<I, m, d, m, R>& flux)
+{
+  return NumericalEngquistOsherFlux<I, d, m, R>(flux);
+}
+
+template <class I, size_t d, size_t m, class R>
+NumericalEngquistOsherFlux<I, d, m, R>
 make_numerical_engquist_osher_flux(const XT::Functions::FunctionInterface<m, d, m, R>& flux)
 {
-  return NumericalEngquistOsherFlux<d, m, R>(flux);
+  return NumericalEngquistOsherFlux<I, d, m, R>(flux);
 }
 
 
diff --git a/dune/gdt/local/numerical-fluxes/generic.hh b/dune/gdt/local/numerical-fluxes/generic.hh
index faf584fa59d42f57cbf3885979074722095fa0a6..519e46f4d6bef0a0dfccf57d053e4e9000980795 100644
--- a/dune/gdt/local/numerical-fluxes/generic.hh
+++ b/dune/gdt/local/numerical-fluxes/generic.hh
@@ -22,25 +22,38 @@ namespace GDT {
 /**
  * \brief Implementation of NumericalFluxInterface for a given lambda expression.
  */
-template <size_t d, size_t m = 1, class R = double>
-class GenericNumericalFlux : public NumericalFluxInterface<d, m, R>
+template <class I, size_t d, size_t m = 1, class R = double>
+class GenericNumericalFlux : public NumericalFluxInterface<I, d, m, R>
 {
-  using ThisType = GenericNumericalFlux<d, m, R>;
-  using BaseType = NumericalFluxInterface<d, m, R>;
+  using ThisType = GenericNumericalFlux;
+  using BaseType = NumericalFluxInterface<I, d, m, R>;
 
 public:
   using typename BaseType::FluxType;
+  using typename BaseType::LocalIntersectionCoords;
   using typename BaseType::PhysicalDomainType;
-  using typename BaseType::StateRangeType;
+  using typename BaseType::StateType;
+  using typename BaseType::XIndependentFluxType;
 
-  using GenericFunctionType = std::function<StateRangeType(
-      const StateRangeType&, const StateRangeType&, const PhysicalDomainType&, const XT::Common::Parameter&)>;
+  using GenericFunctionType = std::function<StateType(const I&,
+                                                      const LocalIntersectionCoords&,
+                                                      const StateType&,
+                                                      const StateType&,
+                                                      const PhysicalDomainType&,
+                                                      const XT::Common::Parameter&)>;
 
   GenericNumericalFlux(const FluxType& flx, GenericFunctionType func, const XT::Common::ParameterType& param_type = {})
     : BaseType(flx, param_type)
     , numerical_flux_(func)
   {}
 
+  GenericNumericalFlux(const XIndependentFluxType& flx,
+                       GenericFunctionType func,
+                       const XT::Common::ParameterType& param_type = {})
+    : BaseType(flx, param_type)
+    , numerical_flux_(func)
+  {}
+
   std::unique_ptr<BaseType> copy() const override final
   {
     return std::make_unique<ThisType>(*this);
@@ -48,12 +61,13 @@ public:
 
   using BaseType::apply;
 
-  StateRangeType apply(const StateRangeType& u,
-                       const StateRangeType& v,
-                       const PhysicalDomainType& n,
-                       const XT::Common::Parameter& param = {}) const override final
+  StateType apply(const LocalIntersectionCoords& x,
+                  const StateType& u,
+                  const StateType& v,
+                  const PhysicalDomainType& n,
+                  const XT::Common::Parameter& param = {}) const override final
   {
-    return numerical_flux_(u, v, n, this->parse_parameter(param));
+    return numerical_flux_(this->intersection(), x, u, v, n, this->parse_parameter(param));
   }
 
 private:
@@ -61,13 +75,22 @@ private:
 }; // class GenericNumericalFlux
 
 
-template <size_t d, size_t m, class R>
-GenericNumericalFlux<d, m, R>
+template <class I, size_t d, size_t m, class R>
+GenericNumericalFlux<I, d, m, R>
+make_generic_numerical_flux(const XT::Functions::FluxFunctionInterface<I, m, d, m, R>& flux,
+                            typename GenericNumericalFlux<I, d, m, R>::GenericFunctionType func,
+                            const XT::Common::ParameterType& param_type = {})
+{
+  return GenericNumericalFlux<I, d, m, R>(flux, func, param_type);
+}
+
+template <class I, size_t d, size_t m, class R>
+GenericNumericalFlux<I, d, m, R>
 make_generic_numerical_flux(const XT::Functions::FunctionInterface<m, d, m, R>& flux,
-                            typename GenericNumericalFlux<d, m, R>::GenericFunctionType func,
+                            typename GenericNumericalFlux<I, d, m, R>::GenericFunctionType func,
                             const XT::Common::ParameterType& param_type = {})
 {
-  return GenericNumericalFlux<d, m, R>(flux, func, param_type);
+  return GenericNumericalFlux<I, d, m, R>(flux, func, param_type);
 }
 
 
diff --git a/dune/gdt/local/numerical-fluxes/interface.hh b/dune/gdt/local/numerical-fluxes/interface.hh
index 136dc3e2e11aab61cd573eff132b8a1cec7c2612..542163b7459ab1bded531eb462cc6501b36e0d90 100644
--- a/dune/gdt/local/numerical-fluxes/interface.hh
+++ b/dune/gdt/local/numerical-fluxes/interface.hh
@@ -15,6 +15,7 @@
 #include <dune/xt/common/memory.hh>
 #include <dune/xt/la/container/vector-interface.hh>
 #include <dune/xt/functions/constant.hh>
+#include <dune/xt/functions/interfaces/flux-function.hh>
 #include <dune/xt/functions/interfaces/function.hh>
 
 #include <dune/gdt/exceptions.hh>
@@ -24,28 +25,63 @@ namespace GDT {
 
 
 /**
- * Given the sought solution of a system of m conservation laws, u: R^d -> R^m, and d flux functions f_s: R^m -> R^m,
- * for 1 <= s <= d (modelled by the flux f: R^m -> R^{d x m}), the purpose of a numerical flux
- * g: R^m x R^m x R^d -> R^m is to approximate f(.) * n, e.g., g(u, u, n) = f(u) * n.
+ * Given the sought solution of a system of m conservation laws, u: R^d -> R^m, and d flux
+ * functions f_s: R^d x R^m -> R^m, for 1 <= s <= d (modelled by the
+ * flux f: R^d x R^m -> R^{d x m}), the purpose of a numerical flux
+ * g: R^m x R^m x R^d -> R^m is to approximate f(.) * n, e.g., g(x, u, u, n) = f(x, u) * n.
  */
-template <size_t d, size_t m = 1, class R = double>
-class NumericalFluxInterface : public XT::Common::ParametricInterface
+template <class Intersection, size_t d, size_t m = 1, class R = double>
+class NumericalFluxInterface
+  : public XT::Grid::IntersectionBoundObject<Intersection>
+  , public XT::Common::ParametricInterface
 {
-  using ThisType = NumericalFluxInterface<d, m, R>;
+  using ThisType = NumericalFluxInterface;
 
 public:
-  using FluxType = XT::Functions::FunctionInterface<m, d, m, R>;
-  using PhysicalDomainType = FieldVector<double, d>;
-  using StateRangeType = typename FluxType::DomainType;
+  using I = Intersection;
+  using E = typename I::Entity;
+  using FluxType = XT::Functions::FluxFunctionInterface<E, m, d, m, R>;
+  using LocalFluxType = typename FluxType::LocalFunctionType;
+  using XIndependentFluxType = XT::Functions::FunctionInterface<m, d, m, R>;
+  using FluxWrapperType = XT::Functions::StateFunctionAsFluxFunctionWrapper<E, m, d, m, R>;
+  using PhysicalDomainType = typename FluxType::DomainType;
+  using LocalIntersectionCoords = FieldVector<typename I::ctype, d - 1>;
+  using StateType = typename FluxType::StateType;
 
   NumericalFluxInterface(const FluxType& flx, const XT::Common::ParameterType& param_type = {})
     : XT::Common::ParametricInterface(param_type + flx.parameter_type())
     , flux_(flx)
+    , local_flux_inside_(flux_.access().local_function())
+    , local_flux_outside_(flux_.access().local_function())
   {}
 
   NumericalFluxInterface(FluxType*&& flx_ptr, const XT::Common::ParameterType& param_type = {})
     : XT::Common::ParametricInterface(param_type + flx_ptr->parameter_type())
     , flux_(flx_ptr)
+    , local_flux_inside_(flux_.access().local_function())
+    , local_flux_outside_(flux_.access().local_function())
+  {}
+
+  NumericalFluxInterface(const XIndependentFluxType& func, const XT::Common::ParameterType& param_type = {})
+    : XT::Common::ParametricInterface(param_type + func.parameter_type())
+    , flux_(new FluxWrapperType(func))
+    , local_flux_inside_(flux_.access().local_function())
+    , local_flux_outside_(flux_.access().local_function())
+  {}
+
+  NumericalFluxInterface(XIndependentFluxType*&& func_ptr, const XT::Common::ParameterType& param_type = {})
+    : XT::Common::ParametricInterface(param_type + func_ptr->parameter_type())
+    , flux_(new FluxWrapperType(func_ptr))
+    , local_flux_inside_(flux_.access().local_function())
+    , local_flux_outside_(flux_.access().local_function())
+  {}
+
+  NumericalFluxInterface(const ThisType& other)
+    : XT::Grid::IntersectionBoundObject<Intersection>(other)
+    , XT::Common::ParametricInterface(other)
+    , flux_(other.flux_)
+    , local_flux_inside_(flux_.access().local_function())
+    , local_flux_outside_(flux_.access().local_function())
   {}
 
   virtual std::unique_ptr<ThisType> copy() const = 0;
@@ -55,45 +91,54 @@ public:
     return false;
   }
 
+  virtual bool x_dependent() const
+  {
+    return flux_.access().x_dependent();
+  }
+
   const FluxType& flux() const
   {
     return flux_.access();
   }
 
-  virtual StateRangeType apply(const StateRangeType& u,
-                               const StateRangeType& v,
-                               const PhysicalDomainType& n,
-                               const XT::Common::Parameter& param = {}) const = 0;
+  virtual StateType apply(const LocalIntersectionCoords& x_in_local_intersection_coords,
+                          const StateType& u,
+                          const StateType& v,
+                          const PhysicalDomainType& n,
+                          const XT::Common::Parameter& param = {}) const = 0;
 
   template <class V>
-  StateRangeType apply(const StateRangeType& u,
-                       const XT::LA::VectorInterface<V>& v,
-                       const PhysicalDomainType& n,
-                       const XT::Common::Parameter& param = {}) const
+  StateType apply(const LocalIntersectionCoords x_in_local_intersection_coords,
+                  const StateType& u,
+                  const XT::LA::VectorInterface<V>& v,
+                  const PhysicalDomainType& n,
+                  const XT::Common::Parameter& param = {}) const
   {
     DUNE_THROW_IF(v.size() != m, Exceptions::numerical_flux_error, "v.size() = " << v.size() << "\n   m = " << m);
     for (size_t ii = 0; ii < m; ++ii)
       v_[ii] = v[ii];
-    return this->apply(u, v_, n, param);
+    return this->apply(x_in_local_intersection_coords, u, v_, n, param);
   }
 
   template <class U>
-  StateRangeType apply(const XT::LA::VectorInterface<U>& u,
-                       const StateRangeType& v,
-                       const PhysicalDomainType& n,
-                       const XT::Common::Parameter& param = {}) const
+  StateType apply(const LocalIntersectionCoords x_in_local_intersection_coords,
+                  const XT::LA::VectorInterface<U>& u,
+                  const StateType& v,
+                  const PhysicalDomainType& n,
+                  const XT::Common::Parameter& param = {}) const
   {
     DUNE_THROW_IF(u.size() != m, Exceptions::numerical_flux_error, "u.size() = " << u.size() << "\n   m = " << m);
     for (size_t ii = 0; ii < m; ++ii)
       u_[ii] = u[ii];
-    return this->apply(u_, v, n, param);
+    return this->apply(x_in_local_intersection_coords, u_, v, n, param);
   }
 
   template <class U, class V>
-  StateRangeType apply(const XT::LA::VectorInterface<U>& u,
-                       const XT::LA::VectorInterface<V>& v,
-                       const PhysicalDomainType& n,
-                       const XT::Common::Parameter& param = {}) const
+  StateType apply(const LocalIntersectionCoords x_in_local_intersection_coords,
+                  const XT::LA::VectorInterface<U>& u,
+                  const XT::LA::VectorInterface<V>& v,
+                  const PhysicalDomainType& n,
+                  const XT::Common::Parameter& param = {}) const
   {
     DUNE_THROW_IF(u.size() != m, Exceptions::numerical_flux_error, "u.size() = " << u.size() << "\n   m = " << m);
     DUNE_THROW_IF(v.size() != m, Exceptions::numerical_flux_error, "v.size() = " << v.size() << "\n   m = " << m);
@@ -101,28 +146,59 @@ public:
       u_[ii] = u[ii];
       v_[ii] = v[ii];
     }
-    return this->apply(u_, v_, n, param);
+    return this->apply(x_in_local_intersection_coords, u_, v_, n, param);
   } // ... apply(...)
 
+  using XT::Grid::IntersectionBoundObject<I>::intersection;
+
 private:
   const XT::Common::ConstStorageProvider<FluxType> flux_;
-  mutable StateRangeType u_;
-  mutable StateRangeType v_;
+  mutable StateType u_;
+  mutable StateType v_;
+
+protected:
+  void post_bind(const I& inter) override
+  {
+    local_flux_inside_->bind(inter.inside());
+    if (inter.neighbor())
+      local_flux_outside_->bind(inter.outside());
+  }
+
+  void compute_entity_coords(const LocalIntersectionCoords& x_in_local_intersection_coords) const
+  {
+    if (this->x_dependent()) {
+      if (!this->is_bound_)
+        DUNE_THROW(Dune::InvalidStateException, "You have to call bind(intersection) before calling this function!");
+      x_in_inside_coords_ =
+          intersection().inside().geometry().local(intersection().geometry().global(x_in_local_intersection_coords));
+      if (intersection().neighbor())
+        x_in_outside_coords_ =
+            intersection().outside().geometry().local(intersection().geometry().global(x_in_local_intersection_coords));
+      else
+        x_in_outside_coords_ = x_in_inside_coords_;
+    }
+  }
+
+  mutable std::unique_ptr<LocalFluxType> local_flux_inside_;
+  mutable std::unique_ptr<LocalFluxType> local_flux_outside_;
+  mutable PhysicalDomainType x_in_inside_coords_;
+  mutable PhysicalDomainType x_in_outside_coords_;
 }; // class NumericalFluxInterface
 
 
 namespace internal {
 
 
-template <size_t d, size_t m = 1, class R = double>
-class ThisNumericalFluxIsNotAvailableForTheseDimensions : public NumericalFluxInterface<d, m, R>
+template <class Intersection, size_t d, size_t m = 1, class R = double>
+class ThisNumericalFluxIsNotAvailableForTheseDimensions : public NumericalFluxInterface<Intersection, d, m, R>
 {
-  using ThisType = ThisNumericalFluxIsNotAvailableForTheseDimensions<d, m, R>;
-  using BaseType = NumericalFluxInterface<d, m, R>;
+  using BaseType = NumericalFluxInterface<Intersection, d, m, R>;
 
 public:
+  using typename BaseType::I;
+  using typename BaseType::LocalIntersectionCoords;
   using typename BaseType::PhysicalDomainType;
-  using typename BaseType::StateRangeType;
+  using typename BaseType::StateType;
 
   template <class... Args>
   explicit ThisNumericalFluxIsNotAvailableForTheseDimensions(Args&&... /*args*/)
@@ -145,13 +221,14 @@ public:
 
   using BaseType::apply;
 
-  StateRangeType apply(const StateRangeType& /*u*/,
-                       const StateRangeType& /*v*/,
-                       const PhysicalDomainType& /*n*/,
-                       const XT::Common::Parameter& /*param*/ = {}) const override final
+  StateType apply(const LocalIntersectionCoords& /*x_in_local_intersection_coords*/,
+                  const StateType& /*u*/,
+                  const StateType& /*v*/,
+                  const PhysicalDomainType& /*n*/,
+                  const XT::Common::Parameter& /*param*/ = {}) const override final
   {
     DUNE_THROW(Exceptions::numerical_flux_error, "d = " << d << "\n   m = " << m);
-    return StateRangeType();
+    return StateType();
   }
 }; // class ThisNumericalFluxIsNotAvailableForTheseDimensions
 
diff --git a/dune/gdt/local/numerical-fluxes/kinetic.hh b/dune/gdt/local/numerical-fluxes/kinetic.hh
new file mode 100644
index 0000000000000000000000000000000000000000..6be4120470c38502e5301f94410b845596039df6
--- /dev/null
+++ b/dune/gdt/local/numerical-fluxes/kinetic.hh
@@ -0,0 +1,151 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 (2018)
+//   René Fritze     (2018)
+
+#ifndef DUNE_GDT_LOCAL_NUMERICAL_FLUXES_KINETIC_HH
+#define DUNE_GDT_LOCAL_NUMERICAL_FLUXES_KINETIC_HH
+
+#include <dune/xt/la/container.hh>
+
+#include <dune/gdt/test/momentmodels/basisfunctions.hh>
+#include <dune/gdt/test/momentmodels/entropyflux.hh>
+
+#include "interface.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+template <class GV, class MomentBasis, class EntropyFluxImp = EntropyBasedFluxFunction<GV, MomentBasis>>
+class NumericalKineticFlux
+  : public NumericalFluxInterface<XT::Grid::extract_intersection_t<GV>,
+                                  MomentBasis::dimFlux,
+                                  MomentBasis::dimRange,
+                                  typename MomentBasis::RangeFieldType>
+{
+  static_assert(std::is_base_of<MomentBasisInterface<typename MomentBasis::D,
+                                                     MomentBasis::d,
+                                                     typename MomentBasis::R,
+                                                     MomentBasis::r,
+                                                     MomentBasis::rC,
+                                                     MomentBasis::d_flux,
+                                                     MomentBasis::entropy>,
+                                MomentBasis>::value,
+                "Basisfunctions have to be derived from MomentBasisInterface");
+  static const size_t d = MomentBasis::dimFlux;
+  static const size_t m = MomentBasis::r;
+  using R = typename MomentBasis::R;
+  using I = XT::Grid::extract_intersection_t<GV>;
+  using ThisType = NumericalKineticFlux;
+  using BaseType = NumericalFluxInterface<I, d, m, R>;
+  using EntropyFluxType = EntropyFluxImp;
+  using SparseMatrixType = typename XT::LA::CommonSparseMatrix<R>;
+
+public:
+  using typename BaseType::FluxType;
+  using typename BaseType::LocalIntersectionCoords;
+  using typename BaseType::PhysicalDomainType;
+  using typename BaseType::StateType;
+  using typename BaseType::XIndependentFluxType;
+
+  NumericalKineticFlux(const FluxType& flx, const MomentBasis& basis)
+    : BaseType(flx)
+    , basis_(basis)
+  {}
+
+  NumericalKineticFlux(const XIndependentFluxType& flx, const MomentBasis& basis)
+    : BaseType(flx)
+    , basis_(basis)
+  {}
+
+  NumericalKineticFlux(const ThisType& other) = default;
+
+  std::unique_ptr<BaseType> copy() const override final
+  {
+    return std::make_unique<ThisType>(*this);
+  }
+
+  using BaseType::apply;
+  using BaseType::flux;
+  using BaseType::intersection;
+
+  StateType apply(const LocalIntersectionCoords& /*x*/,
+                  const StateType& u,
+                  const StateType& v,
+                  const PhysicalDomainType& n,
+                  const XT::Common::Parameter& /*param*/ = {}) const override final
+  {
+    // find direction of unit outer normal (we assume an axis-aligned cube grid)
+    size_t direction = intersection().indexInInside() / 2;
+    if (dynamic_cast<const EntropyFluxType*>(&flux()) != nullptr) {
+      auto ret = dynamic_cast<const EntropyFluxType*>(&flux())->evaluate_kinetic_flux(
+          intersection().inside(),
+          intersection().neighbor() ? intersection().outside() : intersection().inside(),
+          u,
+          v,
+          n,
+          direction);
+      for (auto&& entry : ret)
+        if (std::isnan(entry) || std::isinf(entry))
+          DUNE_THROW(Dune::MathError, "NaN or inf in kinetic flux");
+      return ret;
+    } else {
+      static const auto flux_matrices = initialize_flux_matrices(basis_);
+      StateType ret(0);
+      auto tmp_vec = ret;
+      const auto& inner_flux_matrix = flux_matrices[direction][n[direction] > 0 ? 1 : 0];
+      const auto& outer_flux_matrix = flux_matrices[direction][n[direction] > 0 ? 0 : 1];
+      inner_flux_matrix.mv(u, tmp_vec);
+      outer_flux_matrix.mv(v, ret);
+      ret += tmp_vec;
+      ret *= n[direction];
+      return ret;
+    }
+  }
+
+private:
+  static FieldVector<FieldVector<SparseMatrixType, 2>, d> initialize_flux_matrices(const MomentBasis& basis)
+  {
+    // calculate < v_i b b^T >_- M^{-1} and < v_i b b^T >_+ M^{-1}
+    const auto flux_matrices_dense = basis.kinetic_flux_matrices();
+    FieldVector<FieldVector<SparseMatrixType, 2>, d> flux_matrices(
+        FieldVector<SparseMatrixType, 2>(SparseMatrixType(m, m, size_t(0))));
+    for (size_t dd = 0; dd < d; ++dd)
+      for (size_t kk = 0; kk < 2; ++kk)
+        flux_matrices[dd][kk] = flux_matrices_dense[dd][kk];
+    return flux_matrices;
+  }
+
+  const MomentBasis& basis_;
+}; // class NumericalKineticFlux
+
+
+template <class I, class MomentBasis>
+NumericalKineticFlux<I, MomentBasis> make_numerical_kinetic_flux(
+    const XT::Functions::
+        FluxFunctionInterface<I, MomentBasis::r, MomentBasis::dimFlux, MomentBasis::r, typename MomentBasis::R>& flux,
+    const MomentBasis& basis)
+{
+  return NumericalKineticFlux<I, MomentBasis>(flux, basis);
+}
+
+template <class I, class MomentBasis>
+NumericalKineticFlux<I, MomentBasis> make_numerical_kinetic_flux(
+    const XT::Functions::
+        FunctionInterface<MomentBasis::r, MomentBasis::dimFlux, MomentBasis::r, typename MomentBasis::R>& flux,
+    const MomentBasis& basis)
+{
+  return NumericalKineticFlux<I, MomentBasis>(flux, basis);
+}
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_LOCAL_NUMERICAL_FLUXES_KINETIC_HH
diff --git a/dune/gdt/local/numerical-fluxes/lax-friedrichs.hh b/dune/gdt/local/numerical-fluxes/lax-friedrichs.hh
index 3961767319a97ed80ed6ec258a2c035ada5752e8..36febbe080e0901bf41fa8d21d6ec7f366f146fa 100644
--- a/dune/gdt/local/numerical-fluxes/lax-friedrichs.hh
+++ b/dune/gdt/local/numerical-fluxes/lax-friedrichs.hh
@@ -19,31 +19,35 @@ namespace Dune {
 namespace GDT {
 
 
-template <size_t d, size_t m = 1, class R = double>
-class NumericalLaxFriedrichsFlux : public internal::ThisNumericalFluxIsNotAvailableForTheseDimensions<d, m, R>
+template <class I, size_t d, size_t m, class R = double>
+class NumericalLaxFriedrichsFlux : public NumericalFluxInterface<I, d, m, R>
 {
-public:
-  template <class... Args>
-  explicit NumericalLaxFriedrichsFlux(Args&&... /*args*/)
-    : internal::ThisNumericalFluxIsNotAvailableForTheseDimensions<d, m, R>()
-  {}
-};
-
-template <size_t d, class R>
-class NumericalLaxFriedrichsFlux<d, 1, R> : public NumericalFluxInterface<d, 1, R>
-{
-  static const constexpr size_t m = 1;
-  using ThisType = NumericalLaxFriedrichsFlux<d, m, R>;
-  using BaseType = NumericalFluxInterface<d, m, R>;
+  using ThisType = NumericalLaxFriedrichsFlux;
+  using BaseType = NumericalFluxInterface<I, d, m, R>;
 
 public:
   using typename BaseType::FluxType;
+  using typename BaseType::LocalIntersectionCoords;
   using typename BaseType::PhysicalDomainType;
-  using typename BaseType::StateRangeType;
+  using typename BaseType::StateType;
+  using typename BaseType::XIndependentFluxType;
+  using FluxJacobianType = XT::Common::FieldVector<XT::Common::FieldMatrix<R, m, m>, d>;
 
-  NumericalLaxFriedrichsFlux(const FluxType& flx)
+  NumericalLaxFriedrichsFlux(const FluxType& flx, const double lambda = 0.)
     : BaseType(flx)
-  {}
+    , lambda_(lambda)
+  {
+    if (XT::Common::is_zero(lambda_) && m != 1)
+      DUNE_THROW(Dune::NotImplemented, "Not yet implemented for m > 1 if lambda is not provided!");
+  }
+
+  NumericalLaxFriedrichsFlux(const XIndependentFluxType& func, const double lambda = 0.)
+    : BaseType(func)
+    , lambda_(lambda)
+  {
+    if (XT::Common::is_zero(lambda_) && m != 1)
+      DUNE_THROW(Dune::NotImplemented, "Not yet implemented for m > 1 if lambda is not provided!");
+  }
 
   NumericalLaxFriedrichsFlux(const ThisType& other) = default;
 
@@ -54,23 +58,55 @@ public:
 
   using BaseType::apply;
 
-  StateRangeType apply(const StateRangeType& u,
-                       const StateRangeType& v,
-                       const PhysicalDomainType& n,
-                       const XT::Common::Parameter& param = {}) const override final
+  StateType apply(const LocalIntersectionCoords& x,
+                  const StateType& u,
+                  const StateType& v,
+                  const PhysicalDomainType& n,
+                  const XT::Common::Parameter& param = {}) const override final
   {
-    const auto lambda =
-        1. / std::max(this->flux().jacobian(u, param).infinity_norm(), this->flux().jacobian(v, param).infinity_norm());
-    return 0.5 * ((this->flux().evaluate(u, param) + this->flux().evaluate(v, param)) * n) + 0.5 * ((u - v) / lambda);
+    // prepare
+    this->compute_entity_coords(x);
+    // evaluate
+    R lambda = lambda_;
+    if (XT::Common::is_zero(lambda)) {
+      const auto df_u = local_flux_inside_->jacobian(x_in_inside_coords_, u, param);
+      const auto df_v = local_flux_outside_->jacobian(x_in_outside_coords_, v, param);
+      for (size_t dd = 0; dd < d; ++dd) {
+        lambda = std::max(lambda, df_u[dd].infinity_norm());
+        lambda = std::max(lambda, df_v[dd].infinity_norm());
+      }
+      lambda = 1. / lambda;
+    }
+    const auto f_u = local_flux_inside_->evaluate(x_in_inside_coords_, u, param);
+    const auto f_v = local_flux_outside_->evaluate(x_in_outside_coords_, v, param);
+    StateType ret(0.);
+    for (size_t dd = 0; dd < d; ++dd)
+      ret += (f_u[dd] + f_v[dd]) * (n[dd] * 0.5);
+    ret += (u - v) * (0.5 / lambda);
+    return ret;
   }
+
+private:
+  using BaseType::local_flux_inside_;
+  using BaseType::local_flux_outside_;
+  using BaseType::x_in_inside_coords_;
+  using BaseType::x_in_outside_coords_;
+  const double lambda_;
 }; // class NumericalLaxFriedrichsFlux
 
 
-template <size_t d, size_t m, class R>
-NumericalLaxFriedrichsFlux<d, m, R>
+template <class I, size_t d, size_t m, class R>
+NumericalLaxFriedrichsFlux<I, d, m, R>
+make_numerical_lax_friedrichs_flux(const XT::Functions::FluxFunctionInterface<I, m, d, m, R>& flux)
+{
+  return NumericalLaxFriedrichsFlux<I, d, m, R>(flux);
+}
+
+template <class I, size_t d, size_t m, class R>
+NumericalLaxFriedrichsFlux<I, d, m, R>
 make_numerical_lax_friedrichs_flux(const XT::Functions::FunctionInterface<m, d, m, R>& flux)
 {
-  return NumericalLaxFriedrichsFlux<d, m, R>(flux);
+  return NumericalLaxFriedrichsFlux<I, d, m, R>(flux);
 }
 
 
diff --git a/dune/gdt/local/numerical-fluxes/upwind.hh b/dune/gdt/local/numerical-fluxes/upwind.hh
index d8d0961a0449b131aef36804587728a307825979..b7519530b00775185649fbcfc4e77dbb526bbe1e 100644
--- a/dune/gdt/local/numerical-fluxes/upwind.hh
+++ b/dune/gdt/local/numerical-fluxes/upwind.hh
@@ -17,32 +17,38 @@ namespace Dune {
 namespace GDT {
 
 
-template <size_t d, size_t m = 1, class R = double>
-class NumericalUpwindFlux : public internal::ThisNumericalFluxIsNotAvailableForTheseDimensions<d, m, R>
+template <class I, size_t d, size_t m = 1, class R = double>
+class NumericalUpwindFlux : public internal::ThisNumericalFluxIsNotAvailableForTheseDimensions<I, d, m, R>
 {
 public:
   template <class... Args>
   explicit NumericalUpwindFlux(Args&&... /*args*/)
-    : internal::ThisNumericalFluxIsNotAvailableForTheseDimensions<d, m, R>()
+    : internal::ThisNumericalFluxIsNotAvailableForTheseDimensions<I, d, m, R>()
   {}
 };
 
-template <size_t d, class R>
-class NumericalUpwindFlux<d, 1, R> : public NumericalFluxInterface<d, 1, R>
+template <class I, size_t d, class R>
+class NumericalUpwindFlux<I, d, 1, R> : public NumericalFluxInterface<I, d, 1, R>
 {
   static const constexpr size_t m = 1;
-  using ThisType = NumericalUpwindFlux<d, m, R>;
-  using BaseType = NumericalFluxInterface<d, m, R>;
+  using ThisType = NumericalUpwindFlux;
+  using BaseType = NumericalFluxInterface<I, d, m, R>;
 
 public:
   using typename BaseType::FluxType;
+  using typename BaseType::LocalIntersectionCoords;
   using typename BaseType::PhysicalDomainType;
-  using typename BaseType::StateRangeType;
+  using typename BaseType::StateType;
+  using typename BaseType::XIndependentFluxType;
 
   NumericalUpwindFlux(const FluxType& flx)
     : BaseType(flx)
   {}
 
+  NumericalUpwindFlux(const XIndependentFluxType& func)
+    : BaseType(func)
+  {}
+
   NumericalUpwindFlux(const ThisType& other) = default;
 
   std::unique_ptr<BaseType> copy() const override final
@@ -52,24 +58,39 @@ public:
 
   using BaseType::apply;
 
-  StateRangeType apply(const StateRangeType& u,
-                       const StateRangeType& v,
-                       const PhysicalDomainType& n,
-                       const XT::Common::Parameter& param = {}) const override final
+  StateType apply(const LocalIntersectionCoords& x,
+                  const StateType& u,
+                  const StateType& v,
+                  const PhysicalDomainType& n,
+                  const XT::Common::Parameter& param = {}) const override final
   {
-    const auto df = this->flux().jacobian((u + v) / 2., param);
-    if ((n * df) > 0)
-      return this->flux().evaluate(u, param) * n;
+    this->compute_entity_coords(x);
+    const auto df = local_flux_inside_->jacobian(x_in_inside_coords_, (u + v) / 2., param);
+    if (n * df > 0)
+      return local_flux_inside_->evaluate(x_in_inside_coords_, u, param) * n;
     else
-      return this->flux().evaluate(v, param) * n;
+      return local_flux_outside_->evaluate(x_in_outside_coords_, v, param) * n;
   }
+
+private:
+  using BaseType::local_flux_inside_;
+  using BaseType::local_flux_outside_;
+  using BaseType::x_in_inside_coords_;
+  using BaseType::x_in_outside_coords_;
 }; // class NumericalUpwindFlux
 
 
-template <size_t d, size_t m, class R>
-NumericalUpwindFlux<d, m, R> make_numerical_upwind_flux(const XT::Functions::FunctionInterface<m, d, m, R>& flux)
+template <class I, size_t d, size_t m, class R>
+NumericalUpwindFlux<I, d, m, R>
+make_numerical_upwind_flux(const XT::Functions::FluxFunctionInterface<I, m, d, m, R>& flux)
+{
+  return NumericalUpwindFlux<I, d, m, R>(flux);
+}
+
+template <class I, size_t d, size_t m, class R>
+NumericalUpwindFlux<I, d, m, R> make_numerical_upwind_flux(const XT::Functions::FunctionInterface<m, d, m, R>& flux)
 {
-  return NumericalUpwindFlux<d, m, R>(flux);
+  return NumericalUpwindFlux<I, d, m, R>(flux);
 }
 
 
diff --git a/dune/gdt/local/numerical-fluxes/vijayasundaram.hh b/dune/gdt/local/numerical-fluxes/vijayasundaram.hh
index 770fa292f3a3c2690a44a4327eac9d97613b7984..ffe41e0a4db95bd85b5734afdcab8156e48f5031 100644
--- a/dune/gdt/local/numerical-fluxes/vijayasundaram.hh
+++ b/dune/gdt/local/numerical-fluxes/vijayasundaram.hh
@@ -24,39 +24,72 @@ namespace Dune {
 namespace GDT {
 
 
-template <size_t d, size_t m = 1, class R = double>
-class NumericalVijayasundaramFlux : public NumericalFluxInterface<d, m, R>
+template <class I, size_t d, size_t m = 1, class R = double>
+class NumericalVijayasundaramFlux : public NumericalFluxInterface<I, d, m, R>
 {
-  using ThisType = NumericalVijayasundaramFlux<d, m, R>;
-  using BaseType = NumericalFluxInterface<d, m, R>;
+  using ThisType = NumericalVijayasundaramFlux;
+  using BaseType = NumericalFluxInterface<I, d, m, R>;
 
 public:
+  using typename BaseType::E;
   using typename BaseType::FluxType;
+  using typename BaseType::LocalFluxType;
+  using typename BaseType::LocalIntersectionCoords;
   using typename BaseType::PhysicalDomainType;
-  using typename BaseType::StateRangeType;
-
-  using FluxEigenDecompositionLambdaType = std::function<std::tuple<
-      std::vector<XT::Common::real_t<R>>,
-      XT::Common::FieldMatrix<XT::Common::real_t<R>, m, m>,
-      XT::Common::FieldMatrix<XT::Common::real_t<R>, m, m>>(
-      const FluxType&, const FieldVector<R, m>&, const FieldVector<double, d>&, const XT::Common::Parameter& param)>;
-
-  NumericalVijayasundaramFlux(const FluxType& flx)
-    : BaseType(flx)
-    , flux_eigen_decomposition_([](const auto& f, const auto& w, const auto& n, const auto& param) {
+  using typename BaseType::StateType;
+  using typename BaseType::XIndependentFluxType;
+
+  using FluxEigenDecompositionLambdaType =
+      std::function<std::tuple<std::vector<XT::Common::real_t<R>>,
+                               XT::Common::FieldMatrix<XT::Common::real_t<R>, m, m>,
+                               XT::Common::FieldMatrix<XT::Common::real_t<R>, m, m>>(
+          const LocalFluxType&,
+          const FieldVector<R, m>&,
+          const FieldVector<double, d>&,
+          const XT::Common::Parameter& param)>;
+
+  static FluxEigenDecompositionLambdaType default_flux_eigen_decomposition()
+  {
+    return [](const LocalFluxType& local_flux,
+              const StateType& w,
+              const PhysicalDomainType& n,
+              const XT::Common::Parameter& param) {
       // evaluate flux jacobian, compute P matrix [DF2016, p. 404, (8.17)]
-      const auto df = f.jacobian(w, param);
+      static PhysicalDomainType dummy_x;
+      const auto df = local_flux.jacobian(dummy_x, w, param);
       const auto P = df * n;
       auto eigensolver = XT::LA::make_eigen_solver(
           P, {{"type", XT::LA::eigen_solver_types(P)[0]}, {"assert_real_eigendecomposition", "1e-10"}});
       return std::make_tuple(
           eigensolver.real_eigenvalues(), eigensolver.real_eigenvectors(), eigensolver.real_eigenvectors_inverse());
-    })
+    };
+  }
+
+  NumericalVijayasundaramFlux(const FluxType& flx)
+    : BaseType(flx)
+    , flux_eigen_decomposition_(default_flux_eigen_decomposition())
+  {
+    if (flx->x_dependent())
+      DUNE_THROW(Dune::NotImplemented, "This flux is not yet implemented for x-dependent fluxes!");
+  }
+
+  NumericalVijayasundaramFlux(const XIndependentFluxType& flx)
+    : BaseType(flx)
+    , flux_eigen_decomposition_(default_flux_eigen_decomposition())
   {}
 
   NumericalVijayasundaramFlux(const FluxType& flx, FluxEigenDecompositionLambdaType flux_eigen_decomposition)
     : BaseType(flx)
     , flux_eigen_decomposition_(flux_eigen_decomposition)
+  {
+    if (flx->x_dependent())
+      DUNE_THROW(Dune::NotImplemented, "This flux is not yet implemented for x-dependent fluxes!");
+  }
+
+  NumericalVijayasundaramFlux(const XIndependentFluxType& flx,
+                              FluxEigenDecompositionLambdaType flux_eigen_decomposition)
+    : BaseType(flx)
+    , flux_eigen_decomposition_(flux_eigen_decomposition)
   {}
 
   std::unique_ptr<BaseType> copy() const override final
@@ -64,15 +97,19 @@ public:
     return std::make_unique<ThisType>(*this);
   }
 
+  NumericalVijayasundaramFlux(const ThisType& other) = default;
+
   using BaseType::apply;
 
-  StateRangeType apply(const StateRangeType& u,
-                       const StateRangeType& v,
-                       const PhysicalDomainType& n,
-                       const XT::Common::Parameter& param = {}) const override final
+  StateType apply(const LocalIntersectionCoords& x,
+                  const StateType& u,
+                  const StateType& v,
+                  const PhysicalDomainType& n,
+                  const XT::Common::Parameter& param = {}) const override final
   {
     // compute decomposition
-    const auto eigendecomposition = flux_eigen_decomposition_(this->flux(), 0.5 * (u + v), n, param);
+    this->compute_entity_coords(x);
+    const auto eigendecomposition = flux_eigen_decomposition_(*local_flux_inside_, 0.5 * (u + v), n, param);
     const auto& evs = std::get<0>(eigendecomposition);
     const auto& T = std::get<1>(eigendecomposition);
     const auto& T_inv = std::get<2>(eigendecomposition);
@@ -90,15 +127,23 @@ public:
   } // ... apply(...)
 
 private:
+  using BaseType::local_flux_inside_;
   const FluxEigenDecompositionLambdaType flux_eigen_decomposition_;
 }; // class NumericalVijayasundaramFlux
 
 
-template <size_t d, size_t m, class R, class... Args>
-NumericalVijayasundaramFlux<d, m, R>
+template <class I, size_t d, size_t m, class R, class... Args>
+NumericalVijayasundaramFlux<I, d, m, R>
+make_numerical_vijayasundaram_flux(const XT::Functions::FluxFunctionInterface<I, m, d, m, R>& flux, Args&&... args)
+{
+  return NumericalVijayasundaramFlux<I, d, m, R>(flux, std::forward<Args>(args)...);
+}
+
+template <class I, size_t d, size_t m, class R, class... Args>
+NumericalVijayasundaramFlux<I, d, m, R>
 make_numerical_vijayasundaram_flux(const XT::Functions::FunctionInterface<m, d, m, R>& flux, Args&&... args)
 {
-  return NumericalVijayasundaramFlux<d, m, R>(flux, std::forward<Args>(args)...);
+  return NumericalVijayasundaramFlux<I, d, m, R>(flux, std::forward<Args>(args)...);
 }
 
 
diff --git a/dune/gdt/local/operators/advection-dg.hh b/dune/gdt/local/operators/advection-dg.hh
index 5628844288993cde9eae8e2ae46d6f128eb68653..0183c8ab750ea46dc006714961a183ef78d1b1b6 100644
--- a/dune/gdt/local/operators/advection-dg.hh
+++ b/dune/gdt/local/operators/advection-dg.hh
@@ -41,33 +41,68 @@ namespace GDT {
 template <class SV, class SGV, size_t m = 1, class SF = double, class RF = SF, class RGV = SGV, class RV = SV>
 class LocalAdvectionDgVolumeOperator : public LocalElementOperatorInterface<SV, SGV, m, 1, SF, m, 1, RF, RGV, RV>
 {
-  using ThisType = LocalAdvectionDgVolumeOperator<SV, SGV, m, SF, RF, RGV, RV>;
+  using ThisType = LocalAdvectionDgVolumeOperator;
   using BaseType = LocalElementOperatorInterface<SV, SGV, m, 1, SF, m, 1, RF, RGV, RV>;
 
 public:
   using BaseType::d;
   using typename BaseType::D;
+  using typename BaseType::E;
   using typename BaseType::LocalRangeType;
+  using typename BaseType::LocalSourceType;
+  using typename BaseType::SourceSpaceType;
   using typename BaseType::SourceType;
 
-  using FluxType = XT::Functions::FunctionInterface<m, d, m, RF>;
+  using FluxType = XT::Functions::FluxFunctionInterface<E, m, d, m, RF>;
   using LocalMassMatrixProviderType = LocalMassMatrixProvider<RGV, m, 1, RF>;
 
+  // When using this constructor, source has to be set by a call to with_source before calling apply
   LocalAdvectionDgVolumeOperator(const FluxType& flux)
-    : BaseType(flux.parameter_type())
+    : BaseType(1, flux.parameter_type())
+    , flux_(flux)
+    , local_flux_(flux_.local_function())
+  {}
+
+  LocalAdvectionDgVolumeOperator(const SourceType& source, const FluxType& flux)
+    : BaseType(source, 1, flux.parameter_type())
     , flux_(flux)
+    , local_flux_(flux_.local_function())
   {}
 
+  // When using this constructor, source has to be set by a call to with_source before calling apply
   /// Applies the inverse of the local mass matrix.
   LocalAdvectionDgVolumeOperator(const LocalMassMatrixProviderType& local_mass_matrices, const FluxType& flux)
-    : BaseType(flux.parameter_type())
+    : BaseType(1, flux.parameter_type())
+    , flux_(flux)
+    , local_flux_(flux_.local_function())
+    , local_mass_matrices_(local_mass_matrices)
+  {}
+
+  /// Applies the inverse of the local mass matrix.
+  LocalAdvectionDgVolumeOperator(const SourceType& source,
+                                 const LocalMassMatrixProviderType& local_mass_matrices,
+                                 const FluxType& flux)
+    : BaseType(source, 1, flux.parameter_type())
+    , flux_(flux)
+    , local_flux_(flux_.local_function())
+    , local_mass_matrices_(local_mass_matrices)
+  {}
+
+  /// Applies the inverse of the local mass matrix.
+  LocalAdvectionDgVolumeOperator(const SourceSpaceType& source_space,
+                                 const SV& source_vector,
+                                 const LocalMassMatrixProviderType& local_mass_matrices,
+                                 const FluxType& flux)
+    : BaseType(source_space, source_vector, 1, flux.parameter_type())
     , flux_(flux)
+    , local_flux_(flux_.local_function())
     , local_mass_matrices_(local_mass_matrices)
   {}
 
   LocalAdvectionDgVolumeOperator(const ThisType& other)
-    : BaseType(other.parameter_type())
+    : BaseType(other)
     , flux_(other.flux_)
+    , local_flux_(flux_.local_function())
     , local_mass_matrices_(other.local_mass_matrices_)
   {}
 
@@ -76,18 +111,24 @@ public:
     return std::make_unique<ThisType>(*this);
   }
 
-  void apply(const SourceType& source,
-             LocalRangeType& local_range,
-             const XT::Common::Parameter& param = {}) const override final
+  /// \todo add linear() to XT::Functions::FluxFunctionInterface and return flux_.linear()!
+  bool linear() const override final
   {
+    return false;
+  }
+
+  void apply(LocalRangeType& local_range, const XT::Common::Parameter& param = {}) const override final
+  {
+    const auto& u_ = local_sources_[0];
     const auto& element = local_range.element();
     const auto& basis = local_range.basis();
     local_dofs_.resize(basis.size(param));
     local_dofs_ *= 0.;
-    const auto local_source = source.local_discrete_function(element);
-    const auto local_source_order = local_source->order(param);
+    u_->bind(element);
+    const auto u_order = u_->order(param);
     const auto local_basis_order = basis.order(param);
-    const auto integrand_order = flux_.order(param) * local_source_order + std::max(local_basis_order - 1, 0);
+    local_flux_->bind(element);
+    const auto integrand_order = local_flux_->order(param) * u_order + std::max(local_basis_order - 1, 0);
     for (const auto& quadrature_point : QuadratureRules<D, d>::rule(element.geometry().type(), integrand_order)) {
       // prepare
       const auto point_in_reference_element = quadrature_point.position();
@@ -95,8 +136,8 @@ public:
       const auto quadrature_weight = quadrature_point.weight();
       // evaluate
       basis.jacobians(point_in_reference_element, basis_jacobians_, param);
-      const auto source_value = local_source->evaluate(point_in_reference_element, param);
-      const auto flux_value = flux_.evaluate(source_value, param);
+      const auto source_value = u_->evaluate(point_in_reference_element, param);
+      const auto flux_value = local_flux_->evaluate(point_in_reference_element, source_value, param);
       // compute
       for (size_t ii = 0; ii < basis.size(param); ++ii)
         local_dofs_[ii] += integration_factor * quadrature_weight * -1. * (flux_value * basis_jacobians_[ii]);
@@ -110,7 +151,9 @@ public:
   } // ... apply(...)
 
 private:
+  using BaseType::local_sources_;
   const FluxType& flux_;
+  std::unique_ptr<typename FluxType::LocalFunctionType> local_flux_;
   const XT::Common::ConstStorageProvider<LocalMassMatrixProviderType> local_mass_matrices_;
   mutable std::vector<typename LocalRangeType::LocalBasisType::DerivativeRangeType> basis_jacobians_;
   mutable XT::LA::CommonDenseVector<RF> local_dofs_;
@@ -136,7 +179,7 @@ template <class I,
 class LocalAdvectionDgCouplingOperator
   : public LocalIntersectionOperatorInterface<I, SV, SGV, m, 1, SR, m, 1, IRR, IRGV, IRV, ORGV, ORV>
 {
-  using ThisType = LocalAdvectionDgCouplingOperator<I, SV, SGV, m, SR, IRR, IRGV, IRV, ORR, ORGV, ORV>;
+  using ThisType = LocalAdvectionDgCouplingOperator;
   using BaseType = LocalIntersectionOperatorInterface<I, SV, SGV, m, 1, SR, m, 1, IRR, IRGV, IRV, ORGV, ORV>;
 
 public:
@@ -145,30 +188,71 @@ public:
   using typename BaseType::IntersectionType;
   using typename BaseType::LocalInsideRangeType;
   using typename BaseType::LocalOutsideRangeType;
+  using typename BaseType::LocalSourceType;
+  using typename BaseType::SourceSpaceType;
   using typename BaseType::SourceType;
 
-  using NumericalFluxType = NumericalFluxInterface<d, m, IRR>;
+  using NumericalFluxType = NumericalFluxInterface<I, d, m, IRR>;
   using LocalMassMatrixProviderType = LocalMassMatrixProvider<IRGV, m, 1, IRR>;
 
+  // When using this constructor, source has to be set by a call to with_source before calling apply
   LocalAdvectionDgCouplingOperator(const NumericalFluxType& numerical_flux, bool compute_outside = true)
-    : BaseType(numerical_flux.parameter_type())
+    : BaseType(2, numerical_flux.parameter_type())
+    , numerical_flux_(numerical_flux.copy())
+    , local_flux_(numerical_flux_->flux().local_function())
+    , compute_outside_(compute_outside)
+  {}
+
+  LocalAdvectionDgCouplingOperator(const SourceType& source,
+                                   const NumericalFluxType& numerical_flux,
+                                   bool compute_outside = true)
+    : BaseType(source, 2, numerical_flux.parameter_type())
     , numerical_flux_(numerical_flux.copy())
+    , local_flux_(numerical_flux_->flux().local_function())
     , compute_outside_(compute_outside)
   {}
 
+  // When using this constructor, source has to be set by a call to with_source before calling apply
   /// Applies the inverse of the local mass matrix.
   LocalAdvectionDgCouplingOperator(const LocalMassMatrixProviderType& local_mass_matrices,
                                    const NumericalFluxType& numerical_flux,
                                    bool compute_outside = true)
-    : BaseType(numerical_flux.parameter_type())
+    : BaseType(2, numerical_flux.parameter_type())
+    , numerical_flux_(numerical_flux.copy())
+    , local_flux_(numerical_flux_->flux().local_function())
+    , compute_outside_(compute_outside)
+    , local_mass_matrices_(local_mass_matrices)
+  {}
+
+  /// Applies the inverse of the local mass matrix.
+  LocalAdvectionDgCouplingOperator(const SourceType& source,
+                                   const LocalMassMatrixProviderType& local_mass_matrices,
+                                   const NumericalFluxType& numerical_flux,
+                                   bool compute_outside = true)
+    : BaseType(source, 2, numerical_flux.parameter_type())
+    , numerical_flux_(numerical_flux.copy())
+    , local_flux_(numerical_flux_->flux().local_function())
+    , compute_outside_(compute_outside)
+    , local_mass_matrices_(local_mass_matrices)
+  {}
+
+  /// Applies the inverse of the local mass matrix.
+  LocalAdvectionDgCouplingOperator(const SourceSpaceType& source_space,
+                                   const SV& source_vector,
+                                   const LocalMassMatrixProviderType& local_mass_matrices,
+                                   const NumericalFluxType& numerical_flux,
+                                   bool compute_outside = true)
+    : BaseType(source_space, source_vector, 2, numerical_flux.parameter_type())
     , numerical_flux_(numerical_flux.copy())
+    , local_flux_(numerical_flux_->flux().local_function())
     , compute_outside_(compute_outside)
     , local_mass_matrices_(local_mass_matrices)
   {}
 
   LocalAdvectionDgCouplingOperator(const ThisType& other)
-    : BaseType(other.parameter_type())
+    : BaseType(other)
     , numerical_flux_(other.numerical_flux_->copy())
+    , local_flux_(numerical_flux_->flux().local_function())
     , compute_outside_(other.compute_outside_)
     , local_mass_matrices_(other.local_mass_matrices_)
   {}
@@ -178,12 +262,17 @@ public:
     return std::make_unique<ThisType>(*this);
   }
 
-  void apply(const SourceType& source,
-             const IntersectionType& intersection,
-             LocalInsideRangeType& local_range_inside,
+  bool linear() const override final
+  {
+    return numerical_flux_->linear();
+  }
+
+  void apply(LocalInsideRangeType& local_range_inside,
              LocalOutsideRangeType& local_range_outside,
              const XT::Common::Parameter& param = {}) const override final
   {
+    const auto& u_ = local_sources_[0];
+    const auto& v_ = local_sources_[1];
     const auto& inside_element = local_range_inside.element();
     const auto& outside_element = local_range_outside.element();
     const auto& inside_basis = local_range_inside.basis();
@@ -192,10 +281,17 @@ public:
     outside_local_dofs_.resize(outside_basis.size(param));
     inside_local_dofs_ *= 0.;
     outside_local_dofs_ *= 0.;
-    const auto u = source.local_discrete_function(inside_element);
-    const auto v = source.local_discrete_function(outside_element);
+    numerical_flux_->bind(intersection);
+    local_flux_->bind(intersection.inside());
+    const auto inside_flux_order = local_flux_->order(param);
+    local_flux_->bind(intersection.outside());
+    const auto outside_flux_order = local_flux_->order(param);
+    u_->bind(inside_element);
+    v_->bind(outside_element);
+    const auto u_order = u_->order(param);
+    const auto v_order = v_->order(param);
     const auto integrand_order = std::max(inside_basis.order(param), outside_basis.order(param))
-                                 + numerical_flux_->flux().order(param) * std::max(u->order(param), v->order(param));
+                                 + std::max(inside_flux_order * u_order, outside_flux_order * v_order);
     for (const auto& quadrature_point :
          QuadratureRules<D, d - 1>::rule(intersection.geometry().type(), integrand_order)) {
       // prepare
@@ -211,10 +307,9 @@ public:
       inside_basis.evaluate(point_in_inside_reference_element, inside_basis_values_);
       if (compute_outside_)
         outside_basis.evaluate(point_in_outside_reference_element, outside_basis_values_);
-      const auto g = numerical_flux_->apply(u->evaluate(point_in_inside_reference_element),
-                                            v->evaluate(point_in_outside_reference_element),
-                                            normal,
-                                            param);
+      const auto u_val = u_->evaluate(point_in_inside_reference_element);
+      const auto v_val = v_->evaluate(point_in_outside_reference_element);
+      const auto g = numerical_flux_->apply(point_in_reference_intersection, u_val, v_val, normal, param);
       // compute
       for (size_t ii = 0; ii < inside_basis.size(param); ++ii)
         inside_local_dofs_[ii] += integration_factor * quadrature_weight * (g * inside_basis_values_[ii]);
@@ -237,7 +332,9 @@ public:
   } // ... apply(...)
 
 private:
-  const std::unique_ptr<const NumericalFluxType> numerical_flux_;
+  using BaseType::local_sources_;
+  const std::unique_ptr<NumericalFluxType> numerical_flux_;
+  std::unique_ptr<typename NumericalFluxType::FluxType::LocalFunctionType> local_flux_;
   const bool compute_outside_;
   const XT::Common::ConstStorageProvider<LocalMassMatrixProviderType> local_mass_matrices_;
   mutable std::vector<typename LocalInsideRangeType::LocalBasisType::RangeType> inside_basis_values_;
@@ -256,7 +353,7 @@ template <class I, class SV, class SGV, size_t m = 1, class SF = double, class R
 class LocalAdvectionDgBoundaryTreatmentByCustomNumericalFluxOperator
   : public LocalIntersectionOperatorInterface<I, SV, SGV, m, 1, SF, m, 1, RF, RGV, RV>
 {
-  using ThisType = LocalAdvectionDgBoundaryTreatmentByCustomNumericalFluxOperator<I, SV, SGV, m, SF, RF, RGV, RV>;
+  using ThisType = LocalAdvectionDgBoundaryTreatmentByCustomNumericalFluxOperator;
   using BaseType = LocalIntersectionOperatorInterface<I, SV, SGV, m, 1, SF, m, 1, RF, RGV, RV>;
 
 public:
@@ -265,6 +362,7 @@ public:
   using typename BaseType::IntersectionType;
   using typename BaseType::LocalInsideRangeType;
   using typename BaseType::LocalOutsideRangeType;
+  using typename BaseType::SourceSpaceType;
   using typename BaseType::SourceType;
 
   using StateDomainType = FieldVector<typename SGV::ctype, SGV::dimension>;
@@ -274,28 +372,45 @@ public:
   using LocalMassMatrixProviderType = LocalMassMatrixProvider<RGV, m, 1, RF>;
 
   LocalAdvectionDgBoundaryTreatmentByCustomNumericalFluxOperator(
+      const SourceType& source,
+      LambdaType numerical_boundary_flux,
+      const int numerical_flux_order,
+      const XT::Common::ParameterType& numerical_flux_param_type = {})
+    : BaseType(source, 1, numerical_flux_param_type)
+    , numerical_boundary_flux_(numerical_boundary_flux)
+    , numerical_flux_order_(numerical_flux_order)
+  {}
+
+  /// Applies the inverse of the local mass matrix.
+  LocalAdvectionDgBoundaryTreatmentByCustomNumericalFluxOperator(
+      const SourceType& source,
+      const LocalMassMatrixProviderType& local_mass_matrices,
       LambdaType numerical_boundary_flux,
       const int numerical_flux_order,
       const XT::Common::ParameterType& numerical_flux_param_type = {})
-    : BaseType(numerical_flux_param_type)
+    : BaseType(source, 1, numerical_flux_param_type)
     , numerical_boundary_flux_(numerical_boundary_flux)
     , numerical_flux_order_(numerical_flux_order)
+    , local_mass_matrices_(local_mass_matrices)
   {}
 
   /// Applies the inverse of the local mass matrix.
   LocalAdvectionDgBoundaryTreatmentByCustomNumericalFluxOperator(
+      const SourceSpaceType& source_space,
+      const SV& source_vector,
       const LocalMassMatrixProviderType& local_mass_matrices,
       LambdaType numerical_boundary_flux,
       const int numerical_flux_order,
       const XT::Common::ParameterType& numerical_flux_param_type = {})
-    : BaseType(numerical_flux_param_type)
+    : BaseType(source_space, source_vector, 1, numerical_flux_param_type)
     , numerical_boundary_flux_(numerical_boundary_flux)
     , numerical_flux_order_(numerical_flux_order)
     , local_mass_matrices_(local_mass_matrices)
   {}
 
+
   LocalAdvectionDgBoundaryTreatmentByCustomNumericalFluxOperator(const ThisType& other)
-    : BaseType(other.parameter_type())
+    : BaseType(other)
     , numerical_boundary_flux_(other.numerical_boundary_flux_)
     , numerical_flux_order_(other.numerical_flux_order_)
     , local_mass_matrices_(other.local_mass_matrices_)
@@ -306,18 +421,23 @@ public:
     return std::make_unique<ThisType>(*this);
   }
 
-  void apply(const SourceType& source,
-             const IntersectionType& intersection,
-             LocalInsideRangeType& local_range_inside,
+  /// \todo store some numerical_flux_linear in ctor and return that
+  bool linear() const override final
+  {
+    return false;
+  }
+
+  void apply(LocalInsideRangeType& local_range_inside,
              LocalOutsideRangeType& /*local_range_outside*/,
              const XT::Common::Parameter& param = {}) const override final
   {
+    const auto& u_ = local_sources_[0];
     const auto& element = local_range_inside.element();
     const auto& inside_basis = local_range_inside.basis();
     inside_local_dofs_.resize(inside_basis.size(param));
     inside_local_dofs_ *= 0.;
-    const auto u = source.local_discrete_function(element);
-    const auto integrand_order = inside_basis.order(param) + numerical_flux_order_ * u->order(param);
+    u_->bind(element);
+    const auto integrand_order = inside_basis.order(param) + numerical_flux_order_ * u_->order(param);
     for (const auto& quadrature_point :
          QuadratureRules<D, d - 1>::rule(intersection.geometry().type(), integrand_order)) {
       // prepare
@@ -329,7 +449,7 @@ public:
           intersection.geometryInInside().global(point_in_reference_intersection);
       // evaluate
       inside_basis.evaluate(point_in_inside_reference_element, inside_basis_values_);
-      const auto g = numerical_boundary_flux_(u->evaluate(point_in_inside_reference_element), normal, param);
+      const auto g = numerical_boundary_flux_(u_->evaluate(point_in_inside_reference_element), normal, param);
       // compute
       for (size_t ii = 0; ii < inside_basis.size(param); ++ii)
         inside_local_dofs_[ii] += integration_factor * quadrature_weight * (g * inside_basis_values_[ii]);
@@ -343,6 +463,7 @@ public:
   } // ... apply(...)
 
 private:
+  using BaseType::local_sources_;
   const LambdaType numerical_boundary_flux_;
   const int numerical_flux_order_;
   const XT::Common::ConstStorageProvider<LocalMassMatrixProviderType> local_mass_matrices_;
@@ -360,7 +481,7 @@ template <class I, class SV, class SGV, size_t m = 1, class SF = double, class R
 class LocalAdvectionDgBoundaryTreatmentByCustomExtrapolationOperator
   : public LocalIntersectionOperatorInterface<I, SV, SGV, m, 1, SF, m, 1, RF, RGV, RV>
 {
-  using ThisType = LocalAdvectionDgBoundaryTreatmentByCustomExtrapolationOperator<I, SV, SGV, m, SF, RF, RGV, RV>;
+  using ThisType = LocalAdvectionDgBoundaryTreatmentByCustomExtrapolationOperator;
   using BaseType = LocalIntersectionOperatorInterface<I, SV, SGV, m, 1, SF, m, 1, RF, RGV, RV>;
 
 public:
@@ -368,10 +489,11 @@ public:
   using typename BaseType::IntersectionType;
   using typename BaseType::LocalInsideRangeType;
   using typename BaseType::LocalOutsideRangeType;
+  using typename BaseType::SourceSpaceType;
   using typename BaseType::SourceType;
 
   using D = typename IntersectionType::ctype;
-  using NumericalFluxType = NumericalFluxInterface<d, m, RF>;
+  using NumericalFluxType = NumericalFluxInterface<I, d, m, RF>;
   using FluxType = typename NumericalFluxType::FluxType;
   using StateRangeType = typename XT::Functions::RangeTypeSelector<RF, m, 1>::type;
   using LambdaType =
@@ -383,29 +505,49 @@ public:
   using LocalMassMatrixProviderType = LocalMassMatrixProvider<RGV, m, 1, RF>;
 
   LocalAdvectionDgBoundaryTreatmentByCustomExtrapolationOperator(
+      const SourceType& source,
+      const NumericalFluxType& numerical_flux,
+      LambdaType boundary_extrapolation_lambda,
+      const XT::Common::ParameterType& boundary_treatment_param_type = {})
+    : BaseType(source, 1, numerical_flux.parameter_type() + boundary_treatment_param_type)
+    , numerical_flux_(numerical_flux.copy())
+    , local_flux_(numerical_flux_->flux().local_function())
+    , extrapolate_(boundary_extrapolation_lambda)
+  {}
+
+  /// Applies the inverse of the local mass matrix.
+  LocalAdvectionDgBoundaryTreatmentByCustomExtrapolationOperator(
+      const SourceType& source,
+      const LocalMassMatrixProviderType& local_mass_matrices,
       const NumericalFluxType& numerical_flux,
       LambdaType boundary_extrapolation_lambda,
       const XT::Common::ParameterType& boundary_treatment_param_type = {})
-    : BaseType(numerical_flux.parameter_type() + boundary_treatment_param_type)
+    : BaseType(source, 1, numerical_flux.parameter_type() + boundary_treatment_param_type)
     , numerical_flux_(numerical_flux.copy())
+    , local_flux_(numerical_flux_->flux().local_function())
     , extrapolate_(boundary_extrapolation_lambda)
+    , local_mass_matrices_(local_mass_matrices)
   {}
 
   /// Applies the inverse of the local mass matrix.
   LocalAdvectionDgBoundaryTreatmentByCustomExtrapolationOperator(
+      const SourceSpaceType& source_space,
+      const SV& source_vector,
       const LocalMassMatrixProviderType& local_mass_matrices,
       const NumericalFluxType& numerical_flux,
       LambdaType boundary_extrapolation_lambda,
       const XT::Common::ParameterType& boundary_treatment_param_type = {})
-    : BaseType(numerical_flux.parameter_type() + boundary_treatment_param_type)
+    : BaseType(source_space, source_vector, 1, numerical_flux.parameter_type() + boundary_treatment_param_type)
     , numerical_flux_(numerical_flux.copy())
+    , local_flux_(numerical_flux_->flux().local_function())
     , extrapolate_(boundary_extrapolation_lambda)
     , local_mass_matrices_(local_mass_matrices)
   {}
 
   LocalAdvectionDgBoundaryTreatmentByCustomExtrapolationOperator(const ThisType& other)
-    : BaseType(other.parameter_type())
+    : BaseType(other)
     , numerical_flux_(other.numerical_flux_->copy())
+    , local_flux_(numerical_flux_->flux().local_function())
     , extrapolate_(other.extrapolate_)
     , local_mass_matrices_(other.local_mass_matrices_)
   {}
@@ -415,19 +557,24 @@ public:
     return std::make_unique<ThisType>(*this);
   }
 
-  void apply(const SourceType& source,
-             const IntersectionType& intersection,
-             LocalInsideRangeType& local_range_inside,
+  bool linear() const override final
+  {
+    return numerical_flux_->linear();
+  }
+
+  void apply(LocalInsideRangeType& local_range_inside,
              LocalOutsideRangeType& /*local_range_outside*/,
              const XT::Common::Parameter& param = {}) const override final
   {
+    const auto& u_ = local_sources_[0];
     const auto& element = local_range_inside.element();
     const auto& inside_basis = local_range_inside.basis();
     inside_local_dofs_.resize(inside_basis.size(param));
     inside_local_dofs_ *= 0.;
-    const auto local_source = source.local_discrete_function(element);
-    const auto integrand_order =
-        inside_basis.order(param) + numerical_flux_->flux().order(param) * local_source->order(param);
+    numerical_flux_->bind(intersection);
+    local_flux_->bind(intersection.inside());
+    u_->bind(element);
+    const auto integrand_order = inside_basis.order(param) + local_flux_->order(param) * u_->order(param);
     for (const auto& quadrature_point :
          QuadratureRules<D, d - 1>::rule(intersection.geometry().type(), integrand_order)) {
       // prepare
@@ -439,9 +586,9 @@ public:
           intersection.geometryInInside().global(point_in_reference_intersection);
       // evaluate
       inside_basis.evaluate(point_in_inside_reference_element, inside_basis_values_);
-      const auto u = local_source->evaluate(point_in_inside_reference_element);
+      const auto u = u_->evaluate(point_in_inside_reference_element);
       const auto v = extrapolate_(intersection, point_in_reference_intersection, numerical_flux_->flux(), u, param);
-      const auto g = numerical_flux_->apply(u, v, normal, param);
+      const auto g = numerical_flux_->apply(point_in_reference_intersection, u, v, normal, param);
       // compute
       for (size_t ii = 0; ii < inside_basis.size(param); ++ii)
         inside_local_dofs_[ii] += integration_factor * quadrature_weight * (g * inside_basis_values_[ii]);
@@ -455,7 +602,9 @@ public:
   } // ... apply(...)
 
 private:
-  const std::unique_ptr<const NumericalFluxType> numerical_flux_;
+  using BaseType::local_sources_;
+  const std::unique_ptr<NumericalFluxType> numerical_flux_;
+  std::unique_ptr<typename NumericalFluxType::FluxType::LocalFunctionType> local_flux_;
   const LambdaType extrapolate_;
   const XT::Common::ConstStorageProvider<LocalMassMatrixProviderType> local_mass_matrices_;
   mutable std::vector<typename LocalInsideRangeType::LocalBasisType::RangeType> inside_basis_values_;
@@ -467,36 +616,83 @@ template <class SV, class SGV, size_t m = 1, class SF = double, class RF = SF, c
 class LocalAdvectionDgArtificialViscosityShockCapturingOperator
   : public LocalElementOperatorInterface<SV, SGV, m, 1, SF, m, 1, RF, RGV, RV>
 {
-  using ThisType = LocalAdvectionDgArtificialViscosityShockCapturingOperator<SV, SGV, m, SF, RF, RGV, RV>;
+  using ThisType = LocalAdvectionDgArtificialViscosityShockCapturingOperator;
   using BaseType = LocalElementOperatorInterface<SV, SGV, m, 1, SF, m, 1, RF, RGV, RV>;
 
 public:
   using BaseType::d;
   using typename BaseType::D;
   using typename BaseType::LocalRangeType;
+  using typename BaseType::LocalSourceType;
+  using typename BaseType::SourceSpaceType;
   using typename BaseType::SourceType;
 
   using FluxType = XT::Functions::FunctionInterface<m, d, m, RF>;
   using LocalMassMatrixProviderType = LocalMassMatrixProvider<RGV, m, 1, RF>;
 
+  // When using this constructor, source has to be set by a call to with_source before calling apply
   LocalAdvectionDgArtificialViscosityShockCapturingOperator(const SGV& assembly_grid_view,
                                                             const double& nu_1 = 0.2,
                                                             const double& alpha_1 = 1.0,
                                                             const size_t index = 0)
-    : BaseType()
+    : BaseType(2)
     , assembly_grid_view_(assembly_grid_view)
     , nu_1_(nu_1)
     , alpha_1_(alpha_1)
     , index_(index)
   {}
 
+  LocalAdvectionDgArtificialViscosityShockCapturingOperator(const SourceType& source,
+                                                            const SGV& assembly_grid_view,
+                                                            const double& nu_1 = 0.2,
+                                                            const double& alpha_1 = 1.0,
+                                                            const size_t index = 0)
+    : BaseType(source, 2)
+    , assembly_grid_view_(assembly_grid_view)
+    , nu_1_(nu_1)
+    , alpha_1_(alpha_1)
+    , index_(index)
+  {}
+
+  // When using this constructor, source has to be set by a call to with_source before calling apply
   /// Applies the inverse of the local mass matrix.
   LocalAdvectionDgArtificialViscosityShockCapturingOperator(const LocalMassMatrixProviderType& local_mass_matrices,
                                                             const SGV& assembly_grid_view,
                                                             const double& nu_1 = 0.2,
                                                             const double& alpha_1 = 1.0,
                                                             const size_t index = 0)
-    : BaseType()
+    : BaseType(2)
+    , assembly_grid_view_(assembly_grid_view)
+    , nu_1_(nu_1)
+    , alpha_1_(alpha_1)
+    , index_(index)
+    , local_mass_matrices_(local_mass_matrices)
+  {}
+
+  /// Applies the inverse of the local mass matrix.
+  LocalAdvectionDgArtificialViscosityShockCapturingOperator(const SourceType& source,
+                                                            const LocalMassMatrixProviderType& local_mass_matrices,
+                                                            const SGV& assembly_grid_view,
+                                                            const double& nu_1 = 0.2,
+                                                            const double& alpha_1 = 1.0,
+                                                            const size_t index = 0)
+    : BaseType(source, 2)
+    , assembly_grid_view_(assembly_grid_view)
+    , nu_1_(nu_1)
+    , alpha_1_(alpha_1)
+    , index_(index)
+    , local_mass_matrices_(local_mass_matrices)
+  {}
+
+  /// Applies the inverse of the local mass matrix.
+  LocalAdvectionDgArtificialViscosityShockCapturingOperator(const SourceSpaceType& source_space,
+                                                            const SV& source_vector,
+                                                            const LocalMassMatrixProviderType& local_mass_matrices,
+                                                            const SGV& assembly_grid_view,
+                                                            const double& nu_1 = 0.2,
+                                                            const double& alpha_1 = 1.0,
+                                                            const size_t index = 0)
+    : BaseType(source_space, source_vector, 2)
     , assembly_grid_view_(assembly_grid_view)
     , nu_1_(nu_1)
     , alpha_1_(alpha_1)
@@ -518,16 +714,16 @@ public:
     return std::make_unique<ThisType>(*this);
   }
 
-  void apply(const SourceType& source,
-             LocalRangeType& local_range,
-             const XT::Common::Parameter& param = {}) const override final
+  void apply(LocalRangeType& local_range, const XT::Common::Parameter& param = {}) const override final
   {
+    const auto& u_ = local_sources_[0];
+    const auto& v_ = local_sources_[1];
     const auto& element = local_range.element();
     const auto& basis = local_range.basis();
     local_dofs_.resize(basis.size(param));
     local_dofs_ *= 0.;
-    const auto local_source_element = source.local_discrete_function(element);
-    if (local_source_element->order(param) <= 0 || basis.order(param) <= 0)
+    u_->bind(element);
+    if (u_->order(param) <= 0 || basis.order(param) <= 0)
       return;
     // compute jump indicator (8.176)
     double element_jump_indicator = 0;
@@ -537,9 +733,8 @@ public:
         if (d > 1)
           element_boundary_without_domain_boundary += XT::Grid::diameter(intersection);
         const auto neighbor = intersection.outside();
-        const auto local_source_neighbor = source.local_discrete_function(neighbor);
-        const auto integration_order =
-            std::pow(std::max(local_source_element->order(param), local_source_neighbor->order(param)), 2);
+        v_->bind(neighbor);
+        const auto integration_order = std::pow(std::max(u_->order(param), v_->order(param)), 2);
         for (auto&& quadrature_point :
              QuadratureRules<D, d - 1>::rule(intersection.geometry().type(), integration_order)) {
           const auto point_in_reference_intersection = quadrature_point.position();
@@ -549,8 +744,8 @@ public:
               intersection.geometryInOutside().global(point_in_reference_intersection);
           const auto integration_factor = intersection.geometry().integrationElement(point_in_reference_intersection);
           const auto quadrature_weight = quadrature_point.weight();
-          const auto value_on_element = local_source_element->evaluate(point_in_reference_element, param)[index_];
-          const auto value_on_neighbor = local_source_neighbor->evaluate(point_in_reference_neighbor, param)[index_];
+          const auto value_on_element = u_->evaluate(point_in_reference_element, param)[index_];
+          const auto value_on_neighbor = v_->evaluate(point_in_reference_neighbor, param)[index_];
           element_jump_indicator +=
               integration_factor * quadrature_weight * std::pow(value_on_element - value_on_neighbor, 2);
         }
@@ -572,12 +767,11 @@ public:
     if (smoothed_discrete_jump_indicator > 0) {
       const auto h = element.geometry().volume();
       for (const auto& quadrature_point : QuadratureRules<D, d>::rule(
-               element.type(),
-               std::max(0, local_source_element->order(param) - 1) + std::max(0, basis.order(param) - 1))) {
+               element.type(), std::max(0, u_->order(param) - 1) + std::max(0, basis.order(param) - 1))) {
         const auto point_in_reference_element = quadrature_point.position();
         const auto integration_factor = element.geometry().integrationElement(point_in_reference_element);
         const auto quadrature_weight = quadrature_point.weight();
-        const auto source_jacobian = local_source_element->jacobian(point_in_reference_element, param);
+        const auto source_jacobian = u_->jacobian(point_in_reference_element, param);
         basis.jacobians(point_in_reference_element, basis_jacobians_, param);
         // compute beta_h
         for (size_t ii = 0; ii < basis.size(param); ++ii)
@@ -594,6 +788,7 @@ public:
   } // ... apply(...)
 
 private:
+  using BaseType::local_sources_;
   const SGV& assembly_grid_view_;
   const double nu_1_;
   const double alpha_1_;
diff --git a/dune/gdt/local/operators/advection-fv.hh b/dune/gdt/local/operators/advection-fv.hh
index 93086c9cabfdd2514a73b0302f7875dced8b06ee..6c1ee194842a7f47aafbf4ec88f22b1edf033b41 100644
--- a/dune/gdt/local/operators/advection-fv.hh
+++ b/dune/gdt/local/operators/advection-fv.hh
@@ -48,26 +48,56 @@ template <class I,
 class LocalAdvectionFvCouplingOperator
   : public LocalIntersectionOperatorInterface<I, SV, SGV, m, 1, SR, m, 1, RR, IRGV, IRV, ORGV, ORV>
 {
-  using ThisType = LocalAdvectionFvCouplingOperator<I, SV, SGV, m, SR, RR, IRGV, IRV, ORR, ORGV, ORV>;
+  using ThisType = LocalAdvectionFvCouplingOperator;
   using BaseType = LocalIntersectionOperatorInterface<I, SV, SGV, m, 1, SR, m, 1, RR, IRGV, IRV, ORGV, ORV>;
 
 public:
   using BaseType::d;
+  using typename BaseType::DiscreteSourceType;
   using typename BaseType::IntersectionType;
   using typename BaseType::LocalInsideRangeType;
   using typename BaseType::LocalOutsideRangeType;
+  using typename BaseType::LocalSourceType;
+  using typename BaseType::SourceSpaceType;
   using typename BaseType::SourceType;
+  using StateType = typename XT::Functions::RangeTypeSelector<SR, m, 1>::type;
+  using NumericalFluxType = NumericalFluxInterface<I, d, m, RR>;
+  using LocalIntersectionCoords = typename NumericalFluxType::LocalIntersectionCoords;
+  static const typename LocalSourceType::DomainType static_x;
+
+  // When using this constructor, source has to be set by a call to with_source before calling apply
+  LocalAdvectionFvCouplingOperator(const NumericalFluxType& numerical_flux,
+                                   const bool source_is_elementwise_constant = false)
+    : BaseType(2, numerical_flux.parameter_type())
+    , numerical_flux_(numerical_flux.copy())
+    , source_is_elementwise_constant_(source_is_elementwise_constant)
+  {}
 
-  using NumericalFluxType = NumericalFluxInterface<d, m, RR>;
+  LocalAdvectionFvCouplingOperator(const SourceType& source,
+                                   const NumericalFluxType& numerical_flux,
+                                   const bool source_is_elementwise_constant = false)
+    : BaseType(source, 2, numerical_flux.parameter_type())
+    , numerical_flux_(numerical_flux.copy())
+    , source_is_elementwise_constant_(source_is_elementwise_constant)
+  {}
 
-  LocalAdvectionFvCouplingOperator(const NumericalFluxType& numerical_flux)
-    : BaseType(numerical_flux.parameter_type())
+  LocalAdvectionFvCouplingOperator(const SourceSpaceType& source_space,
+                                   const SV& source_vector,
+                                   const NumericalFluxType& numerical_flux,
+                                   const bool source_is_elementwise_constant = false)
+    : BaseType(source_space, source_vector, 2, numerical_flux.parameter_type())
     , numerical_flux_(numerical_flux.copy())
+    , source_is_elementwise_constant_(source_is_elementwise_constant)
+  {}
+
+  LocalAdvectionFvCouplingOperator(const DiscreteSourceType& source, const NumericalFluxType& numerical_flux)
+    : ThisType(source, numerical_flux, source.space().type() == SpaceType::finite_volume)
   {}
 
   LocalAdvectionFvCouplingOperator(const ThisType& other)
-    : BaseType(other.parameter_type())
+    : BaseType(other)
     , numerical_flux_(other.numerical_flux_->copy())
+    , source_is_elementwise_constant_(other.source_is_elementwise_constant_)
   {}
 
   std::unique_ptr<BaseType> copy() const override final
@@ -75,66 +105,134 @@ public:
     return std::make_unique<ThisType>(*this);
   }
 
-  void apply(const SourceType& source,
-             const IntersectionType& intersection,
-             LocalInsideRangeType& local_range_inside,
+  bool linear() const override final
+  {
+    return numerical_flux_->linear();
+  }
+
+  void apply(LocalInsideRangeType& local_range_inside,
              LocalOutsideRangeType& local_range_outside,
              const XT::Common::Parameter& param = {}) const override final
   {
-    DUNE_THROW_IF((source.space().type() != SpaceType::finite_volume)
-                      || (local_range_inside.space().type() != SpaceType::finite_volume)
+    DUNE_THROW_IF((local_range_inside.space().type() != SpaceType::finite_volume)
                       || (local_range_outside.space().type() != SpaceType::finite_volume),
                   Exceptions::operator_error,
                   "Use LocalAdvectionDgCouplingOperator instead!");
-    const auto& inside_element = local_range_inside.element();
-    const auto& outside_element = local_range_outside.element();
-    const auto u = source.local_discrete_function(inside_element);
-    const auto v = source.local_discrete_function(outside_element);
+    u_ = local_sources_[0]->evaluate(source_is_elementwise_constant_ ? static_x
+                                                                     : intersection.geometryInInside().center());
+    v_ = local_sources_[1]->evaluate(source_is_elementwise_constant_ ? static_x
+                                                                     : intersection.geometryInOutside().center());
     const auto normal = intersection.centerUnitOuterNormal();
-    const auto g = numerical_flux_->apply(u->dofs(), v->dofs(), normal, param);
+    if (numerical_flux_->x_dependent())
+      x_in_intersection_coords_ = intersection.geometry().local(intersection.geometry().center());
+    const auto g = numerical_flux_->apply(x_in_intersection_coords_, u_, v_, normal, param);
     const auto h_intersection = intersection.geometry().volume();
-    const auto h_inside_element = inside_element.geometry().volume();
-    const auto h_outside_element = outside_element.geometry().volume();
+    const auto h_inside_element = intersection.inside().geometry().volume();
+    const auto h_outside_element = intersection.outside().geometry().volume();
     for (size_t ii = 0; ii < m; ++ii) {
       local_range_inside.dofs()[ii] += (g[ii] * h_intersection) / h_inside_element;
       local_range_outside.dofs()[ii] -= (g[ii] * h_intersection) / h_outside_element;
     }
   } // ... apply(...)
 
+protected:
+  void post_bind(const I& inter) override
+  {
+    BaseType::post_bind(inter);
+    numerical_flux_->bind(inter);
+  }
+
 private:
+  using BaseType::local_sources_;
   std::unique_ptr<NumericalFluxType> numerical_flux_;
+  const bool source_is_elementwise_constant_;
+  mutable LocalIntersectionCoords x_in_intersection_coords_;
+  mutable StateType u_;
+  mutable StateType v_;
 }; // class LocalAdvectionFvCouplingOperator
 
+template <class I,
+          class SV,
+          class SGV,
+          size_t m,
+          class SR,
+          class RR,
+          class IRGV,
+          class IRV,
+          class ORR,
+          class ORGV,
+          class ORV>
+const typename LocalAdvectionFvCouplingOperator<I, SV, SGV, m, SR, RR, IRGV, IRV, ORR, ORGV, ORV>::LocalSourceType::
+    DomainType LocalAdvectionFvCouplingOperator<I, SV, SGV, m, SR, RR, IRGV, IRV, ORR, ORGV, ORV>::static_x;
+
 
 template <class I, class SV, class SGV, size_t m = 1, class SF = double, class RF = SF, class RGV = SGV, class RV = SV>
 class LocalAdvectionFvBoundaryTreatmentByCustomNumericalFluxOperator
   : public LocalIntersectionOperatorInterface<I, SV, SGV, m, 1, SF, m, 1, RF, RGV, RV>
 {
-  using ThisType = LocalAdvectionFvBoundaryTreatmentByCustomNumericalFluxOperator<I, SV, SGV, m, SF, RF, RGV, RV>;
+  using ThisType = LocalAdvectionFvBoundaryTreatmentByCustomNumericalFluxOperator;
   using BaseType = LocalIntersectionOperatorInterface<I, SV, SGV, m, 1, SF, m, 1, RF, RGV, RV>;
+  using CouplingOperatorType = LocalAdvectionFvCouplingOperator<I, SV, SGV, m, SF, RF, RGV, RV>;
 
 public:
   using BaseType::d;
+  using typename BaseType::DiscreteSourceType;
   using typename BaseType::IntersectionType;
   using typename BaseType::LocalInsideRangeType;
   using typename BaseType::LocalOutsideRangeType;
+  using typename BaseType::SourceSpaceType;
   using typename BaseType::SourceType;
 
   using StateDomainType = FieldVector<typename SGV::ctype, SGV::dimension>;
-  using StateDofsType = ConstLocalDofVector<SV, SGV>;
-  using StateRangeType = typename XT::Functions::RangeTypeSelector<SF, m, 1>::type;
-  using LambdaType = std::function<StateRangeType(
-      const StateDofsType& /*u*/, const StateDomainType& /*n*/, const XT::Common::Parameter& /*param*/)>;
+  using StateType = typename CouplingOperatorType::StateType;
+  using LambdaType = std::function<StateType(
+      const StateType& /*u*/, const StateDomainType& /*n*/, const XT::Common::Parameter& /*param*/)>;
+
+  // When using this constructor, source has to be set by a call to with_source before calling apply
+  LocalAdvectionFvBoundaryTreatmentByCustomNumericalFluxOperator(
+      LambdaType numerical_boundary_flux_lambda,
+      const XT::Common::ParameterType& boundary_treatment_param_type = {},
+      const bool source_is_elementwise_constant = false)
+    : BaseType(1, boundary_treatment_param_type)
+    , numerical_boundary_flux_(numerical_boundary_flux_lambda)
+    , source_is_elementwise_constant_(source_is_elementwise_constant)
+  {}
 
   LocalAdvectionFvBoundaryTreatmentByCustomNumericalFluxOperator(
-      LambdaType numerical_boundary_flux_lambda, const XT::Common::ParameterType& boundary_treatment_param_type = {})
-    : BaseType(boundary_treatment_param_type)
+      const SourceType& source,
+      LambdaType numerical_boundary_flux_lambda,
+      const XT::Common::ParameterType& boundary_treatment_param_type = {},
+      const bool source_is_elementwise_constant = false)
+    : BaseType(source, 1, boundary_treatment_param_type)
     , numerical_boundary_flux_(numerical_boundary_flux_lambda)
+    , source_is_elementwise_constant_(source_is_elementwise_constant)
+  {}
+
+  LocalAdvectionFvBoundaryTreatmentByCustomNumericalFluxOperator(
+      const SourceSpaceType& source_space,
+      const SV& source_vector,
+      LambdaType numerical_boundary_flux_lambda,
+      const XT::Common::ParameterType& boundary_treatment_param_type = {},
+      const bool source_is_elementwise_constant = false)
+    : BaseType(source_space, source_vector, 1, boundary_treatment_param_type)
+    , numerical_boundary_flux_(numerical_boundary_flux_lambda)
+    , source_is_elementwise_constant_(source_is_elementwise_constant)
+  {}
+
+  LocalAdvectionFvBoundaryTreatmentByCustomNumericalFluxOperator(
+      const DiscreteSourceType& source,
+      LambdaType numerical_boundary_flux_lambda,
+      const XT::Common::ParameterType& boundary_treatment_param_type = {})
+    : ThisType(source,
+               numerical_boundary_flux_lambda,
+               boundary_treatment_param_type,
+               source.space().type() == SpaceType::finite_volume)
   {}
 
   LocalAdvectionFvBoundaryTreatmentByCustomNumericalFluxOperator(const ThisType& other)
-    : BaseType(other.parameter_type())
+    : BaseType(other)
     , numerical_boundary_flux_(other.numerical_boundary_flux_)
+    , source_is_elementwise_constant_(other.source_is_elementwise_constant_)
   {}
 
   std::unique_ptr<BaseType> copy() const override final
@@ -142,20 +240,24 @@ public:
     return std::make_unique<ThisType>(*this);
   }
 
-  void apply(const SourceType& source,
-             const IntersectionType& intersection,
-             LocalInsideRangeType& local_range_inside,
+  /// \todo store some numerical_flux_linear in ctor and return that
+  bool linear() const override final
+  {
+    return false;
+  }
+
+  void apply(LocalInsideRangeType& local_range_inside,
              LocalOutsideRangeType& /*local_range_outside*/,
              const XT::Common::Parameter& param = {}) const override final
   {
-    DUNE_THROW_IF((source.space().type() != SpaceType::finite_volume)
-                      || (local_range_inside.space().type() != SpaceType::finite_volume),
+    DUNE_THROW_IF(local_range_inside.space().type() != SpaceType::finite_volume,
                   Exceptions::operator_error,
                   "Use LocalAdvectionDgBoundaryTreatmentByCustomNumericalFluxOperator instead!");
     const auto& element = local_range_inside.element();
-    const auto u = source.local_discrete_function(element);
+    u_ = local_sources_[0]->evaluate(source_is_elementwise_constant_ ? CouplingOperatorType::static_x
+                                                                     : intersection.geometryInInside().center());
     const auto normal = intersection.centerUnitOuterNormal();
-    const auto g = numerical_boundary_flux_(u->dofs(), normal, param);
+    const auto g = numerical_boundary_flux_(u_, normal, param);
     const auto h_intersection = intersection.geometry().volume();
     const auto h_element = element.geometry().volume();
     for (size_t ii = 0; ii < m; ++ii)
@@ -163,7 +265,10 @@ public:
   } // ... apply(...)
 
 private:
+  using BaseType::local_sources_;
   const LambdaType numerical_boundary_flux_;
+  const bool source_is_elementwise_constant_;
+  mutable StateType u_;
 }; // class LocalAdvectionFvBoundaryTreatmentByCustomNumericalFluxOperator
 
 
@@ -171,41 +276,84 @@ template <class I, class SV, class SGV, size_t m = 1, class SF = double, class R
 class LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator
   : public LocalIntersectionOperatorInterface<I, SV, SGV, m, 1, SF, m, 1, RF, RGV, RV>
 {
-  using ThisType = LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator<I, SV, SGV, m, SF, RF, RGV, RV>;
+  using ThisType = LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator;
   using BaseType = LocalIntersectionOperatorInterface<I, SV, SGV, m, 1, SF, m, 1, RF, RGV, RV>;
+  using CouplingOperatorType = LocalAdvectionFvCouplingOperator<I, SV, SGV, m, SF, RF, RGV, RV>;
 
 public:
   using BaseType::d;
+  using typename BaseType::DiscreteSourceType;
   using typename BaseType::IntersectionType;
   using typename BaseType::LocalInsideRangeType;
   using typename BaseType::LocalOutsideRangeType;
+  using typename BaseType::SourceSpaceType;
   using typename BaseType::SourceType;
 
   using D = typename IntersectionType::ctype;
-  using NumericalFluxType = NumericalFluxInterface<d, m, RF>;
+  using NumericalFluxType = NumericalFluxInterface<I, d, m, RF>;
+  using LocalIntersectionCoords = typename NumericalFluxType::LocalIntersectionCoords;
   using FluxType = typename NumericalFluxType::FluxType;
-  using StateDofsType = ConstLocalDofVector<SV, SGV>;
-  using StateRangeType = typename XT::Functions::RangeTypeSelector<RF, m, 1>::type;
-  using LambdaType =
-      std::function<StateRangeType(const IntersectionType& /*intersection*/,
-                                   const FieldVector<D, d - 1>& /*xx_in_reference_intersection_coordinates*/,
-                                   const FluxType& /*flux*/,
-                                   const StateDofsType& /*u*/,
-                                   const XT::Common::Parameter& /*param*/)>;
+  using StateType = typename CouplingOperatorType::StateType;
+  using LambdaType = std::function<StateType(const IntersectionType& /*intersection*/,
+                                             const FieldVector<D, d - 1>& /*xx_in_reference_intersection_coordinates*/,
+                                             const FluxType& /*flux*/,
+                                             const StateType& /*u*/,
+                                             const XT::Common::Parameter& /*param*/)>;
+
+  // When using this constructor, source has to be set by a call to with_source before calling apply
+  LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator(
+      const NumericalFluxType& numerical_flux,
+      LambdaType boundary_extrapolation_lambda,
+      const XT::Common::ParameterType& boundary_treatment_param_type = {},
+      const bool source_is_elementwise_constant = false)
+    : BaseType(1, numerical_flux.parameter_type() + boundary_treatment_param_type)
+    , numerical_flux_(numerical_flux.copy())
+    , extrapolate_(boundary_extrapolation_lambda)
+    , source_is_elementwise_constant_(source_is_elementwise_constant)
+  {}
 
   LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator(
+      const SourceType& source,
       const NumericalFluxType& numerical_flux,
       LambdaType boundary_extrapolation_lambda,
-      const XT::Common::ParameterType& boundary_treatment_param_type = {})
-    : BaseType(numerical_flux.parameter_type() + boundary_treatment_param_type)
+      const XT::Common::ParameterType& boundary_treatment_param_type = {},
+      const bool source_is_elementwise_constant = false)
+    : BaseType(source, 1, numerical_flux.parameter_type() + boundary_treatment_param_type)
+    , numerical_flux_(numerical_flux.copy())
+    , extrapolate_(boundary_extrapolation_lambda)
+    , source_is_elementwise_constant_(source_is_elementwise_constant)
+  {}
+
+  LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator(
+      const SourceSpaceType& source_space,
+      const SV& source_vector,
+      const NumericalFluxType& numerical_flux,
+      LambdaType boundary_extrapolation_lambda,
+      const XT::Common::ParameterType& boundary_treatment_param_type = {},
+      const bool source_is_elementwise_constant = false)
+    : BaseType(source_space, source_vector, 1, numerical_flux.parameter_type() + boundary_treatment_param_type)
     , numerical_flux_(numerical_flux.copy())
     , extrapolate_(boundary_extrapolation_lambda)
+    , source_is_elementwise_constant_(source_is_elementwise_constant)
+  {}
+
+  LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator(
+      const DiscreteSourceType& source,
+      const NumericalFluxType& numerical_flux,
+      LambdaType boundary_extrapolation_lambda,
+      const XT::Common::ParameterType& boundary_treatment_param_type = {})
+    : ThisType(source,
+               numerical_flux,
+               boundary_extrapolation_lambda,
+               boundary_treatment_param_type,
+               source.space().type() == SpaceType::finite_volume)
   {}
 
   LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator(const ThisType& other)
-    : BaseType(other.parameter_type())
+    : BaseType(other)
     , numerical_flux_(other.numerical_flux_->copy())
     , extrapolate_(other.extrapolate_)
+    , source_is_elementwise_constant_(other.source_is_elementwise_constant_)
   {}
 
   std::unique_ptr<BaseType> copy() const override final
@@ -213,34 +361,50 @@ public:
     return std::make_unique<ThisType>(*this);
   }
 
-  void apply(const SourceType& source,
-             const IntersectionType& intersection,
-             LocalInsideRangeType& local_range_inside,
+  bool linear() const override final
+  {
+    return numerical_flux_->linear();
+  }
+
+  void apply(LocalInsideRangeType& local_range_inside,
              LocalOutsideRangeType& /*local_range_outside*/,
              const XT::Common::Parameter& param = {}) const override final
   {
-    DUNE_THROW_IF((source.space().type() != SpaceType::finite_volume)
-                      || (local_range_inside.space().type() != SpaceType::finite_volume),
+    DUNE_THROW_IF(local_range_inside.space().type() != SpaceType::finite_volume,
                   Exceptions::operator_error,
                   "Use LocalAdvectionDgBoundaryTreatmentByCustomExtrapolationOperator instead!");
-    const auto& element = local_range_inside.element();
-    const auto u = source.local_discrete_function(element);
-    const auto v = extrapolate_(intersection,
-                                ReferenceElements<D, d - 1>::general(intersection.type()).position(0, 0),
-                                numerical_flux_->flux(),
-                                u->dofs(),
-                                param);
+    if (numerical_flux_->x_dependent())
+      x_in_intersection_coords_ = intersection.geometry().local(intersection.geometry().center());
+    u_ = local_sources_[0]->evaluate(source_is_elementwise_constant_ ? CouplingOperatorType::static_x
+                                                                     : intersection.geometryInInside().center());
+    v_ = extrapolate_(intersection,
+                      ReferenceElements<D, d - 1>::general(intersection.type()).position(0, 0),
+                      numerical_flux_->flux(),
+                      u_,
+                      param);
     const auto normal = intersection.centerUnitOuterNormal();
-    const auto g = numerical_flux_->apply(u->dofs(), v, normal, param);
+    const auto g = numerical_flux_->apply(x_in_intersection_coords_, u_, v_, normal, param);
     const auto h_intersection = intersection.geometry().volume();
-    const auto h_element = element.geometry().volume();
+    const auto h_element = intersection.inside().geometry().volume();
     for (size_t ii = 0; ii < m; ++ii)
       local_range_inside.dofs()[ii] += (g[ii] * h_intersection) / h_element;
   } // ... apply(...)
 
+protected:
+  void post_bind(const I& inter) override
+  {
+    BaseType::post_bind(inter);
+    numerical_flux_->bind(inter);
+  }
+
 private:
+  using BaseType::local_sources_;
   std::unique_ptr<NumericalFluxType> numerical_flux_;
   const LambdaType extrapolate_;
+  const bool source_is_elementwise_constant_;
+  mutable LocalIntersectionCoords x_in_intersection_coords_;
+  mutable StateType u_;
+  mutable StateType v_;
 }; // class LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator
 
 
diff --git a/dune/gdt/local/operators/generic.hh b/dune/gdt/local/operators/generic.hh
index 8f6c232b03c560f003d189e91a47a43bb9270af0..72869b7ec25034cf46652f905928ed7347d8abba 100644
--- a/dune/gdt/local/operators/generic.hh
+++ b/dune/gdt/local/operators/generic.hh
@@ -38,23 +38,37 @@ template <class SV,
           class RV = SV>
 class GenericLocalElementOperator : public LocalElementOperatorInterface<SV, SGV, s_r, s_rC, SF, r_r, r_rC, RF, RGV, RV>
 {
-  using ThisType = GenericLocalElementOperator<SV, SGV, s_r, s_rC, SF, r_r, r_rC, RF, RGV, RV>;
+  using ThisType = GenericLocalElementOperator;
   using BaseType = LocalElementOperatorInterface<SV, SGV, s_r, s_rC, SF, r_r, r_rC, RF, RGV, RV>;
 
 public:
   using typename BaseType::LocalRangeType;
+  using typename BaseType::LocalSourceType;
   using typename BaseType::SourceType;
 
-  using GenericFunctionType = std::function<void(
-      const SourceType& /*source*/, LocalRangeType& /*local_range*/, const XT::Common::Parameter& /*param*/)>;
+  using GenericFunctionType = std::function<void(const SourceType& /*source*/,
+                                                 const std::vector<std::unique_ptr<LocalSourceType>>& /*local_source*/,
+                                                 LocalRangeType& /*local_range*/,
+                                                 const XT::Common::Parameter& /*param*/)>;
+
+  // When using this constructor, source has to be set by a call to with_source before calling apply
+  GenericLocalElementOperator(GenericFunctionType func,
+                              const size_t num_local_sources = 0,
+                              const XT::Common::ParameterType& param_type = {})
+    : BaseType(num_local_sources, param_type)
+    , func_(func)
+  {}
 
-  GenericLocalElementOperator(GenericFunctionType func, const XT::Common::ParameterType& param_type = {})
-    : BaseType(param_type)
+  GenericLocalElementOperator(const SourceType& source,
+                              GenericFunctionType func,
+                              const size_t num_local_sources = 0,
+                              const XT::Common::ParameterType& param_type = {})
+    : BaseType(source, num_local_sources, param_type)
     , func_(func)
   {}
 
   GenericLocalElementOperator(const ThisType& other)
-    : BaseType(other.parameter_type())
+    : BaseType(other)
     , func_(other.func_)
   {}
 
@@ -63,11 +77,9 @@ public:
     return std::make_unique<ThisType>(*this);
   }
 
-  void apply(const SourceType& source,
-             LocalRangeType& local_range,
-             const XT::Common::Parameter& param = {}) const override final
+  void apply(LocalRangeType& local_range, const XT::Common::Parameter& param = {}) const override final
   {
-    func_(source, local_range, this->parse_parameter(param));
+    func_(this->source(), this->local_sources(), local_range, this->parse_parameter(param));
   }
 
 private:
@@ -97,28 +109,41 @@ template <class I,
 class GenericLocalIntersectionOperator
   : public LocalIntersectionOperatorInterface<I, SV, SGV, s_r, s_rC, SF, r_r, r_rC, RF, IRGV, IRV, ORGV, ORV>
 {
-  using ThisType = GenericLocalIntersectionOperator<I, SV, SGV, s_r, s_rC, SF, r_r, r_rC, RF, IRGV, IRV, ORGV, ORV>;
+  using ThisType = GenericLocalIntersectionOperator;
   using BaseType = LocalIntersectionOperatorInterface<I, SV, SGV, s_r, s_rC, SF, r_r, r_rC, RF, IRGV, IRV, ORGV, ORV>;
 
 public:
   using typename BaseType::IntersectionType;
   using typename BaseType::LocalInsideRangeType;
   using typename BaseType::LocalOutsideRangeType;
+  using typename BaseType::LocalSourceType;
   using typename BaseType::SourceType;
 
   using GenericFunctionType = std::function<void(const SourceType& /*source*/,
+                                                 const std::vector<std::unique_ptr<LocalSourceType>>& /*local_source*/,
                                                  const IntersectionType& /*intersection*/,
                                                  LocalInsideRangeType& /*local_range_inside*/,
                                                  LocalOutsideRangeType& /*local_range_outside*/,
                                                  const XT::Common::Parameter& /*param*/)>;
 
-  GenericLocalIntersectionOperator(GenericFunctionType func, const XT::Common::ParameterType& param_type = {})
-    : BaseType(param_type)
+  // When using this constructor, source has to be set by a call to with_source before calling apply
+  GenericLocalIntersectionOperator(GenericFunctionType func,
+                                   const size_t num_local_sources = 1,
+                                   const XT::Common::ParameterType& param_type = {})
+    : BaseType(num_local_sources, param_type)
+    , func_(func)
+  {}
+
+  GenericLocalIntersectionOperator(const SourceType& source,
+                                   GenericFunctionType func,
+                                   const size_t num_local_sources = 1,
+                                   const XT::Common::ParameterType& param_type = {})
+    : BaseType(source, num_local_sources, param_type)
     , func_(func)
   {}
 
   GenericLocalIntersectionOperator(const ThisType& other)
-    : BaseType(other.parameter_type())
+    : BaseType(other)
     , func_(other.func_)
   {}
 
@@ -127,13 +152,16 @@ public:
     return std::make_unique<ThisType>(*this);
   }
 
-  void apply(const SourceType& source,
-             const IntersectionType& intersection,
-             LocalInsideRangeType& local_range_inside,
+  void apply(LocalInsideRangeType& local_range_inside,
              LocalOutsideRangeType& local_range_outside,
              const XT::Common::Parameter& param = {}) const override final
   {
-    func_(source, intersection, local_range_inside, local_range_outside, this->parse_parameter(param));
+    func_(this->source(),
+          this->local_sources(),
+          intersection,
+          local_range_inside,
+          local_range_outside,
+          this->parse_parameter(param));
   }
 
 private:
diff --git a/dune/gdt/local/operators/interfaces.hh b/dune/gdt/local/operators/interfaces.hh
index de484154e36398e27d48ba556fcc1ecfc51c2cf5..408005b0247e1c74a0dc9b74b164de595837189f 100644
--- a/dune/gdt/local/operators/interfaces.hh
+++ b/dune/gdt/local/operators/interfaces.hh
@@ -20,7 +20,10 @@
 
 #include <dune/xt/common/parameter.hh>
 #include <dune/xt/common/type_traits.hh>
+#include <dune/xt/common/memory.hh>
+
 #include <dune/xt/grid/type_traits.hh>
+#include <dune/xt/grid/bound-object.hh>
 
 #include <dune/gdt/local/discretefunction.hh>
 #include <dune/gdt/discretefunction/default.hh>
@@ -39,7 +42,9 @@ template <class SourceVector,
           class RangeField = SourceField,
           class RangeGridView = SourceGridView,
           class RangeVector = SourceVector>
-class LocalElementOperatorInterface : public XT::Common::ParametricInterface
+class LocalElementOperatorInterface
+  : public XT::Common::ParametricInterface
+  , public XT::Grid::ElementBoundObject<XT::Grid::extract_entity_t<SourceGridView>>
 {
   static_assert(
       std::is_same<XT::Grid::extract_entity_t<SourceGridView>, XT::Grid::extract_entity_t<RangeGridView>>::value, "");
@@ -50,7 +55,6 @@ public:
   static const constexpr size_t s_r = source_range_dim;
   static const constexpr size_t s_rC = source_range_dim_cols;
   using SR = SourceField;
-  using SourceType = ConstDiscreteFunction<SV, SGV, s_r, s_rC, SR>;
 
   using RV = RangeVector;
   using RGV = RangeGridView;
@@ -63,18 +67,96 @@ public:
   using D = typename LocalRangeType::D;
   using E = typename LocalRangeType::E;
 
-  using ThisType = LocalElementOperatorInterface<SV, SGV, s_r, s_rC, SR, r_r, r_rC, RR, RGV, RV>;
+  using SourceType = XT::Functions::GridFunctionInterface<E, s_r, s_rC, SR>;
+  using LocalSourceType = typename SourceType::LocalFunctionType;
+  using DiscreteSourceType = ConstDiscreteFunction<SV, SGV, s_r, s_rC, SR>;
+  using SourceSpaceType = typename DiscreteSourceType::SpaceType;
+
+  using ThisType = LocalElementOperatorInterface;
+
+  // Allows construction without source, source has to be set by a call to with_source before calling apply
+  LocalElementOperatorInterface(const size_t num_local_sources = 1, const XT::Common::ParameterType& param_type = {})
+    : XT::Common::ParametricInterface(param_type)
+    , source_()
+    , local_sources_(0)
+  {
+    for (size_t ii = 0; ii < num_local_sources; ++ii)
+      local_sources_.emplace_back(nullptr);
+  }
+
+  LocalElementOperatorInterface(const SourceType& source,
+                                const size_t num_local_sources = 1,
+                                const XT::Common::ParameterType& param_type = {})
+    : XT::Common::ParametricInterface(param_type)
+    , source_(source)
+    , local_sources_(0)
+  {
+    for (size_t ii = 0; ii < num_local_sources; ++ii)
+      local_sources_.emplace_back(source_.access().local_function());
+  }
 
-  LocalElementOperatorInterface(const XT::Common::ParameterType& param_type = {})
+  LocalElementOperatorInterface(const SourceSpaceType& source_space,
+                                const SV& source_vector,
+                                const size_t num_local_sources = 1,
+                                const XT::Common::ParameterType& param_type = {})
     : XT::Common::ParametricInterface(param_type)
-  {}
+    , source_(new DiscreteSourceType(source_space, source_vector))
+    , local_sources_(0)
+  {
+    for (size_t ii = 0; ii < num_local_sources; ++ii)
+      local_sources_.emplace_back(source_.access().local_function());
+  }
+
+  LocalElementOperatorInterface(const ThisType& other)
+    : XT::Common::ParametricInterface(other)
+    , XT::Grid::ElementBoundObject<E>()
+    , source_(other.source_)
+    , local_sources_(0)
+  {
+    for (size_t ii = 0; ii < other.local_sources_.size(); ++ii)
+      local_sources_.emplace_back(other.local_sources_[ii] ? source_.access().local_function() : nullptr);
+  }
 
   virtual ~LocalElementOperatorInterface() = default;
 
   virtual std::unique_ptr<ThisType> copy() const = 0;
 
-  virtual void
-  apply(const SourceType& source, LocalRangeType& local_range, const XT::Common::Parameter& param = {}) const = 0;
+  virtual bool linear() const
+  {
+    return false;
+  }
+
+  virtual void apply(LocalRangeType& local_range, const XT::Common::Parameter& param = {}) const = 0;
+
+  virtual std::unique_ptr<ThisType> with_source(const SourceType& src) const
+  {
+    auto ret = copy();
+    ret->source_ = XT::Common::ConstStorageProvider<SourceType>(src);
+    for (size_t ii = 0; ii < local_sources_.size(); ++ii)
+      ret->local_sources_[ii] = ret->source().local_function();
+    return ret;
+  }
+
+  const SourceType& source() const
+  {
+    return source_.access();
+  }
+
+  const std::vector<std::unique_ptr<LocalSourceType>>& local_sources() const
+  {
+    return local_sources_;
+  }
+
+protected:
+  // We are binding the first local_source to ele and leave the others unbound
+  void post_bind(const E& ele) override
+  {
+    if (local_sources_.size() > 0 && local_sources_[0])
+      local_sources_[0]->bind(ele);
+  }
+
+  XT::Common::ConstStorageProvider<SourceType> source_;
+  std::vector<std::unique_ptr<LocalSourceType>> local_sources_;
 }; // class LocalElementOperatorInterface
 
 
@@ -91,7 +173,9 @@ template <class Intersection,
           class InsideRangeVector = SourceVector,
           class OutsideRangeGridView = InsideRangeGridView,
           class OutsideRangeVector = InsideRangeVector>
-class LocalIntersectionOperatorInterface : public XT::Common::ParametricInterface
+class LocalIntersectionOperatorInterface
+  : public XT::Common::ParametricInterface
+  , public XT::Grid::IntersectionBoundObject<Intersection>
 {
   static_assert(XT::Grid::is_intersection<Intersection>::value, "");
   static_assert(std::is_same<typename Intersection::Entity, XT::Grid::extract_entity_t<InsideRangeGridView>>::value,
@@ -99,10 +183,13 @@ class LocalIntersectionOperatorInterface : public XT::Common::ParametricInterfac
   static_assert(std::is_same<typename Intersection::Entity, XT::Grid::extract_entity_t<OutsideRangeGridView>>::value,
                 "");
 
+  using ThisType = LocalIntersectionOperatorInterface;
+
 public:
   static const constexpr size_t d = Intersection::Entity::dimension;
   using D = typename Intersection::ctype;
   using I = Intersection;
+  using E = typename I::Entity;
   using IntersectionType = Intersection;
 
   using SV = SourceVector;
@@ -110,7 +197,10 @@ public:
   static const constexpr size_t s_r = source_range_dim;
   static const constexpr size_t s_rC = source_range_dim_cols;
   using SF = SourceField;
-  using SourceType = ConstDiscreteFunction<SV, SGV, s_r, s_rC, SF>;
+  using SourceType = XT::Functions::GridFunctionInterface<E, s_r, s_rC, SF>;
+  using LocalSourceType = typename SourceType::LocalFunctionType;
+  using DiscreteSourceType = ConstDiscreteFunction<SV, SGV, s_r, s_rC, SF>;
+  using SourceSpaceType = typename DiscreteSourceType::SpaceType;
 
   using IRV = InsideRangeVector;
   using IRGV = InsideRangeGridView;
@@ -123,25 +213,98 @@ public:
   using ORGV = OutsideRangeGridView;
   using LocalOutsideRangeType = LocalDiscreteFunction<ORV, ORGV, r_r, r_rC, RF>;
 
-  using ThisType = LocalIntersectionOperatorInterface<I, SV, SGV, s_r, s_rC, SF, r_r, r_rC, RF, IRGV, IRV, ORGV, ORV>;
+  // Allows construction without source, source has to be set by a call to with_source before calling apply
+  LocalIntersectionOperatorInterface(const size_t num_local_sources = 2,
+                                     const XT::Common::ParameterType& param_type = {})
+    : XT::Common::ParametricInterface(param_type)
+    , source_()
+    , local_sources_(0)
+  {
+    for (size_t ii = 0; ii < num_local_sources; ++ii)
+      local_sources_.emplace_back(nullptr);
+  }
+
+  LocalIntersectionOperatorInterface(const SourceType& src,
+                                     const size_t num_local_sources = 2,
+                                     const XT::Common::ParameterType& param_type = {})
+    : XT::Common::ParametricInterface(param_type)
+    , source_(src)
+    , local_sources_(0)
+  {
+    for (size_t ii = 0; ii < num_local_sources; ++ii)
+      local_sources_.emplace_back(source_.access().local_function());
+  }
 
-  LocalIntersectionOperatorInterface(const XT::Common::ParameterType& param_type = {})
+  LocalIntersectionOperatorInterface(const SourceSpaceType& source_space,
+                                     const SV& source_vector,
+                                     const size_t num_local_sources = 2,
+                                     const XT::Common::ParameterType& param_type = {})
     : XT::Common::ParametricInterface(param_type)
-  {}
+    , source_(new DiscreteSourceType(source_space, source_vector))
+    , local_sources_(0)
+  {
+    for (size_t ii = 0; ii < num_local_sources; ++ii)
+      local_sources_.emplace_back(source_.access().local_function());
+  }
+
+  LocalIntersectionOperatorInterface(const ThisType& other)
+    : XT::Common::ParametricInterface(other)
+    , XT::Grid::IntersectionBoundObject<Intersection>(other)
+    , source_(other.source_)
+    , local_sources_(0)
+  {
+    for (size_t ii = 0; ii < other.local_sources_.size(); ++ii)
+      local_sources_.emplace_back(other.local_sources_[ii] ? source_.access().local_function() : nullptr);
+  }
 
   virtual ~LocalIntersectionOperatorInterface() = default;
 
   virtual std::unique_ptr<ThisType> copy() const = 0;
 
+  virtual bool linear() const
+  {
+    return false;
+  }
+
   /**
    * \note Presumes that local_range_inside is already bound to intersection.inside() and local_range_outside is
    *       already bound to intersection.outside()!
    **/
-  virtual void apply(const SourceType& source,
-                     const IntersectionType& intersection,
-                     LocalInsideRangeType& local_range_inside,
+  virtual void apply(LocalInsideRangeType& local_range_inside,
                      LocalOutsideRangeType& local_range_outside,
                      const XT::Common::Parameter& param = {}) const = 0;
+
+  virtual std::unique_ptr<ThisType> with_source(const SourceType& src) const
+  {
+    auto ret = copy();
+    ret->source_ = XT::Common::ConstStorageProvider<SourceType>(src);
+    for (size_t ii = 0; ii < local_sources_.size(); ++ii)
+      ret->local_sources_[ii] = ret->source().local_function();
+    return ret;
+  }
+
+  const SourceType& source() const
+  {
+    return source_.access();
+  }
+
+  const std::vector<std::unique_ptr<LocalSourceType>>& local_sources() const
+  {
+    return local_sources_;
+  }
+
+protected:
+  // We are binding the first local_source to intersection.inside() and the second one to intersection.outside()
+  void post_bind(const I& inter) override
+  {
+    if (local_sources_.size() > 0 && local_sources_[0])
+      local_sources_[0]->bind(inter.inside());
+    if (local_sources_.size() > 1 && local_sources_[1])
+      local_sources_[1]->bind(inter.outside());
+  }
+
+  XT::Common::ConstStorageProvider<SourceType> source_;
+  std::vector<std::unique_ptr<LocalSourceType>> local_sources_;
 }; // class LocalIntersectionOperatorInterface
 
 
diff --git a/dune/gdt/norms.hh b/dune/gdt/norms.hh
index 185a8e8e9eeb2ab634fcc455b75ca314b1378e30..0cf216524667e970899c220a823b6a9d82126dc2 100644
--- a/dune/gdt/norms.hh
+++ b/dune/gdt/norms.hh
@@ -16,7 +16,7 @@
 #include <dune/xt/functions/interfaces/element-functions.hh>
 
 #include <dune/gdt/local/bilinear-forms/integrals.hh>
-#include <dune/gdt/local/integrands/elliptic.hh>
+#include <dune/gdt/local/integrands/laplace.hh>
 #include <dune/gdt/local/integrands/product.hh>
 #include <dune/gdt/operators/localizable-bilinear-form.hh>
 
@@ -69,7 +69,6 @@ template <class GridViewType, class F, size_t r>
 std::enable_if_t<XT::Grid::is_view<GridViewType>::value, LocalizableBilinearFormBase<GridViewType, r, 1, F, F, 1, 1, F>>
 make_localizable_elliptic_product(
     const GridViewType& grid_view,
-    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>, 1, 1, F>& diffusion_factor,
     const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>,
                                                GridViewType::dimension,
                                                GridViewType::dimension,
@@ -81,42 +80,39 @@ make_localizable_elliptic_product(
   using E = XT::Grid::extract_entity_t<GridViewType>;
   auto localizable_product = make_localizable_bilinear_form(grid_view, left, right);
   localizable_product.append(LocalElementIntegralBilinearForm<E, r, 1, F, F, r, 1, F>(
-      LocalEllipticIntegrand<E, r, F>(diffusion_factor, diffusion_tensor), over_integrate));
+      LocalLaplaceIntegrand<E, r, F>(diffusion_tensor), over_integrate));
   return localizable_product;
 }
 
 
 template <class GridViewType, class F, size_t r>
-std::enable_if_t<XT::Grid::is_view<GridViewType>::value, F> elliptic_product(
-    const GridViewType& grid_view,
-    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>, 1, 1, F>& diffusion_factor,
-    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>,
-                                               GridViewType::dimension,
-                                               GridViewType::dimension,
-                                               F>& diffusion_tensor,
-    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>, r, 1, F>& left,
-    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>, r, 1, F>& right,
-    const int over_integrate = 0)
+std::enable_if_t<XT::Grid::is_view<GridViewType>::value, F>
+elliptic_product(const GridViewType& grid_view,
+                 const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>,
+                                                            GridViewType::dimension,
+                                                            GridViewType::dimension,
+                                                            F>& diffusion_tensor,
+                 const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>, r, 1, F>& left,
+                 const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>, r, 1, F>& right,
+                 const int over_integrate = 0)
 {
-  auto product =
-      make_localizable_elliptic_product(grid_view, diffusion_factor, diffusion_tensor, left, right, over_integrate);
+  auto product = make_localizable_elliptic_product(grid_view, diffusion_tensor, left, right, over_integrate);
   product.assemble();
   return product.result();
 }
 
 
 template <class GridViewType, class F, size_t r>
-std::enable_if_t<XT::Grid::is_view<GridViewType>::value, F> elliptic_norm(
-    const GridViewType& grid_view,
-    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>, 1, 1, F>& diffusion_factor,
-    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>,
-                                               GridViewType::dimension,
-                                               GridViewType::dimension,
-                                               F>& diffusion_tensor,
-    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>, r, 1, F>& func,
-    const int over_integrate = 0)
+std::enable_if_t<XT::Grid::is_view<GridViewType>::value, F>
+elliptic_norm(const GridViewType& grid_view,
+              const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>,
+                                                         GridViewType::dimension,
+                                                         GridViewType::dimension,
+                                                         F>& diffusion_tensor,
+              const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GridViewType>, r, 1, F>& func,
+              const int over_integrate = 0)
 {
-  return std::sqrt(elliptic_product(grid_view, diffusion_factor, diffusion_tensor, func, func, over_integrate));
+  return std::sqrt(elliptic_product(grid_view, diffusion_tensor, func, func, over_integrate));
 }
 
 
diff --git a/dune/gdt/operators/advection-dg.hh b/dune/gdt/operators/advection-dg.hh
index 621515384a2ac8f762f2c02012ed84c262ae5272..284b2c3d2e54ed15c810dbc08902fcadc718834f 100644
--- a/dune/gdt/operators/advection-dg.hh
+++ b/dune/gdt/operators/advection-dg.hh
@@ -59,11 +59,11 @@ static inline size_t advection_dg_artificial_viscosity_default_component()
  *
  * \sa OperatorInterface
  */
-template <class M, class SGV, size_t m = 1, class RGV = SGV>
-class AdvectionDgOperator : public OperatorInterface<M, SGV, m, 1, m, 1, RGV>
+template <class M, class AGV, size_t m = 1, class RGV = AGV, class SGV = AGV>
+class AdvectionDgOperator : public LocalizableOperator<M, AGV, m, 1, m, 1, RGV, SGV>
 {
-  using ThisType = AdvectionDgOperator<M, SGV, m, RGV>;
-  using BaseType = OperatorInterface<M, SGV, m, 1, m, 1, RGV>;
+  using ThisType = AdvectionDgOperator;
+  using BaseType = LocalizableOperator<M, AGV, m, 1, m, 1, RGV, SGV>;
 
 protected:
   static const constexpr size_t d = SGV::dimension;
@@ -73,9 +73,8 @@ public:
   using typename BaseType::F;
   using typename BaseType::V;
 
-  using NumericalFluxType = NumericalFluxInterface<d, m, F>;
-
   using I = XT::Grid::extract_intersection_t<SGV>;
+  using NumericalFluxType = NumericalFluxInterface<I, d, m, F>;
   using BoundaryTreatmentByCustomNumericalFluxOperatorType =
       LocalAdvectionDgBoundaryTreatmentByCustomNumericalFluxOperator<I, V, SGV, m, F, F, RGV, V>;
   using BoundaryTreatmentByCustomExtrapolationOperatorType =
@@ -96,39 +95,41 @@ public:
       const double& artificial_viscosity_nu_1 = advection_dg_artificial_viscosity_default_nu_1(),
       const double& artificial_viscosity_alpha_1 = advection_dg_artificial_viscosity_default_alpha_1(),
       const size_t artificial_viscosity_component = advection_dg_artificial_viscosity_default_component())
-    : BaseType(numerical_flux.parameter_type())
-    , assembly_grid_view_(assembly_grid_view)
+    : BaseType(assembly_grid_view, source_space, range_space)
     , numerical_flux_(numerical_flux.copy())
-    , source_space_(source_space)
-    , range_space_(range_space)
     , periodicity_exception_(periodicity_exception.copy())
-    , local_mass_matrix_provider_(assembly_grid_view_, range_space_)
+    , local_mass_matrix_provider_(assembly_grid_view, range_space)
     , artificial_viscosity_nu_1_(artificial_viscosity_nu_1)
     , artificial_viscosity_alpha_1_(artificial_viscosity_alpha_1)
     , artificial_viscosity_component_(artificial_viscosity_component)
   {
     // we assemble these once, to be used in each apply later on
-    auto walker = XT::Grid::make_walker(assembly_grid_view_);
+    auto walker = XT::Grid::make_walker(assembly_grid_view);
     walker.append(local_mass_matrix_provider_);
     walker.walk(/*use_tbb=*/true);
-  }
+    // element contributions
+    this->append(
+        LocalAdvectionDgVolumeOperator<V, SGV, m, F, F, RGV, V>(local_mass_matrix_provider_, numerical_flux_->flux()));
+    // contributions from inner intersections
+    this->append(LocalAdvectionDgCouplingOperator<I, V, SGV, m, F, F, RGV, V>(
+                     local_mass_matrix_provider_, *numerical_flux_, /*compute_outside=*/false),
+                 XT::Grid::ApplyOn::InnerIntersections<SGV>());
+    // contributions from periodic boundaries
+    this->append(LocalAdvectionDgCouplingOperator<I, V, SGV, m, F, F, RGV, V>(
+                     local_mass_matrix_provider_, *numerical_flux_, /*compute_outside=*/false),
+                 *(XT::Grid::ApplyOn::PeriodicBoundaryIntersections<SGV>() && !(*periodicity_exception_)));
+    // artificial viscosity by shock capturing [DF2015, Sec. 8.5]
+    this->append(LocalAdvectionDgArtificialViscosityShockCapturingOperator<V, SGV, m, F, F, RGV, V>(
+        local_mass_matrix_provider_,
+        this->assembly_grid_view_,
+        artificial_viscosity_nu_1_,
+        artificial_viscosity_alpha_1_,
+        artificial_viscosity_component_));
+  } // AdvectionDgOperator(...)
 
   AdvectionDgOperator(ThisType&& source) = default;
 
-  bool linear() const override final
-  {
-    return numerical_flux_->linear();
-  }
-
-  const SourceSpaceType& source_space() const override final
-  {
-    return source_space_;
-  }
-
-  const RangeSpaceType& range_space() const override final
-  {
-    return range_space_;
-  }
+  using BaseType::append;
 
   /// \name Non-periodic boundary treatment
   /// \{
@@ -139,12 +140,11 @@ public:
          const XT::Common::ParameterType& boundary_treatment_parameter_type = {},
          const XT::Grid::IntersectionFilter<SGV>& filter = XT::Grid::ApplyOn::BoundaryIntersections<SGV>())
   {
-    boundary_treatments_by_custom_numerical_flux_.emplace_back(
-        new BoundaryTreatmentByCustomNumericalFluxOperatorType(local_mass_matrix_provider_,
-                                                               numerical_boundary_treatment_flux,
-                                                               numerical_boundary_treatment_flux_order,
-                                                               boundary_treatment_parameter_type),
-        filter.copy());
+    this->append(BoundaryTreatmentByCustomNumericalFluxOperatorType(local_mass_matrix_provider_,
+                                                                    numerical_boundary_treatment_flux,
+                                                                    numerical_boundary_treatment_flux_order,
+                                                                    boundary_treatment_parameter_type),
+                 filter.copy());
     return *this;
   } // ... append(...)
 
@@ -152,174 +152,44 @@ public:
                    const XT::Common::ParameterType& extrapolation_parameter_type = {},
                    const XT::Grid::IntersectionFilter<SGV>& filter = XT::Grid::ApplyOn::BoundaryIntersections<SGV>())
   {
-    boundary_treatments_by_custom_extrapolation_.emplace_back(
-        new BoundaryTreatmentByCustomExtrapolationOperatorType(
-            local_mass_matrix_provider_, *numerical_flux_, extrapolation, extrapolation_parameter_type),
-        filter.copy());
+    this->append(BoundaryTreatmentByCustomExtrapolationOperatorType(
+                     local_mass_matrix_provider_, *numerical_flux_, extrapolation, extrapolation_parameter_type),
+                 filter.copy());
     return *this;
   }
 
   /// \}
 
-  void apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& param = {}) const override
-  {
-    // some checks
-    DUNE_THROW_IF(!source.valid(), Exceptions::operator_error, "source contains inf or nan!");
-    DUNE_THROW_IF(!(this->parameter_type() <= param.type()),
-                  Exceptions::operator_error,
-                  "this->parameter_type() = " << this->parameter_type() << "\n   param.type() = " << param.type());
-    range.set_all(0);
-    auto source_function = make_discrete_function(this->source_space_, source);
-    auto range_function = make_discrete_function(this->range_space_, range);
-    // set up the actual operator
-    auto localizable_op = make_localizable_operator(this->assembly_grid_view_, source_function, range_function);
-    // element contributions
-    localizable_op.append(
-        LocalAdvectionDgVolumeOperator<V, SGV, m, F, F, RGV, V>(local_mass_matrix_provider_, numerical_flux_->flux()),
-        param);
-    // contributions from inner intersections
-    localizable_op.append(LocalAdvectionDgCouplingOperator<I, V, SGV, m, F, F, RGV, V>(
-                              local_mass_matrix_provider_, *numerical_flux_, /*compute_outside=*/false),
-                          param,
-                          XT::Grid::ApplyOn::InnerIntersections<SGV>());
-    // contributions from periodic boundaries
-    localizable_op.append(LocalAdvectionDgCouplingOperator<I, V, SGV, m, F, F, RGV, V>(
-                              local_mass_matrix_provider_, *numerical_flux_, /*compute_outside=*/false),
-                          param,
-                          *(XT::Grid::ApplyOn::PeriodicBoundaryIntersections<SGV>() && !(*periodicity_exception_)));
-    // contributions from other boundaries by custom numerical flux
-    for (const auto& boundary_treatment : boundary_treatments_by_custom_numerical_flux_) {
-      const auto& boundary_op = *boundary_treatment.first;
-      const auto& filter = *boundary_treatment.second;
-      localizable_op.append(boundary_op, param, filter);
-    }
-    // contributions from other boundaries by custom extrapolation
-    for (const auto& boundary_treatment : boundary_treatments_by_custom_extrapolation_) {
-      const auto& boundary_op = *boundary_treatment.first;
-      const auto& filter = *boundary_treatment.second;
-      localizable_op.append(boundary_op, param, filter);
-    }
-    // artificial viscosity by shock capturing [DF2015, Sec. 8.5]
-    localizable_op.append(LocalAdvectionDgArtificialViscosityShockCapturingOperator<V, SGV, m, F, F, RGV, V>(
-                              local_mass_matrix_provider_,
-                              this->assembly_grid_view_,
-                              artificial_viscosity_nu_1_,
-                              artificial_viscosity_alpha_1_,
-                              artificial_viscosity_component_),
-                          param);
-    // and apply it in a grid walk
-    localizable_op.assemble(/*use_tbb=*/true);
-    DUNE_THROW_IF(!range.valid(), Exceptions::operator_error, "range contains inf or nan!");
-  } // ... apply(...)
-
-  std::vector<std::string> jacobian_options() const override final
-  {
-    return {"finite-differences"};
-  }
-
-  XT::Common::Configuration jacobian_options(const std::string& type) const override final
-  {
-    DUNE_THROW_IF(type != this->jacobian_options().at(0), Exceptions::operator_error, "type = " << type);
-    return {{"type", type}, {"eps", "1e-7"}};
-  }
-
-  using BaseType::jacobian;
-
-  void jacobian(const VectorType& source,
-                MatrixOperatorType& jacobian_op,
-                const XT::Common::Configuration& opts,
-                const XT::Common::Parameter& param = {}) const override final
-  {
-    // some checks
-    DUNE_THROW_IF(!source.valid(), Exceptions::operator_error, "source contains inf or nan!");
-    DUNE_THROW_IF(!(this->parameter_type() <= param.type()),
-                  Exceptions::operator_error,
-                  "this->parameter_type() = " << this->parameter_type() << "\n   param.type() = " << param.type());
-    DUNE_THROW_IF(!opts.has_key("type"), Exceptions::operator_error, opts);
-    DUNE_THROW_IF(opts.get<std::string>("type") != jacobian_options().at(0), Exceptions::operator_error, opts);
-    const auto default_opts = jacobian_options(jacobian_options().at(0));
-    const auto eps = opts.get("eps", default_opts.template get<double>("eps"));
-    const auto parameter = param + XT::Common::Parameter({"finite-difference-jacobians.eps", eps});
-    // append the same local ops with the same filters as in apply() above
-    // element contributions
-    jacobian_op.append(
-        LocalAdvectionDgVolumeOperator<V, SGV, m, F, F, RGV, V>(local_mass_matrix_provider_, numerical_flux_->flux()),
-        source,
-        parameter);
-    // contributions from inner intersections
-    jacobian_op.append(LocalAdvectionDgCouplingOperator<I, V, SGV, m, F, F, RGV, V>(
-                           local_mass_matrix_provider_, *numerical_flux_, /*compute_outside=*/false),
-                       source,
-                       parameter,
-                       XT::Grid::ApplyOn::InnerIntersections<SGV>());
-    // contributions from periodic boundaries
-    jacobian_op.append(LocalAdvectionDgCouplingOperator<I, V, SGV, m, F, F, RGV, V>(
-                           local_mass_matrix_provider_, *numerical_flux_, /*compute_outside=*/false),
-                       source,
-                       parameter,
-                       *(XT::Grid::ApplyOn::PeriodicBoundaryIntersections<SGV>() && !(*periodicity_exception_)));
-    // contributions from other boundaries by custom numerical flux
-    for (const auto& boundary_treatment : boundary_treatments_by_custom_numerical_flux_) {
-      const auto& boundary_op = *boundary_treatment.first;
-      const auto& filter = *boundary_treatment.second;
-      jacobian_op.append(boundary_op, source, parameter, filter);
-    }
-    // contributions from other boundaries by custom extrapolation
-    for (const auto& boundary_treatment : boundary_treatments_by_custom_extrapolation_) {
-      const auto& boundary_op = *boundary_treatment.first;
-      const auto& filter = *boundary_treatment.second;
-      jacobian_op.append(boundary_op, source, parameter, filter);
-    }
-    // artificial viscosity by shock capturing [DF2015, Sec. 8.5]
-    jacobian_op.append(LocalAdvectionDgArtificialViscosityShockCapturingOperator<V, SGV, m, F, F, RGV, V>(
-                           local_mass_matrix_provider_,
-                           this->assembly_grid_view_,
-                           artificial_viscosity_nu_1_,
-                           artificial_viscosity_alpha_1_,
-                           artificial_viscosity_component_),
-                       source,
-                       param);
-  } // ... jacobian(...)
-
 protected:
-  const SGV assembly_grid_view_;
   const std::unique_ptr<const NumericalFluxType> numerical_flux_;
-  const SourceSpaceType& source_space_;
-  const RangeSpaceType& range_space_;
   std::unique_ptr<XT::Grid::IntersectionFilter<SGV>> periodicity_exception_;
   LocalMassMatrixProvider<RGV, m, 1, F> local_mass_matrix_provider_;
   const double artificial_viscosity_nu_1_;
   const double artificial_viscosity_alpha_1_;
   const size_t artificial_viscosity_component_;
-  std::list<std::pair<std::unique_ptr<BoundaryTreatmentByCustomNumericalFluxOperatorType>,
-                      std::unique_ptr<XT::Grid::IntersectionFilter<SGV>>>>
-      boundary_treatments_by_custom_numerical_flux_;
-  std::list<std::pair<std::unique_ptr<BoundaryTreatmentByCustomExtrapolationOperatorType>,
-                      std::unique_ptr<XT::Grid::IntersectionFilter<SGV>>>>
-      boundary_treatments_by_custom_extrapolation_;
 }; // class AdvectionDgOperator
 
 
-template <class MatrixType, class SGV, size_t m, class F, class RGV>
-std::enable_if_t<XT::LA::is_matrix<MatrixType>::value, AdvectionDgOperator<MatrixType, SGV, m, RGV>>
+template <class MatrixType, class AGV, size_t m, class F, class RGV, class SGV>
+std::enable_if_t<XT::LA::is_matrix<MatrixType>::value, AdvectionDgOperator<MatrixType, AGV, m, RGV, SGV>>
 make_advection_dg_operator(
-    const SGV& assembly_grid_view,
-    const NumericalFluxInterface<SGV::dimension, m, F>& numerical_flux,
+    const AGV& assembly_grid_view,
+    const NumericalFluxInterface<XT::Grid::extract_intersection_t<AGV>, AGV::dimension, m, F>& numerical_flux,
     const SpaceInterface<SGV, m, 1, F>& source_space,
     const SpaceInterface<RGV, m, 1, F>& range_space,
-    const XT::Grid::IntersectionFilter<SGV>& periodicity_exception = XT::Grid::ApplyOn::NoIntersections<SGV>(),
+    const XT::Grid::IntersectionFilter<AGV>& periodicity_exception = XT::Grid::ApplyOn::NoIntersections<AGV>(),
     const double& artificial_viscosity_nu_1 = advection_dg_artificial_viscosity_default_nu_1(),
     const double& artificial_viscosity_alpha_1 = advection_dg_artificial_viscosity_default_alpha_1(),
     const size_t artificial_viscosity_component = advection_dg_artificial_viscosity_default_component())
 {
-  return AdvectionDgOperator<MatrixType, SGV, m, RGV>(assembly_grid_view,
-                                                      numerical_flux,
-                                                      source_space,
-                                                      range_space,
-                                                      periodicity_exception,
-                                                      artificial_viscosity_nu_1,
-                                                      artificial_viscosity_alpha_1,
-                                                      artificial_viscosity_component);
+  return AdvectionDgOperator<MatrixType, AGV, m, RGV, SGV>(assembly_grid_view,
+                                                           numerical_flux,
+                                                           source_space,
+                                                           range_space,
+                                                           periodicity_exception,
+                                                           artificial_viscosity_nu_1,
+                                                           artificial_viscosity_alpha_1,
+                                                           artificial_viscosity_component);
 }
 
 
diff --git a/dune/gdt/operators/advection-fv-entropybased.hh b/dune/gdt/operators/advection-fv-entropybased.hh
new file mode 100644
index 0000000000000000000000000000000000000000..5b8462f3e6a846dea6d2cf424595d93bad2197fd
--- /dev/null
+++ b/dune/gdt/operators/advection-fv-entropybased.hh
@@ -0,0 +1,186 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2017 - 2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_OPERATORS_FV_ENTROPYBASED_HH
+#define DUNE_GDT_OPERATORS_FV_ENTROPYBASED_HH
+
+#include <dune/gdt/operators/interfaces.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+template <class OperatorImp, class InverseHessianOperatorImp>
+class EntropicCoordinatesOperator
+  : public OperatorInterface<typename OperatorImp::MatrixType, typename OperatorImp::SGV, OperatorImp::s_r>
+{
+  using BaseType = OperatorInterface<typename OperatorImp::MatrixType, typename OperatorImp::SGV, OperatorImp::s_r>;
+
+public:
+  using typename BaseType::RangeSpaceType;
+  using typename BaseType::SourceSpaceType;
+  using typename BaseType::VectorType;
+
+  using OperatorType = OperatorImp;
+  using InverseHessianOperatorType = InverseHessianOperatorImp;
+
+  EntropicCoordinatesOperator(const OperatorType& operator_in,
+                              const InverseHessianOperatorType& inverse_hessian_operator)
+    : operator_(operator_in)
+    , inverse_hessian_operator_(inverse_hessian_operator)
+  {}
+
+  bool linear() const override final
+  {
+    return false;
+  }
+
+  const SourceSpaceType& source_space() const override final
+  {
+    return operator_.source_space();
+  }
+
+  const RangeSpaceType& range_space() const override final
+  {
+    return operator_.range_space();
+  }
+
+  void apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& param) const override final
+  {
+    VectorType u_update = range;
+    std::fill(u_update.begin(), u_update.end(), 0.);
+    operator_.apply(source, u_update, param);
+    inverse_hessian_operator_.apply_inverse_hessian(source, u_update, range, param);
+  }
+
+  const OperatorType& operator_;
+  const InverseHessianOperatorType& inverse_hessian_operator_;
+}; // class EntropicCoordinatesOperator<...>
+
+template <class DensityOperatorImp, class AdvectionOperatorImp, class RhsOperatorImp, class InverseHessianOperatorImp>
+class EntropicCoordinatesCombinedOperator
+  : public OperatorInterface<typename AdvectionOperatorImp::MatrixType,
+                             typename AdvectionOperatorImp::SGV,
+                             AdvectionOperatorImp::s_r>
+{
+  using BaseType = OperatorInterface<typename AdvectionOperatorImp::MatrixType,
+                                     typename AdvectionOperatorImp::SGV,
+                                     AdvectionOperatorImp::s_r>;
+
+public:
+  using typename BaseType::RangeSpaceType;
+  using typename BaseType::SourceSpaceType;
+  using typename BaseType::VectorType;
+
+  using DensityOperatorType = DensityOperatorImp;
+  using AdvectionOperatorType = AdvectionOperatorImp;
+  using RhsOperatorType = RhsOperatorImp;
+  using InverseHessianOperatorType = InverseHessianOperatorImp;
+
+  EntropicCoordinatesCombinedOperator(const DensityOperatorType& density_op,
+                                      const AdvectionOperatorType& advection_op,
+                                      const RhsOperatorType& rhs_op,
+                                      const InverseHessianOperatorType& inverse_hessian_operator)
+    : density_op_(density_op)
+    , advection_op_(advection_op)
+    , rhs_op_(rhs_op)
+    , inverse_hessian_operator_(inverse_hessian_operator)
+  {}
+
+  bool linear() const override final
+  {
+    return false;
+  }
+
+  const SourceSpaceType& source_space() const override final
+  {
+    return advection_op_.source_space();
+  }
+
+  const RangeSpaceType& range_space() const override final
+  {
+    return advection_op_.range_space();
+  }
+
+  void apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& param) const override final
+  {
+    density_op_.apply(source, range, param);
+    VectorType u_update = range;
+    std::fill(u_update.begin(), u_update.end(), 0.);
+    advection_op_.apply(source, u_update, param);
+    u_update *= -1.;
+    rhs_op_.apply(source, u_update, param);
+    inverse_hessian_operator_.apply_inverse_hessian(source, u_update, range, param);
+  }
+
+  const DensityOperatorType& density_op_;
+  const AdvectionOperatorType& advection_op_;
+  const RhsOperatorType& rhs_op_;
+  const InverseHessianOperatorType& inverse_hessian_operator_;
+}; // class EntropicCoordinatesOperator<...>
+
+
+template <class AdvectionOperatorImp, class EntropySolverImp>
+class EntropyBasedMomentFvOperator
+  : public OperatorInterface<typename AdvectionOperatorImp::MatrixType,
+                             typename AdvectionOperatorImp::SGV,
+                             AdvectionOperatorImp::s_r>
+{
+  using BaseType = OperatorInterface<typename AdvectionOperatorImp::MatrixType,
+                                     typename AdvectionOperatorImp::SGV,
+                                     AdvectionOperatorImp::s_r>;
+
+public:
+  using typename BaseType::RangeSpaceType;
+  using typename BaseType::SourceSpaceType;
+  using typename BaseType::VectorType;
+
+  using AdvectionOperatorType = AdvectionOperatorImp;
+  using EntropySolverType = EntropySolverImp;
+
+  EntropyBasedMomentFvOperator(const AdvectionOperatorType& advection_operator, const EntropySolverType& entropy_solver)
+    : advection_operator_(advection_operator)
+    , entropy_solver_(entropy_solver)
+  {}
+
+  bool linear() const override final
+  {
+    return false;
+  }
+
+  const SourceSpaceType& source_space() const override final
+  {
+    return advection_operator_.source_space();
+  }
+
+  const RangeSpaceType& range_space() const override final
+  {
+    return advection_operator_.range_space();
+  }
+
+  void apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& param) const override final
+  {
+    // solve optimization problems and regularize if necessary
+    VectorType regularized = range;
+    entropy_solver_.apply(source, regularized, param);
+
+    std::fill(range.begin(), range.end(), 0.);
+    advection_operator_.apply(regularized, range, param);
+  }
+
+  const AdvectionOperatorType& advection_operator_;
+  const EntropySolverType& entropy_solver_;
+}; // class EntropyBasedMomentFvOperator<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_OPERATORS_FV_ENTROPYBASED_HH
diff --git a/dune/gdt/operators/advection-fv.hh b/dune/gdt/operators/advection-fv.hh
index ecd296021602d418383848ad55e23a1b387ac727..19cb4f456889f9f21ed13fb9b7c8196e0e8613a6 100644
--- a/dune/gdt/operators/advection-fv.hh
+++ b/dune/gdt/operators/advection-fv.hh
@@ -12,6 +12,8 @@
 #ifndef DUNE_GDT_OPERATORS_ADVECTION_FV_HH
 #define DUNE_GDT_OPERATORS_ADVECTION_FV_HH
 
+#include <dune/grid/common/partitionset.hh>
+
 #include <dune/xt/common/type_traits.hh>
 #include <dune/xt/grid/type_traits.hh>
 #include <dune/xt/grid/filters.hh>
@@ -36,23 +38,26 @@ namespace GDT {
  *
  * \sa OperatorInterface
  */
-template <class M, class SGV, size_t m = 1, class RGV = SGV>
-class AdvectionFvOperator : public OperatorInterface<M, SGV, m, 1, m, 1, RGV>
+template <class M, class AGV, size_t m = 1, class RGV = AGV, class SGV = AGV>
+class AdvectionFvOperator : public LocalizableOperator<M, AGV, m, 1, m, 1, RGV, SGV>
 {
-  using ThisType = AdvectionFvOperator<M, SGV, m, RGV>;
-  using BaseType = OperatorInterface<M, SGV, m, 1, m, 1, RGV>;
+  using ThisType = AdvectionFvOperator;
+  using BaseType = LocalizableOperator<M, AGV, m, 1, m, 1, RGV, SGV>;
 
 public:
+  using BaseType::s_r;
+  using BaseType::s_rC;
   using typename BaseType::F;
   using typename BaseType::V;
 
-  using NumericalFluxType = NumericalFluxInterface<SGV::dimension, m, F>;
-
   using I = XT::Grid::extract_intersection_t<SGV>;
+  using E = XT::Grid::extract_entity_t<SGV>;
+  using NumericalFluxType = NumericalFluxInterface<I, SGV::dimension, m, F>;
   using BoundaryTreatmentByCustomNumericalFluxOperatorType =
       LocalAdvectionFvBoundaryTreatmentByCustomNumericalFluxOperator<I, V, SGV, m, F, F, RGV, V>;
   using BoundaryTreatmentByCustomExtrapolationOperatorType =
       LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator<I, V, SGV, m, F, F, RGV, V>;
+  using SourceType = XT::Functions::GridFunctionInterface<E, s_r, s_rC, F>;
 
   using typename BaseType::MatrixOperatorType;
   using typename BaseType::RangeSpaceType;
@@ -65,30 +70,25 @@ public:
       const SourceSpaceType& source_space,
       const RangeSpaceType& range_space,
       const XT::Grid::IntersectionFilter<SGV>& periodicity_exception = XT::Grid::ApplyOn::NoIntersections<SGV>())
-    : BaseType(numerical_flux.parameter_type())
-    , assembly_grid_view_(assembly_grid_view)
+    : BaseType(assembly_grid_view, source_space, range_space)
     , numerical_flux_(numerical_flux.copy())
-    , source_space_(source_space)
-    , range_space_(range_space)
     , periodicity_exception_(periodicity_exception.copy())
-  {}
-
-  AdvectionFvOperator(ThisType&& source) = default;
-
-  bool linear() const override final
   {
-    return numerical_flux_->linear();
+    // contributions from inner intersections
+    this->append(LocalAdvectionFvCouplingOperator<I, V, SGV, m, F, F, RGV, V>(*numerical_flux_),
+                 XT::Grid::ApplyOn::InnerIntersectionsOnce<SGV>());
+    // contributions from periodic boundaries
+    this->append(LocalAdvectionFvCouplingOperator<I, V, SGV, m, F, F, RGV, V>(*numerical_flux_),
+                 *(XT::Grid::ApplyOn::PeriodicBoundaryIntersectionsOnce<SGV>() && !(*periodicity_exception_)));
   }
 
-  const SourceSpaceType& source_space() const override final
-  {
-    return source_space_;
-  }
+  AdvectionFvOperator(ThisType&& source)
+    : BaseType(std::move(source))
+    , numerical_flux_(std::move(source.numerical_flux_))
+    , periodicity_exception_(std::move(source.periodicity_exception_))
+  {}
 
-  const RangeSpaceType& range_space() const override final
-  {
-    return range_space_;
-  }
+  using BaseType::append;
 
   /// \name Non-periodic boundary treatment
   /// \{
@@ -98,12 +98,9 @@ public:
          const XT::Common::ParameterType& boundary_treatment_parameter_type = {},
          const XT::Grid::IntersectionFilter<SGV>& filter = XT::Grid::ApplyOn::BoundaryIntersections<SGV>())
   {
-    boundary_treatments_by_custom_numerical_flux_.emplace_back();
-    boundary_treatments_by_custom_numerical_flux_.back().first =
-        std::make_unique<BoundaryTreatmentByCustomNumericalFluxOperatorType>(numerical_boundary_treatment_flux,
-                                                                             boundary_treatment_parameter_type);
-    boundary_treatments_by_custom_numerical_flux_.back().second =
-        std::unique_ptr<XT::Grid::IntersectionFilter<SGV>>(filter.copy());
+    this->append(BoundaryTreatmentByCustomNumericalFluxOperatorType(numerical_boundary_treatment_flux,
+                                                                    boundary_treatment_parameter_type),
+                 filter);
     return *this;
   }
 
@@ -111,134 +108,30 @@ public:
                    const XT::Common::ParameterType& extrapolation_parameter_type = {},
                    const XT::Grid::IntersectionFilter<SGV>& filter = XT::Grid::ApplyOn::BoundaryIntersections<SGV>())
   {
-    boundary_treatments_by_custom_extrapolation_.emplace_back();
-    boundary_treatments_by_custom_extrapolation_.back().first =
-        std::make_unique<BoundaryTreatmentByCustomExtrapolationOperatorType>(
-            *numerical_flux_, extrapolation, extrapolation_parameter_type),
-    boundary_treatments_by_custom_extrapolation_.back().second =
-        std::unique_ptr<XT::Grid::IntersectionFilter<SGV>>(filter.copy());
+    this->append(BoundaryTreatmentByCustomExtrapolationOperatorType(
+                     *numerical_flux_, extrapolation, extrapolation_parameter_type),
+                 filter);
     return *this;
   }
 
   /// \}
 
-  using BaseType::apply;
-
-  void apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& param = {}) const override final
-  {
-    // some checks
-    DUNE_THROW_IF(!source.valid(), Exceptions::operator_error, "source contains inf or nan!");
-    DUNE_THROW_IF(!(this->parameter_type() <= param.type()),
-                  Exceptions::operator_error,
-                  "this->parameter_type() = " << this->parameter_type() << "\n   param.type() = " << param.type());
-    range.set_all(0);
-    const auto source_function = make_discrete_function(source_space_, source);
-    auto range_function = make_discrete_function(range_space_, range);
-    // set up the actual operator
-    auto localizable_op = make_localizable_operator(assembly_grid_view_, source_function, range_function);
-    // contributions from inner intersections
-    localizable_op.append(LocalAdvectionFvCouplingOperator<I, V, SGV, m, F, F, RGV, V>(*numerical_flux_),
-                          param,
-                          XT::Grid::ApplyOn::InnerIntersectionsOnce<SGV>());
-    // contributions from periodic boundaries
-    localizable_op.append(LocalAdvectionFvCouplingOperator<I, V, SGV, m, F, F, RGV, V>(*numerical_flux_),
-                          param,
-                          *(XT::Grid::ApplyOn::PeriodicBoundaryIntersectionsOnce<SGV>() && !(*periodicity_exception_)));
-    // contributions from other boundaries by custom numerical flux
-    for (const auto& boundary_treatment : boundary_treatments_by_custom_numerical_flux_) {
-      const auto& boundary_op = *boundary_treatment.first;
-      const auto& filter = *boundary_treatment.second;
-      localizable_op.append(boundary_op, param, filter);
-    }
-    // contributions from other boundaries by custom extrapolation
-    for (const auto& boundary_treatment : boundary_treatments_by_custom_extrapolation_) {
-      const auto& boundary_op = *boundary_treatment.first;
-      const auto& filter = *boundary_treatment.second;
-      localizable_op.append(boundary_op, param, filter);
-    }
-    // do the actual work
-    localizable_op.assemble(/*use_tbb=*/true);
-    DUNE_THROW_IF(!range.valid(), Exceptions::operator_error, "range contains inf or nan!");
-  } // ... apply(...)
-
-  std::vector<std::string> jacobian_options() const override final
-  {
-    return {"finite-differences"};
-  }
-
-  XT::Common::Configuration jacobian_options(const std::string& type) const override final
-  {
-    DUNE_THROW_IF(type != this->jacobian_options().at(0), Exceptions::operator_error, "type = " << type);
-    return {{"type", type}, {"eps", "1e-7"}};
-  }
-
-  using BaseType::jacobian;
-
-  void jacobian(const VectorType& source,
-                MatrixOperatorType& jacobian_op,
-                const XT::Common::Configuration& opts,
-                const XT::Common::Parameter& param = {}) const override final
-  {
-    // some checks
-    DUNE_THROW_IF(!source.valid(), Exceptions::operator_error, "source contains inf or nan!");
-    DUNE_THROW_IF(!(this->parameter_type() <= param.type()),
-                  Exceptions::operator_error,
-                  "this->parameter_type() = " << this->parameter_type() << "\n   param.type() = " << param.type());
-    DUNE_THROW_IF(!opts.has_key("type"), Exceptions::operator_error, opts);
-    DUNE_THROW_IF(opts.get<std::string>("type") != jacobian_options().at(0), Exceptions::operator_error, opts);
-    const auto default_opts = jacobian_options(jacobian_options().at(0));
-    const auto eps = opts.get("eps", default_opts.template get<double>("eps"));
-    const auto parameter = param + XT::Common::Parameter({"finite-difference-jacobians.eps", eps});
-    // append the same local ops with the same filters as in apply() above
-    // contributions from inner intersections
-    jacobian_op.append(LocalAdvectionFvCouplingOperator<I, V, SGV, m, F, F, RGV, V>(*numerical_flux_),
-                       source,
-                       parameter,
-                       XT::Grid::ApplyOn::InnerIntersectionsOnce<SGV>());
-    // contributions from periodic boundaries
-    jacobian_op.append(LocalAdvectionFvCouplingOperator<I, V, SGV, m, F, F, RGV, V>(*numerical_flux_),
-                       source,
-                       parameter,
-                       *(XT::Grid::ApplyOn::PeriodicBoundaryIntersectionsOnce<SGV>() && !(*periodicity_exception_)));
-    // contributions from other boundaries by custom numerical flux
-    for (const auto& boundary_treatment : boundary_treatments_by_custom_numerical_flux_) {
-      const auto& boundary_op = *boundary_treatment.first;
-      const auto& filter = *boundary_treatment.second;
-      jacobian_op.append(boundary_op, source, parameter, filter);
-    }
-    // contributions from other boundaries by custom extrapolation
-    for (const auto& boundary_treatment : boundary_treatments_by_custom_extrapolation_) {
-      const auto& boundary_op = *boundary_treatment.first;
-      const auto& filter = *boundary_treatment.second;
-      jacobian_op.append(boundary_op, source, parameter, filter);
-    }
-  } // ... jacobian(...)
-
 private:
-  const SGV assembly_grid_view_;
   std::unique_ptr<const NumericalFluxType> numerical_flux_;
-  const SourceSpaceType& source_space_;
-  const RangeSpaceType& range_space_;
   std::unique_ptr<XT::Grid::IntersectionFilter<SGV>> periodicity_exception_;
-  std::list<std::pair<std::unique_ptr<BoundaryTreatmentByCustomNumericalFluxOperatorType>,
-                      std::unique_ptr<XT::Grid::IntersectionFilter<SGV>>>>
-      boundary_treatments_by_custom_numerical_flux_;
-  std::list<std::pair<std::unique_ptr<BoundaryTreatmentByCustomExtrapolationOperatorType>,
-                      std::unique_ptr<XT::Grid::IntersectionFilter<SGV>>>>
-      boundary_treatments_by_custom_extrapolation_;
 }; // class AdvectionFvOperator
 
 
-template <class MatrixType, class SGV, size_t m, class F, class RGV>
-std::enable_if_t<XT::LA::is_matrix<MatrixType>::value, AdvectionFvOperator<MatrixType, SGV, m, RGV>>
+template <class MatrixType, class AGV, size_t m, class F, class RGV, class SGV>
+std::enable_if_t<XT::LA::is_matrix<MatrixType>::value, AdvectionFvOperator<MatrixType, AGV, m, RGV, SGV>>
 make_advection_fv_operator(
-    const SGV& assembly_grid_view,
-    const NumericalFluxInterface<SGV::dimension, m, F>& numerical_flux,
+    const AGV& assembly_grid_view,
+    const NumericalFluxInterface<XT::Grid::extract_intersection_t<AGV>, AGV::dimension, m, F>& numerical_flux,
     const SpaceInterface<SGV, m, 1, F>& source_space,
     const SpaceInterface<RGV, m, 1, F>& range_space,
-    const XT::Grid::IntersectionFilter<SGV>& periodicity_exception = XT::Grid::ApplyOn::NoIntersections<SGV>())
+    const XT::Grid::IntersectionFilter<AGV>& periodicity_exception = XT::Grid::ApplyOn::NoIntersections<AGV>())
 {
-  return AdvectionFvOperator<MatrixType, SGV, m, RGV>(
+  return AdvectionFvOperator<MatrixType, AGV, m, RGV, SGV>(
       assembly_grid_view, numerical_flux, source_space, range_space, periodicity_exception);
 }
 
diff --git a/dune/gdt/operators/advection-with-reconstruction.hh b/dune/gdt/operators/advection-with-reconstruction.hh
new file mode 100644
index 0000000000000000000000000000000000000000..0fb6eb227585d62b33342be28323996de27914a1
--- /dev/null
+++ b/dune/gdt/operators/advection-with-reconstruction.hh
@@ -0,0 +1,142 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2017 - 2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_OPERATORS_ADVECTION_WITH_RECONSTRUCTION_HH
+#define DUNE_GDT_OPERATORS_ADVECTION_WITH_RECONSTRUCTION_HH
+
+#include <dune/gdt/operators/interfaces.hh>
+
+#include "reconstruction/linear.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+template <class AdvectionOperatorImp, class ReconstructionOperatorImp>
+class AdvectionWithReconstructionOperator
+  : public OperatorInterface<typename AdvectionOperatorImp::MatrixType,
+                             typename AdvectionOperatorImp::SourceSpaceType::GridViewType,
+                             AdvectionOperatorImp::s_r>
+{
+  using BaseType = OperatorInterface<typename AdvectionOperatorImp::MatrixType,
+                                     typename AdvectionOperatorImp::SourceSpaceType::GridViewType,
+                                     AdvectionOperatorImp::s_r>;
+
+public:
+  using typename BaseType::RangeSpaceType;
+  using typename BaseType::SourceSpaceType;
+  using AdvectionOperatorType = AdvectionOperatorImp;
+  using ReconstructionOperatorType = ReconstructionOperatorImp;
+  using GridViewType = typename AdvectionOperatorType::SourceSpaceType::GridViewType;
+  static const size_t r = AdvectionOperatorType::s_r;
+  using VectorType = typename AdvectionOperatorType::VectorType;
+  using ReconstructionType = DiscreteFunction<VectorType, GridViewType, r>;
+
+  AdvectionWithReconstructionOperator(const AdvectionOperatorType& advection_operator,
+                                      const ReconstructionOperatorType& reconstruction_operator)
+    : advection_operator_(advection_operator)
+    , reconstruction_operator_(reconstruction_operator)
+    , reconstruction_(reconstruction_operator.range_space().mapper().size())
+  {}
+
+  bool linear() const override final
+  {
+    return false;
+  }
+
+  const SourceSpaceType& source_space() const override final
+  {
+    return reconstruction_operator_.source_space();
+  }
+
+  const RangeSpaceType& range_space() const override final
+  {
+    return advection_operator_.range_space();
+  }
+
+  void apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& param) const override final
+  {
+    // do reconstruction
+    reconstruction_operator_.apply(source, reconstruction_, param);
+
+    // apply advection operator
+    std::fill(range.begin(), range.end(), 0.);
+    advection_operator_.apply(reconstruction_, range, param);
+  }
+
+  const AdvectionOperatorType& advection_operator_;
+  const ReconstructionOperatorType& reconstruction_operator_;
+  mutable VectorType reconstruction_;
+}; // class AdvectionWithReconstructionOperator<...>
+
+
+template <class AdvectionOperatorImp, class ReconstructionOperatorImp>
+class AdvectionWithPointwiseReconstructionOperator
+  : public OperatorInterface<typename AdvectionOperatorImp::MatrixType,
+                             typename AdvectionOperatorImp::SourceSpaceType::GridViewType,
+                             AdvectionOperatorImp::s_r>
+{
+  using BaseType = OperatorInterface<typename AdvectionOperatorImp::MatrixType,
+                                     typename AdvectionOperatorImp::SourceSpaceType::GridViewType,
+                                     AdvectionOperatorImp::s_r>;
+
+public:
+  using typename BaseType::RangeSpaceType;
+  using typename BaseType::SourceSpaceType;
+  using AdvectionOperatorType = AdvectionOperatorImp;
+  using ReconstructionOperatorType = ReconstructionOperatorImp;
+  using GV = typename AdvectionOperatorType::SourceSpaceType::GridViewType;
+  static const size_t r = AdvectionOperatorType::s_r;
+  using VectorType = typename AdvectionOperatorType::VectorType;
+
+  AdvectionWithPointwiseReconstructionOperator(const AdvectionOperatorType& advection_operator,
+                                               const ReconstructionOperatorType& reconstruction_operator)
+    : advection_operator_(advection_operator)
+    , reconstruction_operator_(reconstruction_operator)
+  {}
+
+  bool linear() const override final
+  {
+    return false;
+  }
+
+  const SourceSpaceType& source_space() const override final
+  {
+    return advection_operator_.source_space();
+  }
+
+  const RangeSpaceType& range_space() const override final
+  {
+    return advection_operator_.range_space();
+  }
+
+  void apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& param) const override final
+  {
+    // do reconstruction
+    typename ReconstructionOperatorType::ReconstructedValuesType reconstructed_values(
+        source_space().grid_view().indexSet().size(0));
+    typename ReconstructionOperatorType::ReconstructedFunctionType reconstructed_function(source_space().grid_view(),
+                                                                                          reconstructed_values);
+    reconstruction_operator_.apply(source, reconstructed_function, param);
+
+    // apply advection operator
+    std::fill(range.begin(), range.end(), 0.);
+    advection_operator_.apply(reconstructed_function, range, param);
+  }
+
+  const AdvectionOperatorType& advection_operator_;
+  const ReconstructionOperatorType& reconstruction_operator_;
+}; // class AdvectionWithReconstructionOperator<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_OPERATORS_ADVECTION_WITH_RECONSTRUCTION_HH
diff --git a/dune/gdt/operators/constant.hh b/dune/gdt/operators/constant.hh
index 80c58228f217eba4c3016b7cfadb23c960cf59f7..c32b52462ce4c246a2b17b1bfbc44b634d6d97e4 100644
--- a/dune/gdt/operators/constant.hh
+++ b/dune/gdt/operators/constant.hh
@@ -31,7 +31,7 @@ namespace GDT {
 template <class M, class SGV, size_t s_r = 1, size_t s_rC = 1, size_t r_r = s_r, size_t r_rC = s_rC, class RGV = SGV>
 class ConstantOperator : public OperatorInterface<M, SGV, s_r, s_rC, r_r, r_rC, RGV>
 {
-  using ThisType = ConstantOperator<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
+  using ThisType = ConstantOperator;
   using BaseType = OperatorInterface<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
 
 public:
diff --git a/dune/gdt/operators/identity.hh b/dune/gdt/operators/identity.hh
index ce0c29e00775e5587ab50e77e513586e90eb0d80..34c98dd03fe04f1f49d306ce7ea76da75814eb13 100644
--- a/dune/gdt/operators/identity.hh
+++ b/dune/gdt/operators/identity.hh
@@ -31,7 +31,7 @@ namespace GDT {
 template <class M, class GV, size_t r = 1, size_t rC = 1>
 class IdentityOperator : public OperatorInterface<M, GV, r, rC>
 {
-  using ThisType = IdentityOperator<M, GV, r, rC>;
+  using ThisType = IdentityOperator;
   using BaseType = OperatorInterface<M, GV, r, rC>;
 
 public:
diff --git a/dune/gdt/operators/interfaces.hh b/dune/gdt/operators/interfaces.hh
index 4003ba4ec939e86ce1065259fb7ecb7f3d3e994f..e0eac6f609f250ceab627c88ffedb1f18581ff0d 100644
--- a/dune/gdt/operators/interfaces.hh
+++ b/dune/gdt/operators/interfaces.hh
@@ -133,6 +133,7 @@ public:
   using F = FieldType;
 
   using SGV = SourceGridView;
+  using E = XT::Grid::extract_entity_t<SGV>;
   static const constexpr size_t s_r = source_dim;
   static const constexpr size_t s_rC = source_dim_cols;
 
@@ -141,6 +142,7 @@ public:
   static const constexpr size_t r_rC = range_dim_cols;
 
   using SourceSpaceType = SpaceInterface<SGV, s_r, s_rC, F>;
+  using SourceFunctionInterfaceType = XT::Functions::GridFunctionInterface<E, s_r, s_rC, F>;
   using SourceFunctionType = DiscreteFunction<V, SGV, s_r, s_rC, F>;
   using ConstSourceFunctionType = ConstDiscreteFunction<V, SGV, s_r, s_rC, F>;
 
@@ -148,7 +150,7 @@ public:
   using RangeFunctionType = DiscreteFunction<V, RGV, r_r, r_rC, F>;
   using ConstRangeFunctionType = ConstDiscreteFunction<V, RGV, r_r, r_rC, F>;
 
-  using ThisType = OperatorInterface<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
+  using ThisType = OperatorInterface;
   using MatrixOperatorType = MatrixOperator<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
   using ConstLincombOperatorType = ConstLincombOperator<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
   using LincombOperatorType = LincombOperator<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
@@ -531,7 +533,7 @@ invert_options(some_type).get<std::string>("type") == some_type
 
   virtual VectorType apply(const VectorType& source, const XT::Common::Parameter& param = {}) const
   {
-    VectorType range(this->range_space().mapper().size(), 0);
+    VectorType range(this->range_space().mapper().size(), 0.);
     this->apply(source, range, param);
     return range;
   }
diff --git a/dune/gdt/operators/ipdg-flux-reconstruction.hh b/dune/gdt/operators/ipdg-flux-reconstruction.hh
index 92ac8825b392832bfae1a9bfadf98c4f9d398789..187ae6b157ffa1227256b853adbd0a47a189b18c 100644
--- a/dune/gdt/operators/ipdg-flux-reconstruction.hh
+++ b/dune/gdt/operators/ipdg-flux-reconstruction.hh
@@ -41,7 +41,7 @@ class IpdgFluxReconstructionOperator : public OperatorInterface<M, SGV, 1, 1, RG
 {
   static_assert(XT::Grid::is_view<AssemblyGridView>::value, "");
   using BaseType = OperatorInterface<M, SGV, 1, 1, RGV::dimension, 1, RGV>;
-  using ThisType = IpdgFluxReconstructionOperator<M, AssemblyGridView, ipdg, SGV, RGV>;
+  using ThisType = IpdgFluxReconstructionOperator;
 
 public:
   using typename BaseType::F;
diff --git a/dune/gdt/operators/lincomb.hh b/dune/gdt/operators/lincomb.hh
index 772263af30ff0b878e1459f6a3d41e6c0dae7cce..6447a00cef2bb872afaedb6bb1942be1095ba7a7 100644
--- a/dune/gdt/operators/lincomb.hh
+++ b/dune/gdt/operators/lincomb.hh
@@ -28,7 +28,7 @@ namespace GDT {
 template <class M, class SGV, size_t s_r = 1, size_t s_rC = 1, size_t r_r = s_r, size_t r_rC = s_rC, class RGV = SGV>
 class ConstLincombOperator : public OperatorInterface<M, SGV, s_r, s_rC, r_r, r_rC, RGV>
 {
-  using ThisType = ConstLincombOperator<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
+  using ThisType = ConstLincombOperator;
   using BaseType = OperatorInterface<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
 
 public:
@@ -338,7 +338,7 @@ make_const_lincomb_operator(const SpaceInterface<GV, r, rC, F>& space)
 template <class M, class SGV, size_t s_r = 1, size_t s_rC = 1, size_t r_r = s_r, size_t r_rC = s_rC, class RGV = SGV>
 class LincombOperator : public ConstLincombOperator<M, SGV, s_r, s_rC, r_r, r_rC, RGV>
 {
-  using ThisType = LincombOperator<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
+  using ThisType = LincombOperator;
   using BaseType = ConstLincombOperator<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
 
 public:
diff --git a/dune/gdt/operators/localizable-operator.hh b/dune/gdt/operators/localizable-operator.hh
index bbc43dc2076cbaff181213db2e88d7d6c1d6f15b..4e915cda68de28400d3847111ec9ecc041d6cb35 100644
--- a/dune/gdt/operators/localizable-operator.hh
+++ b/dune/gdt/operators/localizable-operator.hh
@@ -11,6 +11,9 @@
 #ifndef DUNE_GDT_OPERATORS_LOCALIZABLE_OPERATOR_HH
 #define DUNE_GDT_OPERATORS_LOCALIZABLE_OPERATOR_HH
 
+#include <list>
+
+#include <dune/xt/common/deprecated.hh>
 #include <dune/xt/la/type_traits.hh>
 #include <dune/xt/grid/type_traits.hh>
 #include <dune/xt/grid/walker.hh>
@@ -20,13 +23,17 @@
 #include <dune/gdt/local/operators/generic.hh>
 #include <dune/gdt/local/operators/interfaces.hh>
 
+#include "interfaces.hh"
+
 namespace Dune {
 namespace GDT {
 
 
 /**
- * \todo Rename this one to LocalizableDiscreteOperatorBase, create LocalizableOperatorBase which accepts a GridFunction
- *       as source, derive LocalizableDiscreteOperatorBase from LocalizableOperatorBase.
+ * \todo Create LocalizableOperatorApplicator which accepts a GridFunction as source, derive
+ *       LocalizableDiscreteOperatorApplicator from LocalizableOperatorApplicator.
+ *
+ * \note Most likely, you want to use LocalizableOperator.
  */
 template <class AssemblyGridView,
           class SourceVector,
@@ -39,7 +46,7 @@ template <class AssemblyGridView,
           class RangeField = SourceField,
           class RangeGridView = SourceGridView,
           class RangeVector = SourceVector>
-class LocalizableOperatorBase : public XT::Grid::Walker<AssemblyGridView>
+class LocalizableDiscreteOperatorApplicator : public XT::Grid::Walker<AssemblyGridView>
 {
   static_assert(XT::Grid::is_view<AssemblyGridView>::value, "");
   static_assert(XT::LA::is_vector<SourceVector>::value, "");
@@ -47,17 +54,17 @@ class LocalizableOperatorBase : public XT::Grid::Walker<AssemblyGridView>
   static_assert(XT::Grid::is_view<RangeGridView>::value, "");
   static_assert(XT::LA::is_vector<RangeVector>::value, "");
 
-  using ThisType = LocalizableOperatorBase<AssemblyGridView,
-                                           SourceVector,
-                                           source_range_dim,
-                                           source_range_dim_cols,
-                                           SourceField,
-                                           SourceGridView,
-                                           range_range_dim,
-                                           range_range_dim_cols,
-                                           RangeField,
-                                           RangeGridView,
-                                           RangeVector>;
+  using ThisType = LocalizableDiscreteOperatorApplicator<AssemblyGridView,
+                                                         SourceVector,
+                                                         source_range_dim,
+                                                         source_range_dim_cols,
+                                                         SourceField,
+                                                         SourceGridView,
+                                                         range_range_dim,
+                                                         range_range_dim_cols,
+                                                         RangeField,
+                                                         RangeGridView,
+                                                         RangeVector>;
   using BaseType = XT::Grid::Walker<AssemblyGridView>;
 
 public:
@@ -66,10 +73,12 @@ public:
 
   using SV = SourceVector;
   using SGV = SourceGridView;
+  using E = XT::Grid::extract_entity_t<SGV>;
   static const constexpr size_t s_r = source_range_dim;
   static const constexpr size_t s_rC = source_range_dim_cols;
   using SF = SourceField;
-  using SourceType = ConstDiscreteFunction<SV, SGV, s_r, s_rC, SF>;
+  using DiscreteSourceType = ConstDiscreteFunction<SV, SGV, s_r, s_rC, SF>;
+  using SourceType = XT::Functions::GridFunctionInterface<E, s_r, s_rC, SF>;
 
   using RV = RangeVector;
   using RGV = RangeGridView;
@@ -90,7 +99,7 @@ public:
       GenericLocalIntersectionOperator<I, SV, SGV, s_r, s_rC, SF, r_r, r_rC, RF, RGV, RV>;
   using GenericLocalIntersectionFunctionType = typename GenericLocalIntersectionOperatorType::GenericFunctionType;
 
-  LocalizableOperatorBase(AssemblyGridViewType assembly_grid_view, const SourceType& src, RangeType& rng)
+  LocalizableDiscreteOperatorApplicator(AssemblyGridViewType assembly_grid_view, const SourceType& src, RangeType& rng)
     : BaseType(assembly_grid_view)
     , source_(src)
     , range_(rng)
@@ -122,7 +131,7 @@ public:
                    const XT::Common::Parameter& param = {},
                    const ElementFilterType& filter = ApplyOnAllElements())
   {
-    this->append(make_local_element_operator_applicator(local_operator, source_, range_, param).release(), filter);
+    this->append(make_local_element_operator_applicator(local_operator, range_, param).release(), filter);
     return *this;
   }
 
@@ -130,7 +139,7 @@ public:
                    const XT::Common::Parameter& param = {},
                    const ElementFilterType& filter = ApplyOnAllElements())
   {
-    this->append(GenericLocalElementOperatorType(generic_function, param.type()), param, filter);
+    this->append(GenericLocalElementOperatorType(source(), generic_function, param.type()), param, filter);
     return *this;
   }
 
@@ -139,7 +148,7 @@ public:
          const XT::Common::Parameter& param = {},
          const IntersectionFilterType& filter = ApplyOnAllIntersections())
   {
-    this->append(make_local_intersection_operator_applicator(local_operator, source_, range_, param).release(), filter);
+    this->append(make_local_intersection_operator_applicator(local_operator, range_, param).release(), filter);
     return *this;
   }
 
@@ -147,7 +156,7 @@ public:
                    const XT::Common::Parameter& param = {},
                    const IntersectionFilterType& filter = ApplyOnAllIntersections())
   {
-    this->append(GenericLocalIntersectionFunctionType(generic_function, param.type()), param, filter);
+    this->append(GenericLocalIntersectionOperatorType(source(), generic_function, param.type()), param, filter);
     return *this;
   }
 
@@ -164,8 +173,75 @@ protected:
   const SourceType& source_;
   RangeType& range_;
   bool assembled_;
-}; // class LocalizableOperatorBase
+}; // class LocalizableDiscreteOperatorApplicator
+
+
+template <class AssemblyGridView,
+          class SourceVector,
+          size_t source_range_dim = 1,
+          size_t source_range_dim_cols = 1,
+          class SourceField = double,
+          class SourceGridView = AssemblyGridView,
+          size_t range_range_dim = source_range_dim,
+          size_t range_range_dim_cols = source_range_dim_cols,
+          class RangeField = SourceField,
+          class RangeGridView = SourceGridView,
+          class RangeVector = SourceVector>
+using LocalizableOperatorBase DXT_DEPRECATED_MSG("Use LocalizableDiscreteOperatorApplicator instead (12.09.2019)!") =
+    LocalizableDiscreteOperatorApplicator<AssemblyGridView,
+                                          SourceVector,
+                                          source_range_dim,
+                                          source_range_dim_cols,
+                                          SourceField,
+                                          SourceGridView,
+                                          range_range_dim,
+                                          range_range_dim_cols,
+                                          RangeField,
+                                          RangeGridView,
+                                          RangeVector>;
+
+
+template <class AGV,
+          class SV,
+          size_t s_r,
+          size_t s_rC,
+          class SF,
+          class SGV,
+          size_t r_r,
+          size_t r_rC,
+          class RF,
+          class RGV,
+          class RV>
+std::enable_if_t<XT::Grid::is_layer<AGV>::value,
+                 LocalizableDiscreteOperatorApplicator<AGV, SV, s_r, s_rC, SF, SGV, r_r, r_rC, RF, RGV, RV>>
+make_localizable_operator_applicator(AGV assembly_grid_view,
+                                     const ConstDiscreteFunction<SV, SGV, s_r, s_rC, SF>& source,
+                                     DiscreteFunction<RV, RGV, r_r, r_rC, RF>& range)
+{
+  return LocalizableDiscreteOperatorApplicator<AGV, SV, s_r, s_rC, SF, SGV, r_r, r_rC, RF, RGV, RV>(
+      assembly_grid_view, source, range);
+}
 
+template <class AGV,
+          size_t s_r,
+          size_t s_rC,
+          class SF,
+          size_t r_r,
+          size_t r_rC,
+          class RF,
+          class RGV,
+          class RV,
+          class SV = RV>
+std::enable_if_t<XT::Grid::is_layer<AGV>::value,
+                 LocalizableDiscreteOperatorApplicator<AGV, SV, s_r, s_rC, SF, AGV, r_r, r_rC, RF, RGV, RV>>
+make_localizable_operator_applicator(
+    AGV assembly_grid_view,
+    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<AGV>, s_r, s_rC, SF>& source,
+    DiscreteFunction<RV, RGV, r_r, r_rC, RF>& range)
+{
+  return LocalizableDiscreteOperatorApplicator<AGV, SV, s_r, s_rC, SF, AGV, r_r, r_rC, RF, RGV, RV>(
+      assembly_grid_view, source, range);
+}
 
 template <class AGV,
           class SV,
@@ -180,15 +256,191 @@ template <class AGV,
           class RV>
 std::enable_if_t<XT::Grid::is_layer<AGV>::value,
                  LocalizableOperatorBase<AGV, SV, s_r, s_rC, SF, SGV, r_r, r_rC, RF, RGV, RV>>
-make_localizable_operator(AGV assembly_grid_view,
-                          const ConstDiscreteFunction<SV, SGV, s_r, s_rC, SF>& source,
-                          DiscreteFunction<RV, RGV, r_r, r_rC, RF>& range)
+make_localizable_operator(
+    AGV assembly_grid_view,
+    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<SGV>, s_r, s_rC, SF>& source,
+    DiscreteFunction<RV, RGV, r_r, r_rC, RF>& range)
 {
   return LocalizableOperatorBase<AGV, SV, s_r, s_rC, SF, SGV, r_r, r_rC, RF, RGV, RV>(
       assembly_grid_view, source, range);
 }
 
 
+/**
+ * \note See OperatorInterface for a description of the template arguments.
+ *
+ * \sa OperatorInterface
+ */
+template <class M,
+          class AssemblyGridView,
+          size_t s = 1,
+          size_t sC = 1,
+          size_t r = s,
+          size_t rC = sC,
+          class RGV = AssemblyGridView,
+          class SGV = AssemblyGridView>
+class LocalizableOperator : public OperatorInterface<M, SGV, s, sC, r, rC, RGV>
+{
+  static_assert(XT::Grid::is_view<AssemblyGridView>::value, "");
+  using ThisType = LocalizableOperator;
+  using BaseType = OperatorInterface<M, SGV, s, sC, r, rC, RGV>;
+
+public:
+  using AGV = AssemblyGridView;
+  using typename BaseType::F;
+  using typename BaseType::V;
+
+  using I = XT::Grid::extract_intersection_t<SGV>;
+
+  using typename BaseType::MatrixOperatorType;
+  using typename BaseType::RangeSpaceType;
+  using typename BaseType::SourceFunctionInterfaceType;
+  using typename BaseType::SourceSpaceType;
+  using typename BaseType::VectorType;
+
+  using LocalElementOperatorType = LocalElementOperatorInterface<V, SGV, s, sC, F, r, rC, F, RGV, V>;
+  using LocalIntersectionOperatorType = LocalIntersectionOperatorInterface<I, V, SGV, s, sC, F, r, rC, F, RGV, V>;
+
+  LocalizableOperator(const AGV& assembly_grid_view,
+                      const SourceSpaceType& source_space,
+                      const RangeSpaceType& range_space)
+    : BaseType()
+    , assembly_grid_view_(assembly_grid_view)
+    , source_space_(source_space)
+    , range_space_(range_space)
+    , linear_(true)
+  {}
+
+  LocalizableOperator(ThisType&& source) = default;
+
+  bool linear() const override final
+  {
+    return linear_;
+  }
+
+  const SourceSpaceType& source_space() const override final
+  {
+    return source_space_;
+  }
+
+  const RangeSpaceType& range_space() const override final
+  {
+    return range_space_;
+  }
+
+  ThisType& append(const LocalElementOperatorType& local_operator,
+                   const XT::Grid::ElementFilter<AGV>& filter = XT::Grid::ApplyOn::AllElements<AGV>())
+  {
+    linear_ = linear_ && local_operator.linear();
+    this->extend_parameter_type(local_operator.parameter_type());
+    local_element_operators_.emplace_back(local_operator.copy(), filter.copy());
+    return *this;
+  }
+
+  ThisType& append(const LocalIntersectionOperatorType& local_operator,
+                   const XT::Grid::IntersectionFilter<AGV>& filter = XT::Grid::ApplyOn::AllIntersections<AGV>())
+  {
+    linear_ = linear_ && local_operator.linear();
+    this->extend_parameter_type(local_operator.parameter_type());
+    local_intersection_operators_.emplace_back(local_operator.copy(), filter.copy());
+    return *this;
+  }
+
+  using BaseType::apply;
+
+  void apply(const SourceFunctionInterfaceType& source_function,
+             VectorType& range,
+             const XT::Common::Parameter& param = {}) const
+  {
+    DUNE_THROW_IF(!(this->parameter_type() <= param.type()),
+                  Exceptions::operator_error,
+                  "this->parameter_type() = " << this->parameter_type() << "\n   param.type() = " << param.type());
+    range.set_all(0);
+    auto range_function = make_discrete_function(this->range_space_, range);
+    // set up the actual operator
+    auto localizable_op =
+        make_localizable_operator_applicator(this->assembly_grid_view_, source_function, range_function);
+    // - element contributions
+    for (const auto& op_and_filter : local_element_operators_) {
+      const auto local_op = op_and_filter.first->with_source(source_function);
+      const auto& filter = *op_and_filter.second;
+      localizable_op.append(*local_op, param, filter);
+    }
+    // - intersection contributions
+    for (const auto& op_and_filter : local_intersection_operators_) {
+      const auto local_op = op_and_filter.first->with_source(source_function);
+      const auto& filter = *op_and_filter.second;
+      localizable_op.append(*local_op, param, filter);
+    }
+    // and apply it in a grid walk
+    localizable_op.assemble(/*use_tbb=*/true);
+    DUNE_THROW_IF(!range.valid(), Exceptions::operator_error, "range contains inf or nan!");
+  } // ... apply(...)
+
+  void apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& param = {}) const override
+  {
+    DUNE_THROW_IF(!source.valid(), Exceptions::operator_error, "source contains inf or nan!");
+    const auto source_function = make_discrete_function(this->source_space_, source);
+    apply(source_function, range, param);
+  } // ... apply(...)
+
+  std::vector<std::string> jacobian_options() const override final
+  {
+    return {"finite-differences"};
+  }
+
+  XT::Common::Configuration jacobian_options(const std::string& type) const override final
+  {
+    DUNE_THROW_IF(type != this->jacobian_options().at(0), Exceptions::operator_error, "type = " << type);
+    return {{"type", type}, {"eps", "1e-7"}};
+  }
+
+  using BaseType::jacobian;
+
+  void jacobian(const VectorType& source,
+                MatrixOperatorType& jacobian_op,
+                const XT::Common::Configuration& opts,
+                const XT::Common::Parameter& param = {}) const override final
+  {
+    // some checks
+    DUNE_THROW_IF(!source.valid(), Exceptions::operator_error, "source contains inf or nan!");
+    DUNE_THROW_IF(!(this->parameter_type() <= param.type()),
+                  Exceptions::operator_error,
+                  "this->parameter_type() = " << this->parameter_type() << "\n   param.type() = " << param.type());
+    DUNE_THROW_IF(!opts.has_key("type"), Exceptions::operator_error, opts);
+    DUNE_THROW_IF(opts.get<std::string>("type") != jacobian_options().at(0), Exceptions::operator_error, opts);
+    const auto default_opts = jacobian_options(jacobian_options().at(0));
+    const auto eps = opts.get("eps", default_opts.template get<double>("eps"));
+    const auto parameter = param + XT::Common::Parameter({"finite-difference-jacobians.eps", eps});
+    // append the same local ops with the same filters as in apply() above
+    // - element contributions
+    const auto source_function = make_discrete_function(this->source_space_, source);
+    for (const auto& op_and_filter : local_element_operators_) {
+      const auto local_op = op_and_filter.first->with_source(source_function);
+      const auto& filter = *op_and_filter.second;
+      jacobian_op.append(*local_op, source, parameter, filter);
+    }
+    // - intersection contributions
+    for (const auto& op_and_filter : local_intersection_operators_) {
+      const auto local_op = op_and_filter.first->with_source(source_function);
+      const auto& filter = *op_and_filter.second;
+      jacobian_op.append(*local_op, source, parameter, filter);
+    }
+  } // ... jacobian(...)
+
+protected:
+  const AGV assembly_grid_view_;
+  const SourceSpaceType& source_space_;
+  const RangeSpaceType& range_space_;
+  bool linear_;
+  std::list<std::pair<std::unique_ptr<LocalElementOperatorType>, std::unique_ptr<XT::Grid::ElementFilter<AGV>>>>
+      local_element_operators_;
+  std::list<
+      std::pair<std::unique_ptr<LocalIntersectionOperatorType>, std::unique_ptr<XT::Grid::IntersectionFilter<AGV>>>>
+      local_intersection_operators_;
+}; // class LocalizableOperator
+
+
 } // namespace GDT
 } // namespace Dune
 
diff --git a/dune/gdt/operators/matrix-based.hh b/dune/gdt/operators/matrix-based.hh
index 168360e0763d7a3d4b5bd8f7937f548d10788ab4..eaf035f265a5107b910b0d73509f5cc8993eb42c 100644
--- a/dune/gdt/operators/matrix-based.hh
+++ b/dune/gdt/operators/matrix-based.hh
@@ -46,7 +46,7 @@ namespace GDT {
 template <class M, class SGV, size_t s_r = 1, size_t s_rC = 1, size_t r_r = s_r, size_t r_rC = s_rC, class RGV = SGV>
 class ConstMatrixOperator : public OperatorInterface<M, SGV, s_r, s_rC, r_r, r_rC, RGV>
 {
-  using ThisType = ConstMatrixOperator<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
+  using ThisType = ConstMatrixOperator;
   using BaseType = OperatorInterface<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
 
 public:
@@ -221,7 +221,7 @@ class MatrixOperator
   , public ConstMatrixOperator<M, SGV, s_r, s_rC, r_r, r_rC, RGV>
   , public XT::Grid::Walker<SGV>
 {
-  using ThisType = MatrixOperator<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
+  using ThisType = MatrixOperator;
   using MatrixStorage = XT::Common::StorageProvider<M>;
   using OperatorBaseType = ConstMatrixOperator<M, SGV, s_r, s_rC, r_r, r_rC, RGV>;
   using WalkerBaseType = XT::Grid::Walker<SGV>;
@@ -293,6 +293,12 @@ public:
 
   FieldType scaling;
 
+  void clear()
+  {
+    WalkerBaseType::clear();
+    assembled_ = false;
+  }
+
   using WalkerBaseType::append;
 
   ThisType& append(const LocalElementBilinearFormInterface<E, r_r, r_rC, F, F, s_r, s_rC, F>& local_bilinear_form,
@@ -379,7 +385,7 @@ public:
                 const XT::Common::Configuration& opts,
                 const XT::Common::Parameter& param = {}) const override
   {
-    DUNE_THROW_IF(!assembled_, Exceptions::operator_error, "This operator has to be assembled to povide a jacobian!");
+    DUNE_THROW_IF(!assembled_, Exceptions::operator_error, "This operator has to be assembled to provide a jacobian!");
     OperatorBaseType::jacobian(source, jacobian_op, opts, param);
   }
 
diff --git a/dune/gdt/operators/oswald-interpolation.hh b/dune/gdt/operators/oswald-interpolation.hh
index a250ef03548429c7c2ba023ab7e839ddb185f3fd..b719170145744576ef57798d635cd5cf17b43d9b 100644
--- a/dune/gdt/operators/oswald-interpolation.hh
+++ b/dune/gdt/operators/oswald-interpolation.hh
@@ -45,7 +45,7 @@ class OswaldInterpolationOperator : public OperatorInterface<M, SGV, dim, dim_co
   static_assert(dim == 1, "I did not think about this yet, feel free to implement!");
   static_assert(dim_cols == 1, "I did not think about this yet, feel free to implement!");
   using BaseType = OperatorInterface<M, SGV, dim, dim_cols, dim, dim_cols, RGV>;
-  using ThisType = OswaldInterpolationOperator<M, AssemblyGridView, dim, dim_cols, SGV, RGV>;
+  using ThisType = OswaldInterpolationOperator;
 
 public:
   using typename BaseType::RangeSpaceType;
diff --git a/dune/gdt/operators/reconstruction/internal.hh b/dune/gdt/operators/reconstruction/internal.hh
new file mode 100644
index 0000000000000000000000000000000000000000..4b7309a79ddc13c2f1a49a21ba34abdbae06d1d1
--- /dev/null
+++ b/dune/gdt/operators/reconstruction/internal.hh
@@ -0,0 +1,521 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2017 - 2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_OPERATORS_RECONSTRUCTION_INTERNAL_HH
+#define DUNE_GDT_OPERATORS_RECONSTRUCTION_INTERNAL_HH
+
+#include <dune/xt/common/debug.hh>
+#include <dune/xt/common/fvector.hh>
+#include <dune/xt/common/lapacke.hh>
+#include <dune/xt/common/matrix.hh>
+#include <dune/xt/common/parameter.hh>
+
+#include <dune/xt/la/algorithms/qr.hh>
+#include <dune/xt/la/eigen-solver.hh>
+
+#include <dune/gdt/operators/interfaces.hh>
+
+namespace Dune {
+namespace GDT {
+namespace internal {
+
+
+template <class MatImp>
+XT::Common::Configuration hyperbolic_default_eigensolver_options()
+{
+  using EigenSolverOptionsType = typename XT::LA::EigenSolverOptions<MatImp>;
+  using MatrixInverterOptionsType = typename XT::LA::MatrixInverterOptions<MatImp>;
+  XT::Common::Configuration eigensolver_options = EigenSolverOptionsType::options(EigenSolverOptionsType::types()[0]);
+  //  XT::Common::Configuration eigensolver_options = EigenSolverOptionsType::options("shifted_qr");
+  eigensolver_options["assert_eigendecomposition"] = "1e-6";
+  eigensolver_options["assert_real_eigendecomposition"] = "1e-6";
+  eigensolver_options["disable_checks"] =
+#ifdef NDEBUG
+      "true";
+#else
+      "false";
+#endif
+  XT::Common::Configuration matrix_inverter_options = MatrixInverterOptionsType::options();
+  matrix_inverter_options["post_check_is_left_inverse"] = "1e-6";
+  matrix_inverter_options["post_check_is_right_inverse"] = "1e-6";
+  eigensolver_options.add(matrix_inverter_options, "matrix-inverter");
+  return eigensolver_options;
+} // ... hyperbolic_default_eigensolver_options()
+
+
+// Wrapper for thread-safe and consistent handling of the different jacobians (Usual matrices vs. block matrices in the
+// partial moment case)
+template <class AnalyticalFluxType, class MatrixImp, class VectorImp>
+class EigenvectorWrapperBase
+{
+public:
+  using MatrixType = MatrixImp;
+  using VectorType = VectorImp;
+  static constexpr size_t dimDomain = AnalyticalFluxType::r;
+  static constexpr size_t dimRange = AnalyticalFluxType::rC;
+  using RangeFieldType = typename AnalyticalFluxType::R;
+  using DomainType = FieldVector<RangeFieldType, dimDomain>;
+  using E = typename AnalyticalFluxType::E;
+  using LocalFluxType = typename AnalyticalFluxType::LocalFunctionType;
+  using FluxDomainType = XT::Common::FieldVector<typename AnalyticalFluxType::D, dimRange>;
+
+  EigenvectorWrapperBase(const AnalyticalFluxType& analytical_flux, const bool flux_is_affine)
+    : analytical_flux_(analytical_flux)
+    , local_flux_(analytical_flux_.local_function())
+    , flux_is_affine_(flux_is_affine)
+    , computed_(false)
+  {}
+
+  virtual ~EigenvectorWrapperBase() {}
+
+  virtual void compute_eigenvectors(const E& entity,
+                                    const DomainType& x_local,
+                                    const VectorType& u,
+                                    const XT::Common::Parameter& param)
+  {
+    if (!computed_ || !flux_is_affine_) {
+      compute_eigenvectors_impl(entity, x_local, u, param);
+      computed_ = true;
+    }
+  }
+
+  virtual void compute_eigenvectors_impl(const E& entity,
+                                         const DomainType& x_local,
+                                         const VectorType& u,
+                                         const XT::Common::Parameter& param) = 0;
+
+  virtual void apply_eigenvectors(const size_t dd, const VectorType& u, VectorType& ret) const = 0;
+
+  virtual void apply_inverse_eigenvectors(const size_t dd, const VectorType& u, VectorType& ret) const = 0;
+
+  virtual const MatrixType& eigenvectors(const size_t dd) const = 0;
+
+  bool affine() const
+  {
+    return flux_is_affine_;
+  }
+
+protected:
+  const AnalyticalFluxType& analytical_flux_;
+  const std::unique_ptr<LocalFluxType> local_flux_;
+  const bool flux_is_affine_;
+  bool computed_;
+}; // class EigenvectorWrapperBase<...>
+
+template <class AnalyticalFluxType, class MatrixImp, class VectorImp>
+constexpr size_t EigenvectorWrapperBase<AnalyticalFluxType, MatrixImp, VectorImp>::dimDomain;
+
+template <class AnalyticalFluxType, class MatrixImp, class VectorImp>
+constexpr size_t EigenvectorWrapperBase<AnalyticalFluxType, MatrixImp, VectorImp>::dimRange;
+
+// This class does not perform any computations, use this class if you want to reconstruct in ordinary coordinates
+// instead of characteristic coordinates
+template <class AnalyticalFluxType, class VectorImp>
+class DummyEigenVectorWrapper : public EigenvectorWrapperBase<AnalyticalFluxType, int, VectorImp>
+{
+  using BaseType = EigenvectorWrapperBase<AnalyticalFluxType, int, VectorImp>;
+
+public:
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::MatrixType;
+  using typename BaseType::VectorType;
+
+  DummyEigenVectorWrapper(const AnalyticalFluxType& analytical_flux, const bool flux_is_affine)
+    : BaseType(analytical_flux, flux_is_affine)
+  {}
+
+  void apply_eigenvectors(const size_t /*dd*/, const VectorType& u, VectorType& ret) const override final
+  {
+    ret = u;
+  }
+  virtual void
+  apply_inverse_eigenvectors(const size_t /*dd*/, const VectorType& u, VectorType& ret) const override final
+  {
+    ret = u;
+  }
+
+  virtual void compute_eigenvectors(const E& /*entity*/,
+                                    const DomainType& /*x_local*/,
+                                    const VectorType& /*u*/,
+                                    const XT::Common::Parameter& /*param*/) const override final
+  {}
+
+  virtual void compute_eigenvectors_impl(const E& /*entity*/,
+                                         const DomainType& /*x_local*/,
+                                         const VectorType& /*u*/,
+                                         const XT::Common::Parameter& /*param*/) const override final
+  {}
+
+  const MatrixType& eigenvectors(const size_t /*dd*/) const override final
+  {
+    DUNE_THROW(Dune::NotImplemented, "This class does not provide eigenvectors!");
+    return zero_;
+  }
+
+private:
+  static const MatrixType zero_ = 0.;
+};
+
+template <class AnalyticalFluxType,
+          class MatrixType = XT::LA::CommonDenseMatrix<typename AnalyticalFluxType::R>,
+          class VectorType = FieldVector<typename AnalyticalFluxType::RangeFieldType, AnalyticalFluxType::rC>>
+class EigenvectorWrapper : public EigenvectorWrapperBase<AnalyticalFluxType, MatrixType, VectorType>
+{
+  using BaseType = EigenvectorWrapperBase<AnalyticalFluxType, MatrixType, VectorType>;
+
+protected:
+  using V = XT::Common::VectorAbstraction<VectorType>;
+  using M = XT::Common::MatrixAbstraction<MatrixType>;
+  using EigenSolverType = typename XT::LA::EigenSolver<MatrixType>;
+
+public:
+  using BaseType::dimDomain;
+  using BaseType::dimRange;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::RangeFieldType;
+  using JacobianType = DynamicVector<MatrixType>;
+
+  EigenvectorWrapper(const AnalyticalFluxType& analytical_flux, const bool flux_is_affine)
+    : BaseType(analytical_flux, flux_is_affine)
+    , work_(1)
+    , scale_(dimRange)
+    , rconde_(dimRange)
+    , rcondv_(dimRange)
+    , iwork_(2 * dimRange - 2)
+    , jacobian_(std::make_unique<JacobianType>(dimDomain, MatrixType(dimRange, dimRange, 0., 0)))
+    , eigenvectors_(std::make_unique<JacobianType>(dimDomain, MatrixType(dimRange, dimRange, 0., 0)))
+    , eigenvectors_rcond_(1.)
+    , eigenvalues_(std::vector<RangeFieldType>(dimRange))
+    , QR_(std::make_unique<JacobianType>(dimDomain, MatrixType(dimRange, dimRange, 0., 0)))
+    , tau_(V::create(dimRange))
+  {
+#if HAVE_MKL || HAVE_LAPACKE
+    int ilo, ihi;
+    double norm;
+    if (M::storage_layout == XT::Common::StorageLayout::dense_row_major) {
+      // get optimal working size in work[0] (requested by lwork = -1)
+      int info = XT::Common::Lapacke::dgeevx_work(XT::Common::Lapacke::row_major(),
+                                                  /*both diagonally scale and permute*/ 'B',
+                                                  /*do_not_compute_left_eigenvectors:*/ 'N',
+                                                  /*compute_right_eigenvectors:*/ 'V',
+                                                  /*do not compute condition numbers*/ 'N',
+                                                  static_cast<int>(dimRange),
+                                                  M::data((*jacobian_)[0]),
+                                                  static_cast<int>(dimRange),
+                                                  &(eigenvalues_[0][0]),
+                                                  &(dummy_complex_eigenvalues_[0]),
+                                                  nullptr,
+                                                  static_cast<int>(dimRange),
+                                                  M::data((*eigenvectors_)[0]),
+                                                  static_cast<int>(dimRange),
+                                                  &ilo,
+                                                  &ihi,
+                                                  scale_.data(),
+                                                  &norm,
+                                                  rconde_.data(),
+                                                  rcondv_.data(),
+                                                  work_.data(),
+                                                  -1,
+                                                  iwork_.data());
+      if (info != 0)
+        DUNE_THROW(Dune::XT::LA::Exceptions::eigen_solver_failed, "The lapack backend reported '" << info << "'!");
+      work_.resize(static_cast<size_t>(work_[0] + 0.5));
+    }
+#endif
+  }
+
+  virtual void compute_eigenvectors_impl(const E& entity,
+                                         const DomainType& x_local,
+                                         const VectorType& u,
+                                         const XT::Common::Parameter& param) override final
+  {
+    local_flux_->bind(entity);
+    try {
+      local_flux_->jacobian(x_local, u, *jacobian_, param);
+      for (size_t dd = 0; dd < dimDomain; ++dd) {
+        if (false) {
+          ;
+#if HAVE_MKL || HAVE_LAPACKE
+        } else if (M::storage_layout == XT::Common::StorageLayout::dense_row_major) {
+          int ilo, ihi;
+          double norm;
+          int info = XT::Common::Lapacke::dgeevx_work(XT::Common::Lapacke::row_major(),
+                                                      /*both diagonally scale and permute*/ 'B',
+                                                      /*do not compute left eigenvectors*/ 'N',
+                                                      /*compute right eigenvectors*/ 'V',
+                                                      /*do not compute condition numbers*/ 'N',
+                                                      static_cast<int>(dimRange),
+                                                      M::data((*jacobian_)[dd]),
+                                                      static_cast<int>(dimRange),
+                                                      eigenvalues_[dd].data(),
+                                                      &(dummy_complex_eigenvalues_[0]),
+                                                      nullptr,
+                                                      static_cast<int>(dimRange),
+                                                      M::data((*eigenvectors_)[dd]),
+                                                      static_cast<int>(dimRange),
+                                                      &ilo,
+                                                      &ihi,
+                                                      scale_.data(),
+                                                      &norm,
+                                                      rconde_.data(),
+                                                      rcondv_.data(),
+                                                      work_.data(),
+                                                      static_cast<int>(work_.size()),
+                                                      iwork_.data());
+          if (info != 0)
+            DUNE_THROW(Dune::MathError, "The lapack backend reported '" << info << "'!");
+#endif // HAVE_MKL || HAVE_LAPACKE
+        } else {
+          static auto eigensolver_options = hyperbolic_default_eigensolver_options<MatrixType>();
+          const auto eigensolver = EigenSolverType((*jacobian_)[dd], &eigensolver_options);
+          (*eigenvectors_)[dd] = eigensolver.real_eigenvectors();
+          eigenvalues_[dd] = eigensolver.real_eigenvalues();
+        }
+        (*QR_)[dd] = (*eigenvectors_)[dd];
+        XT::LA::qr((*QR_)[dd], tau_[dd], permutations_[dd]);
+#if HAVE_MKL || HAVE_LAPACKE
+        int info = XT::Common::Lapacke::dtrcon(XT::Common::Lapacke::row_major(),
+                                               '1',
+                                               'U',
+                                               'N',
+                                               static_cast<int>(dimRange),
+                                               M::data((*QR_)[dd]),
+                                               static_cast<int>(dimRange),
+                                               &eigenvectors_rcond_);
+        if (info || eigenvectors_rcond_ < 1e-5)
+          DUNE_THROW(Dune::MathError, "Eigenvector condition too high!");
+#endif
+      } // dd
+    } catch (const Dune::MathError&) {
+      for (size_t dd = 0; dd < dimDomain; ++dd) {
+        // use scalar limiters, i.e. eigenvectors matrix is eye-matrix.
+        XT::LA::eye_matrix((*eigenvectors_)[dd]);
+        std::fill(eigenvalues_[dd].begin(), eigenvalues_[dd].end(), 1.);
+        (*QR_)[dd] = (*eigenvectors_)[dd];
+        XT::LA::qr((*QR_)[dd], tau_[dd], permutations_[dd]);
+      } // dd
+    }
+
+    // we do not need jacobian_ anymore if the flux is affine, so save memory
+    if (flux_is_affine_)
+      jacobian_ = nullptr;
+  }
+
+  void apply_eigenvectors(const size_t dd, const VectorType& u, VectorType& ret) const override final
+  {
+    (*eigenvectors_)[dd].mv(u, ret);
+  }
+
+  void apply_inverse_eigenvectors(const size_t dd, const VectorType& u, VectorType& ret) const override final
+  {
+    thread_local VectorType work = V::create(dimRange);
+    XT::LA::solve_qr_factorized((*QR_)[dd], tau_[dd], permutations_[dd], ret, u, &work);
+  }
+
+  const MatrixType& eigenvectors(const size_t dd) const override final
+  {
+    return (*eigenvectors_)[dd];
+  }
+
+protected:
+  using BaseType::computed_;
+  using BaseType::flux_is_affine_;
+  using BaseType::local_flux_;
+  std::vector<RangeFieldType> work_, scale_, rconde_, rcondv_;
+  std::vector<int> iwork_;
+  std::unique_ptr<JacobianType> jacobian_;
+  std::unique_ptr<JacobianType> eigenvectors_;
+  RangeFieldType eigenvectors_rcond_;
+  FieldVector<std::vector<RangeFieldType>, dimDomain> eigenvalues_;
+  FieldVector<RangeFieldType, dimRange> dummy_complex_eigenvalues_;
+  std::unique_ptr<JacobianType> QR_;
+  FieldVector<VectorType, dimDomain> tau_;
+  FieldVector<FieldVector<int, dimRange>, dimDomain> permutations_;
+}; // class EigenvectorWrapper<...>
+
+
+template <class AnalyticalFluxType, size_t block_size = (AnalyticalFluxType::r == 1) ? 2 : 4>
+class BlockedEigenvectorWrapper
+  : public EigenvectorWrapperBase<
+        AnalyticalFluxType,
+        XT::Common::BlockedFieldMatrix<typename AnalyticalFluxType::R, AnalyticalFluxType::rC / block_size, block_size>,
+        XT::Common::BlockedFieldVector<typename AnalyticalFluxType::R, AnalyticalFluxType::rC / block_size, block_size>>
+{
+  using BaseType = EigenvectorWrapperBase<
+      AnalyticalFluxType,
+      XT::Common::BlockedFieldMatrix<typename AnalyticalFluxType::R, AnalyticalFluxType::rC / block_size, block_size>,
+      XT::Common::BlockedFieldVector<typename AnalyticalFluxType::R, AnalyticalFluxType::rC / block_size, block_size>>;
+
+public:
+  using BaseType::dimDomain;
+  using BaseType::dimRange;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::FluxDomainType;
+  using typename BaseType::MatrixType;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::VectorType;
+  using JacobianType = XT::Common::FieldVector<MatrixType, dimDomain>;
+  static constexpr size_t num_blocks = VectorType::num_blocks;
+  static_assert(dimRange % block_size == 0, "dimRange has to be a multiple of block_size");
+  using BlockedIntVectorType = XT::Common::BlockedFieldVector<int, num_blocks, block_size>;
+  using LocalVectorType = typename VectorType::BlockType;
+  using LocalMatrixType = typename MatrixType::BlockType;
+  using EigenSolverType = typename XT::LA::EigenSolver<LocalMatrixType>;
+  using LocalM = typename XT::Common::MatrixAbstraction<LocalMatrixType>;
+  using LocalV = typename XT::Common::VectorAbstraction<LocalVectorType>;
+  using NonblockedJacobianType = DynamicVector<XT::LA::CommonDenseMatrix<RangeFieldType>>;
+
+  BlockedEigenvectorWrapper(const AnalyticalFluxType& analytical_flux, const bool flux_is_affine)
+    : BaseType(analytical_flux, flux_is_affine)
+    , jacobian_(std::make_unique<JacobianType>())
+    , nonblocked_jacobian_(std::make_unique<NonblockedJacobianType>(
+          dimDomain, XT::LA::CommonDenseMatrix<RangeFieldType>(dimRange, dimRange, 0., 0)))
+  {
+    std::fill_n(&(eigenvalues_[0][0]), dimDomain * num_blocks, std::vector<double>(block_size, 0.));
+  }
+
+  virtual void compute_eigenvectors_impl(const E& entity,
+                                         const DomainType& x_local,
+                                         const VectorType& u,
+                                         const XT::Common::Parameter& param) override final
+  {
+    local_flux_->bind(entity);
+    const FluxDomainType nonblocked_u = u.operator FluxDomainType();
+    try {
+      local_flux_->jacobian(x_local, nonblocked_u, *nonblocked_jacobian_, param);
+      *jacobian_ = *nonblocked_jacobian_;
+      for (size_t dd = 0; dd < dimDomain; ++dd) {
+        for (size_t jj = 0; jj < num_blocks; ++jj) {
+          if (block_size == 2) {
+            const auto& jac = (*jacobian_)[dd].block(jj);
+            const auto trace = jac[0][0] + jac[1][1];
+            const auto det = jac[0][0] * jac[1][1] - jac[0][1] * jac[1][0];
+            const auto sqrt_val = std::sqrt(0.25 * trace * trace - det);
+            auto& eigvals = eigenvalues_[dd][jj];
+            eigvals[0] = 0.5 * trace + sqrt_val;
+            eigvals[1] = 0.5 * trace - sqrt_val;
+            auto& eigvecs = eigenvectors_[dd].block(jj);
+            if (std::abs(jac[1][0]) > std::abs(jac[0][1])) {
+              if (XT::Common::FloatCmp::ne(jac[1][0], 0.)) {
+                eigvecs[0][0] = eigvals[0] - jac[1][1];
+                eigvecs[0][1] = eigvals[1] - jac[1][1];
+                eigvecs[1][0] = eigvecs[1][1] = jac[1][0];
+              } else {
+                eigvecs[0][0] = eigvecs[1][1] = 1.;
+                eigvecs[0][1] = eigvecs[1][0] = 0.;
+              }
+            } else {
+              if (XT::Common::FloatCmp::ne(jac[0][1], 0.)) {
+                eigvecs[1][0] = eigvals[0] - jac[0][0];
+                eigvecs[1][1] = eigvals[1] - jac[0][0];
+                eigvecs[0][0] = eigvecs[0][1] = jac[0][1];
+              } else {
+                eigvecs[0][0] = eigvecs[1][1] = 1.;
+                eigvecs[0][1] = eigvecs[1][0] = 0.;
+              }
+            }
+            // normalize such that the eigenvectors have norm 1
+            for (size_t col = 0; col < 2; ++col) {
+              RangeFieldType two_norm = 0;
+              two_norm = std::sqrt(std::pow(eigvecs[0][col], 2) + std::pow(eigvecs[1][col], 2));
+              for (size_t row = 0; row < 2; ++row)
+                eigvecs[row][col] /= two_norm;
+            }
+          } else {
+            // For the small matrices (usually 4x4) used here it causes a lot of overhead to call into LAPACK every
+            // time, so we just use our own eigensolver most of the time. Occasionally, however, our eigensolver fails
+            // where the LAPACK eigensolver succeeds (due to a superior shifting strategy), so in these cases we call
+            // LAPACK.
+            try {
+              XT::LA::internal::fmatrix_compute_real_eigenvalues_and_real_right_eigenvectors_using_qr(
+                  (*jacobian_)[dd].block(jj), eigenvalues_[dd][jj], eigenvectors_[dd].block(jj));
+            } catch (const Dune::MathError&) {
+              // Our own eigensolver failed, try the default one instead (Lapacke, Eigen or Numpy, if none of these is
+              // available, we solve again using our own eigensolver, which will throw the error again.
+              static auto eigensolver_options = hyperbolic_default_eigensolver_options<LocalMatrixType>();
+              const auto eigensolver = EigenSolverType((*jacobian_)[dd].block(jj), &eigensolver_options);
+              eigenvectors_[dd].block(jj) = eigensolver.real_eigenvectors();
+              eigenvalues_[dd][jj] = eigensolver.real_eigenvalues();
+            }
+          } // else (block_size == 2)
+          QR_[dd].block(jj) = eigenvectors_[dd].block(jj);
+          XT::LA::qr(QR_[dd].block(jj), tau_[dd].block(jj), permutations_[dd].block(jj));
+#if HAVE_MKL || HAVE_LAPACKE
+          int info = XT::Common::Lapacke::dtrcon(XT::Common::Lapacke::row_major(),
+                                                 '1',
+                                                 'U',
+                                                 'N',
+                                                 static_cast<int>(block_size),
+                                                 LocalM::data(QR_[dd].block(jj)),
+                                                 static_cast<int>(block_size),
+                                                 &eigenvectors_rcond_);
+          if (info || eigenvectors_rcond_ < 1e-5)
+            DUNE_THROW(Dune::MathError, "Eigenvector condition too high!");
+#endif
+        } // jj
+      } // dd
+    } catch (const Dune::MathError&) {
+      for (size_t dd = 0; dd < dimDomain; ++dd) {
+        for (size_t jj = 0; jj < num_blocks; ++jj) {
+          // use scalar limiters, i.e. eigenvectors matrix is eye-matrix.
+          XT::LA::eye_matrix(eigenvectors_[dd].block(jj));
+          std::fill(eigenvalues_[dd][jj].begin(), eigenvalues_[dd][jj].end(), 1.);
+          QR_[dd].block(jj) = eigenvectors_[dd].block(jj);
+          XT::LA::qr(QR_[dd].block(jj), tau_[dd].block(jj), permutations_[dd].block(jj));
+        } // jj
+      } // dd
+    }
+
+    if (flux_is_affine_) {
+      jacobian_ = nullptr;
+      nonblocked_jacobian_ = nullptr;
+    }
+  }
+
+  void apply_eigenvectors(const size_t dd, const VectorType& u, VectorType& ret) const override final
+  {
+    eigenvectors_[dd].mv(u, ret);
+  }
+
+  void apply_inverse_eigenvectors(const size_t dd, const VectorType& u, VectorType& ret) const override final
+  {
+    LocalVectorType work;
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      XT::LA::solve_qr_factorized(
+          QR_[dd].block(jj), tau_[dd].block(jj), permutations_[dd].block(jj), ret.block(jj), u.block(jj), &work);
+  }
+
+  const MatrixType& eigenvectors(const size_t dd) const override final
+  {
+    return eigenvectors_[dd];
+  }
+
+protected:
+  using BaseType::flux_is_affine_;
+  using BaseType::local_flux_;
+  std::unique_ptr<JacobianType> jacobian_;
+  std::unique_ptr<NonblockedJacobianType> nonblocked_jacobian_;
+  FieldVector<FieldVector<std::vector<double>, num_blocks>, dimDomain> eigenvalues_;
+  FieldVector<MatrixType, dimDomain> eigenvectors_;
+  FieldVector<MatrixType, dimDomain> QR_;
+  FieldVector<VectorType, dimDomain> tau_;
+  RangeFieldType eigenvectors_rcond_;
+  FieldVector<BlockedIntVectorType, dimDomain> permutations_;
+}; // BlockedEigenvectorWrapper<...>
+
+
+} // namespace internal
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_OPERATORS_RECONSTRUCTION_INTERNAL_HH
diff --git a/dune/gdt/operators/reconstruction/linear.hh b/dune/gdt/operators/reconstruction/linear.hh
new file mode 100644
index 0000000000000000000000000000000000000000..52532936f8b9de44eda7cc46ca3e0112debda94b
--- /dev/null
+++ b/dune/gdt/operators/reconstruction/linear.hh
@@ -0,0 +1,488 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2017 - 2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_OPERATORS_RECONSTRUCTION_LINEAR_HH
+#define DUNE_GDT_OPERATORS_RECONSTRUCTION_LINEAR_HH
+
+#include <dune/geometry/quadraturerules.hh>
+
+#include <dune/xt/common/fvector.hh>
+#include <dune/xt/common/parameter.hh>
+
+#include <dune/xt/grid/walker.hh>
+
+#include <dune/gdt/operators/interfaces.hh>
+#include <dune/gdt/spaces/l2/discontinuous-lagrange.hh>
+#include <dune/gdt/tools/discretevalued-grid-function.hh>
+
+#include "slopes.hh"
+#include "internal.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+template <class AnalyticalFluxType, class BoundaryValueType, class GV, class EigenvectorWrapperType>
+class LinearSlopeElementFunctor : public XT::Grid::ElementFunctor<GV>
+{
+  using ThisType = LinearSlopeElementFunctor;
+  using BaseType = XT::Grid::ElementFunctor<GV>;
+
+public:
+  using typename BaseType::E;
+  using SlopeType = SlopeBase<E, EigenvectorWrapperType>;
+  using LocalVectorType = typename EigenvectorWrapperType::VectorType;
+  using StencilType = DynamicVector<LocalVectorType>;
+  using StencilsType = std::vector<StencilType>;
+  using DomainType = typename BoundaryValueType::DomainType;
+  using RangeType = typename BoundaryValueType::RangeReturnType;
+  static constexpr size_t d = BoundaryValueType::d;
+  static constexpr size_t stencil_size = 3;
+
+  LinearSlopeElementFunctor(const GV& grid_view,
+                            const std::vector<LocalVectorType>& source_values,
+                            const BoundaryValueType& boundary_values,
+                            const AnalyticalFluxType& analytical_flux,
+                            const SlopeType& slope,
+                            const XT::Common::Parameter& param,
+                            const bool flux_is_affine = false)
+
+    : grid_view_(grid_view)
+    , source_values_(source_values)
+    , boundary_values_(boundary_values)
+    , analytical_flux_(analytical_flux)
+    , slope_(slope.copy())
+    , param_(param)
+    , eigenvector_wrapper_(analytical_flux, flux_is_affine)
+    , slopes_(d)
+    , stencils_(d, StencilType(stencil_size))
+  {}
+
+  LinearSlopeElementFunctor(const LinearSlopeElementFunctor& other)
+    : BaseType(other)
+    , grid_view_(other.grid_view_)
+    , source_values_(other.source_values_)
+    , boundary_values_(other.boundary_values_)
+    , analytical_flux_(other.analytical_flux_)
+    , slope_(other.slope_->copy())
+    , param_(other.param_)
+    , eigenvector_wrapper_(analytical_flux_, other.eigenvector_wrapper_.affine())
+    , slopes_(d)
+    , stencils_(d, StencilType(stencil_size))
+  {}
+
+  XT::Grid::ElementFunctor<GV>* copy() override final
+  {
+    return new ThisType(*this);
+  }
+
+  ThisType* copy_derived()
+  {
+    return new ThisType(*this);
+  }
+
+  void apply_local(const E& entity) override final
+  {
+    // In a MPI parallel run, if entity is on boundary of overlap, we do not have to reconstruct
+    if (!fill_stencils(entity))
+      return;
+
+    // get eigenvectors of flux
+    if (analytical_flux_.x_dependent())
+      x_local_ = entity.geometry().local(entity.geometry().center());
+    const auto entity_index = grid_view_.indexSet().index(entity);
+    eigenvector_wrapper_.compute_eigenvectors(entity, x_local_, source_values_[entity_index], param_);
+
+    for (size_t dd = 0; dd < d; ++dd) {
+      // no need to reconstruct in all directions, as we are only regarding the center of the face, which will
+      // always have the same value assigned, independent of the slope in the other directions
+      slopes_[dd] = slope_->get(entity, stencils_[dd], eigenvector_wrapper_, dd);
+    } // dd
+  }
+
+  const std::vector<RangeType>& slopes() const
+  {
+    return slopes_;
+  }
+
+  const LocalVectorType& u_entity() const
+  {
+    return stencils_[0][1];
+  }
+
+private:
+  bool fill_stencils(const E& entity)
+  {
+    const auto entity_index = grid_view_.indexSet().index(entity);
+    for (size_t dd = 0; dd < d; ++dd)
+      stencils_[dd][1] = source_values_[entity_index];
+    for (const auto& intersection : Dune::intersections(grid_view_, entity)) {
+      const size_t dd = intersection.indexInInside() / 2;
+      const size_t index = (intersection.indexInInside() % 2) * 2;
+      if (intersection.boundary() && !intersection.neighbor()) // boundary intersections
+        stencils_[dd][index] = boundary_values_.evaluate(intersection.geometry().center());
+      else if (intersection.neighbor()) // inner and periodic intersections
+        stencils_[dd][index] = source_values_[grid_view_.indexSet().index(intersection.outside())];
+      else if (!intersection.neighbor() && !intersection.boundary()) // processor boundary
+        return false;
+      else
+        DUNE_THROW(Dune::NotImplemented, "This should not happen!");
+    } // intersections
+    return true;
+  } // void fill_stencils(...)
+
+  const GV& grid_view_;
+  const std::vector<LocalVectorType>& source_values_;
+  const BoundaryValueType& boundary_values_;
+  const AnalyticalFluxType& analytical_flux_;
+  std::unique_ptr<SlopeType> slope_;
+  const XT::Common::Parameter& param_;
+  EigenvectorWrapperType eigenvector_wrapper_;
+  DomainType x_local_;
+  std::vector<RangeType> slopes_;
+  StencilsType stencils_;
+};
+
+template <class AnalyticalFluxType,
+          class BoundaryValueType,
+          class GV,
+          class EigenvectorWrapperType = internal::EigenvectorWrapper<AnalyticalFluxType>>
+class LocalPointwiseLinearReconstructionOperator : public XT::Grid::ElementFunctor<GV>
+{
+  using ThisType = LocalPointwiseLinearReconstructionOperator;
+  using BaseType = XT::Grid::ElementFunctor<GV>;
+  static constexpr size_t dimDomain = BoundaryValueType::d;
+  static constexpr size_t dimRange = BoundaryValueType::r;
+  using EntityType = typename GV::template Codim<0>::Entity;
+  using DomainType = typename BoundaryValueType::DomainType;
+  using RangeType = typename BoundaryValueType::RangeReturnType;
+  using RangeFieldType = typename BoundaryValueType::RangeFieldType;
+  using LocalVectorType = typename EigenvectorWrapperType::VectorType;
+  using SlopeType = SlopeBase<EntityType, EigenvectorWrapperType>;
+  using SlopeFunctorType = LinearSlopeElementFunctor<AnalyticalFluxType, BoundaryValueType, GV, EigenvectorWrapperType>;
+  using ReconstructedFunctionType = DiscreteValuedGridFunction<GV, dimRange, 1, RangeFieldType>;
+
+public:
+  explicit LocalPointwiseLinearReconstructionOperator(ReconstructedFunctionType& reconstructed_function,
+                                                      const GV& grid_view,
+                                                      const std::vector<LocalVectorType>& source_values,
+                                                      const BoundaryValueType& boundary_values,
+                                                      const AnalyticalFluxType& analytical_flux,
+                                                      const SlopeType& slope,
+                                                      const XT::Common::Parameter& param,
+                                                      const bool flux_is_affine = false)
+    : slope_functor_(std::make_unique<SlopeFunctorType>(
+          grid_view, source_values, boundary_values, analytical_flux, slope, param, flux_is_affine))
+    , reconstructed_function_(reconstructed_function)
+  {}
+
+  LocalPointwiseLinearReconstructionOperator(const ThisType& other)
+    : BaseType(other)
+    , slope_functor_(other.slope_functor_->copy_derived())
+    , reconstructed_function_(other.reconstructed_function_)
+  {}
+
+  XT::Grid::ElementFunctor<GV>* copy() override final
+  {
+    return new ThisType(*this);
+  }
+
+  void apply_local(const EntityType& entity) override final
+  {
+    slope_functor_->apply_local(entity);
+    // reconstructed function is f(x) = u_entity + slope_matrix * (x - (0.5, 0.5, 0.5, ...))
+    auto& local_reconstructed_values = reconstructed_function_.local_values(entity);
+    const RangeType u_entity = slope_functor_->u_entity();
+    for (size_t dd = 0; dd < dimDomain; ++dd) {
+      for (size_t ii = 0; ii < 2; ++ii) {
+        DomainType coord(0.5);
+        coord[dd] = ii;
+        local_reconstructed_values[coord] = u_entity + slope_functor_->slopes()[dd] * (ii - 0.5);
+      } // ii
+    } // dd
+  } // void apply_local(...)
+
+private:
+  std::unique_ptr<SlopeFunctorType> slope_functor_;
+  ReconstructedFunctionType& reconstructed_function_;
+}; // class LocalPointwiseLinearReconstructionOperator
+
+
+template <class AnalyticalFluxType,
+          class BoundaryValueType,
+          class GV,
+          class TargetSpaceType,
+          class TargetVectorType = typename XT::LA::Container<typename AnalyticalFluxType::R>::VectorType,
+          class EigenvectorWrapperType = internal::EigenvectorWrapper<AnalyticalFluxType>>
+class LocalLinearReconstructionOperator : public XT::Grid::ElementFunctor<GV>
+{
+  using BaseType = XT::Grid::ElementFunctor<GV>;
+  static constexpr size_t dimDomain = BoundaryValueType::d;
+  static constexpr size_t dimRange = BoundaryValueType::r;
+  using EntityType = typename GV::template Codim<0>::Entity;
+  using RangeType = typename BoundaryValueType::RangeReturnType;
+  using RangeFieldType = typename BoundaryValueType::RangeFieldType;
+  using LocalVectorType = typename EigenvectorWrapperType::VectorType;
+  using SlopeType = SlopeBase<EntityType, EigenvectorWrapperType>;
+  using TargetType = DiscreteFunction<TargetVectorType, GV, dimRange, 1, RangeFieldType>;
+  using TargetBasisType = typename TargetType::SpaceType::GlobalBasisType::LocalizedType;
+  using LocalDofVectorType = typename TargetType::DofVectorType::LocalDofVectorType;
+  using SlopeFunctorType = LinearSlopeElementFunctor<AnalyticalFluxType, BoundaryValueType, GV, EigenvectorWrapperType>;
+
+public:
+  explicit LocalLinearReconstructionOperator(const std::vector<LocalVectorType>& source_values,
+                                             TargetSpaceType target_space,
+                                             TargetVectorType& target_vector,
+                                             const AnalyticalFluxType& analytical_flux,
+                                             const BoundaryValueType& boundary_values,
+                                             const SlopeType& slope,
+                                             const XT::Common::Parameter& param,
+                                             const bool flux_is_affine = false)
+    : slope_functor_(std::make_unique<SlopeFunctorType>(
+          target_space.grid_view(), source_values, boundary_values, analytical_flux, slope, param, flux_is_affine))
+    , target_space_(target_space)
+    , target_vector_(target_vector)
+    , target_(target_space_, target_vector_, "range")
+    , target_basis_(target_.space().basis().localize())
+    , local_dof_vector_(target_.dofs().localize())
+  {}
+
+  LocalLinearReconstructionOperator(const LocalLinearReconstructionOperator& other)
+    : BaseType(other)
+    , slope_functor_(other.slope_functor_->copy_derived())
+    , target_space_(other.target_space_)
+    , target_vector_(other.target_vector_)
+    , target_(target_space_, target_vector_, "range")
+    , target_basis_(target_.space().basis().localize())
+    , local_dof_vector_(target_.dofs().localize())
+  {}
+
+  XT::Grid::ElementFunctor<GV>* copy() override final
+  {
+    return new LocalLinearReconstructionOperator(*this);
+  }
+
+  void apply_local(const EntityType& entity) override final
+  {
+    slope_functor_->apply_local(entity);
+    // reconstructed function is f(x) = u_entity + slope_matrix * (x - (0.5, 0.5, 0.5, ...))
+    local_dof_vector_.bind(entity);
+    target_basis_->bind(entity);
+    const RangeType u_entity = slope_functor_->u_entity();
+    target_basis_->interpolate(
+        [&](const auto& xx) {
+          auto ret = u_entity;
+          for (size_t dd = 0; dd < dimDomain; ++dd)
+            ret += slope_functor_->slopes()[dd] * (xx[dd] - 0.5);
+          return ret;
+        },
+        1,
+        local_dof_vector_);
+  } // void apply_local(...)
+
+private:
+  std::unique_ptr<SlopeFunctorType> slope_functor_;
+  TargetSpaceType target_space_;
+  TargetVectorType& target_vector_;
+  TargetType target_;
+  std::unique_ptr<TargetBasisType> target_basis_;
+  LocalDofVectorType local_dof_vector_;
+}; // class LocalLinearReconstructionOperator
+
+template <class AnalyticalFluxImp,
+          class BoundaryValueImp,
+          class GV,
+          class MatrixImp = typename XT::LA::Container<typename AnalyticalFluxImp::R>::MatrixType,
+          class EigenvectorWrapperImp = internal::EigenvectorWrapper<
+              AnalyticalFluxImp,
+              FieldMatrix<typename BoundaryValueImp::R, BoundaryValueImp::r, BoundaryValueImp::r>,
+              FieldVector<typename BoundaryValueImp::R, BoundaryValueImp::r>>>
+class LinearReconstructionOperator : public OperatorInterface<MatrixImp, GV, BoundaryValueImp::r>
+{
+  using BaseType = OperatorInterface<MatrixImp, GV, BoundaryValueImp::r>;
+
+public:
+  using typename BaseType::RangeSpaceType;
+  using typename BaseType::SourceSpaceType;
+  using typename BaseType::VectorType;
+
+  using AnalyticalFluxType = AnalyticalFluxImp;
+  using BoundaryValueType = BoundaryValueImp;
+  using EigenvectorWrapperType = EigenvectorWrapperImp;
+  using LocalVectorType = typename EigenvectorWrapperType::VectorType;
+  using MatrixType = typename EigenvectorWrapperType::MatrixType;
+  using E = XT::Grid::extract_entity_t<GV>;
+  using SlopeType = SlopeBase<E, EigenvectorWrapperType>;
+  static constexpr size_t dimDomain = BoundaryValueType::d;
+  static constexpr size_t dimRange = BoundaryValueType::r;
+  using ReconstructionSpaceType = DiscontinuousLagrangeSpace<GV, dimRange, typename AnalyticalFluxType::R>;
+
+  LinearReconstructionOperator(const AnalyticalFluxType& analytical_flux,
+                               const BoundaryValueType& boundary_values,
+                               const SourceSpaceType& source_space,
+                               const SlopeType& slope = default_minmod_slope(),
+                               const bool flux_is_affine = false)
+    : analytical_flux_(analytical_flux)
+    , boundary_values_(boundary_values)
+    , source_space_(source_space)
+    , range_space_(source_space_.grid_view(), 1)
+    , slope_(slope)
+    , flux_is_affine_(flux_is_affine)
+  {}
+
+  bool linear() const override final
+  {
+    return false;
+  }
+
+  const SourceSpaceType& source_space() const override final
+  {
+    return source_space_;
+  }
+
+  const RangeSpaceType& range_space() const override final
+  {
+    return range_space_;
+  }
+
+  void apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& param) const override
+  {
+    // evaluate cell averages
+    const auto& grid_view = source_space_.grid_view();
+    const auto& index_set = grid_view.indexSet();
+    std::vector<LocalVectorType> source_values(index_set.size(0));
+    auto source_func = make_discrete_function(source_space(), source);
+    const auto local_source = source_func.local_function();
+    for (const auto& entity : Dune::elements(grid_view)) {
+      local_source->bind(entity);
+      const auto entity_index = index_set.index(entity);
+      source_values[entity_index] = local_source->evaluate(entity.geometry().local(entity.geometry().center()));
+    }
+
+    // do reconstruction
+    auto local_reconstruction_operator = LocalLinearReconstructionOperator<AnalyticalFluxType,
+                                                                           BoundaryValueType,
+                                                                           GV,
+                                                                           ReconstructionSpaceType,
+                                                                           VectorType,
+                                                                           EigenvectorWrapperType>(
+        source_values, range_space_, range, analytical_flux_, boundary_values_, slope_, param, flux_is_affine_);
+    auto walker = XT::Grid::Walker<GV>(grid_view);
+    walker.append(local_reconstruction_operator);
+    walker.walk(true);
+  } // void apply(...)
+
+private:
+  static SlopeType& default_minmod_slope()
+  {
+    static MinmodSlope<E, EigenvectorWrapperType> minmod_slope_;
+    return minmod_slope_;
+  }
+
+  const AnalyticalFluxType& analytical_flux_;
+  const BoundaryValueType& boundary_values_;
+  const SourceSpaceType& source_space_;
+  ReconstructionSpaceType range_space_;
+  const SlopeType& slope_;
+  const bool flux_is_affine_;
+}; // class LinearReconstructionOperator<...>
+
+
+// Does not reconstruct a full first-order DG function, but only stores the reconstructed values at the intersection
+// centers. This avoids the interpolation in this operator and the evaluation of the reconstructed function in the
+// finite volume operator which are both quite expensive for large dimRange.
+template <class AnalyticalFluxImp,
+          class BoundaryValueImp,
+          class GV,
+          class VectorType,
+          class EigenvectorWrapperImp = internal::EigenvectorWrapper<
+              AnalyticalFluxImp,
+              FieldMatrix<typename BoundaryValueImp::R, BoundaryValueImp::r, BoundaryValueImp::r>,
+              FieldVector<typename BoundaryValueImp::R, BoundaryValueImp::r>>>
+class PointwiseLinearReconstructionOperator
+{
+public:
+  using AnalyticalFluxType = AnalyticalFluxImp;
+  using BoundaryValueType = BoundaryValueImp;
+  using EigenvectorWrapperType = EigenvectorWrapperImp;
+  using LocalVectorType = typename EigenvectorWrapperType::VectorType;
+  using E = XT::Grid::extract_entity_t<GV>;
+  using SlopeType = SlopeBase<E, EigenvectorWrapperType>;
+  using R = typename BoundaryValueType::R;
+  static constexpr size_t dimDomain = BoundaryValueType::d;
+  static constexpr size_t dimRange = BoundaryValueType::r;
+  using SpaceType = SpaceInterface<GV, dimRange, 1, R>;
+  using ReconstructedFunctionType = DiscreteValuedGridFunction<GV, dimRange, 1, R>;
+  using ReconstructedValuesType = std::vector<typename ReconstructedFunctionType::LocalFunctionValuesType>;
+
+  PointwiseLinearReconstructionOperator(const AnalyticalFluxType& analytical_flux,
+                                        const BoundaryValueType& boundary_values,
+                                        const SpaceType& space,
+                                        const SlopeType& slope = default_minmod_slope(),
+                                        const bool flux_is_affine = false)
+    : analytical_flux_(analytical_flux)
+    , boundary_values_(boundary_values)
+    , space_(space)
+    , slope_(slope)
+    , flux_is_affine_(flux_is_affine)
+  {}
+
+  bool linear() const
+  {
+    return false;
+  }
+
+  const SpaceType& space() const
+  {
+    return space_;
+  }
+
+  void apply(const VectorType& source, ReconstructedFunctionType& range, const XT::Common::Parameter& param) const
+  {
+    // evaluate cell averages
+    const auto& grid_view = space_.grid_view();
+    const auto& index_set = grid_view.indexSet();
+    std::vector<LocalVectorType> source_values(index_set.size(0));
+    auto source_func = make_discrete_function(space_, source);
+    const auto local_source = source_func.local_function();
+    for (const auto& entity : Dune::elements(grid_view)) {
+      local_source->bind(entity);
+      const auto entity_index = index_set.index(entity);
+      source_values[entity_index] = local_source->evaluate(entity.geometry().local(entity.geometry().center()));
+    }
+
+    // do reconstruction
+    auto local_reconstruction_operator =
+        LocalPointwiseLinearReconstructionOperator<AnalyticalFluxType, BoundaryValueType, GV, EigenvectorWrapperType>(
+            range, grid_view, source_values, boundary_values_, analytical_flux_, slope_, param, flux_is_affine_);
+    auto walker = XT::Grid::Walker<GV>(grid_view);
+    walker.append(local_reconstruction_operator);
+    walker.walk(true);
+  } // void apply(...)
+
+private:
+  static SlopeType& default_minmod_slope()
+  {
+    static MinmodSlope<E, EigenvectorWrapperType> minmod_slope_;
+    return minmod_slope_;
+  }
+
+  const AnalyticalFluxType& analytical_flux_;
+  const BoundaryValueType& boundary_values_;
+  const SpaceType& space_;
+  const SlopeType& slope_;
+  const bool flux_is_affine_;
+}; // class PointwiseLinearReconstructionOperator<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_OPERATORS_RECONSTRUCTION_LINEAR_HH
diff --git a/dune/gdt/operators/reconstruction/linear_kinetic.hh b/dune/gdt/operators/reconstruction/linear_kinetic.hh
new file mode 100644
index 0000000000000000000000000000000000000000..863065af003aa8b78105b5309fbb00484c358676
--- /dev/null
+++ b/dune/gdt/operators/reconstruction/linear_kinetic.hh
@@ -0,0 +1,182 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner (2019)
+
+#ifndef DUNE_GDT_OPERATORS_RECONSTRUCTION_LINEAR_KINETIC_HH
+#define DUNE_GDT_OPERATORS_RECONSTRUCTION_LINEAR_KINETIC_HH
+
+#include <dune/geometry/quadraturerules.hh>
+
+#include <dune/xt/common/fvector.hh>
+#include <dune/xt/common/parameter.hh>
+
+#include <dune/xt/grid/walker.hh>
+
+#include <dune/gdt/operators/interfaces.hh>
+#include <dune/gdt/spaces/l2/discontinuous-lagrange.hh>
+#include <dune/gdt/tools/discretevalued-grid-function.hh>
+
+#include "slopes.hh"
+#include "internal.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+template <class GV, class AnalyticalFluxType, class BoundaryValueType, class LocalVectorType>
+class LocalPointwiseLinearKineticReconstructionOperator : public XT::Grid::ElementFunctor<GV>
+{
+  using ThisType = LocalPointwiseLinearKineticReconstructionOperator;
+  using BaseType = XT::Grid::ElementFunctor<GV>;
+  static constexpr size_t dimDomain = BoundaryValueType::d;
+  static constexpr size_t dimRange = BoundaryValueType::r;
+  using EntityType = typename GV::template Codim<0>::Entity;
+  using RangeFieldType = typename BoundaryValueType::RangeFieldType;
+  static constexpr size_t stencil_size = 3;
+  using StencilType = XT::Common::FieldVector<size_t, stencil_size>;
+  using StencilsType = FieldVector<StencilType, dimDomain>;
+  using DomainType = typename BoundaryValueType::DomainType;
+  using RangeType = typename BoundaryValueType::RangeReturnType;
+  using ReconstructedFunctionType = DiscreteValuedGridFunction<GV, dimRange, 1, RangeFieldType>;
+
+public:
+  explicit LocalPointwiseLinearKineticReconstructionOperator(ReconstructedFunctionType& reconstructed_function,
+                                                             const GV& grid_view,
+                                                             const AnalyticalFluxType& analytical_flux,
+                                                             const XT::Common::Parameter& param)
+    : reconstructed_function_(reconstructed_function)
+    , grid_view_(grid_view)
+    , analytical_flux_(analytical_flux)
+    , param_(param)
+  {}
+
+  LocalPointwiseLinearKineticReconstructionOperator(const ThisType& other)
+    : BaseType(other)
+    , reconstructed_function_(other.reconstructed_function_)
+    , grid_view_(other.grid_view_)
+    , analytical_flux_(other.analytical_flux_)
+    , param_(other.param_)
+  {}
+
+  XT::Grid::ElementFunctor<GV>* copy() override final
+  {
+    return new ThisType(*this);
+  }
+
+  void apply_local(const EntityType& entity) override final
+  {
+    // In a MPI parallel run, if entity is on boundary of overlap, we do not have to reconstruct
+    if (!fill_stencils(entity))
+      return;
+
+    auto& local_reconstructed_values = reconstructed_function_.local_values(entity);
+    for (size_t dd = 0; dd < dimDomain; ++dd)
+      analytical_flux_.calculate_reconstructed_fluxes(
+          stencils_[dd], boundary_dirs_[dd], local_reconstructed_values, dd);
+  } // void apply_local(...)
+
+private:
+  bool fill_stencils(const EntityType& entity)
+  {
+    for (size_t dd = 0; dd < dimDomain; ++dd)
+      stencils_[dd][1] = grid_view_.indexSet().index(entity);
+    for (const auto& intersection : Dune::intersections(grid_view_, entity)) {
+      const size_t dd = intersection.indexInInside() / 2;
+      const size_t index = (intersection.indexInInside() % 2) * 2;
+      if (intersection.boundary() && !intersection.neighbor()) { // boundary intersections
+        stencils_[dd][index] = size_t(-1);
+        boundary_dirs_[dd][index] = intersection.indexInInside() % 2;
+      } else if (intersection.neighbor()) { // inner and periodic intersections {
+        stencils_[dd][index] = grid_view_.indexSet().index(intersection.outside());
+      } else if (!intersection.neighbor() && !intersection.boundary()) { // processor boundary
+        return false;
+      } else {
+        DUNE_THROW(Dune::NotImplemented, "This should not happen!");
+      }
+    } // intersections
+    return true;
+  } // void fill_stencils(...)
+
+  ReconstructedFunctionType& reconstructed_function_;
+  const GV& grid_view_;
+  const AnalyticalFluxType& analytical_flux_;
+  const XT::Common::Parameter& param_;
+  StencilsType stencils_;
+  XT::Common::FieldVector<XT::Common::FieldVector<size_t, stencil_size>, dimDomain> boundary_dirs_;
+
+}; // class LocalPointwiseLinearKineticReconstructionOperator
+
+// Does not reconstruct a full first-order DG function, but only stores the reconstructed values at the intersection
+// centers. This avoids the interpolation in this operator and the evaluation of the reconstructed function in the
+// finite volume operator which are both quite expensive for large dimRange.
+template <class GV, class BoundaryValueImp, class AnalyticalFluxType, class VectorType, class LocalVectorType>
+class PointwiseLinearKineticReconstructionOperator
+{
+public:
+  using BoundaryValueType = BoundaryValueImp;
+  using E = XT::Grid::extract_entity_t<GV>;
+  using EigenVectorWrapperType = internal::DummyEigenVectorWrapper<AnalyticalFluxType, LocalVectorType>;
+  using R = typename BoundaryValueType::R;
+  static constexpr size_t dimDomain = BoundaryValueType::d;
+  static constexpr size_t dimRange = BoundaryValueType::r;
+  using SpaceType = SpaceInterface<GV, dimRange, 1, R>;
+  using ReconstructedFunctionType = DiscreteValuedGridFunction<GV, dimRange, 1, R>;
+  using ReconstructedValuesType = std::vector<typename ReconstructedFunctionType::LocalFunctionValuesType>;
+
+  PointwiseLinearKineticReconstructionOperator(const BoundaryValueType& boundary_values,
+                                               const SpaceType& space,
+                                               const AnalyticalFluxType& analytical_flux)
+    : boundary_values_(boundary_values)
+    , space_(space)
+    , analytical_flux_(analytical_flux)
+  {}
+
+  bool linear() const
+  {
+    return false;
+  }
+
+  const SpaceType& space() const
+  {
+    return space_;
+  }
+
+  void apply(const VectorType& source, ReconstructedFunctionType& range, const XT::Common::Parameter& param) const
+  {
+    // evaluate cell averages
+    const auto& grid_view = space_.grid_view();
+    const auto& index_set = grid_view.indexSet();
+    std::vector<LocalVectorType> source_values(index_set.size(0));
+    auto source_func = make_discrete_function(space_, source);
+    const auto local_source = source_func.local_function();
+    for (const auto& entity : Dune::elements(grid_view)) {
+      local_source->bind(entity);
+      const auto entity_index = index_set.index(entity);
+      source_values[entity_index] = local_source->evaluate(entity.geometry().local(entity.geometry().center()));
+    }
+
+    // do reconstruction
+    auto local_reconstruction_operator =
+        LocalPointwiseLinearKineticReconstructionOperator<GV, AnalyticalFluxType, BoundaryValueType, LocalVectorType>(
+            range, grid_view, analytical_flux_, param);
+    auto walker = XT::Grid::Walker<GV>(grid_view);
+    walker.append(local_reconstruction_operator);
+    walker.walk(true);
+  } // void apply(...)
+
+private:
+  const BoundaryValueType& boundary_values_;
+  const SpaceType& space_;
+  const AnalyticalFluxType& analytical_flux_;
+}; // class PointwiseLinearKineticReconstructionOperator<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_OPERATORS_RECONSTRUCTION_LINEAR_KINETIC_HH
diff --git a/dune/gdt/operators/reconstruction/slopes.hh b/dune/gdt/operators/reconstruction/slopes.hh
new file mode 100644
index 0000000000000000000000000000000000000000..1819ad44fa0794686e795c922315aec2b79b679a
--- /dev/null
+++ b/dune/gdt/operators/reconstruction/slopes.hh
@@ -0,0 +1,1128 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_OPERATORS_FV_SLOPES_HH
+#define DUNE_GDT_OPERATORS_FV_SLOPES_HH
+
+#if HAVE_CLP
+#  include <coin/ClpSimplex.hpp>
+#endif // HAVE_CLP
+
+#if HAVE_QHULL
+#  include <dune/xt/common/disable_warnings.hh>
+#  include <libqhullcpp/Qhull.h>
+#  include <libqhullcpp/QhullFacetList.h>
+#  include <dune/xt/common/reenable_warnings.hh>
+#endif // HAVE_QHULL
+
+#include <dune/geometry/quadraturerules.hh>
+
+#include <dune/xt/common/float_cmp.hh>
+#include <dune/xt/common/fvector.hh>
+#include <dune/xt/common/parallel/threadstorage.hh>
+
+#include <dune/gdt/test/momentmodels/basisfunctions/partial_moments.hh>
+#include <dune/gdt/test/momentmodels/entropyflux.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+template <class E, class EigenVectorWrapperType, size_t stencil_size = 3>
+class SlopeBase
+{
+public:
+  virtual ~SlopeBase() = default;
+
+  using VectorType = typename EigenVectorWrapperType::VectorType;
+  using MatrixType = typename EigenVectorWrapperType::MatrixType;
+  using StencilType = FieldVector<VectorType, stencil_size>;
+
+  virtual SlopeBase* copy() const = 0;
+
+  // at least one of the following two methods has to be implemented
+  // returns (limited) slope in ordinary coordinates
+  virtual VectorType
+  get(const E& entity, const StencilType& stencil, const EigenVectorWrapperType& eigenvectors, const size_t dd) const
+  {
+    VectorType slope_char = get_char(entity, stencil, eigenvectors, dd);
+    VectorType slope;
+    eigenvectors.apply_eigenvectors(dd, slope_char, slope);
+    return slope;
+  }
+
+  // returns (limited) slope in characteristic coordinates
+  virtual VectorType get_char(const E& entity,
+                              const StencilType& stencil,
+                              const EigenVectorWrapperType& eigenvectors,
+                              const size_t dd) const
+  {
+    VectorType slope = get(entity, stencil, eigenvectors, dd);
+    // convert to characteristic coordinates
+    VectorType slope_char;
+    eigenvectors.apply_inverse_eigenvectors(dd, slope, slope_char);
+    return slope_char;
+  }
+};
+
+
+// Central slope without any limiting
+template <class E, class EigenVectorWrapperType, size_t stencil_size>
+class CentralSlope : public SlopeBase<E, EigenVectorWrapperType, stencil_size>
+{
+  using BaseType = SlopeBase<E, EigenVectorWrapperType, stencil_size>;
+  using ThisType = CentralSlope;
+
+public:
+  using typename BaseType::StencilType;
+  using typename BaseType::VectorType;
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  virtual VectorType get(const E& /*entity*/,
+                         const StencilType& stencil,
+                         const EigenVectorWrapperType& /*eigenvectors*/,
+                         const size_t /*dd*/) const override final
+  {
+    const VectorType& u_left = stencil[0];
+    const VectorType& u_right = stencil[2];
+    return (u_right - u_left) / 2.;
+  }
+};
+
+
+// Left slope without any limiting
+template <class E, class EigenVectorWrapperType, size_t stencil_size>
+class LeftSlope : public SlopeBase<E, EigenVectorWrapperType, stencil_size>
+{
+  using BaseType = SlopeBase<E, EigenVectorWrapperType, stencil_size>;
+  using ThisType = LeftSlope;
+
+public:
+  using typename BaseType::StencilType;
+  using typename BaseType::VectorType;
+
+  BaseType* copy() const override final
+  {
+    return new ThisType();
+  }
+
+  virtual VectorType get(const E& /*entity*/,
+                         const StencilType& stencil,
+                         const EigenVectorWrapperType& /*eigenvectors*/,
+                         const size_t /*dd*/) const override final
+  {
+    const VectorType& u_left = stencil[0];
+    const VectorType& u = stencil[1];
+    return u - u_left;
+  }
+};
+
+
+// Right slope without any limiting
+template <class E, class EigenVectorWrapperType, size_t stencil_size>
+class RightSlope : public SlopeBase<E, EigenVectorWrapperType, stencil_size>
+{
+  using BaseType = SlopeBase<E, EigenVectorWrapperType, stencil_size>;
+  using ThisType = RightSlope;
+
+public:
+  using typename BaseType::StencilType;
+  using typename BaseType::VectorType;
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  virtual VectorType get(const E& /*entity*/,
+                         const StencilType& stencil,
+                         const EigenVectorWrapperType& /*eigenvectors*/,
+                         const size_t /*dd*/) const override final
+  {
+    const VectorType& u = stencil[1];
+    const VectorType& u_right = stencil[2];
+    return u_right - u;
+  }
+};
+
+
+// Zero slope
+template <class E, class EigenVectorWrapperType, size_t stencil_size>
+class NoSlope : public SlopeBase<E, EigenVectorWrapperType, stencil_size>
+{
+  using BaseType = SlopeBase<E, EigenVectorWrapperType, stencil_size>;
+  using ThisType = NoSlope;
+  using typename BaseType::VectorType;
+
+public:
+  using typename BaseType::StencilType;
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  virtual VectorType get(const E& /*entity*/,
+                         const StencilType& /*stencil*/,
+                         const EigenVectorWrapperType& /*eigenvectors*/,
+                         const size_t /*dd*/) const override final
+  {
+    return VectorType(0.);
+  }
+};
+
+
+template <class E, class EigenVectorWrapperType>
+class MinmodSlope : public SlopeBase<E, EigenVectorWrapperType, 3>
+{
+  using BaseType = SlopeBase<E, EigenVectorWrapperType, 3>;
+  using ThisType = MinmodSlope;
+  using typename BaseType::VectorType;
+
+public:
+  using typename BaseType::StencilType;
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  virtual VectorType get_char(const E& /*entity*/,
+                              const StencilType& stencil,
+                              const EigenVectorWrapperType& eigenvectors,
+                              const size_t dd) const override final
+  {
+    const VectorType slope_left = stencil[1] - stencil[0];
+    const VectorType slope_right = stencil[2] - stencil[1];
+    VectorType slope_left_char, slope_right_char;
+    eigenvectors.apply_inverse_eigenvectors(dd, slope_left, slope_left_char);
+    eigenvectors.apply_inverse_eigenvectors(dd, slope_right, slope_right_char);
+    return minmod(slope_left_char, slope_right_char);
+  }
+
+  static VectorType minmod(const VectorType& first_slope, const VectorType& second_slope)
+  {
+    VectorType ret;
+    for (size_t ii = 0; ii < first_slope.size(); ++ii)
+      ret[ii] = XT::Common::minmod(first_slope[ii], second_slope[ii]);
+    return ret;
+  }
+};
+
+
+template <class E, class EigenVectorWrapperType>
+class McSlope : public SlopeBase<E, EigenVectorWrapperType, 3>
+{
+  using BaseType = SlopeBase<E, EigenVectorWrapperType, 3>;
+  using ThisType = McSlope;
+  using MinmodType = MinmodSlope<E, EigenVectorWrapperType>;
+
+public:
+  using typename BaseType::StencilType;
+  using typename BaseType::VectorType;
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  virtual VectorType get_char(const E& /*entity*/,
+                              const StencilType& stencil,
+                              const EigenVectorWrapperType& eigenvectors,
+                              const size_t dd) const override final
+  {
+    const VectorType& u_left = stencil[0];
+    const VectorType& u = stencil[1];
+    const VectorType& u_right = stencil[2];
+    const VectorType slope_left = (u - u_left) * 2.;
+    const VectorType slope_right = (u_right - u) * 2.;
+    const VectorType slope_center = (u_right - u_left) / 2.;
+    VectorType slope_left_char, slope_right_char, slope_center_char;
+    eigenvectors.apply_inverse_eigenvectors(dd, slope_left, slope_left_char);
+    eigenvectors.apply_inverse_eigenvectors(dd, slope_right, slope_right_char);
+    eigenvectors.apply_inverse_eigenvectors(dd, slope_center, slope_center_char);
+    const VectorType first_slope = MinmodType::minmod(slope_left_char, slope_right_char);
+    return MinmodType::minmod(first_slope, slope_center_char);
+  }
+};
+
+
+template <class E, class EigenVectorWrapperType>
+class SuperbeeSlope : public SlopeBase<E, EigenVectorWrapperType, 3>
+{
+  using BaseType = SlopeBase<E, EigenVectorWrapperType, 3>;
+  using ThisType = SuperbeeSlope;
+  using MinmodType = MinmodSlope<E, EigenVectorWrapperType>;
+
+public:
+  using typename BaseType::StencilType;
+  using typename BaseType::VectorType;
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  virtual VectorType get_char(const E& /*entity*/,
+                              const StencilType& stencil,
+                              const EigenVectorWrapperType& eigenvectors,
+                              const size_t dd) const override final
+  {
+    const VectorType slope_left = stencil[1] - stencil[0];
+    const VectorType slope_right = stencil[2] - stencil[1];
+    VectorType slope_left_char, slope_right_char;
+    eigenvectors.apply_inverse_eigenvectors(dd, slope_left, slope_left_char);
+    eigenvectors.apply_inverse_eigenvectors(dd, slope_right, slope_right_char);
+    return superbee(slope_left_char, slope_right_char);
+  }
+
+  static VectorType superbee(const VectorType& first_slope, const VectorType& second_slope)
+  {
+    VectorType ret(0.);
+    for (size_t ii = 0; ii < first_slope.size(); ++ii)
+      ret[ii] = superbee(first_slope[ii], second_slope[ii]);
+    return ret;
+  }
+}; // class SuperbeeSlope
+
+
+template <class GV, class MomentBasis>
+class RealizabilityLimiterBase
+{
+public:
+  using E = XT::Grid::extract_entity_t<GV>;
+  using EntropyFluxType = EntropyBasedFluxFunction<GV, MomentBasis>;
+  using StateType = typename EntropyFluxType::StateType;
+
+  RealizabilityLimiterBase(const EntropyFluxType& entropy_flux)
+    : entropy_flux_(entropy_flux)
+    , local_flux_(entropy_flux_.derived_local_function())
+  {}
+
+  RealizabilityLimiterBase(const RealizabilityLimiterBase& other)
+    : entropy_flux_(other.entropy_flux_)
+    , local_flux_(entropy_flux_.derived_local_function())
+  {}
+
+  // Ensures we are able to solve the optimization problems for the reconstructed values without regularization. If
+  // optimization fails, we just set the slope to 0.
+  template <class VectorType>
+  void ensure_solvability(const E& entity, const VectorType& u, VectorType& slope) const
+  {
+    local_flux_->bind(entity);
+    try {
+      const auto u_left = u - slope * 0.5;
+      const auto u_right = u + slope * 0.5;
+      local_flux_->get_alpha(u_left, false);
+      local_flux_->get_alpha(u_right, false);
+    } catch (const Dune::MathError&) {
+      std::fill(slope.begin(), slope.end(), 0.);
+    }
+  }
+
+private:
+  const EntropyFluxType& entropy_flux_;
+  std::unique_ptr<typename EntropyFluxType::Localfunction> local_flux_;
+};
+
+
+// Realizability limiter that ensures positivity of the components of u in noncharacteristic variables. Uses single
+// limiter variable for all components.
+template <class GV,
+          class MomentBasis,
+          class EigenVectorWrapperType,
+          class SlopeType = MinmodSlope<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType>>
+class PositivityLimitedSlope
+  : public SlopeBase<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType, 3>
+  , public RealizabilityLimiterBase<GV, MomentBasis>
+{
+  using ThisType = PositivityLimitedSlope;
+  using RangeFieldType = typename MomentBasis::RangeFieldType;
+  static const size_t dimRange = MomentBasis::dimRange;
+  using RealizabilityBaseType = RealizabilityLimiterBase<GV, MomentBasis>;
+  using typename RealizabilityBaseType::E;
+  using typename RealizabilityBaseType::EntropyFluxType;
+  using BaseType = SlopeBase<E, EigenVectorWrapperType, 3>;
+  using typename BaseType::VectorType;
+
+public:
+  using typename BaseType::StencilType;
+
+  // This limiter ensures u_i >= epsilon for all components u_i of u.
+  PositivityLimitedSlope(const EntropyFluxType& entropy_flux, const RangeFieldType epsilon = 0.)
+    : RealizabilityBaseType(entropy_flux)
+    , epsilon_(epsilon)
+  {}
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  virtual VectorType get(const E& entity,
+                         const StencilType& stencil,
+                         const EigenVectorWrapperType& eigenvectors,
+                         const size_t dd) const override final
+  {
+    static const VectorType zero_vector(0.);
+    const VectorType& u_bar = stencil[1];
+    if (!is_epsilon_realizable(u_bar, epsilon_))
+      return zero_vector;
+    const VectorType slope = slope_limiter_.get(entity, stencil, eigenvectors, dd);
+    // this needs to be changed for other interface quadratures (see above)
+    const FieldVector<VectorType, 2> reconstructed_values{u_bar - 0.5 * slope, u_bar + 0.5 * slope};
+    VectorType thetas(0.);
+    for (size_t kk = 0; kk < reconstructed_values.size(); ++kk) {
+      const VectorType& u = reconstructed_values[kk];
+      for (size_t ii = 0; ii < u.size(); ++ii) {
+        if (u[ii] >= u_bar[ii])
+          continue;
+        else if (u[ii] < epsilon_)
+          thetas[ii] = std::max(thetas[ii], (epsilon_ - u[ii]) / (u_bar[ii] - u[ii]));
+      }
+    } // kk
+
+    const auto theta_max = *std::max_element(thetas.begin(), thetas.end());
+    assert(XT::Common::FloatCmp::le(theta_max, 1.) && XT::Common::FloatCmp::ge(theta_max, 0.));
+    VectorType ret = slope * (1 - theta_max);
+    this->ensure_solvability(entity, u_bar, ret);
+    return ret;
+  }
+
+private:
+  static bool is_epsilon_realizable(const VectorType& u, const RangeFieldType epsilon)
+  {
+    for (const auto& val : u)
+      if (val < epsilon)
+        return false;
+    return true;
+  }
+
+  const RangeFieldType epsilon_;
+  const SlopeType slope_limiter_;
+}; // class PositivityLimitedSlope<...>
+
+
+// Realizability limiter that ensures positivity of the components of u in noncharacteristic variables. Uses single
+// limiter variable for all components.
+template <class GV,
+          class RangeFieldType,
+          size_t dimRange,
+          class EigenVectorWrapperType,
+          EntropyType entropy = EntropyType::MaxwellBoltzmann,
+          class SlopeType = MinmodSlope<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType>>
+class Dg1dRealizabilityLimitedSlope
+  : public SlopeBase<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType, 3>
+  , public RealizabilityLimiterBase<
+        GV,
+        PartialMomentBasis<typename GV::ctype, 1, RangeFieldType, dimRange, 1, 1, 1, entropy>>
+{
+  using ThisType = Dg1dRealizabilityLimitedSlope;
+  using MomentBasis = Dune::GDT::PartialMomentBasis<RangeFieldType, 1, RangeFieldType, dimRange, 1, 1, 1, entropy>;
+  using RealizabilityBaseType = RealizabilityLimiterBase<GV, MomentBasis>;
+  using typename RealizabilityBaseType::E;
+  using typename RealizabilityBaseType::EntropyFluxType;
+  using BaseType = SlopeBase<E, EigenVectorWrapperType, 3>;
+  using typename BaseType::VectorType;
+
+public:
+  using typename BaseType::StencilType;
+
+  // This limiter ensures u_i >= epsilon for all components u_i of u.
+  Dg1dRealizabilityLimitedSlope(const EntropyFluxType& entropy_flux,
+                                const MomentBasis& basis_functions,
+                                const RangeFieldType epsilon = 0.)
+    : RealizabilityBaseType(entropy_flux)
+    , basis_functions_(basis_functions)
+    , epsilon_(epsilon)
+  {}
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  virtual VectorType get(const E& entity,
+                         const StencilType& stencil,
+                         const EigenVectorWrapperType& eigenvectors,
+                         const size_t dd) const override final
+  {
+    const VectorType slope = slope_limiter_.get(entity, stencil, eigenvectors, dd);
+    const VectorType& u_bar = stencil[1];
+    const FieldVector<VectorType, 2> reconstructed_values{u_bar - slope * 0.5, u_bar + slope * 0.5};
+
+    VectorType thetas(0.);
+    for (size_t kk = 0; kk < reconstructed_values.size(); ++kk) {
+      const VectorType& u = reconstructed_values[kk];
+      for (size_t ii = 0; ii < dimRange / 2; ++ii) {
+        const auto& u0 = u[2 * ii];
+        const auto& u1 = u[2 * ii + 1];
+        const auto& ubar0 = u_bar[2 * ii];
+        const auto& ubar1 = u_bar[2 * ii + 1];
+        const auto& vj = basis_functions_.partitioning()[ii];
+        const auto& vjplus1 = basis_functions_.partitioning()[ii + 1];
+        FieldVector<RangeFieldType, 3> thetas_ii;
+        if (!is_epsilon_realizable(ubar0, ubar1, vj, vjplus1, epsilon_)) {
+          thetas[2 * ii] = 1.;
+        } else {
+          thetas_ii[0] = (epsilon_ - u0) / (ubar0 - u0);
+          thetas_ii[1] =
+              (u0 * vj - u1 + epsilon_ * std::sqrt(std::pow(vj, 2) + 1)) / ((ubar1 - u1) - (ubar0 - u0) * vj);
+          thetas_ii[2] = (u0 * vjplus1 - u1 - epsilon_ * std::sqrt(std::pow(vjplus1, 2) + 1))
+                         / ((ubar1 - u1) - (ubar0 - u0) * vjplus1);
+          for (size_t ll = 0; ll < 3; ++ll)
+            if (thetas_ii[ll] >= 0. && thetas_ii[ll] <= 1.)
+              thetas[2 * ii] = std::max(thetas[2 * ii], thetas_ii[ll]);
+        } // else (!realizable)
+        thetas[2 * ii + 1] = thetas[2 * ii];
+      } // ii
+    } // local_reconstructed_values
+
+    VectorType ret;
+    for (size_t ii = 0; ii < dimRange; ++ii) {
+      assert(XT::Common::FloatCmp::le(thetas[ii], 1.) && XT::Common::FloatCmp::ge(thetas[ii], 0.));
+      ret[ii] = slope[ii] * (1 - thetas[ii]);
+    }
+    this->ensure_solvability(entity, u_bar, ret);
+    return ret;
+  }
+
+private:
+  static bool is_epsilon_realizable(const RangeFieldType ubar0,
+                                    const RangeFieldType ubar1,
+                                    const RangeFieldType v0,
+                                    const RangeFieldType v1,
+                                    const RangeFieldType eps)
+  {
+    bool ret = (ubar0 >= eps) && (ubar1 <= v1 * ubar0 - eps * std::sqrt(std::pow(v1, 2) + 1))
+               && (v0 * ubar0 + eps * std::sqrt(std::pow(v0, 2) + 1) <= ubar1);
+    return ret;
+  }
+
+  const MomentBasis& basis_functions_;
+  const RangeFieldType epsilon_;
+  const SlopeType slope_limiter_;
+}; // class Dg1dRealizabilityLimitedSlope<...>
+
+
+#if HAVE_QHULL
+// Realizability limiter that ensures that the limited values are within the convex hull of the quadrature points. Uses
+// single limiter variable for all components.
+template <class GV,
+          class MomentBasis,
+          class EigenVectorWrapperType,
+          class SlopeType = MinmodSlope<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType>>
+class ConvexHullRealizabilityLimitedSlope
+  : public SlopeBase<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType, 3>
+  , public RealizabilityLimiterBase<GV, MomentBasis>
+{
+  using ThisType = ConvexHullRealizabilityLimitedSlope;
+  using RangeFieldType = typename MomentBasis::RangeFieldType;
+  static const size_t dimRange = MomentBasis::dimRange;
+  using RealizabilityBaseType = RealizabilityLimiterBase<GV, MomentBasis>;
+  using typename RealizabilityBaseType::E;
+  using typename RealizabilityBaseType::EntropyFluxType;
+  using BaseType = SlopeBase<E, EigenVectorWrapperType, 3>;
+  using typename BaseType::VectorType;
+  using PlaneCoefficientsType = typename std::vector<std::pair<VectorType, RangeFieldType>>;
+
+public:
+  using typename BaseType::StencilType;
+
+  ConvexHullRealizabilityLimitedSlope(const EntropyFluxType& entropy_flux,
+                                      const MomentBasis& basis_functions,
+                                      const RangeFieldType epsilon = 0.)
+    : RealizabilityBaseType(entropy_flux)
+    , basis_functions_(basis_functions)
+    , epsilon_(epsilon)
+  {
+    calculate_plane_coefficients();
+  }
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  virtual VectorType get(const E& entity,
+                         const StencilType& stencil,
+                         const EigenVectorWrapperType& eigenvectors,
+                         const size_t dd) const override final
+  {
+    static const VectorType zero_vector(0.);
+    const VectorType& u_bar = stencil[1];
+    if (!is_epsilon_realizable(u_bar, epsilon_))
+      return zero_vector;
+    const VectorType slope = slope_limiter_.get(entity, stencil, eigenvectors, dd);
+
+    const FieldVector<VectorType, 2> reconstructed_values{u_bar - 0.5 * slope, u_bar + 0.5 * slope};
+
+    RangeFieldType theta(-epsilon_);
+    for (size_t kk = 0; kk < reconstructed_values.size(); ++kk) {
+      const VectorType& u = reconstructed_values[kk];
+      // rescale u_l, u_bar
+      auto u_bar_minus_u = u_bar - u;
+      const auto factor = std::max(basis_functions_.density(u), basis_functions_.density(u_bar)) / 2.;
+      u /= factor;
+      u_bar_minus_u /= factor;
+
+      for (const auto& coeffs : plane_coefficients_) {
+        const auto& a = coeffs.first;
+        const auto& b = coeffs.second;
+        RangeFieldType theta_li = (b - a * u) / (a * u_bar_minus_u);
+        if (XT::Common::FloatCmp::le(theta_li, 1.))
+          theta = std::max(theta, theta_li);
+      } // coeffs
+    } // kk
+    theta = std::min(epsilon_ + theta, 1.);
+
+    assert(XT::Common::FloatCmp::le(theta, 1.) && XT::Common::FloatCmp::ge(theta, 0.));
+    VectorType ret = slope * (1 - theta);
+    this->ensure_solvability(entity, u_bar, ret);
+    return ret;
+  }
+
+private:
+  // calculate half space representation of realizable set
+  void calculate_plane_coefficients()
+  {
+    using orgQhull::Qhull;
+    Qhull qhull;
+    const auto& quadrature = basis_functions_.quadratures().merged();
+    std::vector<FieldVector<RangeFieldType, dimRange>> points(quadrature.size() + 1);
+    points[0] = FieldVector<RangeFieldType, dimRange>(0);
+    size_t ii = 1;
+    for (const auto& quad_point : quadrature)
+      points[ii++] = basis_functions_.evaluate(quad_point.position());
+    std::cout << "Starting qhull..." << std::endl;
+    qhull.runQhull("Realizable set", int(dimRange), int(points.size()), &(points[0][0]), "Qt T1");
+    std::cout << "qhull done" << std::endl;
+    //    qhull.outputQhull("n");
+    const auto facet_end = qhull.endFacet();
+    plane_coefficients_ = std::make_shared<PlaneCoefficientsType>(qhull.facetList().count());
+    ii = 0;
+    for (auto facet = qhull.beginFacet(); facet != facet_end; facet = facet.next(), ++ii) {
+      for (size_t jj = 0; jj < dimRange; ++jj)
+        plane_coefficients_[ii].first[jj] = *(facet.hyperplane().coordinates() + jj);
+      plane_coefficients_[ii].second = -facet.hyperplane().offset();
+    }
+  } // void calculate_plane_coefficients()
+
+  const MomentBasis& basis_functions_;
+  const RangeFieldType epsilon_;
+  const SlopeType slope_limiter_;
+  PlaneCoefficientsType plane_coefficients_;
+}; // class ConvexHullRealizabilityLimitedSlope<...>
+
+
+// Realizability limiter that ensures that the limited values are within the convex hull of the quadrature points. Uses
+// single limiter variable for all components.
+template <class GV,
+          class MomentBasis,
+          class EigenVectorWrapperType,
+          class SlopeType = MinmodSlope<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType>>
+class DgConvexHullRealizabilityLimitedSlope
+  : public SlopeBase<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType, 3>
+  , public RealizabilityLimiterBase<GV, MomentBasis>
+{
+  using ThisType = DgConvexHullRealizabilityLimitedSlope;
+  using RangeFieldType = typename MomentBasis::RangeFieldType;
+  static const size_t dimRange = MomentBasis::dimRange;
+  static const size_t block_size = (MomentBasis::dimDomain == 1) ? 2 : 4;
+  static const size_t num_blocks = dimRange / block_size;
+  using RealizabilityBaseType = RealizabilityLimiterBase<GV, MomentBasis>;
+  using typename RealizabilityBaseType::E;
+  using typename RealizabilityBaseType::EntropyFluxType;
+  using BaseType = SlopeBase<E, EigenVectorWrapperType, 3>;
+  using typename BaseType::VectorType;
+  using BlockRangeType = typename VectorType::BlockType;
+  using BlockPlaneCoefficientsType = typename std::vector<std::pair<BlockRangeType, RangeFieldType>>;
+  using PlaneCoefficientsType = FieldVector<BlockPlaneCoefficientsType, num_blocks>;
+
+public:
+  using typename BaseType::StencilType;
+
+  DgConvexHullRealizabilityLimitedSlope(const EntropyFluxType& entropy_flux,
+                                        const MomentBasis& basis_functions,
+                                        const RangeFieldType epsilon = 0.)
+    : RealizabilityBaseType(entropy_flux)
+    , basis_functions_(basis_functions)
+    , epsilon_(epsilon)
+  {
+    if (!basis_functions_.plane_coefficients()[0].size())
+      basis_functions_.calculate_plane_coefficients();
+    // replace the coefficients b by \tilde{b} = b - eps  \twonorm(a) to guarantee distance of eps to boundary
+    plane_coefficients_ = basis_functions_.plane_coefficients();
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      for (auto& coeff : plane_coefficients_[jj])
+        coeff.second -= epsilon_ * coeff.first.two_norm();
+  }
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  virtual VectorType get(const E& entity,
+                         const StencilType& stencil,
+                         const EigenVectorWrapperType& eigenvectors,
+                         const size_t dd) const override final
+  {
+    const VectorType& u_bar = stencil[1];
+    const VectorType slope = slope_limiter_.get(entity, stencil, eigenvectors, dd);
+    const FieldVector<VectorType, 2> reconstructed_values{u_bar - slope * 0.5, u_bar + slope * 0.5};
+
+    // vector to store thetas for each local reconstructed value
+    FieldVector<RangeFieldType, num_blocks> thetas(0.);
+    for (size_t kk = 0; kk < reconstructed_values.size(); ++kk) {
+      const VectorType& u = reconstructed_values[kk];
+      for (size_t jj = 0; jj < num_blocks; ++jj) {
+        // Check realizability of u_bar in this block. The first condition avoids unnecessary repeated checking.
+        if (thetas[jj] == 1. || !is_epsilon_realizable(u_bar.block(jj), jj)) {
+          thetas[jj] = 1.;
+          continue;
+        }
+        thetas[jj] = std::max(thetas[jj], get_block_theta(u.block(jj), u_bar.block(jj), jj));
+      } // jj
+    } // kk
+
+    VectorType ret;
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      assert(XT::Common::FloatCmp::le(thetas[jj], 1.) && XT::Common::FloatCmp::ge(thetas[jj], 0.));
+      for (size_t ii = 0; ii < block_size; ++ii)
+        ret.block(jj)[ii] = slope.block(jj)[ii] * (1 - thetas[jj]);
+    }
+    this->ensure_solvability(entity, u_bar, ret);
+    return ret;
+  } // ... get(...)
+
+private:
+  bool is_epsilon_realizable(const BlockRangeType& u, const size_t jj) const
+  {
+    for (const auto& coeff : plane_coefficients_[jj])
+      if (u * coeff.first > coeff.second)
+        return false;
+    return true;
+  }
+
+  RangeFieldType get_block_theta(const BlockRangeType& u, const BlockRangeType& u_bar, const size_t jj) const
+  {
+    RangeFieldType theta(0.);
+    auto u_bar_minus_u = u_bar - u;
+    for (const auto& coeffs : plane_coefficients_[jj]) {
+      const auto& a = coeffs.first;
+      const auto& b = coeffs.second;
+      RangeFieldType theta_li = (b - a * u) / (a * u_bar_minus_u);
+      if (XT::Common::FloatCmp::le(theta_li, 1.))
+        theta = std::max(theta, theta_li);
+    } // coeffs
+    return theta;
+  } // ... get_block_theta(...)
+
+  const MomentBasis& basis_functions_;
+  const RangeFieldType epsilon_;
+  const SlopeType slope_limiter_;
+  PlaneCoefficientsType plane_coefficients_;
+}; // class DgConvexHullRealizabilityLimitedSlope<...>
+
+#else // HAVE_QHULL
+
+template <class GV,
+          class MomentBasis,
+          class EigenVectorWrapperType,
+          class SlopeType = MinmodSlope<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType>>
+class ConvexHullRealizabilityLimitedSlope
+{
+  static_assert(Dune::AlwaysFalse<MomentBasis>::value, "You are missing Qhull!");
+};
+
+template <class GV,
+          class MomentBasis,
+          class EigenVectorWrapperType,
+          class SlopeType = MinmodSlope<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType>>
+class DgConvexHullRealizabilityLimitedSlopeSlope
+{
+  static_assert(Dune::AlwaysFalse<MomentBasis>::value, "You are missing Qhull!");
+};
+#endif // HAVE_QHULL
+
+#if HAVE_CLP
+// Characteristic component-wise realizability limiter that ensures positivity of the components of u in
+// noncharacteristic variables by solving a linear program.
+template <class GV,
+          class MomentBasis,
+          class EigenVectorWrapperType,
+          class SlopeType = MinmodSlope<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType>>
+class LpPositivityLimitedSlope
+  : public SlopeBase<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType, 3>
+  , public RealizabilityLimiterBase<GV, MomentBasis>
+{
+  using ThisType = LpPositivityLimitedSlope;
+  using RangeFieldType = typename MomentBasis::RangeFieldType;
+  static const size_t dimRange = MomentBasis::dimRange;
+  using RealizabilityBaseType = RealizabilityLimiterBase<GV, MomentBasis>;
+  using typename RealizabilityBaseType::E;
+  using typename RealizabilityBaseType::EntropyFluxType;
+  using BaseType = SlopeBase<E, EigenVectorWrapperType, 3>;
+  using typename BaseType::MatrixType;
+  using typename BaseType::VectorType;
+
+public:
+  using typename BaseType::StencilType;
+
+  LpPositivityLimitedSlope(const EntropyFluxType& entropy_flux, const RangeFieldType epsilon)
+    : RealizabilityBaseType(entropy_flux)
+    , epsilon_(epsilon)
+  {}
+
+  LpPositivityLimitedSlope(const ThisType& other)
+    : BaseType(other)
+    , RealizabilityBaseType(other)
+    , epsilon_(other.epsilon_)
+    , A_tilde_transposed_(std::make_unique<MatrixType>(dimRange, dimRange))
+    , slope_limiter_(other.slope_limiter_)
+  {}
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  virtual VectorType get(const E& entity,
+                         const StencilType& stencil,
+                         const EigenVectorWrapperType& eigenvectors,
+                         const size_t dd) const override final
+  {
+    static const VectorType zero_vector(0.);
+    const VectorType& u_bar = stencil[1];
+    if (!is_epsilon_realizable(u_bar, epsilon_))
+      return zero_vector;
+    const VectorType slope_char = slope_limiter_.get_char(entity, stencil, eigenvectors, dd);
+    if (XT::Common::FloatCmp::eq(slope_char, zero_vector))
+      return zero_vector;
+    FieldVector<VectorType, 2> thetas;
+    VectorType u_bar_char;
+    eigenvectors.apply_inverse_eigenvectors(dd, u_bar, u_bar_char);
+    const FieldVector<VectorType, 2> reconstructed_values{u_bar_char - 0.5 * slope_char, u_bar_char + 0.5 * slope_char};
+    auto& A_tilde_transposed = *A_tilde_transposed_;
+    for (size_t kk = 0; kk < reconstructed_values.size(); ++kk) {
+      const VectorType& u_char = reconstructed_values[kk];
+      thetas[kk] = solve_linear_program(u_char, u_bar_char, eigenvectors.eigenvectors(dd), A_tilde_transposed);
+    } // kk
+
+    VectorType slope;
+    eigenvectors.apply_eigenvectors(dd, slope_char, slope);
+    for (size_t ii = 0; ii < dimRange; ++ii) {
+      const auto theta_max_ii = std::max(thetas[0][ii], thetas[1][ii]);
+      slope[ii] = slope[ii] * (1 - theta_max_ii);
+    }
+    this->ensure_solvability(entity, u_bar, slope);
+    return slope;
+  }
+
+private:
+  static bool is_epsilon_realizable(const VectorType& u, const RangeFieldType epsilon)
+  {
+    for (const auto& val : u)
+      if (val < epsilon)
+        return false;
+    return true;
+  }
+
+  VectorType solve_linear_program(const VectorType& u_char,
+                                  const VectorType u_bar_char,
+                                  const MatrixType& A,
+                                  MatrixType& A_tilde_transposed) const
+  {
+    // calculate data
+    VectorType u;
+    A.mv(u_char, u);
+    VectorType u_minus_u_bar = u_char - u_bar_char;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      for (size_t jj = 0; jj < dimRange; ++jj)
+        A_tilde_transposed.set_entry(jj, ii, A.get_entry(ii, jj) * u_minus_u_bar[jj]);
+
+    // Create LP with dimRange rows and columns */
+    constexpr int num_rows = static_cast<int>(dimRange);
+    constexpr int num_cols = static_cast<int>(dimRange);
+    auto lp_ptr = std::make_unique<ClpSimplex>(false);
+    auto& lp = *lp_ptr;
+
+    // set number of rows, columns will be added below
+    lp.resize(num_rows, 0);
+
+    // Clp wants the row indices that are non-zero in each column. We have a dense matrix, so provide all indices
+    // 0..num_rows
+    std::array<int, num_rows> row_indices;
+    for (int ii = 0; ii < num_rows; ++ii)
+      row_indices[ii] = ii;
+
+    // set columns
+    for (int jj = 0; jj < num_cols; ++jj)
+      lp.addColumn(num_rows, row_indices.data(), &(A_tilde_transposed[jj][0]), 0., 1., 1.);
+
+    lp.setLogLevel(0);
+
+    lp.setMaximumWallSeconds(60);
+
+    // set rhs
+    for (int ii = 0; ii < num_rows; ++ii)
+      lp.setRowUpper(ii, u[ii] - epsilon_);
+
+    // Now solve
+    VectorType ret;
+    lp.primal();
+    const auto* thetas = lp.primalColumnSolution();
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      ret[ii] = thetas[ii];
+    if (!lp.isProvenOptimal())
+      std::fill(ret.begin(), ret.end(), 1.);
+    return ret;
+  } // void solve_linear_program(...)
+
+  const RangeFieldType epsilon_;
+  mutable std::unique_ptr<MatrixType> A_tilde_transposed_;
+  const SlopeType slope_limiter_;
+}; // class LpPositivityLimitedSlope<...>
+
+template <class GV, class MomentBasis, class EigenVectorWrapperType, class SlopeType>
+constexpr size_t LpPositivityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType, SlopeType>::dimRange;
+
+
+// Realizability limiter that solves a linear program to ensure the reconstructed values are still in the numerically
+// realizable set, i.e. in the convex hull of basis evaluations.
+template <class GV,
+          class MomentBasis,
+          class EigenVectorWrapperType,
+          class SlopeType = MinmodSlope<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType>>
+class LpConvexhullRealizabilityLimitedSlope
+  : public SlopeBase<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType>
+  , public RealizabilityLimiterBase<GV, MomentBasis>
+{
+  using ThisType = LpConvexhullRealizabilityLimitedSlope;
+  using RangeFieldType = typename MomentBasis::RangeFieldType;
+  static constexpr size_t dimRange = MomentBasis::dimRange;
+  static_assert(dimRange < std::numeric_limits<int>::max(), "");
+  static constexpr size_t num_rows = dimRange;
+  using RealizabilityBaseType = RealizabilityLimiterBase<GV, MomentBasis>;
+  using typename RealizabilityBaseType::E;
+  using typename RealizabilityBaseType::EntropyFluxType;
+  using BaseType = SlopeBase<E, EigenVectorWrapperType, 3>;
+  using typename BaseType::MatrixType;
+  using typename BaseType::VectorType;
+
+public:
+  using typename BaseType::StencilType;
+
+  LpConvexhullRealizabilityLimitedSlope(const EntropyFluxType& entropy_flux,
+                                        const MomentBasis& basis_functions,
+                                        const RangeFieldType epsilon)
+    : RealizabilityBaseType(entropy_flux)
+    , epsilon_(epsilon)
+    , basis_functions_(basis_functions)
+    , basis_values_(
+          std::make_shared<std::vector<VectorType>>(XT::Data::merged_quadrature(basis_functions_.quadratures()).size()))
+  {
+    const auto& quadrature = XT::Data::merged_quadrature(basis_functions_.quadratures());
+    for (size_t ii = 0; ii < quadrature.size(); ++ii)
+      (*basis_values_)[ii] = basis_functions.evaluate(quadrature[ii].position());
+  }
+
+  LpConvexhullRealizabilityLimitedSlope(const LpConvexhullRealizabilityLimitedSlope& other)
+    : BaseType(other)
+    , RealizabilityBaseType(other)
+    , epsilon_(other.epsilon_)
+    , basis_functions_(other.basis_functions_)
+    , basis_values_(other.basis_values_)
+    , lp_(nullptr)
+    , A_tilde_transposed_(std::make_unique<MatrixType>(dimRange, dimRange))
+    , slope_limiter_(other.slope_limiter_)
+  {}
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
+  virtual VectorType get(const E& entity,
+                         const StencilType& stencil,
+                         const EigenVectorWrapperType& eigenvectors,
+                         const size_t dd) const override final
+  {
+    VectorType slope_char = slope_limiter_.get_char(entity, stencil, eigenvectors, dd);
+    static const VectorType zero_vector(0.);
+    if (XT::Common::FloatCmp::eq(slope_char, zero_vector))
+      return zero_vector;
+    FieldVector<VectorType, 2> thetas;
+    const VectorType& u_bar = stencil[1];
+    VectorType u_bar_char;
+    eigenvectors.apply_inverse_eigenvectors(dd, u_bar, u_bar_char);
+    const FieldVector<VectorType, 2> reconstructed_values_char{u_bar_char - 0.5 * slope_char,
+                                                               u_bar_char + 0.5 * slope_char};
+    auto& A_tilde_transposed = *A_tilde_transposed_;
+    for (size_t kk = 0; kk < reconstructed_values_char.size(); ++kk)
+      thetas[kk] = solve_linear_program(
+          reconstructed_values_char[kk], u_bar_char, eigenvectors.eigenvectors(dd), A_tilde_transposed);
+    for (size_t ii = 0; ii < dimRange; ++ii) {
+      auto theta_max_ii = std::max(thetas[0][ii], thetas[1][ii]);
+      if (theta_max_ii > 0.)
+        theta_max_ii = std::min(1., theta_max_ii + epsilon_);
+      slope_char[ii] = slope_char[ii] * (1 - theta_max_ii);
+    }
+    // Ensure positive density
+    // For that purpose, get slope in ordinary coordinates
+    VectorType slope;
+    eigenvectors.apply_eigenvectors(dd, slope_char, slope);
+    const FieldVector<VectorType, 2> reconstructed_values{u_bar - 0.5 * slope, u_bar + 0.5 * slope};
+    RangeFieldType theta_pos(0);
+    for (size_t kk = 0; kk < reconstructed_values.size(); ++kk) {
+      const auto& u = reconstructed_values[kk];
+      const auto density_u = basis_functions_.density(u);
+      const auto density_u_bar = basis_functions_.density(u_bar);
+      if (density_u >= density_u_bar)
+        continue;
+      else if (density_u < epsilon_)
+        theta_pos = std::max(theta_pos, (epsilon_ - density_u) / (density_u_bar - density_u));
+    } // kk
+    slope *= 1 - theta_pos;
+    this->ensure_solvability(entity, u_bar, slope);
+    return slope;
+  }
+
+private:
+  void setup_linear_program() const
+  {
+    // We start with creating a model with dimRange rows and num_quad_points+1 columns */
+    assert(basis_values_->size() + dimRange < std::numeric_limits<int>::max());
+    size_t num_cols = basis_values_->size() + dimRange; /* variables are x_1, ..., x_{num_quad_points}, theta_1,
+                                                                            ..., theta_{dimRange} */
+    lp_ = std::make_unique<ClpSimplex>(false);
+    auto& lp = *lp_;
+    // set number of rows
+    lp.resize(num_rows, 0);
+
+    // set columns for quadrature points
+    assert(basis_values_->size() == num_cols - dimRange);
+    static auto row_indices = create_row_indices();
+    for (size_t ii = 0; ii < num_cols - dimRange; ++ii) {
+      // First argument: number of elements in column
+      // Second/Third argument: indices/values of column entries
+      // Fourth/Fifth argument: lower/upper column bound, i.e. lower/upper bound for x_i. As all x_i should be
+      // positive, set to 0/inf, which is the default.
+      // Sixth argument: Prefactor in objective for x_i, this is 0 for all x_i, which is also the default;
+      lp.addColumn(static_cast<int>(num_rows), row_indices.data(), &((*basis_values_)[ii][0]));
+    }
+
+    // add theta columns (set to random values, will be set correctly in solve_linear_program)
+    // The bounds for theta should be [0,1]. Also sets the prefactor in the objective to 1 for the thetas.
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      lp.addColumn(static_cast<int>(num_rows), row_indices.data(), &((*basis_values_)[0][0]), 0., 1., 1.);
+    lp.setLogLevel(0);
+  } // void setup_linear_program()
+
+  VectorType solve_linear_program(const VectorType& u_char,
+                                  const VectorType& u_bar_char,
+                                  const MatrixType& A,
+                                  MatrixType& A_tilde_transposed) const
+  {
+    // calculate data
+    VectorType u_minus_u_bar_char = u_char - u_bar_char;
+    VectorType u;
+    A.mv(u_char, u);
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      for (size_t jj = 0; jj < dimRange; ++jj)
+        A_tilde_transposed.set_entry(jj, ii, A.get_entry(ii, jj) * u_minus_u_bar_char[jj]);
+
+    // setup linear program
+    setup_linear_program();
+    auto& lp = *lp_;
+    size_t num_cols = basis_values_->size() + dimRange; // see above
+
+    // set rhs (equality constraints, so set both bounds equal)
+    for (size_t ii = 0; ii < num_rows; ++ii) {
+      lp.setRowLower(static_cast<int>(ii), u[ii]);
+      lp.setRowUpper(static_cast<int>(ii), u[ii]);
+    }
+
+    // delete old theta columns.
+    FieldVector<int, dimRange> theta_columns;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      theta_columns[ii] = static_cast<int>(num_cols - dimRange + ii);
+    lp.deleteColumns(dimRange, &(theta_columns[0]));
+
+    // set new theta columns
+    static auto row_indices = create_row_indices();
+    for (size_t jj = 0; jj < dimRange; ++jj)
+      lp.addColumn(num_rows, row_indices.data(), &(A_tilde_transposed[jj][0]), 0., 1., 1.);
+
+    // Now solve
+    lp.setMaximumWallSeconds(60);
+    lp.primal();
+    const auto* thetas_ptr = lp.primalColumnSolution();
+    VectorType thetas;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      thetas[ii] = thetas_ptr[ii];
+    if (!lp.isProvenOptimal())
+      std::fill(thetas.begin(), thetas.end(), 1.);
+    return thetas;
+  }
+
+  // Clp wants the row indices that are non-zero in each column. We have a dense matrix, so provide all indices
+  // 0..num_rows
+  static std::array<int, num_rows> create_row_indices()
+  {
+    std::array<int, dimRange> ret;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      ret[ii] = static_cast<int>(ii);
+    return ret;
+  }
+
+  const RangeFieldType epsilon_;
+  const MomentBasis& basis_functions_;
+  std::shared_ptr<std::vector<VectorType>> basis_values_;
+  mutable std::unique_ptr<ClpSimplex> lp_;
+  mutable std::unique_ptr<MatrixType> A_tilde_transposed_;
+  const SlopeType slope_limiter_;
+};
+
+template <class GV, class MomentBasis, class EigenVectorWrapperType, class SlopeType>
+constexpr size_t LpConvexhullRealizabilityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType, SlopeType>::dimRange;
+
+#else // HAVE_CLP
+
+template <class GV,
+          class MomentBasis,
+          class EigenVectorWrapperType,
+          class SlopeType = MinmodSlope<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType>>
+class LpPositivityLimitedSlope
+{
+  static_assert(Dune::AlwaysFalse<MomentBasis>::value, "You are missing Clp!");
+};
+
+template <class GV,
+          class MomentBasis,
+          class EigenVectorWrapperType,
+          class SlopeType = MinmodSlope<XT::Grid::extract_entity_t<GV>, EigenVectorWrapperType>>
+class LpConvexhullRealizabilityLimitedSlope
+{
+  static_assert(Dune::AlwaysFalse<MomentBasis>::value, "You are missing Clp!");
+};
+
+#endif // HAVE_CLP
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_OPERATORS_FV_SLOPES_HH
diff --git a/dune/gdt/spaces/basis/default.hh b/dune/gdt/spaces/basis/default.hh
index 32ef1b0b3e13815615dc62efefd33a963260aa6f..62dedfe1d5781c896a896100272a3cd4f87ff90d 100644
--- a/dune/gdt/spaces/basis/default.hh
+++ b/dune/gdt/spaces/basis/default.hh
@@ -31,7 +31,7 @@ namespace GDT {
 template <class GV, size_t r = 1, size_t rC = 1, class R = double>
 class DefaultGlobalBasis : public GlobalBasisInterface<GV, r, rC, R>
 {
-  using ThisType = DefaultGlobalBasis<GV, r, rC, R>;
+  using ThisType = DefaultGlobalBasis;
   using BaseType = GlobalBasisInterface<GV, r, rC, R>;
 
 public:
@@ -54,7 +54,7 @@ public:
                      const int order)
     : grid_view_(grid_view)
     , local_finite_elements_(local_finite_elements)
-    , order_(order)
+    , fe_order_(order)
     , max_size_(0)
   {}
 
@@ -74,7 +74,7 @@ public:
   {
     max_size_ = 0;
     for (const auto& gt : grid_view_.indexSet().types(0))
-      max_size_ = std::max(max_size_, local_finite_elements_.get(gt, order_).size());
+      max_size_ = std::max(max_size_, local_finite_elements_.get(gt, fe_order_).size());
   }
 
 private:
@@ -115,7 +115,7 @@ private:
     void post_bind(const ElementType& elemnt) override final
     {
       current_local_fe_ = XT::Common::ConstStorageProvider<LocalFiniteElementInterface<D, d, R, r, rC>>(
-          self_.local_finite_elements_.get(elemnt.geometry().type(), self_.order_));
+          self_.local_finite_elements_.get(elemnt.geometry().type(), self_.fe_order_));
     }
 
   public:
@@ -206,7 +206,7 @@ private:
 
   const GridViewType& grid_view_;
   const FiniteElementFamilyType& local_finite_elements_;
-  const int order_;
+  const int fe_order_;
   size_t max_size_;
 }; // class DefaultGlobalBasis
 
diff --git a/dune/gdt/spaces/basis/finite-volume.hh b/dune/gdt/spaces/basis/finite-volume.hh
index 3eb09edc26108a14d4bece7037aadaeb0385ca80..ca2ab1c1d97a1a20a2c8a13c3e617a42d96d7cd8 100644
--- a/dune/gdt/spaces/basis/finite-volume.hh
+++ b/dune/gdt/spaces/basis/finite-volume.hh
@@ -27,7 +27,7 @@ namespace GDT {
 template <class GV, size_t r = 1, class R = double>
 class FiniteVolumeGlobalBasis : public GlobalBasisInterface<GV, r, 1, R>
 {
-  using ThisType = FiniteVolumeGlobalBasis<GV, r, R>;
+  using ThisType = FiniteVolumeGlobalBasis;
   using BaseType = GlobalBasisInterface<GV, r, 1, R>;
 
 public:
@@ -247,11 +247,8 @@ private:
     {
       if (dofs.size() != r)
         dofs.resize(r);
-      for (unsigned int comp = 0; comp < r; ++comp) {
-        dofs[comp] = XT::Grid::element_integral<R>(
-                         this->element(), [&](const auto& x) { return element_function(x)[comp]; }, order)
-                     / this->element().geometry().volume();
-      }
+      dofs = DynamicVector<R>(XT::Grid::element_integral<RangeType>(this->element(), element_function, order));
+      dofs /= this->element().geometry().volume();
     } // ... interpolate(...)
 
   private:
diff --git a/dune/gdt/spaces/basis/raviart-thomas.hh b/dune/gdt/spaces/basis/raviart-thomas.hh
index 8fd4c3269e9f4f9ee574d5a5fbb8c283b5fa2ec1..7eb71b4702a29895f4c8ad669bd199e4341270b7 100644
--- a/dune/gdt/spaces/basis/raviart-thomas.hh
+++ b/dune/gdt/spaces/basis/raviart-thomas.hh
@@ -37,7 +37,7 @@ namespace GDT {
 template <class GL, class R = double>
 class RaviartThomasGlobalBasis : public GlobalBasisInterface<GL, GL::dimension, 1, R>
 {
-  using ThisType = RaviartThomasGlobalBasis<GL, R>;
+  using ThisType = RaviartThomasGlobalBasis;
   using BaseType = GlobalBasisInterface<GL, GL::dimension, 1, R>;
 
 public:
diff --git a/dune/gdt/spaces/h1/continuous-flattop.hh b/dune/gdt/spaces/h1/continuous-flattop.hh
new file mode 100644
index 0000000000000000000000000000000000000000..cbaa5809601d34be3054586cb047e3b5bf0aa2c9
--- /dev/null
+++ b/dune/gdt/spaces/h1/continuous-flattop.hh
@@ -0,0 +1,178 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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)
+
+#ifndef DUNE_GDT_SPACES_H1_CONTINUOUS_FLATTOP_HH
+#define DUNE_GDT_SPACES_H1_CONTINUOUS_FLATTOP_HH
+
+#include <memory>
+//#include <vector>
+
+//#include <dune/common/typetraits.hh>
+
+//#include <dune/geometry/type.hh>
+
+#include <dune/grid/common/gridview.hh>
+
+//#include <dune/xt/common/exceptions.hh>
+//#include <dune/xt/grid/type_traits.hh>
+
+#include <dune/gdt/local/finite-elements/flattop.hh>
+#include <dune/gdt/spaces/basis/default.hh>
+#include <dune/gdt/spaces/mapper/continuous.hh>
+#include <dune/gdt/spaces/interface.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+/**
+ * \sa make_local_lagrange_finite_element
+ */
+template <class GV, size_t r = 1, class R = double>
+class ContinuousFlatTopSpace : public SpaceInterface<GV, r, 1, R>
+{
+  using ThisType = ContinuousFlatTopSpace;
+  using BaseType = SpaceInterface<GV, r, 1, R>;
+
+public:
+  using BaseType::d;
+  using typename BaseType::D;
+  using typename BaseType::GlobalBasisType;
+  using typename BaseType::GridViewType;
+  using typename BaseType::LocalFiniteElementFamilyType;
+  using typename BaseType::MapperType;
+
+private:
+  using MapperImplementation = ContinuousMapper<GridViewType, LocalFiniteElementFamilyType>;
+  using GlobalBasisImplementation = DefaultGlobalBasis<GridViewType, r, 1, R>;
+
+public:
+  ContinuousFlatTopSpace(GridViewType grd_vw, const int fe_order, const D& overlap = 0.5)
+    : grid_view_(grd_vw)
+    , fe_order_(fe_order)
+    , local_finite_elements_(std::make_unique<LocalFlatTopFiniteElementFamily<D, d, R, r>>(overlap))
+    , mapper_(nullptr)
+    , basis_(nullptr)
+  {
+    this->update_after_adapt();
+  }
+
+  ContinuousFlatTopSpace(const ThisType&) = default;
+  ContinuousFlatTopSpace(ThisType&&) = default;
+
+  ThisType& operator=(const ThisType&) = delete;
+  ThisType& operator=(ThisType&&) = delete;
+
+  const GridViewType& grid_view() const override final
+  {
+    return grid_view_;
+  }
+
+  const MapperType& mapper() const override final
+  {
+    assert(mapper_ && "This must not happen!");
+    return *mapper_;
+  }
+
+  const GlobalBasisType& basis() const override final
+  {
+    assert(basis_ && "This must not happen!");
+    return *basis_;
+  }
+
+  const LocalFiniteElementFamilyType& finite_elements() const override final
+  {
+    return *local_finite_elements_;
+  }
+
+  SpaceType type() const override final
+  {
+    return SpaceType::continuous_lagrange;
+  }
+
+  int min_polorder() const override final
+  {
+    return fe_order_;
+  }
+
+  int max_polorder() const override final
+  {
+    return fe_order_ + 1;
+  }
+
+  bool continuous(const int diff_order) const override final
+  {
+    return diff_order == 0;
+  }
+
+  bool continuous_normal_components() const override final
+  {
+    return true;
+  }
+
+  bool is_lagrangian() const override final
+  {
+    return true;
+  }
+
+  void update_after_adapt() override final
+  {
+    // check: the mapper does not work for non-conforming intersections
+    if (d == 3 && grid_view_.indexSet().types(0).size() != 1)
+      DUNE_THROW(Exceptions::space_error,
+                 "in ContinuousFlatTopSpace: non-conforming intersections are not (yet) "
+                 "supported, and more than one element type in 3d leads to non-conforming intersections!");
+    // create/update mapper ...
+    if (mapper_)
+      mapper_->update_after_adapt();
+    else
+      mapper_ = std::make_unique<MapperImplementation>(grid_view_, *local_finite_elements_, fe_order_);
+    // ... and basis
+    if (basis_)
+      basis_->update_after_adapt();
+    else
+      basis_ = std::make_unique<GlobalBasisImplementation>(grid_view_, *local_finite_elements_, fe_order_);
+    this->create_communicator();
+  } // ... update_after_adapt(...)
+
+private:
+  const GridViewType grid_view_;
+  const int fe_order_;
+  std::unique_ptr<const LocalFlatTopFiniteElementFamily<D, d, R, r>> local_finite_elements_;
+  std::unique_ptr<MapperImplementation> mapper_;
+  std::unique_ptr<GlobalBasisImplementation> basis_;
+}; // class ContinuousFlatTopSpace
+
+
+/**
+ * \sa ContinuousFlatTopSpace
+ */
+template <size_t r, class GV, class R = double>
+ContinuousFlatTopSpace<GV, r, R>
+make_continuous_flattop_space(GV grid_view, const int order, const double& overlap = 0.5)
+{
+  return ContinuousFlatTopSpace<GV, r, R>(grid_view, order, overlap);
+}
+
+
+/**
+ * \sa ContinuousFlatTopSpace
+ */
+template <class GV, class R = double>
+ContinuousFlatTopSpace<GV, 1, R>
+make_continuous_flattop_space(GV grid_view, const int order, const double& overlap = 0.5)
+{
+  return ContinuousFlatTopSpace<GV, 1, R>(grid_view, order, overlap);
+}
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_SPACES_H1_CONTINUOUS_FLATTOP_HH
diff --git a/dune/gdt/spaces/h1/continuous-lagrange.hh b/dune/gdt/spaces/h1/continuous-lagrange.hh
index 8e101a2ee241701b92f9c260e10e49654a613685..54b54488221774c4c361a854df4795ad6abfcfa5 100644
--- a/dune/gdt/spaces/h1/continuous-lagrange.hh
+++ b/dune/gdt/spaces/h1/continuous-lagrange.hh
@@ -61,13 +61,13 @@ public:
   using typename BaseType::MapperType;
 
 private:
-  using MapperImplementation = ContinuousMapper<GridViewType, LocalFiniteElementFamilyType, r>;
+  using MapperImplementation = ContinuousMapper<GridViewType, LocalFiniteElementFamilyType>;
   using GlobalBasisImplementation = DefaultGlobalBasis<GridViewType, r, 1, R>;
 
 public:
   ContinuousLagrangeSpace(GridViewType grd_vw, const int order)
     : grid_view_(grd_vw)
-    , order_(order)
+    , fe_order_(order)
     , local_finite_elements_(std::make_unique<LocalLagrangeFiniteElementFamily<D, d, R, r>>())
     , mapper_(nullptr)
     , basis_(nullptr)
@@ -75,7 +75,21 @@ public:
     this->update_after_adapt();
   }
 
-  ContinuousLagrangeSpace(const ThisType&) = default;
+  ContinuousLagrangeSpace(const ThisType& other)
+    : grid_view_(other.grid_view_)
+    , fe_order_(other.fe_order_)
+    , local_finite_elements_(std::make_unique<LocalLagrangeFiniteElementFamily<D, d, R, r>>())
+    , mapper_(nullptr)
+    , basis_(nullptr)
+  {
+    this->update_after_adapt();
+  }
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
   ContinuousLagrangeSpace(ThisType&&) = default;
 
   ThisType& operator=(const ThisType&) = delete;
@@ -110,12 +124,12 @@ public:
 
   int min_polorder() const override final
   {
-    return order_;
+    return fe_order_;
   }
 
   int max_polorder() const override final
   {
-    return order_;
+    return fe_order_;
   }
 
   bool continuous(const int diff_order) const override final
@@ -144,18 +158,20 @@ public:
     if (mapper_)
       mapper_->update_after_adapt();
     else
-      mapper_ = std::make_unique<MapperImplementation>(grid_view_, *local_finite_elements_, order_);
+      mapper_ = std::make_unique<MapperImplementation>(grid_view_, *local_finite_elements_, fe_order_);
     // ... and basis
     if (basis_)
       basis_->update_after_adapt();
     else
-      basis_ = std::make_unique<GlobalBasisImplementation>(grid_view_, *local_finite_elements_, order_);
+      basis_ = std::make_unique<GlobalBasisImplementation>(grid_view_, *local_finite_elements_, fe_order_);
     this->create_communicator();
   } // ... update_after_adapt(...)
 
 private:
   const GridViewType grid_view_;
-  const int order_;
+  const int fe_order_;
+  int min_polorder_;
+  int max_polorder_;
   std::unique_ptr<const LocalLagrangeFiniteElementFamily<D, d, R, r>> local_finite_elements_;
   std::unique_ptr<MapperImplementation> mapper_;
   std::unique_ptr<GlobalBasisImplementation> basis_;
diff --git a/dune/gdt/spaces/hdiv/raviart-thomas.hh b/dune/gdt/spaces/hdiv/raviart-thomas.hh
index f780f2cb9395a9b00ada04557bcac627412e7cb5..fbdd20a47c0b4035f08081d224133fe762afc158 100644
--- a/dune/gdt/spaces/hdiv/raviart-thomas.hh
+++ b/dune/gdt/spaces/hdiv/raviart-thomas.hh
@@ -49,7 +49,7 @@ namespace GDT {
 template <class GV, class R = double>
 class RaviartThomasSpace : public SpaceInterface<GV, GV::dimension, 1, R>
 {
-  using ThisType = RaviartThomasSpace<GV, R>;
+  using ThisType = RaviartThomasSpace;
   using BaseType = SpaceInterface<GV, GV::dimension, 1, R>;
 
 public:
@@ -80,7 +80,23 @@ public:
     this->update_after_adapt();
   }
 
-  RaviartThomasSpace(const ThisType&) = default;
+  RaviartThomasSpace(const ThisType& other)
+    : grid_view_(other.grid_view_)
+    , order_(other.order_)
+    , local_finite_elements_(std::make_unique<const LocalRaviartThomasFiniteElementFamily<D, d, R>>())
+    , element_indices_(grid_view_)
+    , fe_data_()
+    , mapper_(nullptr)
+    , basis_(nullptr)
+  {
+    this->update_after_adapt();
+  }
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
   RaviartThomasSpace(ThisType&&) = default;
 
   ThisType& operator=(const ThisType&) = delete;
diff --git a/dune/gdt/spaces/interface.hh b/dune/gdt/spaces/interface.hh
index 21d6248538254a5dc9baf65199e1caa5ec2707d8..535baed8904435e94e7c3ab3555499df1baa28f1 100644
--- a/dune/gdt/spaces/interface.hh
+++ b/dune/gdt/spaces/interface.hh
@@ -48,6 +48,7 @@ template <class GridView, size_t range_dim = 1, size_t range_dim_columns = 1, cl
 class SpaceInterface
 {
   static_assert(XT::Grid::is_view<GridView>::value, "");
+  using ThisType = SpaceInterface;
 
 public:
   using GridViewType = GridView;
@@ -73,6 +74,8 @@ public:
     , adapted_(false)
   {}
 
+  virtual ThisType* copy() const = 0;
+
   virtual ~SpaceInterface() = default;
 
   /// \name These methods provide most functionality, they have to be implemented.
diff --git a/dune/gdt/spaces/l2/discontinuous-lagrange.hh b/dune/gdt/spaces/l2/discontinuous-lagrange.hh
index 793fdfe37edd5fcbd07e37347068eadd44e88d81..23c8fee7a3d376b9ba11883754a3f184255b5f84 100644
--- a/dune/gdt/spaces/l2/discontinuous-lagrange.hh
+++ b/dune/gdt/spaces/l2/discontinuous-lagrange.hh
@@ -59,7 +59,7 @@ namespace GDT {
 template <class GV, size_t r = 1, class R = double>
 class DiscontinuousLagrangeSpace : public SpaceInterface<GV, r, 1, R>
 {
-  using ThisType = DiscontinuousLagrangeSpace<GV, r, R>;
+  using ThisType = DiscontinuousLagrangeSpace;
   using BaseType = SpaceInterface<GV, r, 1, R>;
 
 public:
@@ -75,7 +75,7 @@ private:
   using GlobalBasisImplementation = DefaultGlobalBasis<GridViewType, r, 1, R>;
 
 public:
-  DiscontinuousLagrangeSpace(GridViewType grd_vw, const int order)
+  DiscontinuousLagrangeSpace(GridViewType grd_vw, const int order = 1)
     : grid_view_(grd_vw)
     , order_(order)
     , local_finite_elements_(std::make_unique<const LocalLagrangeFiniteElementFamily<D, d, R, r>>())
@@ -85,7 +85,21 @@ public:
     this->update_after_adapt();
   }
 
-  DiscontinuousLagrangeSpace(const ThisType&) = default;
+  DiscontinuousLagrangeSpace(const ThisType& other)
+    : grid_view_(other.grid_view_)
+    , order_(other.order_)
+    , local_finite_elements_(std::make_unique<const LocalLagrangeFiniteElementFamily<D, d, R, r>>())
+    , mapper_(nullptr)
+    , basis_(nullptr)
+  {
+    this->update_after_adapt();
+  }
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
   DiscontinuousLagrangeSpace(ThisType&&) = default;
 
   ThisType& operator=(const ThisType&) = delete;
diff --git a/dune/gdt/spaces/l2/finite-volume.hh b/dune/gdt/spaces/l2/finite-volume.hh
index 226e057be7880db8dc041ba055d33c1e8353a0eb..25910b5c90b549586bcfbc97d792fbb87c751f24 100644
--- a/dune/gdt/spaces/l2/finite-volume.hh
+++ b/dune/gdt/spaces/l2/finite-volume.hh
@@ -38,7 +38,7 @@ class FiniteVolumeSpace
 template <class GV, size_t r, class R>
 class FiniteVolumeSpace<GV, r, 1, R> : public SpaceInterface<GV, r, 1, R>
 {
-  using ThisType = FiniteVolumeSpace<GV, r, 1, R>;
+  using ThisType = FiniteVolumeSpace;
   using BaseType = SpaceInterface<GV, r, 1, R>;
 
 public:
@@ -65,7 +65,20 @@ public:
     this->update_after_adapt();
   }
 
-  FiniteVolumeSpace(const ThisType&) = default;
+  FiniteVolumeSpace(const ThisType& other)
+    : grid_view_(other.grid_view_)
+    , local_finite_elements_(std::make_unique<const LocalLagrangeFiniteElementFamily<D, d, R, r>>())
+    , mapper_(grid_view_)
+    , basis_(grid_view_)
+  {
+    this->update_after_adapt();
+  }
+
+  BaseType* copy() const override final
+  {
+    return new ThisType(*this);
+  }
+
   FiniteVolumeSpace(ThisType&&) = default;
 
   ThisType& operator=(const ThisType&) = delete;
diff --git a/dune/gdt/spaces/mapper/continuous.hh b/dune/gdt/spaces/mapper/continuous.hh
index 9b8df2613fd112bd8c67117392671011ff794077..82135969b8155f2c33a5357b0a4aa66c332d7e2b 100644
--- a/dune/gdt/spaces/mapper/continuous.hh
+++ b/dune/gdt/spaces/mapper/continuous.hh
@@ -29,11 +29,11 @@ namespace Dune {
 namespace GDT {
 
 
-template <class GV, class LocalFiniteElementFamily, size_t basis_functions_per_subentity = 1>
+template <class GV, class LocalFiniteElementFamily>
 class ContinuousMapper : public MapperInterface<GV>
 {
   static_assert(is_local_finite_element_family<LocalFiniteElementFamily>::value, "");
-  using ThisType = ContinuousMapper<GV, LocalFiniteElementFamily, basis_functions_per_subentity>;
+  using ThisType = ContinuousMapper;
   using BaseType = MapperInterface<GV>;
 
   template <int d>
@@ -62,15 +62,23 @@ public:
   using typename BaseType::ElementType;
   using typename BaseType::GridViewType;
 
-  ContinuousMapper(const GridViewType& grd_vw, const LocalFiniteElementFamily& local_finite_elements, const int order)
+  ContinuousMapper(const GridViewType& grd_vw,
+                   const LocalFiniteElementFamily& local_finite_elements,
+                   const int fe_order)
     : grid_view_(grd_vw)
     , local_finite_elements_(local_finite_elements)
-    , order_(order)
+    , fe_order_(fe_order)
     , max_local_size_(0)
     , mapper_(grid_view_, [&](const auto& geometry_type, const auto /*grid_dim*/) {
-      return all_DoF_attached_geometry_types_.count(geometry_type) > 0;
+      return (all_DoF_attached_geometry_types_.count(geometry_type) > 0) ? geometry_type_to_local_size_[geometry_type]
+                                                                         : 0;
     })
   {
+    if (d >= 2 && fe_order_ >= 3 && !XT::Grid::is_cube_alugrid<typename GV::Grid>::value
+        && !XT::Grid::is_yaspgrid<typename GV::Grid>::value && !XT::Grid::is_uggrid<typename GV::Grid>::value)
+      DUNE_THROW(Dune::NotImplemented,
+                 "For order > 2, there are problems with the local-to-global mapping on some grids, see the comment in "
+                 "the global_index method!");
     this->update_after_adapt();
   }
 
@@ -88,12 +96,12 @@ public:
   const LocalFiniteElementCoefficientsInterface<D, d>&
   local_coefficients(const GeometryType& geometry_type) const override final
   {
-    return local_finite_elements_.get(geometry_type, order_).coefficients();
+    return local_finite_elements_.get(geometry_type, fe_order_).coefficients();
   }
 
   size_t size() const override final
   {
-    return basis_functions_per_subentity * mapper_.size();
+    return mapper_.size();
   }
 
   size_t max_local_size() const override final
@@ -113,8 +121,19 @@ public:
       DUNE_THROW(Exceptions::mapper_error,
                  "local_size(element) = " << coeffs.size() << "\n   local_index = " << local_index);
     const auto& local_key = coeffs.local_key(local_index);
-    return basis_functions_per_subentity * mapper_.subIndex(element, local_key.subEntity(), local_key.codim())
-           + local_key.index();
+    // TODO: If there are several DoFs on one subEntity (which is the case e.g. for third order lagrange elements), this
+    // mapping only works if the DoFs are numbered consistently between the elements. For example, if there are two DoFs
+    // on a shared edge between to codim 0 elements, the local_key.index() has to be the same for the same DoF in both
+    // elements. This does not seem to be the case for the simplex grids, the same DoF might be assigned the index 0 in
+    // one element and index 1 in the other element. Fixing this could be done by assigning an orientation to the edge
+    // by looking at the (indices of the) vertices of the edge and reordering the local indices if the orientation is
+    // not the same in all elements sharing the subentity.
+#ifndef NDEBUG
+    if (d >= 2 && fe_order_ >= 3)
+      assert(element.geometry().type() == Dune::GeometryTypes::cube(d)
+             && "Not implemented for this element, see comment above!");
+#endif
+    return mapper_.subIndex(element, local_key.subEntity(), local_key.codim()) + local_key.index();
   } // ... mapToGlobal(...)
 
   using BaseType::global_indices;
@@ -128,8 +147,7 @@ public:
       indices.resize(local_sz, 0);
     for (size_t ii = 0; ii < local_sz; ++ii) {
       const auto& local_key = coeffs.local_key(ii);
-      indices[ii] = basis_functions_per_subentity * mapper_.subIndex(element, local_key.subEntity(), local_key.codim())
-                    + local_key.index();
+      indices[ii] = mapper_.subIndex(element, local_key.subEntity(), local_key.codim()) + local_key.index();
     }
   } // ... globalIndices(...)
 
@@ -142,24 +160,21 @@ public:
     // collect all entities (for all codims) which are used to attach DoFs to
     all_DoF_attached_geometry_types_.clear();
     for (auto&& geometry_type : grid_view_.indexSet().types(0)) {
-      const auto& finite_element = local_finite_elements_.get(geometry_type, order_);
+      const auto& finite_element = local_finite_elements_.get(geometry_type, fe_order_);
       max_local_size_ = std::max(max_local_size_, finite_element.size());
       // loop over all keys of this finite element
       const auto& reference_element = ReferenceElements<D, d>::general(geometry_type);
       const auto& coeffs = finite_element.coefficients();
+      const auto& local_key_indices = coeffs.local_key_indices();
       for (size_t ii = 0; ii < coeffs.size(); ++ii) {
         const auto& local_key = coeffs.local_key(ii);
-        // Currently only works if each subEntity has exactly basis_functions_per_subentity DoFs, if there is a variable
-        // number of DoFs per element we would need to do more complicated things in the global index mapping.
-        DUNE_THROW_IF(!(local_key.index() < basis_functions_per_subentity),
-                      Exceptions::mapper_error,
-                      "This case is not covered yet, when we have a variable number of DoFs per (sub)entity!");
         // find the (sub)entity for this key
         const auto sub_entity = local_key.subEntity();
         const auto codim = local_key.codim();
         const auto& subentity_geometry_type = reference_element.type(sub_entity, codim);
         // and add the respective geometry type
         all_DoF_attached_geometry_types_.insert(subentity_geometry_type);
+        geometry_type_to_local_size_[subentity_geometry_type] = local_key_indices[codim][sub_entity].size();
       }
     }
     DUNE_THROW_IF(all_DoF_attached_geometry_types_.size() == 0,
@@ -171,9 +186,11 @@ public:
 private:
   const GridViewType& grid_view_;
   const LocalFiniteElementFamily& local_finite_elements_;
-  const int order_;
+  const int fe_order_;
+  size_t global_size_;
   size_t max_local_size_;
   std::set<GeometryType> all_DoF_attached_geometry_types_;
+  std::map<GeometryType, size_t> geometry_type_to_local_size_;
   Implementation mapper_;
 }; // class ContinuousMapper
 
diff --git a/dune/gdt/spaces/mapper/discontinuous.hh b/dune/gdt/spaces/mapper/discontinuous.hh
index d38e36c9f3eaedc5aaa1170d447f966352a87cb9..4b8758b8a8007fc1a35aff237fffb9ff56942b6c 100644
--- a/dune/gdt/spaces/mapper/discontinuous.hh
+++ b/dune/gdt/spaces/mapper/discontinuous.hh
@@ -31,7 +31,7 @@ template <class GV, class LocalFiniteElementFamily>
 class DiscontinuousMapper : public MapperInterface<GV>
 {
   static_assert(is_local_finite_element_family<LocalFiniteElementFamily>::value, "");
-  using ThisType = DiscontinuousMapper<GV, LocalFiniteElementFamily>;
+  using ThisType = DiscontinuousMapper;
   using BaseType = MapperInterface<GV>;
 
 public:
diff --git a/dune/gdt/spaces/mapper/finite-volume.hh b/dune/gdt/spaces/mapper/finite-volume.hh
index af1fc28a5af3db16dabc15fc2e84dd893051e166..e6ffe681bfb5df8176ced1530999bcd0ead206cc 100644
--- a/dune/gdt/spaces/mapper/finite-volume.hh
+++ b/dune/gdt/spaces/mapper/finite-volume.hh
@@ -32,7 +32,7 @@ class FiniteVolumeMapper : public MapperInterface<GV>
 {
   static_assert(rC == 1, "The FiniteVolumeMapper is not yet available for rC > 1!");
 
-  using ThisType = FiniteVolumeMapper<GV, r, rC>;
+  using ThisType = FiniteVolumeMapper;
   using BaseType = MapperInterface<GV>;
 
 public:
diff --git a/dune/gdt/test/burgers/base.hh b/dune/gdt/test/burgers/base.hh
index 48e0f503a6bc80c3e65ad88c59abee072956d7cb..bfff979eb6fa8551f239f041f87265a43182cc1c 100644
--- a/dune/gdt/test/burgers/base.hh
+++ b/dune/gdt/test/burgers/base.hh
@@ -135,7 +135,7 @@ protected:
     this->num_additional_refinements_for_reference_ = 2;
   }
 
-  virtual void compute_reference_solution() override
+  void compute_reference_solution() override
   {
     auto& self = *this;
     auto st = self.space_type_;
diff --git a/dune/gdt/test/burgers/burgers__1d__explicit__dg_p1.cc b/dune/gdt/test/burgers/burgers__1d__explicit__dg_p1.cc
index 280532c5af1a6bbcd37a10c5f389d0a99479a8a8..684623d91ba853aff9e11b8cea31c5531ff676d6 100644
--- a/dune/gdt/test/burgers/burgers__1d__explicit__dg_p1.cc
+++ b/dune/gdt/test/burgers/burgers__1d__explicit__dg_p1.cc
@@ -26,9 +26,12 @@ using Burgers1dExplicitDgP1Test = BurgersExplicitTest<YASP_1D_EQUIDISTANT_OFFSET
 TEST_F(Burgers1dExplicitDgP1Test, periodic_boundaries__numerical_engquist_osher_flux)
 {
   this->visualization_steps_ = DXTC_TEST_CONFIG_GET("setup.visualization_steps", 0);
+  this->num_refinements_ = DXTC_TEST_CONFIG_GET("setup.num_refinements", 2);
+  this->num_additional_refinements_for_reference_ =
+      DXTC_TEST_CONFIG_GET("setup.num_additional_refinements_for_reference", 2);
   this->space_type_ = "dg_p1";
   this->numerical_flux_type_ = "engquist_osher";
-  /*const auto actual_results =*/this->run();
-  //  const auto expected_results = DXTC_TEST_CONFIG_SUB("results");
-  //  XT::Test::check_eoc_study_for_success(expected_results, actual_results);
+  const auto actual_results = this->run();
+  const auto expected_results = DXTC_TEST_CONFIG_SUB("results");
+  XT::Test::check_eoc_study_for_success(expected_results, actual_results);
 }
diff --git a/dune/gdt/test/burgers/burgers__1d__explicit__dg_p1.mini b/dune/gdt/test/burgers/burgers__1d__explicit__dg_p1.mini
new file mode 100644
index 0000000000000000000000000000000000000000..d0efcd0112b5664e053f6662bcdf4456cbd2f7fe
--- /dev/null
+++ b/dune/gdt/test/burgers/burgers__1d__explicit__dg_p1.mini
@@ -0,0 +1,14 @@
+__name = Burgers1dExplicitDgP1Test
+
+[Burgers1dExplicitDgP1Test.periodic_boundaries__numerical_engquist_osher_flux.setup]
+visualization_steps                      = 0
+num_refinements                          = 1
+num_additional_refinements_for_reference = 1
+
+[Burgers1dExplicitDgP1Test.periodic_boundaries__numerical_engquist_osher_flux.results]
+target.h                        = [6.25e-02 3.12e-02]
+norm.L_infty_L_2                = [7.47e-02 6.21e-02]
+quantity.rel_mass_conserv_error = [0.00e+00 0.00e+00]
+quantity.num_timesteps          = [5.00e+01 9.90e+01]
+quantity.CFL                    = [4.82e-01 5.09e-01]
+
diff --git a/dune/gdt/test/burgers/burgers__1d__explicit__dg_p2.cc b/dune/gdt/test/burgers/burgers__1d__explicit__dg_p2.cc
index 9d5462ba4f90db8b5f58610e7dddb042c2919549..b07a6f81dce079f27cf29526beded2122d75f543 100644
--- a/dune/gdt/test/burgers/burgers__1d__explicit__dg_p2.cc
+++ b/dune/gdt/test/burgers/burgers__1d__explicit__dg_p2.cc
@@ -28,11 +28,12 @@ using Burgers1dExplicitDgP2Test = BurgersExplicitTest<YASP_1D_EQUIDISTANT_OFFSET
 TEST_F(Burgers1dExplicitDgP2Test, periodic_boundaries__numerical_engquist_osher_flux)
 {
   this->visualization_steps_ = DXTC_TEST_CONFIG_GET("setup.visualization_steps", 0);
-  this->num_refinements_ = 2;
-  this->num_additional_refinements_for_reference_ = 3;
+  this->num_refinements_ = DXTC_TEST_CONFIG_GET("setup.num_refinements", 2);
+  this->num_additional_refinements_for_reference_ =
+      DXTC_TEST_CONFIG_GET("setup.num_additional_refinements_for_reference", 2);
   this->space_type_ = "dg_p2";
   this->numerical_flux_type_ = "engquist_osher";
-  /*const auto actual_results =*/this->run();
-  //  const auto expected_results = DXTC_TEST_CONFIG_SUB("results");
-  //  XT::Test::check_eoc_study_for_success(expected_results, actual_results);
+  const auto actual_results = this->run();
+  const auto expected_results = DXTC_TEST_CONFIG_SUB("results");
+  XT::Test::check_eoc_study_for_success(expected_results, actual_results);
 }
diff --git a/dune/gdt/test/burgers/burgers__1d__explicit__dg_p2.mini b/dune/gdt/test/burgers/burgers__1d__explicit__dg_p2.mini
new file mode 100644
index 0000000000000000000000000000000000000000..ac2477a2bb999115cf7c060a1fd9bb39f207cf90
--- /dev/null
+++ b/dune/gdt/test/burgers/burgers__1d__explicit__dg_p2.mini
@@ -0,0 +1,14 @@
+__name = Burgers1dExplicitDgP2Test
+
+[Burgers1dExplicitDgP2Test.periodic_boundaries__numerical_engquist_osher_flux.setup]
+visualization_steps                      = 0
+num_refinements                          = 1
+num_additional_refinements_for_reference = 1
+
+[Burgers1dExplicitDgP2Test.periodic_boundaries__numerical_engquist_osher_flux.results]
+target.h                        = [6.25e-02 3.12e-02]
+norm.L_infty_L_2                = [7.41e-02 6.14e-02]
+quantity.rel_mass_conserv_error = [0.00e+00 0.00e+00]
+quantity.num_timesteps          = [1.32e+02 2.57e+02]
+quantity.CFL                    = [1.94e-01 1.98e-01]
+
diff --git a/dune/gdt/test/burgers/burgers__1d__explicit__dg_p3.cc b/dune/gdt/test/burgers/burgers__1d__explicit__dg_p3.cc
index 74a4b256535098957fe0b1a6aed0e785e3f70de8..3f3bfafa277b3562626da9fa2c2122b5e18779e5 100644
--- a/dune/gdt/test/burgers/burgers__1d__explicit__dg_p3.cc
+++ b/dune/gdt/test/burgers/burgers__1d__explicit__dg_p3.cc
@@ -28,11 +28,12 @@ using Burgers1dExplicitDgP3Test = BurgersExplicitTest<YASP_1D_EQUIDISTANT_OFFSET
 TEST_F(Burgers1dExplicitDgP3Test, periodic_boundaries__numerical_engquist_osher_flux)
 {
   this->visualization_steps_ = DXTC_TEST_CONFIG_GET("setup.visualization_steps", 0);
-  this->num_refinements_ = 2;
-  this->num_additional_refinements_for_reference_ = 3;
+  this->num_refinements_ = DXTC_TEST_CONFIG_GET("setup.num_refinements", 2);
+  this->num_additional_refinements_for_reference_ =
+      DXTC_TEST_CONFIG_GET("setup.num_additional_refinements_for_reference", 2);
   this->space_type_ = "dg_p3";
   this->numerical_flux_type_ = "engquist_osher";
-  /*const auto actual_results =*/this->run();
-  //  const auto expected_results = DXTC_TEST_CONFIG_SUB("results");
-  //  XT::Test::check_eoc_study_for_success(expected_results, actual_results);
+  const auto actual_results = this->run();
+  const auto expected_results = DXTC_TEST_CONFIG_SUB("results");
+  XT::Test::check_eoc_study_for_success(expected_results, actual_results);
 }
diff --git a/dune/gdt/test/burgers/burgers__1d__explicit__dg_p3.mini b/dune/gdt/test/burgers/burgers__1d__explicit__dg_p3.mini
new file mode 100644
index 0000000000000000000000000000000000000000..c1a47eee92afad51f48f519d43ab9eb811b03497
--- /dev/null
+++ b/dune/gdt/test/burgers/burgers__1d__explicit__dg_p3.mini
@@ -0,0 +1,14 @@
+__name = Burgers1dExplicitDgP3Test
+
+[Burgers1dExplicitDgP3Test.periodic_boundaries__numerical_engquist_osher_flux.setup]
+visualization_steps                      = 0
+num_refinements                          = 1
+num_additional_refinements_for_reference = 1
+
+[Burgers1dExplicitDgP3Test.periodic_boundaries__numerical_engquist_osher_flux.results]
+target.h                        = [6.25e-02 3.12e-02]
+norm.L_infty_L_2                = [7.72e-02 6.07e-02]
+quantity.rel_mass_conserv_error = [5.61e-15 1.03e-14]
+quantity.num_timesteps          = [2.92e+02 6.07e+02]
+quantity.CFL                    = [8.70e-02 8.32e-02]
+
diff --git a/dune/gdt/test/instationary-eocstudies/base.hh b/dune/gdt/test/instationary-eocstudies/base.hh
index 61c9b450b439b6fb4b55d056dd0817d15326f6ae..e82f500f66981402d5d9864d1ac04742a4642eb8 100644
--- a/dune/gdt/test/instationary-eocstudies/base.hh
+++ b/dune/gdt/test/instationary-eocstudies/base.hh
@@ -102,17 +102,17 @@ public:
     , reference_solution_on_reference_grid_(nullptr)
   {}
 
-  virtual size_t num_refinements() const override
+  size_t num_refinements() const override
   {
     return num_refinements_;
   }
 
-  virtual std::vector<std::string> targets() const override
+  std::vector<std::string> targets() const override
   {
     return {"h"};
   }
 
-  virtual std::vector<std::string> norms() const override
+  std::vector<std::string> norms() const override
   {
     // We currently support the following temporal norms:
     //   L_infty
@@ -123,12 +123,12 @@ public:
     return {"L_infty/L_2"};
   }
 
-  virtual std::vector<std::pair<std::string, std::string>> estimates() const override
+  std::vector<std::pair<std::string, std::string>> estimates() const override
   {
     return {};
   }
 
-  virtual std::vector<std::string> quantities() const override
+  std::vector<std::string> quantities() const override
   {
     std::vector<std::string> ret = {"time to solution (s)", "rel mass conserv error", "num timesteps"};
     if (this->adaptive_timestepping()) {
@@ -140,7 +140,7 @@ public:
     return ret;
   } // ... quantities(...)
 
-  virtual std::string discretization_info_title() const override
+  std::string discretization_info_title() const override
   {
     return " |grid| |   #DoFs";
   }
@@ -151,7 +151,7 @@ protected:
   virtual std::unique_ptr<S> make_space(const GP& current_grid) = 0;
 
 public:
-  virtual std::string discretization_info(const size_t refinement_level) override
+  std::string discretization_info(const size_t refinement_level) override
   {
     if (current_refinement_ != refinement_level) {
       // clear the current state
diff --git a/dune/gdt/test/instationary-eocstudies/hyperbolic-nonconforming.hh b/dune/gdt/test/instationary-eocstudies/hyperbolic-nonconforming.hh
index c82b72dd4a92aabaabaf64923399cf27cd959c4a..2a15923d79e23e2828edb6e205f56a30ed265e6b 100644
--- a/dune/gdt/test/instationary-eocstudies/hyperbolic-nonconforming.hh
+++ b/dune/gdt/test/instationary-eocstudies/hyperbolic-nonconforming.hh
@@ -60,9 +60,10 @@ protected:
   using typename BaseType::R;
   using typename BaseType::S;
   using typename BaseType::V;
+  using I = XT::Grid::extract_intersection_t<GV>;
 
   using F = XT::Functions::FunctionInterface<m, d, m>;
-  using NF = NumericalFluxInterface<d, m>;
+  using NF = NumericalFluxInterface<I, d, m>;
 
 public:
   InstationaryNonconformingHyperbolicEocStudy(
@@ -89,7 +90,7 @@ protected:
 
   virtual DF make_initial_values(const S& space) = 0;
 
-  virtual std::unique_ptr<S> make_space(const GP& current_grid) override
+  std::unique_ptr<S> make_space(const GP& current_grid) override
   {
     if (space_type_ == "fv")
       return std::make_unique<FiniteVolumeSpace<GV, m>>(XT::Grid::make_periodic_grid_layer(current_grid.leaf_view()));
@@ -103,17 +104,17 @@ protected:
     }
   } // ... make_space(...)
 
-  virtual std::unique_ptr<O> make_lhs_operator(const S& space) override
+  std::unique_ptr<O> make_lhs_operator(const S& space) override
   {
     std::unique_ptr<NF> numerical_flux;
     if (numerical_flux_type_ == "upwind")
-      numerical_flux = std::make_unique<NumericalUpwindFlux<d, m>>(flux());
+      numerical_flux = std::make_unique<NumericalUpwindFlux<I, d, m>>(flux());
     else if (numerical_flux_type_ == "vijayasundaram")
-      numerical_flux = std::make_unique<NumericalVijayasundaramFlux<d, m>>(flux());
+      numerical_flux = std::make_unique<NumericalVijayasundaramFlux<I, d, m>>(flux());
     else if (numerical_flux_type_ == "lax_friedrichs")
-      numerical_flux = std::make_unique<NumericalLaxFriedrichsFlux<d, m>>(flux());
+      numerical_flux = std::make_unique<NumericalLaxFriedrichsFlux<I, d, m>>(flux());
     else if (numerical_flux_type_ == "engquist_osher")
-      numerical_flux = std::make_unique<NumericalEngquistOsherFlux<d, m>>(flux());
+      numerical_flux = std::make_unique<NumericalEngquistOsherFlux<I, d, m>>(flux());
     else {
       DUNE_THROW(XT::Common::Exceptions::wrong_input_given, "numerical_flux_type_ = " << numerical_flux_type_);
       return nullptr;
diff --git a/dune/gdt/test/integrands/integrands.hh b/dune/gdt/test/integrands/integrands.hh
new file mode 100644
index 0000000000000000000000000000000000000000..48f5845a49797175c139f3140c0e642d6c7802c8
--- /dev/null
+++ b/dune/gdt/test/integrands/integrands.hh
@@ -0,0 +1,183 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner (2019)
+
+#ifndef DUNE_GDT_TEST_INTEGRANDS_INTEGRANDS_HH
+#define DUNE_GDT_TEST_INTEGRANDS_INTEGRANDS_HH
+
+#include <array>
+
+#include <dune/geometry/quadraturerules.hh>
+#include <dune/geometry/type.hh>
+
+#include <dune/xt/common/test/gtest/gtest.h>
+#include <dune/xt/common/fvector.hh>
+
+#include <dune/xt/grid/gridprovider.hh>
+#include <dune/xt/grid/grids.hh>
+#include <dune/xt/grid/type_traits.hh>
+
+#include <dune/xt/functions/generic/element-function.hh>
+#include <dune/xt/functions/generic/function.hh>
+#include <dune/xt/functions/generic/grid-function.hh>
+
+#include <dune/gdt/operators/matrix-based.hh>
+#include <dune/gdt/spaces/h1/continuous-lagrange.hh>
+#include <dune/gdt/tools/sparsity-pattern.hh>
+
+namespace Dune {
+namespace GDT {
+namespace Test {
+
+
+template <class G>
+struct IntegrandTest : public ::testing::Test
+{
+  static_assert(XT::Grid::is_grid<G>::value, "");
+
+  using GV = typename G::LeafGridView;
+  using D = typename GV::ctype;
+  static const constexpr size_t d = GV::dimension;
+  using E = XT::Grid::extract_entity_t<GV>;
+  using I = XT::Grid::extract_intersection_t<GV>;
+  using LocalScalarBasisType = XT::Functions::GenericElementFunctionSet<E, 1, 1>;
+  using DomainType = typename LocalScalarBasisType::DomainType;
+  using ScalarRangeType = typename LocalScalarBasisType::RangeType;
+  using ScalarJacobianType = typename LocalScalarBasisType::DerivativeRangeType;
+  using LocalVectorBasisType = XT::Functions::GenericElementFunctionSet<E, 2, 1>;
+  using VectorRangeType = typename LocalVectorBasisType::RangeType;
+  using VectorJacobianType = typename LocalVectorBasisType::DerivativeRangeType;
+  using MatrixType = typename XT::LA::Container<double, XT::LA::default_sparse_backend>::MatrixType;
+
+  std::shared_ptr<XT::Grid::GridProvider<G>> grid_provider_;
+  std::shared_ptr<LocalScalarBasisType> scalar_ansatz_;
+  std::shared_ptr<LocalScalarBasisType> scalar_test_;
+  std::shared_ptr<LocalVectorBasisType> vector_ansatz_;
+  std::shared_ptr<LocalVectorBasisType> vector_test_;
+  static constexpr bool is_simplex_grid_ = XT::Grid::is_uggrid<G>::value || XT::Grid::is_simplex_alugrid<G>::value;
+
+  virtual std::shared_ptr<XT::Grid::GridProvider<G>> make_grid()
+  {
+    return std::make_shared<XT::Grid::GridProvider<G>>(
+        XT::Grid::make_cube_grid<G>(XT::Common::from_string<FieldVector<double, d>>("[0 0 0 0]"),
+                                    XT::Common::from_string<FieldVector<double, d>>("[3 1 1 1]"),
+                                    XT::Common::from_string<std::array<unsigned int, d>>("[9 2 2 2]")));
+  }
+
+  void SetUp() override
+  {
+    grid_provider_ = make_grid();
+    // {x, x^2 y}
+    scalar_ansatz_ = std::make_shared<LocalScalarBasisType>(
+        /*size = */ 2,
+        /*ord = */ 3,
+        /*evaluate = */
+        [](const DomainType& x, std::vector<ScalarRangeType>& ret, const XT::Common::Parameter&) {
+          ret = {{x[0]}, {std::pow(x[0], 2) * x[1]}};
+        },
+        /*param_type = */ XT::Common::ParameterType{},
+        /*jacobian = */
+        [](const DomainType& x, std::vector<ScalarJacobianType>& ret, const XT::Common::Parameter&) {
+          // jacobians both only have a single row
+          ret[0][0] = {1., 0.};
+          ret[1][0] = {2. * x[0] * x[1], std::pow(x[0], 2)};
+        });
+    // {y, x y^3}
+    scalar_test_ = std::make_shared<LocalScalarBasisType>(
+        /*size = */ 2,
+        /*ord = */ 4,
+        /*evaluate = */
+        [](const DomainType& x, std::vector<ScalarRangeType>& ret, const XT::Common::Parameter&) {
+          ret = {{x[1]}, {x[0] * std::pow(x[1], 3)}};
+        },
+        /*param_type = */ XT::Common::ParameterType{},
+        /*jacobian = */
+        [](const DomainType& x, std::vector<ScalarJacobianType>& ret, const XT::Common::Parameter&) {
+          // jacobians both only have a single row
+          ret[0][0] = {0., 1.};
+          ret[1][0] = {std::pow(x[1], 3), 3 * x[0] * std::pow(x[1], 2)};
+        });
+    // { (x,y)^T, (x^2, y^2)^T}
+    vector_ansatz_ = std::make_shared<LocalVectorBasisType>(
+        /*size = */ 2,
+        /*ord = */ 2,
+        /*evaluate = */
+        [](const DomainType& x, std::vector<VectorRangeType>& ret, const XT::Common::Parameter&) {
+          ret = {{x[0], x[1]}, {std::pow(x[0], 2), std::pow(x[1], 2)}};
+        },
+        /*param_type = */ XT::Common::ParameterType{},
+        /*jacobian = */
+        [](const DomainType& x, std::vector<VectorJacobianType>& ret, const XT::Common::Parameter&) {
+          // jacobian of first function
+          ret[0][0] = {1., 0.};
+          ret[0][1] = {0., 1.};
+          // jacobian of second function
+          ret[1][0] = {2 * x[0], 0.};
+          ret[1][1] = {0., 2 * x[1]};
+        });
+    // { (x,y)^T, (x^2, y^2)^T}
+    vector_test_ = std::make_shared<LocalVectorBasisType>(
+        /*size = */ 2,
+        /*ord = */ 2,
+        /*evaluate = */
+        [](const DomainType& x, std::vector<VectorRangeType>& ret, const XT::Common::Parameter&) {
+          ret = {{1, 2}, {x[0] * x[1], x[0] + x[1]}};
+        },
+        /*param_type = */ XT::Common::ParameterType{},
+        /*jacobian = */
+        [](const DomainType& x, std::vector<VectorJacobianType>& ret, const XT::Common::Parameter&) {
+          // jacobian of first function
+          ret[0][0] = {0., 0.};
+          ret[0][1] = {0., 0.};
+          // jacobian of second function
+          ret[1][0] = {x[1], x[0]};
+          ret[1][1] = {1., 1.};
+        });
+  } // ... SetUp(...)
+
+  virtual void is_constructable() = 0;
+}; // struct IntegrandTest
+
+
+} // namespace Test
+} // namespace GDT
+} // namespace Dune
+
+
+using Grids2D = ::testing::Types<YASP_2D_EQUIDISTANT_OFFSET
+#if HAVE_DUNE_ALUGRID
+                                 ,
+                                 ALU_2D_SIMPLEX_CONFORMING,
+                                 ALU_2D_SIMPLEX_NONCONFORMING,
+                                 ALU_2D_CUBE
+#endif
+#if HAVE_DUNE_UGGRID || HAVE_UG
+                                 ,
+                                 UG_2D
+#endif
+#if HAVE_ALBERTA
+                                 ,
+                                 ALBERTA_2D
+#endif
+                                 >;
+
+DUNE_XT_COMMON_TYPENAME(YASP_2D_EQUIDISTANT_OFFSET)
+#if HAVE_DUNE_ALUGRID
+DUNE_XT_COMMON_TYPENAME(ALU_2D_SIMPLEX_CONFORMING)
+DUNE_XT_COMMON_TYPENAME(ALU_2D_SIMPLEX_NONCONFORMING)
+DUNE_XT_COMMON_TYPENAME(ALU_2D_CUBE)
+#endif
+#if HAVE_DUNE_UGGRID || HAVE_UG
+DUNE_XT_COMMON_TYPENAME(UG_2D)
+#endif
+#if HAVE_ALBERTA
+DUNE_XT_COMMON_TYPENAME(ALBERTA_2D)
+#endif
+
+
+#endif // DUNE_GDT_TEST_INTEGRANDS_INTEGRANDS_HH
diff --git a/dune/gdt/test/integrands/integrands_div.cc b/dune/gdt/test/integrands/integrands_div.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f91bc2c5b4d215233179d1f0e7bd9da219e455a0
--- /dev/null
+++ b/dune/gdt/test/integrands/integrands_div.cc
@@ -0,0 +1,152 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner (2019)
+
+#include <dune/xt/common/test/main.hxx> // <- this one has to come first (includes the config.h)!
+
+#include <dune/gdt/local/integrands/div.hh>
+
+#include <dune/gdt/test/integrands/integrands.hh>
+
+namespace Dune {
+namespace GDT {
+namespace Test {
+
+
+template <class G>
+struct DivIntegrandTest : public IntegrandTest<G>
+{
+  using BaseType = IntegrandTest<G>;
+  using BaseType::d;
+  using typename BaseType::D;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::GV;
+  using typename BaseType::MatrixType;
+  using typename BaseType::VectorJacobianType;
+  using TestDivIntegrandType = LocalElementAnsatzValueTestDivProductIntegrand<E>;
+  using AnsatzDivIntegrandType = LocalElementAnsatzDivTestValueProductIntegrand<E>;
+
+  virtual void is_constructable() override final
+  {
+    TestDivIntegrandType test_div_integrand1;
+    TestDivIntegrandType test_div_integrand2(1.);
+    const XT::Functions::GenericGridFunction<E, 1> scalar_grid_function(
+        2, [](const E&) {}, [](const DomainType& x, const XT::Common::Parameter&) { return x[0] * x[1]; });
+    TestDivIntegrandType test_div_integrand3(scalar_grid_function);
+    const XT::Functions::GenericFunction<d, 1> scalar_function(
+        2, [](const DomainType& x, const XT::Common::Parameter&) { return x[0] * x[1]; });
+    TestDivIntegrandType test_div_integrand4(scalar_function);
+    DUNE_UNUSED_PARAMETER(test_div_integrand1);
+    DUNE_UNUSED_PARAMETER(test_div_integrand2);
+    DUNE_UNUSED_PARAMETER(test_div_integrand3);
+    DUNE_UNUSED_PARAMETER(test_div_integrand4);
+    AnsatzDivIntegrandType ansatz_div_integrand1;
+    AnsatzDivIntegrandType ansatz_div_integrand2(1.);
+    AnsatzDivIntegrandType ansatz_div_integrand3(scalar_grid_function);
+    AnsatzDivIntegrandType ansatz_div_integrand4(scalar_function);
+    DUNE_UNUSED_PARAMETER(ansatz_div_integrand1);
+    DUNE_UNUSED_PARAMETER(ansatz_div_integrand2);
+    DUNE_UNUSED_PARAMETER(ansatz_div_integrand3);
+    DUNE_UNUSED_PARAMETER(ansatz_div_integrand4);
+  }
+
+  virtual void evaluates_correctly()
+  {
+    const XT::Functions::GenericGridFunction<E, 1> inducing_function(
+        2, [](const E&) {}, [](const DomainType& x, const XT::Common::Parameter&) { return x[0] * x[1]; });
+    TestDivIntegrandType test_div_integrand(inducing_function);
+    AnsatzDivIntegrandType ansatz_div_integrand(inducing_function);
+    const auto element = *(grid_provider_->leaf_view().template begin<0>());
+    test_div_integrand.bind(element);
+    ansatz_div_integrand.bind(element);
+    const auto test_div_integrand_order = test_div_integrand.order(*vector_test_, *scalar_ansatz_);
+    const auto ansatz_div_integrand_order = ansatz_div_integrand.order(*scalar_test_, *vector_ansatz_);
+    DynamicMatrix<D> test_div_result(2, 2, 0.);
+    DynamicMatrix<D> ansatz_div_result(2, 2, 0.);
+    for (const auto& quadrature_point : Dune::QuadratureRules<D, d>::rule(
+             element.geometry().type(), std::max(test_div_integrand_order, ansatz_div_integrand_order))) {
+      const auto& x = quadrature_point.position();
+      test_div_integrand.evaluate(*vector_test_, *scalar_ansatz_, x, test_div_result);
+      ansatz_div_integrand.evaluate(*scalar_test_, *vector_ansatz_, x, ansatz_div_result);
+      DynamicMatrix<D> expected_result_test_div{{0, 0}, {x[0] * (x[1] + 1), std::pow(x[0], 2) * x[1] * (x[1] + 1)}};
+      DynamicMatrix<D> expected_result_ansatz_div{
+          {2 * x[1], 2 * (x[0] + x[1]) * x[1]},
+          {2 * x[0] * std::pow(x[1], 3), 2 * (x[0] + x[1]) * x[0] * std::pow(x[1], 3)}};
+      expected_result_test_div *= x[0] * x[1];
+      expected_result_ansatz_div *= x[0] * x[1];
+      for (size_t ii = 0; ii < 2; ++ii) {
+        for (size_t jj = 0; jj < 2; ++jj) {
+          EXPECT_DOUBLE_EQ(expected_result_test_div[ii][jj], test_div_result[ii][jj]);
+          EXPECT_DOUBLE_EQ(expected_result_ansatz_div[ii][jj], ansatz_div_result[ii][jj]);
+        } // jj
+      } // ii
+    } // quadrature points
+  } // ... evaluates_correctly()
+
+  virtual void is_integrated_correctly()
+  {
+    TestDivIntegrandType test_div_integrand(1.);
+    AnsatzDivIntegrandType ansatz_div_integrand(1.);
+    const auto& grid_view = grid_provider_->leaf_view();
+    const auto scalar_space = make_continuous_lagrange_space<1>(grid_view, /*polorder=*/2);
+    const auto vector_space = make_continuous_lagrange_space<d>(grid_view, /*polorder=*/2);
+    const auto m = scalar_space.mapper().size();
+    const auto n = vector_space.mapper().size();
+    MatrixType test_div_mat(n, m, make_element_sparsity_pattern(vector_space, scalar_space, grid_view));
+    MatrixType ansatz_div_mat(m, n, make_element_sparsity_pattern(scalar_space, vector_space, grid_view));
+    MatrixOperator<MatrixType, GV, 1, 1, d, 1> test_div_op(grid_view, scalar_space, vector_space, test_div_mat);
+    MatrixOperator<MatrixType, GV, d, 1, 1, 1> ansatz_div_op(grid_view, vector_space, scalar_space, ansatz_div_mat);
+    test_div_op.append(LocalElementIntegralBilinearForm<E, d, 1, double, double, 1, 1>(test_div_integrand));
+    test_div_op.assemble(true);
+    ansatz_div_op.append(LocalElementIntegralBilinearForm<E, 1, 1, double, double, d, 1>(ansatz_div_integrand));
+    ansatz_div_op.assemble(true);
+    EXPECT_TRUE(XT::Common::FloatCmp::eq(test_div_mat, XT::Common::transposed(ansatz_div_mat), 1e-14, 1e-14));
+    const auto mat_data_ptr = XT::Common::serialize_rowwise(test_div_mat);
+    const auto min_entry = *std::min_element(mat_data_ptr.get(), mat_data_ptr.get() + n * m);
+    const auto max_entry = *std::max_element(mat_data_ptr.get(), mat_data_ptr.get() + n * m);
+    const auto square_sum = std::accumulate(
+        mat_data_ptr.get(), mat_data_ptr.get() + n * m, 0., [](const auto& a, const auto& b) { return a + b * b; });
+    EXPECT_NEAR((is_simplex_grid_ ? -0.133333333333333 : -0.177777777777778), min_entry, 1e-13);
+    EXPECT_NEAR((is_simplex_grid_ ? 0.133333333333333 : 0.177777777777778), max_entry, 1e-13);
+    EXPECT_NEAR((is_simplex_grid_ ? 4.515925925925922 : 4.589465020576143), square_sum, 1e-13);
+    // std::cout << XT::Common::to_string(test_div_mat, 15) << std::endl;
+  }
+
+  using BaseType::grid_provider_;
+  using BaseType::is_simplex_grid_;
+  using BaseType::scalar_ansatz_;
+  using BaseType::scalar_test_;
+  using BaseType::vector_ansatz_;
+  using BaseType::vector_test_;
+}; // struct DivIntegrandTest
+
+
+} // namespace Test
+} // namespace GDT
+} // namespace Dune
+
+
+template <class G>
+using DivIntegrandTest = Dune::GDT::Test::DivIntegrandTest<G>;
+TYPED_TEST_CASE(DivIntegrandTest, Grids2D);
+
+TYPED_TEST(DivIntegrandTest, is_constructable)
+{
+  this->is_constructable();
+}
+
+TYPED_TEST(DivIntegrandTest, evaluates_correctly)
+{
+  this->evaluates_correctly();
+}
+
+TYPED_TEST(DivIntegrandTest, integrates_correctly)
+{
+  this->is_integrated_correctly();
+}
diff --git a/dune/gdt/test/integrands/integrands_gradient_value.cc b/dune/gdt/test/integrands/integrands_gradient_value.cc
new file mode 100644
index 0000000000000000000000000000000000000000..91e05dc29019aee4116250e196f03d2a24c74e69
--- /dev/null
+++ b/dune/gdt/test/integrands/integrands_gradient_value.cc
@@ -0,0 +1,206 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner (2019)
+
+#include <dune/xt/common/test/main.hxx> // <- this one has to come first (includes the config.h)!
+
+#include <dune/geometry/quadraturerules.hh>
+
+#include <dune/xt/functions/generic/function.hh>
+#include <dune/xt/functions/generic/grid-function.hh>
+
+#include <dune/gdt/local/integrands/gradient-value.hh>
+
+#include <dune/gdt/test/integrands/integrands.hh>
+
+namespace Dune {
+namespace GDT {
+namespace Test {
+
+
+template <class G>
+struct GradientValueIntegrandTest : public IntegrandTest<G>
+{
+  using BaseType = IntegrandTest<G>;
+  using BaseType::d;
+  using typename BaseType::D;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::GV;
+  using typename BaseType::MatrixType;
+  using typename BaseType::VectorJacobianType;
+  using ScalarIntegrandType = LocalElementGradientValueIntegrand<E, 1>;
+  using ScalarIntegrandTestGradType = LocalElementGradientValueIntegrand<E, 1, 1, D, D, D, true>;
+  using VectorIntegrandType = LocalElementGradientValueIntegrand<E, d>;
+  using VectorIntegrandTestGradType = LocalElementGradientValueIntegrand<E, d, 1, D, D, D, true>;
+
+  virtual void is_constructable() override final
+  {
+    const XT::Functions::GenericGridFunction<E, d> vector_grid_function(
+        2,
+        [](const E&) {},
+        [](const DomainType& x, const XT::Common::Parameter&) {
+          return FieldVector<D, d>{{x[0], x[0] * x[1]}};
+        });
+    const XT::Functions::GenericFunction<d, d> vector_function(2,
+                                                               [](const DomainType& x, const XT::Common::Parameter&) {
+                                                                 return FieldVector<D, d>{{x[0], x[0] * x[1]}};
+                                                               });
+    ScalarIntegrandType scalar_integrand1(vector_grid_function);
+    ScalarIntegrandType scalar_integrand2(vector_function);
+    DUNE_UNUSED_PARAMETER(scalar_integrand1);
+    DUNE_UNUSED_PARAMETER(scalar_integrand2);
+    VectorIntegrandType vector_integrand1(vector_grid_function);
+    VectorIntegrandType vector_integrand2(vector_function);
+    DUNE_UNUSED_PARAMETER(vector_integrand1);
+    DUNE_UNUSED_PARAMETER(vector_integrand2);
+  }
+
+  virtual void evaluates_correctly_for_scalar_bases()
+  {
+    const XT::Functions::GenericGridFunction<E, d> vector_grid_function(
+        2,
+        [](const E&) {},
+        [](const DomainType& x, const XT::Common::Parameter&) {
+          return FieldVector<D, d>{{x[0], x[0] * x[1]}};
+        });
+    ScalarIntegrandType scalar_integrand(vector_grid_function);
+    ScalarIntegrandTestGradType scalar_integrand2(vector_grid_function);
+    const auto element = *(grid_provider_->leaf_view().template begin<0>());
+    scalar_integrand.bind(element);
+    scalar_integrand2.bind(element);
+    const auto integrand_order = scalar_integrand.order(*scalar_test_, *scalar_ansatz_);
+    const auto integrand_order2 = scalar_integrand2.order(*scalar_test_, *scalar_ansatz_);
+    DynamicMatrix<D> result(2, 2, 0.);
+    DynamicMatrix<D> result2(2, 2, 0.);
+    for (const auto& quadrature_point :
+         Dune::QuadratureRules<D, d>::rule(element.geometry().type(), std::max(integrand_order, integrand_order2))) {
+      const auto& x = quadrature_point.position();
+      scalar_integrand.evaluate(*scalar_test_, *scalar_ansatz_, x, result);
+      scalar_integrand2.evaluate(*scalar_test_, *scalar_ansatz_, x, result2);
+      DynamicMatrix<D> expected_result{
+          {x[0] * x[1], (2 + x[0]) * std::pow(x[0] * x[1], 2)},
+          {std::pow(x[0] * x[1], 2) * x[1], (2 * x[1] + x[0] * x[1]) * std::pow(x[0] * x[1], 3)}};
+      DynamicMatrix<D> expected_result2{{std::pow(x[0], 2) * x[1], std::pow(x[0] * x[1], 2) * x[0]},
+                                        {std::pow(x[1], 3) * (std::pow(x[0], 2) + 3 * std::pow(x[0], 3)),
+                                         std::pow(x[1], 4) * (std::pow(x[0], 3) + 3 * std::pow(x[0], 4))}};
+      for (size_t ii = 0; ii < 2; ++ii) {
+        for (size_t jj = 0; jj < 2; ++jj) {
+          EXPECT_DOUBLE_EQ(expected_result[ii][jj], result[ii][jj]);
+          EXPECT_DOUBLE_EQ(expected_result2[ii][jj], result2[ii][jj]);
+        } // jj
+      } // ii
+    } // quad_points
+  }
+
+  virtual void evaluates_correctly_for_vector_bases()
+  {
+    const XT::Functions::GenericGridFunction<E, d> vector_grid_function(
+        2,
+        [](const E&) {},
+        [](const DomainType& x, const XT::Common::Parameter&) {
+          return FieldVector<D, d>{{x[0], x[0] * x[1]}};
+        });
+    VectorIntegrandType integrand(vector_grid_function);
+    VectorIntegrandTestGradType integrand2(vector_grid_function);
+    const auto element = *(grid_provider_->leaf_view().template begin<0>());
+    integrand.bind(element);
+    integrand2.bind(element);
+    const auto integrand_order = integrand.order(*vector_test_, *vector_ansatz_);
+    const auto integrand_order2 = integrand2.order(*vector_test_, *vector_ansatz_);
+    DynamicMatrix<D> result(2, 2, 0.);
+    DynamicMatrix<D> result2(2, 2, 0.);
+    for (const auto& quadrature_point :
+         Dune::QuadratureRules<D, d>::rule(element.geometry().type(), std::max(integrand_order, integrand_order2))) {
+      const auto& x = quadrature_point.position();
+      integrand.evaluate(*vector_test_, *vector_ansatz_, x, result);
+      integrand2.evaluate(*vector_test_, *vector_ansatz_, x, result2);
+      DynamicMatrix<D> expected_result{{x[0] * (1 + 2 * x[1]), 2 * std::pow(x[0], 2) + 4 * x[0] * std::pow(x[1], 2)},
+                                       {2 * std::pow(x[0], 2) * x[1] + x[0] * std::pow(x[1], 2),
+                                        2 * std::pow(x[0], 3) * x[1] + 2 * x[0] * std::pow(x[1], 2) * (x[0] + x[1])}};
+      DynamicMatrix<D> expected_result2{
+          {0, 0},
+          {x[1] * (x[0] + std::pow(x[0], 2) + std::pow(x[0], 3)) + x[0] * std::pow(x[1], 2),
+           x[1] * (std::pow(x[0], 3) + std::pow(x[0], 4)) + x[0] * (std::pow(x[1], 2) + std::pow(x[1], 3))}};
+      for (size_t ii = 0; ii < 2; ++ii) {
+        for (size_t jj = 0; jj < 2; ++jj) {
+          EXPECT_DOUBLE_EQ(expected_result[ii][jj], result[ii][jj]);
+          EXPECT_DOUBLE_EQ(expected_result2[ii][jj], result2[ii][jj]);
+        } // jj
+      } // ii
+    } // quad_points
+  }
+
+  virtual void is_integrated_correctly()
+  {
+    const XT::Functions::GenericFunction<d, d> vector_function(
+        0, [](const DomainType& /*x*/, const XT::Common::Parameter&) {
+          return FieldVector<D, d>{{1., 1.}};
+        });
+    VectorIntegrandType ansatz_grad_integrand(vector_function);
+    VectorIntegrandTestGradType test_grad_integrand(vector_function);
+    const auto& grid_view = grid_provider_->leaf_view();
+    const auto space = make_continuous_lagrange_space<d>(grid_view, /*polorder=*/2);
+    const auto n = space.mapper().size();
+    MatrixType test_grad_mat(n, n, make_element_sparsity_pattern(space, space, grid_view));
+    MatrixType ansatz_grad_mat(n, n, make_element_sparsity_pattern(space, space, grid_view));
+    MatrixOperator<MatrixType, GV, d, 1, d, 1> test_grad_op(grid_view, space, space, test_grad_mat);
+    MatrixOperator<MatrixType, GV, d, 1, d, 1> ansatz_grad_op(grid_view, space, space, ansatz_grad_mat);
+    test_grad_op.append(LocalElementIntegralBilinearForm<E, d, 1, double, double, d, 1>{test_grad_integrand});
+    test_grad_op.assemble(true);
+    ansatz_grad_op.append(LocalElementIntegralBilinearForm<E, d, 1, double, double, d, 1>{ansatz_grad_integrand});
+    ansatz_grad_op.assemble(true);
+    EXPECT_TRUE(XT::Common::FloatCmp::eq(test_grad_mat, XT::Common::transposed(ansatz_grad_mat), 1e-14, 1e-14));
+    const auto mat_data_ptr = XT::Common::serialize_rowwise(test_grad_mat);
+    const auto min_entry = *std::min_element(mat_data_ptr.get(), mat_data_ptr.get() + n * n);
+    const auto max_entry = *std::max_element(mat_data_ptr.get(), mat_data_ptr.get() + n * n);
+    const auto square_sum = std::accumulate(
+        mat_data_ptr.get(), mat_data_ptr.get() + n * n, 0., [](const auto& a, const auto& b) { return a + b * b; });
+    EXPECT_NEAR((is_simplex_grid_ ? -0.133333333333333 : -0.177777777777778), min_entry, 1e-13);
+    EXPECT_NEAR((is_simplex_grid_ ? 0.133333333333333 : 0.177777777777778), max_entry, 1e-13);
+    EXPECT_NEAR((is_simplex_grid_ ? 5.208148148148139 : 9.178930041152277), square_sum, 1e-13);
+    // std::cout << XT::Common::to_string(test_grad_mat, 15) << std::endl;
+  }
+
+  using BaseType::grid_provider_;
+  using BaseType::is_simplex_grid_;
+  using BaseType::scalar_ansatz_;
+  using BaseType::scalar_test_;
+  using BaseType::vector_ansatz_;
+  using BaseType::vector_test_;
+}; // struct GradientValueIntegrandTest
+
+
+} // namespace Test
+} // namespace GDT
+} // namespace Dune
+
+
+template <class G>
+using GradientValueIntegrandTest = Dune::GDT::Test::GradientValueIntegrandTest<G>;
+TYPED_TEST_CASE(GradientValueIntegrandTest, Grids2D);
+
+TYPED_TEST(GradientValueIntegrandTest, is_constructable)
+{
+  this->is_constructable();
+}
+
+TYPED_TEST(GradientValueIntegrandTest, evaluates_correctly_for_scalar_bases)
+{
+  this->evaluates_correctly_for_scalar_bases();
+}
+
+TYPED_TEST(GradientValueIntegrandTest, evaluates_correctly_for_vector_bases)
+{
+  this->evaluates_correctly_for_vector_bases();
+}
+
+TYPED_TEST(GradientValueIntegrandTest, is_integrated_correctly)
+{
+  this->is_integrated_correctly();
+}
diff --git a/dune/gdt/test/integrands/integrands_laplace.cc b/dune/gdt/test/integrands/integrands_laplace.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f1363386af5b29c05926075e7b1a7978de5ef571
--- /dev/null
+++ b/dune/gdt/test/integrands/integrands_laplace.cc
@@ -0,0 +1,181 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner (2019)
+
+#include <dune/xt/common/test/main.hxx> // <- this one has to come first (includes the config.h)!
+
+#include <dune/xt/common/matrix.hh>
+
+#include <dune/xt/la/container/eye-matrix.hh>
+
+#include <dune/xt/functions/generic/function.hh>
+#include <dune/xt/functions/generic/grid-function.hh>
+
+#include <dune/gdt/local/integrands/laplace.hh>
+
+#include <dune/gdt/test/integrands/integrands.hh>
+
+namespace Dune {
+namespace GDT {
+namespace Test {
+
+
+template <class G>
+struct LaplaceIntegrandTest : public IntegrandTest<G>
+{
+  using BaseType = IntegrandTest<G>;
+  using BaseType::d;
+  using typename BaseType::D;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::GV;
+  using typename BaseType::MatrixType;
+  using typename BaseType::VectorJacobianType;
+  using ScalarIntegrandType = LocalLaplaceIntegrand<E, 1>;
+  using VectorIntegrandType = LocalLaplaceIntegrand<E, d>;
+
+  virtual void SetUp() override
+  {
+    BaseType::SetUp();
+    diffusion_tensor_ = std::make_shared<XT::Functions::GenericGridFunction<E, 2, 2>>(
+        3,
+        [](const E&) {},
+        [](const DomainType& x, const XT::Common::Parameter&) {
+          VectorJacobianType ret{{x[0], x[1]}, {1., 2.}};
+          ret *= x[0] * x[1];
+          return ret;
+        });
+  }
+
+  virtual void is_constructable() override final
+  {
+    ScalarIntegrandType scalar_integrand1;
+    ScalarIntegrandType scalar_integrand2(XT::LA::eye_matrix<FieldMatrix<D, d, d>>(d, d));
+    const XT::Functions::GenericFunction<d, 2, 2> matrix_function(
+        1, [](const DomainType& x, const XT::Common::Parameter&) {
+          return VectorJacobianType{{x[0], x[1]}, {1., 2.}};
+        });
+    ScalarIntegrandType scalar_integrand3(matrix_function);
+    ScalarIntegrandType scalar_integrand4(*diffusion_tensor_);
+    DUNE_UNUSED_PARAMETER(scalar_integrand1);
+    DUNE_UNUSED_PARAMETER(scalar_integrand2);
+    DUNE_UNUSED_PARAMETER(scalar_integrand3);
+    DUNE_UNUSED_PARAMETER(scalar_integrand4);
+    VectorIntegrandType vector_integrand1;
+    VectorIntegrandType vector_integrand2(XT::LA::eye_matrix<FieldMatrix<D, d, d>>(d, d));
+    VectorIntegrandType vector_integrand3(matrix_function);
+    VectorIntegrandType vector_integrand4(*diffusion_tensor_);
+    DUNE_UNUSED_PARAMETER(vector_integrand1);
+    DUNE_UNUSED_PARAMETER(vector_integrand2);
+    DUNE_UNUSED_PARAMETER(vector_integrand3);
+    DUNE_UNUSED_PARAMETER(vector_integrand4);
+  }
+
+  virtual void evaluates_correctly_for_scalar_bases()
+  {
+    ScalarIntegrandType scalar_integrand(*diffusion_tensor_);
+    const auto element = *(grid_provider_->leaf_view().template begin<0>());
+    scalar_integrand.bind(element);
+    const auto integrand_order = scalar_integrand.order(*scalar_test_, *scalar_ansatz_);
+    DynamicMatrix<D> result(2, 2, 0.);
+    for (const auto& quadrature_point : Dune::QuadratureRules<D, d>::rule(element.geometry().type(), integrand_order)) {
+      const auto& x = quadrature_point.position();
+      scalar_integrand.evaluate(*scalar_test_, *scalar_ansatz_, x, result);
+      DynamicMatrix<D> expected_result{
+          {1, 2 * (x[0] * x[1] + std::pow(x[0], 2))},
+          {x[0] * std::pow(x[1], 3) + 3 * x[0] * std::pow(x[1], 2),
+           3 * std::pow(x[0], 2) * std::pow(x[1], 4)
+               + 6 * (std::pow(x[0], 2) * std::pow(x[1], 3) + std::pow(x[0], 3) * std::pow(x[1], 2))}};
+      expected_result *= x[0] * x[1];
+      for (size_t ii = 0; ii < 2; ++ii)
+        for (size_t jj = 0; jj < 2; ++jj)
+          EXPECT_DOUBLE_EQ(expected_result[ii][jj], result[ii][jj]);
+    }
+  }
+
+  virtual void evaluates_correctly_for_vector_bases()
+  {
+    VectorIntegrandType integrand(*diffusion_tensor_);
+    const auto element = *(grid_provider_->leaf_view().template begin<0>());
+    integrand.bind(element);
+    const auto integrand_order = integrand.order(*vector_test_, *vector_ansatz_);
+    DynamicMatrix<D> result(2, 2, 0.);
+    for (const auto& quadrature_point : Dune::QuadratureRules<D, d>::rule(element.geometry().type(), integrand_order)) {
+      const auto& x = quadrature_point.position();
+      integrand.evaluate(*vector_test_, *vector_ansatz_, x, result);
+      DynamicMatrix<D> expected_result{
+          {0., 0.},
+          {x[0] * x[1] + x[0] + x[1] + 2,
+           2 * (std::pow(x[0], 2) * x[1] + std::pow(x[1], 2) + std::pow(x[0], 2) + 2 * x[1])}};
+      expected_result *= x[0] * x[1];
+      for (size_t ii = 1; ii < 2; ++ii)
+        for (size_t jj = 0; jj < 2; ++jj) {
+          EXPECT_DOUBLE_EQ(expected_result[ii][jj], result[ii][jj]);
+        }
+    }
+  }
+
+  virtual void is_integrated_correctly()
+  {
+    ScalarIntegrandType integrand(1.);
+    const auto& grid_view = grid_provider_->leaf_view();
+    // std::string grid_name = XT::Common::Typename<G>::value();
+    const auto space = make_continuous_lagrange_space<1>(grid_view, /*polorder=*/2);
+    const auto n = space.mapper().size();
+    MatrixType stiffness_matrix(n, n, make_element_sparsity_pattern(space, space, grid_view));
+    MatrixOperator<MatrixType, GV, 1> laplace_operator(grid_view, space, space, stiffness_matrix);
+    laplace_operator.append(LocalElementIntegralBilinearForm<E, 1>(integrand));
+    laplace_operator.assemble(true);
+    const auto mat_data_ptr = XT::Common::serialize_rowwise(stiffness_matrix);
+    const auto min_entry = *std::min_element(mat_data_ptr.get(), mat_data_ptr.get() + n * n);
+    const auto max_entry = *std::max_element(mat_data_ptr.get(), mat_data_ptr.get() + n * n);
+    const auto square_sum = std::accumulate(
+        mat_data_ptr.get(), mat_data_ptr.get() + n * n, 0., [](const auto& a, const auto& b) { return a + b * b; });
+    EXPECT_NEAR((is_simplex_grid_ ? -2. : -1.896296296296300), min_entry, 1e-13);
+    EXPECT_NEAR((is_simplex_grid_ ? 5.777777777777780 : 6.162962962962970), max_entry, 1e-13);
+    EXPECT_NEAR((is_simplex_grid_ ? 2481.524691358029 : 1704.099039780521), square_sum, 5e-12);
+    // std::cout << XT::Common::to_string(stiffness_matrix, 15) << std::endl;
+  }
+
+  using BaseType::grid_provider_;
+  using BaseType::is_simplex_grid_;
+  using BaseType::scalar_ansatz_;
+  using BaseType::scalar_test_;
+  using BaseType::vector_ansatz_;
+  using BaseType::vector_test_;
+  std::shared_ptr<XT::Functions::GenericGridFunction<E, 2, 2>> diffusion_tensor_;
+}; // struct LaplaceIntegrandTest
+
+
+} // namespace Test
+} // namespace GDT
+} // namespace Dune
+
+
+template <class G>
+using LaplaceIntegrandTest = Dune::GDT::Test::LaplaceIntegrandTest<G>;
+TYPED_TEST_CASE(LaplaceIntegrandTest, Grids2D);
+
+TYPED_TEST(LaplaceIntegrandTest, is_constructable)
+{
+  this->is_constructable();
+}
+TYPED_TEST(LaplaceIntegrandTest, evaluates_correctly_for_scalar_bases)
+{
+  this->evaluates_correctly_for_scalar_bases();
+}
+
+TYPED_TEST(LaplaceIntegrandTest, evaluates_correctly_for_vector_bases)
+{
+  this->evaluates_correctly_for_vector_bases();
+}
+
+TYPED_TEST(LaplaceIntegrandTest, is_integrated_correctly)
+{
+  this->is_integrated_correctly();
+}
diff --git a/dune/gdt/test/integrands/integrands_product.cc b/dune/gdt/test/integrands/integrands_product.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a848d262e648782e0534326be56af5d379cb21e9
--- /dev/null
+++ b/dune/gdt/test/integrands/integrands_product.cc
@@ -0,0 +1,172 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner (2019)
+
+#include <dune/xt/common/test/main.hxx> // <- this one has to come first (includes the config.h)!
+
+#include <dune/gdt/local/integrands/product.hh>
+
+#include <dune/gdt/test/integrands/integrands.hh>
+
+namespace Dune {
+namespace GDT {
+namespace Test {
+
+
+template <class G>
+struct ProductIntegrandTest : public IntegrandTest<G>
+{
+  using BaseType = IntegrandTest<G>;
+  using BaseType::d;
+  using typename BaseType::D;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::GV;
+  using typename BaseType::MatrixType;
+  using typename BaseType::VectorJacobianType;
+  using ScalarIntegrandType = LocalElementProductIntegrand<E, 1>;
+  using VectorIntegrandType = LocalElementProductIntegrand<E, d>;
+
+  virtual void is_constructable() override final
+  {
+    ScalarIntegrandType scalar_integrand1;
+    ScalarIntegrandType scalar_integrand2(1.);
+    const XT::Functions::GenericGridFunction<E, 1> scalar_grid_function(
+        2, [](const E&) {}, [](const DomainType& x, const XT::Common::Parameter&) { return x[0] * x[1]; });
+    ScalarIntegrandType scalar_integrand3(scalar_grid_function);
+    const XT::Functions::GenericFunction<d, 1> scalar_function(
+        2, [](const DomainType& x, const XT::Common::Parameter&) { return x[0] * x[1]; });
+    ScalarIntegrandType scalar_integrand4(scalar_function);
+    DUNE_UNUSED_PARAMETER(scalar_integrand1);
+    DUNE_UNUSED_PARAMETER(scalar_integrand2);
+    DUNE_UNUSED_PARAMETER(scalar_integrand3);
+    DUNE_UNUSED_PARAMETER(scalar_integrand4);
+    VectorIntegrandType vector_integrand1;
+    VectorIntegrandType vector_integrand2(1.);
+    VectorIntegrandType vector_integrand3(scalar_grid_function);
+    VectorIntegrandType vector_integrand4(scalar_function);
+    const XT::Functions::GenericGridFunction<E, 2, 2> matrix_grid_function(
+        1,
+        [](const E&) {},
+        [](const DomainType& x, const XT::Common::Parameter&) {
+          return VectorJacobianType{{x[0], x[1]}, {1., 2.}};
+        });
+    VectorIntegrandType vector_integrand5(matrix_grid_function);
+    DUNE_UNUSED_PARAMETER(vector_integrand1);
+    DUNE_UNUSED_PARAMETER(vector_integrand2);
+    DUNE_UNUSED_PARAMETER(vector_integrand3);
+    DUNE_UNUSED_PARAMETER(vector_integrand4);
+    DUNE_UNUSED_PARAMETER(vector_integrand5);
+  }
+
+  virtual void evaluates_correctly_for_scalar_bases()
+  {
+    const XT::Functions::GenericGridFunction<E, 1> scalar_inducing_function(
+        2, [](const E&) {}, [](const DomainType& x, const XT::Common::Parameter&) { return x[0] * x[1]; });
+    ScalarIntegrandType scalar_integrand(scalar_inducing_function);
+    const auto element = *(grid_provider_->leaf_view().template begin<0>());
+    scalar_integrand.bind(element);
+    const auto integrand_order = scalar_integrand.order(*scalar_test_, *scalar_ansatz_);
+    DynamicMatrix<D> result(2, 2, 0.);
+    for (const auto& quadrature_point : Dune::QuadratureRules<D, d>::rule(element.geometry().type(), integrand_order)) {
+      const auto& x = quadrature_point.position();
+      scalar_integrand.evaluate(*scalar_test_, *scalar_ansatz_, x, result);
+      DynamicMatrix<D> expected_result{{std::pow(x[0] * x[1], 2), std::pow(x[0] * x[1], 3)},
+                                       {std::pow(x[0], 3) * std::pow(x[1], 4), std::pow(x[0], 4) * std::pow(x[1], 5)}};
+      for (size_t ii = 0; ii < 2; ++ii)
+        for (size_t jj = 0; jj < 2; ++jj)
+          EXPECT_DOUBLE_EQ(expected_result[ii][jj], result[ii][jj]);
+    }
+  }
+
+  virtual void evaluates_correctly_for_vector_bases()
+  {
+    const XT::Functions::GenericGridFunction<E, 2, 2> inducing_function(
+        1,
+        [](const E&) {},
+        [](const DomainType& x, const XT::Common::Parameter&) {
+          return VectorJacobianType{{x[0], x[1]}, {1., 2.}};
+        });
+    VectorIntegrandType integrand(inducing_function);
+    const auto element = *(grid_provider_->leaf_view().template begin<0>());
+    integrand.bind(element);
+    const auto integrand_order = integrand.order(*vector_test_, *vector_ansatz_);
+    DynamicMatrix<D> result(2, 2, 0.);
+    for (const auto& quadrature_point : Dune::QuadratureRules<D, d>::rule(element.geometry().type(), integrand_order)) {
+      const auto& x = quadrature_point.position();
+      integrand.evaluate(*vector_test_, *vector_ansatz_, x, result);
+      DynamicMatrix<D> expected_result{{std::pow(x[0], 2) + 2 * x[0] * x[1] + 5 * x[1],
+                                        std::pow(x[0], 3) + 2 * std::pow(x[0], 2) * x[1] + 5 * std::pow(x[1], 2)},
+                                       {std::pow(x[0], 3) * x[1] + std::pow(x[0], 2) * x[1]
+                                            + 2 * x[0] * std::pow(x[1], 2) + 2 * x[0] * x[1] + 2 * std::pow(x[1], 2),
+                                        std::pow(x[0], 4) * x[1] + std::pow(x[0], 3) * x[1]
+                                            + std::pow(x[0], 2) * std::pow(x[1], 2) + x[0] * std::pow(x[1], 3)
+                                            + +2 * x[0] * std::pow(x[1], 2) + 2 * std::pow(x[1], 3)}};
+      for (size_t ii = 0; ii < 2; ++ii)
+        for (size_t jj = 0; jj < 2; ++jj)
+          EXPECT_DOUBLE_EQ(expected_result[ii][jj], result[ii][jj]);
+    }
+  }
+
+  virtual void is_integrated_correctly()
+  {
+    ScalarIntegrandType integrand(1.);
+    const auto& grid_view = grid_provider_->leaf_view();
+    const auto space = make_continuous_lagrange_space<1>(grid_view, /*polorder=*/2);
+    const auto n = space.mapper().size();
+    MatrixType mass_matrix(n, n, make_element_sparsity_pattern(space, space, grid_view));
+    MatrixOperator<MatrixType, GV, 1> product_operator(grid_view, space, space, mass_matrix);
+    product_operator.append(LocalElementIntegralBilinearForm<E, 1>(integrand));
+    product_operator.assemble(true);
+    const auto mat_data_ptr = XT::Common::serialize_rowwise(mass_matrix);
+    const auto min_entry = *std::min_element(mat_data_ptr.get(), mat_data_ptr.get() + n * n);
+    const auto max_entry = *std::max_element(mat_data_ptr.get(), mat_data_ptr.get() + n * n);
+    const auto square_sum = std::accumulate(
+        mat_data_ptr.get(), mat_data_ptr.get() + n * n, 0., [](const auto& a, const auto& b) { return a + b * b; });
+    EXPECT_NEAR((is_simplex_grid_ ? -0.001851851851852 : -0.002962962962963), min_entry, 1e-13);
+    EXPECT_NEAR((is_simplex_grid_ ? 0.029629629629630 : 0.047407407407407), max_entry, 1e-13);
+    EXPECT_NEAR((is_simplex_grid_ ? 0.058804012345679 : 0.066475994513031), square_sum, 1e-13);
+    // std::cout << XT::Common::to_string(mass_matrix, 15) << std::endl;
+  }
+
+  using BaseType::grid_provider_;
+  using BaseType::is_simplex_grid_;
+  using BaseType::scalar_ansatz_;
+  using BaseType::scalar_test_;
+  using BaseType::vector_ansatz_;
+  using BaseType::vector_test_;
+}; // struct ProductIntegrandTest
+
+
+} // namespace Test
+} // namespace GDT
+} // namespace Dune
+
+
+template <class G>
+using ProductIntegrandTest = Dune::GDT::Test::ProductIntegrandTest<G>;
+TYPED_TEST_CASE(ProductIntegrandTest, Grids2D);
+
+TYPED_TEST(ProductIntegrandTest, is_constructable)
+{
+  this->is_constructable();
+}
+TYPED_TEST(ProductIntegrandTest, evaluates_correctly_for_scalar_bases)
+{
+  this->evaluates_correctly_for_scalar_bases();
+}
+
+TYPED_TEST(ProductIntegrandTest, evaluates_correctly_for_vector_bases)
+{
+  this->evaluates_correctly_for_vector_bases();
+}
+
+TYPED_TEST(ProductIntegrandTest, is_integrated_correctly)
+{
+  this->is_integrated_correctly();
+}
diff --git a/dune/gdt/test/integrands/integrands_symmetric_elliptic.cc b/dune/gdt/test/integrands/integrands_symmetric_elliptic.cc
new file mode 100644
index 0000000000000000000000000000000000000000..62db03b333dbefb701f835fef013f5a02df22617
--- /dev/null
+++ b/dune/gdt/test/integrands/integrands_symmetric_elliptic.cc
@@ -0,0 +1,120 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner (2019)
+
+#include <dune/xt/common/test/main.hxx> // <- this one has to come first (includes the config.h)!
+
+#include <dune/gdt/local/integrands/symmetrized-laplace.hh>
+
+#include <dune/gdt/test/integrands/integrands.hh>
+
+namespace Dune {
+namespace GDT {
+namespace Test {
+
+
+template <class G>
+struct SymmetrizedLaplaceIntegrandTest : public IntegrandTest<G>
+{
+  using BaseType = IntegrandTest<G>;
+  using BaseType::d;
+  using typename BaseType::D;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::GV;
+  using typename BaseType::LocalVectorBasisType;
+  using typename BaseType::VectorJacobianType;
+  using typename BaseType::VectorRangeType;
+  using VectorIntegrandType = LocalSymmetrizedLaplaceIntegrand<E>;
+
+  virtual void SetUp() override
+  {
+    BaseType::SetUp();
+    diffusion_factor_ = std::make_shared<XT::Functions::GenericGridFunction<E, 1>>(
+        2, [](const E&) {}, [](const DomainType& x, const XT::Common::Parameter&) { return x[0] * x[1]; });
+    // { (x,x+y)^T, (x^2, x^2 + y^2)^T}
+    vector_ansatz_ = std::make_shared<LocalVectorBasisType>(
+        /*size = */ 2,
+        /*ord = */ 2,
+        /*evaluate = */
+        [](const DomainType& x, std::vector<VectorRangeType>& ret, const XT::Common::Parameter&) {
+          ret = {{x[0], x[0] + x[1]}, {std::pow(x[0], 2), std::pow(x[0], 2) + std::pow(x[1], 2)}};
+        },
+        /*param_type = */ XT::Common::ParameterType{},
+        /*jacobian = */
+        [](const DomainType& x, std::vector<VectorJacobianType>& ret, const XT::Common::Parameter&) {
+          // jacobian of first function
+          ret[0][0] = {1., 0.};
+          ret[0][1] = {1., 1.};
+          // jacobian of second function
+          ret[1][0] = {2 * x[0], 0.};
+          ret[1][1] = {2 * x[0], 2 * x[1]};
+        });
+  }
+
+  virtual void is_constructable() override final
+  {
+    VectorIntegrandType vector_integrand1;
+    VectorIntegrandType vector_integrand2(1.);
+    const XT::Functions::GenericFunction<d, 1> scalar_function(
+        2, [](const DomainType& x, const XT::Common::Parameter&) { return x[0] * x[1]; });
+    VectorIntegrandType vector_integrand3(scalar_function);
+    VectorIntegrandType vector_integrand4(*diffusion_factor_);
+    DUNE_UNUSED_PARAMETER(vector_integrand1);
+    DUNE_UNUSED_PARAMETER(vector_integrand2);
+    DUNE_UNUSED_PARAMETER(vector_integrand3);
+    DUNE_UNUSED_PARAMETER(vector_integrand4);
+  }
+
+  virtual void evaluates_correctly()
+  {
+    VectorIntegrandType integrand(*diffusion_factor_);
+    const auto element = *(grid_provider_->leaf_view().template begin<0>());
+    integrand.bind(element);
+    const auto integrand_order = integrand.order(*vector_test_, *vector_ansatz_);
+    DynamicMatrix<D> result(2, 2, 0.);
+    for (const auto& quadrature_point : Dune::QuadratureRules<D, d>::rule(element.geometry().type(), integrand_order)) {
+      const auto& x = quadrature_point.position();
+      integrand.evaluate(*vector_test_, *vector_ansatz_, x, result);
+      DynamicMatrix<D> expected_result{
+          {0., 0.}, {x[1] + 0.5 * x[0] + 1.5, 2 * x[0] * x[1] + std::pow(x[0], 2) + x[0] + 2 * x[1]}};
+      expected_result *= x[0] * x[1];
+      for (size_t ii = 1; ii < 2; ++ii)
+        for (size_t jj = 0; jj < 2; ++jj) {
+          EXPECT_DOUBLE_EQ(expected_result[ii][jj], result[ii][jj]);
+        }
+    }
+  }
+
+  using BaseType::grid_provider_;
+  using BaseType::scalar_ansatz_;
+  using BaseType::scalar_test_;
+  using BaseType::vector_ansatz_;
+  using BaseType::vector_test_;
+  std::shared_ptr<XT::Functions::GenericGridFunction<E, 1>> diffusion_factor_;
+}; // struct SymmetrizedLaplaceIntegrandTest
+
+
+} // namespace Test
+} // namespace GDT
+} // namespace Dune
+
+
+template <class G>
+using SymmetrizedLaplaceIntegrandTest = Dune::GDT::Test::SymmetrizedLaplaceIntegrandTest<G>;
+TYPED_TEST_CASE(SymmetrizedLaplaceIntegrandTest, Grids2D);
+
+TYPED_TEST(SymmetrizedLaplaceIntegrandTest, is_constructable)
+{
+  this->is_constructable();
+}
+
+TYPED_TEST(SymmetrizedLaplaceIntegrandTest, evaluates_correctly)
+{
+  this->evaluates_correctly();
+}
diff --git a/dune/gdt/test/interpolations/default.hh b/dune/gdt/test/interpolations/default.hh
new file mode 100644
index 0000000000000000000000000000000000000000..66fe62faa9d85833d7de6f04482d42d031c32768
--- /dev/null
+++ b/dune/gdt/test/interpolations/default.hh
@@ -0,0 +1,122 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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)
+
+#ifndef DUNE_GDT_TEST_INTERPOLATIONS_DEFAULT_HH
+#define DUNE_GDT_TEST_INTERPOLATIONS_DEFAULT_HH
+
+#include <dune/xt/common/fvector.hh>
+#include <dune/xt/common/fmatrix.hh>
+#include <dune/xt/common/string.hh>
+#include <dune/xt/common/test/gtest/gtest.h>
+#include <dune/xt/common/test/common.hh>
+#include <dune/xt/grid/boundaryinfo/normalbased.hh>
+#include <dune/xt/grid/gridprovider/cube.hh>
+#include <dune/xt/grid/structuredgridfactory.hh>
+#include <dune/xt/grid/type_traits.hh>
+#include <dune/xt/functions/constant.hh>
+#include <dune/xt/functions/generic/function.hh>
+
+#include <dune/gdt/discretefunction/default.hh>
+#include <dune/gdt/interpolations/boundary.hh>
+#include <dune/gdt/local/bilinear-forms/integrals.hh>
+#include <dune/gdt/local/integrands/product.hh>
+#include <dune/gdt/norms.hh>
+#include <dune/gdt/spaces/h1/continuous-lagrange.hh>
+
+namespace Dune {
+namespace GDT {
+namespace Test {
+
+
+template <class G>
+struct DefaultInterpolationOnLeafViewTest : public ::testing::Test
+{
+  static_assert(XT::Grid::is_grid<G>::value, "");
+
+  using GV = typename G::LeafGridView;
+  using D = typename GV::ctype;
+  static const constexpr size_t d = GV::dimension;
+  using E = XT::Grid::extract_entity_t<GV>;
+  using I = XT::Grid::extract_intersection_t<GV>;
+  using M = XT::LA::IstlRowMajorSparseMatrix<double>;
+  using V = XT::LA::IstlDenseVector<double>;
+
+  std::shared_ptr<XT::Grid::GridProvider<G>> grid_provider;
+  std::shared_ptr<XT::Functions::GenericFunction<d>> source;
+  std::shared_ptr<ContinuousLagrangeSpace<GV>> space;
+  std::shared_ptr<DiscreteFunction<V, GV>> range;
+
+  virtual std::shared_ptr<XT::Grid::GridProvider<G>> make_grid()
+  {
+    return std::make_shared<XT::Grid::GridProvider<G>>(
+        XT::Grid::make_cube_grid<G>(XT::Common::from_string<FieldVector<double, d>>("[-1 0 0 0]"),
+                                    XT::Common::from_string<FieldVector<double, d>>("[1 1 1 1]"),
+                                    XT::Common::from_string<std::array<unsigned int, d>>("[5 5 5 2]")));
+  }
+
+  void SetUp() override
+  {
+    grid_provider = make_grid();
+    space =
+        std::make_shared<ContinuousLagrangeSpace<GV>>(make_continuous_lagrange_space(grid_provider->leaf_view(), 2));
+    source = std::make_shared<XT::Functions::GenericFunction<d>>(
+        [](const auto&) { return 2; },
+        [](const auto& x, const auto&) {
+          const auto x_dependent = 2 * std::pow(x[0], 2) - x[0] + 3;
+          const auto xy_dependent = x_dependent + x[0] * x[1] + 0.5 * x[1] - std::pow(x[1], 2);
+          const auto xyz_dependent = xy_dependent + 0.5 * std::pow(x[2], 2) + x[2] * x[0] - 3 * x[2] * x[1];
+          if (d == 1)
+            return x_dependent;
+          else if (d == 2)
+            return xy_dependent;
+          else
+            return xyz_dependent;
+        },
+        "second order polynomial",
+        XT::Common::ParameterType{},
+        [](const auto& x, const auto&) {
+          const std::vector<double> x_dependent_jacobian{4 * x[0] - 1, 0, 0, 0};
+          const std::vector<double> y_dependent_jacobian{x[1], x[0] + 0.5 - 2 * x[1], 0, 0};
+          const std::vector<double> z_dependent_jacobian{x[2], -3 * x[2], x[2] + x[0] - 3 * x[1], 0};
+          XT::Common::FieldMatrix<double, 1, d> jacobian;
+          for (size_t ii = 0; ii < d; ++ii) {
+            jacobian[0][ii] = x_dependent_jacobian[ii];
+            if (d >= 2)
+              jacobian[0][ii] += y_dependent_jacobian[ii];
+            if (d >= 3)
+              jacobian[0][ii] += z_dependent_jacobian[ii];
+          }
+          return jacobian;
+        });
+    range = std::make_shared<DiscreteFunction<V, GV>>(*space);
+  } // ... SetUp(...)
+
+  void interpolates_correctly(const double expected_l2_error = 1e-14)
+  {
+    default_interpolation(*source, *range, space->grid_view());
+    const auto l2_error = l2_norm(space->grid_view(), source->template as_grid_function<E>() - *range);
+    EXPECT_LT(l2_error, expected_l2_error)
+        << "XT::Common::Test::get_unique_test_name() = '" << XT::Common::Test::get_unique_test_name() << "'";
+    const auto local_range = range->local_discrete_function();
+    for (auto&& element : Dune::elements(space->grid_view())) {
+      local_range->bind(element);
+      const auto center = element.geometry().center();
+      const auto range_jacobian = local_range->jacobian(element.geometry().local(center));
+      const auto expected_jacobian = source->jacobian(center);
+      EXPECT_LT((range_jacobian - expected_jacobian)[0].two_norm(), expected_l2_error);
+    }
+  } // ... interpolates_correctly(...)
+}; // struct DefaultInterpolationOnLeafViewTest
+
+
+} // namespace Test
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_TEST_INTERPOLATIONS_DEFAULT_HH
diff --git a/dune/gdt/test/interpolations/interpolations_default__cubic_2d_grids.cc b/dune/gdt/test/interpolations/interpolations_default__cubic_2d_grids.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b2578937ed784aeaf8586bd3c772d5172cdc2c5e
--- /dev/null
+++ b/dune/gdt/test/interpolations/interpolations_default__cubic_2d_grids.cc
@@ -0,0 +1,35 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 <dune/xt/common/test/main.hxx> // <- this one has to come first (includes the config.h)!
+
+#include <dune/xt/grid/grids.hh>
+
+#include "default.hh"
+
+
+using Cubic2dGrids = ::testing::Types<YASP_2D_EQUIDISTANT_OFFSET
+#if HAVE_DUNE_ALUGRID
+                                      ,
+                                      ALU_2D_CUBE
+#endif
+#if HAVE_DUNE_UGGRID
+                                      ,
+                                      UG_2D
+#endif
+                                      >;
+
+
+template <class G>
+using InterpolationTest = Dune::GDT::Test::DefaultInterpolationOnLeafViewTest<G>;
+TYPED_TEST_CASE(InterpolationTest, Cubic2dGrids);
+TYPED_TEST(InterpolationTest, interpolates_correctly)
+{
+  this->interpolates_correctly(4e-14);
+}
diff --git a/dune/gdt/test/interpolations/interpolations_default__cubic_3d_grids.cc b/dune/gdt/test/interpolations/interpolations_default__cubic_3d_grids.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2d9dee9a032bde6445fe2d471e76272947fe60e3
--- /dev/null
+++ b/dune/gdt/test/interpolations/interpolations_default__cubic_3d_grids.cc
@@ -0,0 +1,35 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 <dune/xt/common/test/main.hxx> // <- this one has to come first (includes the config.h)!
+
+#include <dune/xt/grid/grids.hh>
+
+#include "default.hh"
+
+
+using Cubic3dGrids = ::testing::Types<YASP_3D_EQUIDISTANT_OFFSET
+#if HAVE_DUNE_ALUGRID
+                                      ,
+                                      ALU_3D_CUBE
+#endif
+#if HAVE_DUNE_UGGRID
+                                      ,
+                                      UG_3D
+#endif
+                                      >;
+
+
+template <class G>
+using InterpolationTest = Dune::GDT::Test::DefaultInterpolationOnLeafViewTest<G>;
+TYPED_TEST_CASE(InterpolationTest, Cubic3dGrids);
+TYPED_TEST(InterpolationTest, interpolates_correctly)
+{
+  this->interpolates_correctly(2e-13);
+}
diff --git a/dune/gdt/test/stokes/stokes-testcase1.cc b/dune/gdt/test/interpolations/interpolations_default__cubic_4d_grids.cc
similarity index 50%
rename from dune/gdt/test/stokes/stokes-testcase1.cc
rename to dune/gdt/test/interpolations/interpolations_default__cubic_4d_grids.cc
index 0124c1af018b57a54d8b801e488014479a163454..832fde0632bd52eff4abc72e33a63d5a95232042 100644
--- a/dune/gdt/test/stokes/stokes-testcase1.cc
+++ b/dune/gdt/test/interpolations/interpolations_default__cubic_4d_grids.cc
@@ -5,30 +5,22 @@
 //      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
 //          with "runtime exception" (http://www.dune-project.org/license.html)
 // Authors:
-//   Tobias Leibner (2019)
-
-#define DUNE_XT_COMMON_TEST_MAIN_ENABLE_TIMED_LOGGING 1
-#define DUNE_XT_COMMON_TEST_MAIN_ENABLE_INFO_LOGGING 1
-
-#define DUNE_XT_COMMON_TEST_MAIN_CATCH_EXCEPTIONS 1
+//   Felix Schindler (2019)
 
 #include <dune/xt/common/test/main.hxx> // <- this one has to come first (includes the config.h)!
 
 #include <dune/xt/grid/grids.hh>
 
-#include <dune/gdt/test/stokes/stokes-taylorhood.hh>
+#include "default.hh"
 
-#if HAVE_DUNE_ISTL
 
-using namespace Dune;
-using namespace Dune::GDT::Test;
+using Cubic4dGrids = ::testing::Types<YASP_4D_EQUIDISTANT_OFFSET>;
 
-using StokesTest = StokesTestcase1<YASP_2D_EQUIDISTANT_OFFSET>;
-// using StokesTest = StokesTestcase1<ALU_2D_SIMPLEX_CONFORMING>;
-// using StokesTest = StokesTestcase1<UG_2D>;
-TEST_F(StokesTest, run)
+
+template <class G>
+using InterpolationTest = Dune::GDT::Test::DefaultInterpolationOnLeafViewTest<G>;
+TYPED_TEST_CASE(InterpolationTest, Cubic4dGrids);
+TYPED_TEST(InterpolationTest, interpolates_correctly)
 {
-  this->run();
+  this->interpolates_correctly(6e-13);
 }
-
-#endif // HAVE_DUNE_ISTL
diff --git a/dune/gdt/test/interpolations/interpolations_default__simplicial_1d_grids.cc b/dune/gdt/test/interpolations/interpolations_default__simplicial_1d_grids.cc
new file mode 100644
index 0000000000000000000000000000000000000000..78c2e9ba041e1ee3451fdc520eef6c903a7dcc0a
--- /dev/null
+++ b/dune/gdt/test/interpolations/interpolations_default__simplicial_1d_grids.cc
@@ -0,0 +1,26 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 <dune/xt/common/test/main.hxx> // <- this one has to come first (includes the config.h)!
+
+#include <dune/xt/grid/grids.hh>
+
+#include "default.hh"
+
+
+using Simplicial1dGrids = ::testing::Types<ONED_1D, YASP_1D_EQUIDISTANT_OFFSET>;
+
+
+template <class G>
+using InterpolationTest = Dune::GDT::Test::DefaultInterpolationOnLeafViewTest<G>;
+TYPED_TEST_CASE(InterpolationTest, Simplicial1dGrids);
+TYPED_TEST(InterpolationTest, interpolates_correctly)
+{
+  this->interpolates_correctly();
+}
diff --git a/dune/gdt/test/interpolations/interpolations_default__simplicial_2d_grids.cc b/dune/gdt/test/interpolations/interpolations_default__simplicial_2d_grids.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f292951c7ed7e35470fe3ee25d6858d2b00d43c8
--- /dev/null
+++ b/dune/gdt/test/interpolations/interpolations_default__simplicial_2d_grids.cc
@@ -0,0 +1,37 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 <dune/xt/common/test/main.hxx> // <- this one has to come first (includes the config.h)!
+
+#include <dune/xt/grid/grids.hh>
+
+#include "default.hh"
+
+
+using Simplicial2dGrids = ::testing::Types<
+#if HAVE_DUNE_ALUGRID
+    ALU_2D_SIMPLEX_CONFORMING,
+    ALU_2D_SIMPLEX_NONCONFORMING
+#endif
+#if HAVE_DUNE_ALUGRID && HAVE_DUNE_UGGRID
+    ,
+#endif
+#if HAVE_DUNE_UGGRID
+    UG_2D
+#endif
+    >;
+
+
+template <class G>
+using InterpolationTest = Dune::GDT::Test::DefaultInterpolationOnLeafViewTest<G>;
+TYPED_TEST_CASE(InterpolationTest, Simplicial2dGrids);
+TYPED_TEST(InterpolationTest, interpolates_correctly)
+{
+  this->interpolates_correctly();
+}
diff --git a/dune/gdt/test/interpolations/interpolations_default__simplicial_3d_grids.cc b/dune/gdt/test/interpolations/interpolations_default__simplicial_3d_grids.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e632f3f44bcf6edf69186a76c141fd6cfb8006ce
--- /dev/null
+++ b/dune/gdt/test/interpolations/interpolations_default__simplicial_3d_grids.cc
@@ -0,0 +1,37 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 <dune/xt/common/test/main.hxx> // <- this one has to come first (includes the config.h)!
+
+#include <dune/xt/grid/grids.hh>
+
+#include "default.hh"
+
+
+using Simplicial3dGrids = ::testing::Types<
+#if HAVE_DUNE_ALUGRID
+    ALU_3D_SIMPLEX_CONFORMING,
+    ALU_3D_SIMPLEX_NONCONFORMING
+#endif
+#if HAVE_DUNE_ALUGRID && HAVE_DUNE_UGGRID
+    ,
+#endif
+#if HAVE_DUNE_UGGRID
+    UG_3D
+#endif
+    >;
+
+
+template <class G>
+using InterpolationTest = Dune::GDT::Test::DefaultInterpolationOnLeafViewTest<G>;
+TYPED_TEST_CASE(InterpolationTest, Simplicial3dGrids);
+TYPED_TEST(InterpolationTest, interpolates_correctly)
+{
+  this->interpolates_correctly(3e-14);
+}
diff --git a/dune/gdt/test/inviscid-compressible-flow/base.hh b/dune/gdt/test/inviscid-compressible-flow/base.hh
index e2d62700d27beeb31c8bae73ae692ba7bab99f44..2f8a45915044ede32c06c74c8687d1266d0379c2 100644
--- a/dune/gdt/test/inviscid-compressible-flow/base.hh
+++ b/dune/gdt/test/inviscid-compressible-flow/base.hh
@@ -96,6 +96,7 @@ protected:
   using typename BaseType::F;
   using typename BaseType::GP;
   using typename BaseType::GV;
+  using typename BaseType::I;
   using typename BaseType::M;
   using typename BaseType::O;
   using typename BaseType::R;
@@ -153,9 +154,9 @@ protected:
   {
     auto& self = *this;
     const auto& euler_tools = this->access().euler_tools;
-    const NumericalVijayasundaramFlux<d, m> numerical_flux(
+    const NumericalVijayasundaramFlux<I, d, m> numerical_flux(
         self.flux(),
-        /*flux_eigen_decomposition=*/[&](const auto& /*f*/, const auto& w, const auto& n, const auto&
+        /*flux_eigen_decomposition=*/[&](const auto& /*local_f*/, const auto& w, const auto& n, const auto&
                                          /*param*/) {
           return std::make_tuple(euler_tools.eigenvalues_flux_jacobian(w, n),
                                  euler_tools.eigenvectors_flux_jacobian(w, n),
diff --git a/python/dune/gdt/operators/elliptic/istl.cc b/dune/gdt/test/momentmodels/basisfunctions.hh
similarity index 53%
rename from python/dune/gdt/operators/elliptic/istl.cc
rename to dune/gdt/test/momentmodels/basisfunctions.hh
index 46fa5046cd590d34775190a53807d30ab82fea8a..bdb8e4cb1d1bf7137ea67b20b6922000f2c0ce04 100644
--- a/python/dune/gdt/operators/elliptic/istl.cc
+++ b/dune/gdt/test/momentmodels/basisfunctions.hh
@@ -5,17 +5,15 @@
 //      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
 //          with "runtime exception" (http://www.dune-project.org/license.html)
 // Authors:
-//   Felix Schindler (2017 - 2018)
-//   René Fritze     (2018)
+//   Rene Milk      (2018)
+//   Tobias Leibner (2017)
 
-#include "config.h"
+#ifndef DUNE_GDT_MOMENTMODELS_BASISFUNCTIONS_HH
+#define DUNE_GDT_MOMENTMODELS_BASISFUNCTIONS_HH
 
-#if HAVE_DUNE_PYBINDXI
+#include "basisfunctions/legendre.hh"
+#include "basisfunctions/hatfunctions.hh"
+#include "basisfunctions/partial_moments.hh"
+#include "basisfunctions/spherical_harmonics.hh"
 
-#  include <python/dune/gdt/operators/elliptic/bindings.hh>
-
-
-DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_ISTL(template);
-
-
-#endif // HAVE_DUNE_PYBINDXI
+#endif // DUNE_GDT_MOMENTMODELS_BASISFUNCTIONS_HH
diff --git a/dune/gdt/test/momentmodels/basisfunctions/hatfunctions.hh b/dune/gdt/test/momentmodels/basisfunctions/hatfunctions.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c5929c639cfb08838616a6516f62b6eeeb35f104
--- /dev/null
+++ b/dune/gdt/test/momentmodels/basisfunctions/hatfunctions.hh
@@ -0,0 +1,752 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_HATFUNCTIONS_HH
+#define DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_HATFUNCTIONS_HH
+
+#include <vector>
+#include <string>
+
+#include <dune/xt/common/fmatrix.hh>
+
+#include <dune/gdt/test/momentmodels/triangulation.hh>
+
+#include "interface.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+template <class DomainFieldType,
+          size_t dimDomain,
+          class RangeFieldType,
+          size_t dimRange,
+          size_t dimRangeCols = 1,
+          size_t dimFlux = dimDomain,
+          EntropyType entropy = EntropyType::MaxwellBoltzmann>
+class HatFunctionMomentBasis
+{
+  //  static_assert(false, "Not implemented for this dimension!");
+};
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t rangeDim,
+          size_t rangeDimCols,
+          size_t fluxDim,
+          EntropyType entropy>
+class HatFunctionMomentBasis<DomainFieldType, 1, RangeFieldType, rangeDim, rangeDimCols, fluxDim, entropy>
+  : public MomentBasisInterface<DomainFieldType, 1, RangeFieldType, rangeDim, rangeDimCols, fluxDim, entropy>
+{
+public:
+  static const size_t dimDomain = 1;
+  static const size_t dimRange = rangeDim;
+  static const size_t dimRangeCols = rangeDimCols;
+  static const size_t num_intervals = dimRange - 1;
+
+private:
+  using BaseType =
+      MomentBasisInterface<DomainFieldType, dimDomain, RangeFieldType, dimRange, dimRangeCols, fluxDim, entropy>;
+
+public:
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicRangeType;
+  using typename BaseType::MatrixType;
+  using typename BaseType::QuadraturesType;
+  using typename BaseType::RangeType;
+  using typename BaseType::SphericalTriangulationType;
+  using typename BaseType::StringifierType;
+  using typename BaseType::VisualizerType;
+  using PartitioningType = typename BaseType::Partitioning1dType;
+  template <class DiscreteFunctionType>
+
+  static std::string static_id()
+  {
+    return "hatfunctions";
+  }
+
+  static size_t default_quad_order()
+  {
+    return 15;
+  }
+
+  using BaseType::default_quad_refinements;
+
+  HatFunctionMomentBasis(const QuadraturesType& quadratures)
+    : BaseType(quadratures)
+    , partitioning_(BaseType::create_1d_partitioning(num_intervals))
+  {
+    BaseType::initialize_base_values();
+  }
+
+  HatFunctionMomentBasis(const SphericalTriangulationType& /*triangulation*/, const QuadraturesType& quadratures)
+    : HatFunctionMomentBasis(quadratures)
+  {}
+
+  HatFunctionMomentBasis(const size_t quad_order = default_quad_order(),
+                         const size_t quad_refinements = default_quad_refinements())
+    : BaseType(BaseType::gauss_lobatto_quadratures(num_intervals, quad_order, quad_refinements))
+    , partitioning_(BaseType::create_1d_partitioning(num_intervals))
+  {
+    BaseType::initialize_base_values();
+  }
+
+  DynamicRangeType evaluate(const DomainType& v) const override final
+  {
+    DynamicRangeType ret(dimRange, 0);
+    const auto& mu = partitioning_;
+    for (size_t ii = 0; ii < dimRange; ++ii) {
+      if (ii < num_intervals && XT::Common::FloatCmp::ge(v[0], mu[ii]) && XT::Common::FloatCmp::le(v[0], mu[ii + 1]))
+        ret[ii] = (v - mu[ii + 1]) / (mu[ii] - mu[ii + 1]);
+      if (ii > 0 && XT::Common::FloatCmp::ge(v[0], mu[ii - 1]) && XT::Common::FloatCmp::le(v[0], mu[ii]))
+        ret[ii] = (v - mu[ii - 1]) / (mu[ii] - mu[ii - 1]);
+    }
+    return ret;
+  } // ... evaluate(...)
+
+  DynamicRangeType evaluate(const DomainType& v, const size_t interval_index) const override final
+  {
+    DynamicRangeType ret(dimRange, 0);
+    const auto& mu = partitioning_;
+    ret[interval_index] = (v - mu[interval_index + 1]) / (mu[interval_index] - mu[interval_index + 1]);
+    ret[interval_index + 1] = (v - mu[interval_index]) / (mu[interval_index + 1] - mu[interval_index]);
+    return ret;
+  } // ... evaluate(...)
+
+  XT::Common::FieldVector<RangeFieldType, 2> evaluate_on_interval(const DomainType& v,
+                                                                  const size_t interval_index) const
+  {
+    XT::Common::FieldVector<RangeFieldType, 2> ret;
+    const auto& mu = partitioning_;
+    ret[0] = (v - mu[interval_index + 1]) / (mu[interval_index] - mu[interval_index + 1]);
+    ret[1] = (v - mu[interval_index]) / (mu[interval_index + 1] - mu[interval_index]);
+    return ret;
+  } // ... evaluate(...)
+
+  DynamicRangeType integrated() const override final
+  {
+    DynamicRangeType ret(dimRange, 0);
+    const auto& mu = partitioning_;
+    ret[0] = mu[1] - mu[0];
+    for (size_t ii = 1; ii < num_intervals; ++ii)
+      ret[ii] = mu[ii + 1] - mu[ii - 1];
+    ret[num_intervals] = mu[num_intervals] - mu[num_intervals - 1];
+    ret *= 0.5;
+    return ret;
+  }
+
+  // returns matrix with entries <h_i h_j>
+  MatrixType mass_matrix() const override final
+  {
+    MatrixType ret(dimRange, dimRange, 0);
+    const auto& mu = partitioning_;
+    ret[0][0] = (mu[1] - mu[0]) / 3.;
+    for (size_t rr = 0; rr < dimRange; ++rr) {
+      if (rr > 0 && rr < num_intervals)
+        ret[rr][rr] = (mu[rr + 1] - mu[rr - 1]) / 3.;
+      if (rr > 0)
+        ret[rr][rr - 1] = (mu[rr] - mu[rr - 1]) / 6.;
+      if (rr < num_intervals)
+        ret[rr][rr + 1] = (mu[rr + 1] - mu[rr]) / 6.;
+    }
+    ret[num_intervals][num_intervals] = (mu[num_intervals] - mu[num_intervals - 1]) / 3.;
+    return ret;
+  }
+
+  MatrixType mass_matrix_inverse() const override final
+  {
+    return tridiagonal_matrix_inverse<RangeFieldType, dimRange>(mass_matrix());
+  }
+
+  // returns matrix with entries <v h_i h_j>
+  FieldVector<MatrixType, 1> flux_matrix() const override final
+  {
+    MatrixType ret(dimRange, dimRange, 0.);
+    const auto& mu = partitioning_;
+    ret[0][0] = (std::pow(mu[1], 2) + 2 * mu[1] * mu[0] - 3 * std::pow(mu[0], 2)) / 12.;
+    for (size_t rr = 0; rr < dimRange; ++rr) {
+      if (rr > 0 && rr < num_intervals)
+        ret[rr][rr] =
+            (std::pow(mu[rr + 1], 2) + 2 * mu[rr + 1] * mu[rr] - 2 * mu[rr] * mu[rr - 1] - std::pow(mu[rr - 1], 2))
+            / 12.;
+      if (rr > 0)
+        ret[rr][rr - 1] = (std::pow(mu[rr], 2) - std::pow(mu[rr - 1], 2)) / 12.;
+      if (rr < num_intervals)
+        ret[rr][rr + 1] = (std::pow(mu[rr + 1], 2) - std::pow(mu[rr], 2)) / 12.;
+    }
+    ret[num_intervals][num_intervals] =
+        (3 * std::pow(mu[num_intervals], 2) - 2 * mu[num_intervals] * mu[num_intervals - 1]
+         - std::pow(mu[num_intervals - 1], 2))
+        / 12.;
+    return ret;
+  }
+
+  // returns V M^-1 where the matrix V has entries <v h_i h_j>_- and <v h_i h_j>_+
+  FieldVector<FieldVector<MatrixType, 2>, 1> kinetic_flux_matrices() const override final
+  {
+    FieldVector<FieldVector<MatrixType, 2>, 1> ret(FieldVector<MatrixType, 2>(MatrixType(dimRange, dimRange, 0.)));
+    auto mm_with_v = flux_matrix();
+    auto& ret_neg = ret[0][0];
+    auto& ret_pos = ret[0][1];
+    size_t N = dimRange;
+    const auto& mu = partitioning_;
+    for (size_t nn = 0; nn < N; ++nn) {
+      for (size_t mm = (nn > 0 ? nn - 1 : 0); mm <= (nn < N - 1 ? nn + 1 : nn); ++mm) {
+        if (N % 2) {
+          if (nn < N / 2 || mm < N / 2)
+            ret_neg[nn][mm] = mm_with_v[0][nn][mm];
+          else if (nn > N / 2 || mm > N / 2)
+            ret_pos[nn][mm] = mm_with_v[0][nn][mm];
+          else { // nn == mm == N/2
+            ret_neg[nn][mm] = -std::pow(mu[mm - 1], 2) / 12.;
+            ret_pos[nn][mm] = std::pow(mu[mm + 1], 2) / 12.;
+          }
+        } else {
+          if (nn < N / 2 - 1 || mm < N / 2 - 1)
+            ret_neg[nn][mm] = mm_with_v[0][nn][mm];
+          else if (nn > N / 2 || mm > N / 2)
+            ret_pos[nn][mm] = mm_with_v[0][nn][mm];
+          else if (nn == N / 2 && mm == nn) {
+            ret_neg[nn][mm] = -std::pow(mu[mm], 2) / 48.;
+            ret_pos[nn][mm] = (5 * std::pow(mu[mm], 2) + 8 * mu[mm] * mu[mm + 1] + 4 * std::pow(mu[mm + 1], 2)) / 48.;
+          } else if (nn == N / 2 - 1 && mm == nn) {
+            ret_neg[nn][mm] = (-5 * std::pow(mu[mm], 2) - 8 * mu[mm] * mu[mm - 1] - 4 * std::pow(mu[mm - 1], 2)) / 48.;
+            ret_pos[nn][mm] = std::pow(mu[mm], 2) / 48.;
+          } else { // (((mm == N / 2 && nn == N / 2 - 1) || (mm == N / 2 - 1 && nn == N / 2))) {
+            ret_neg[nn][mm] = -std::pow(mu[mm], 2) / 16.;
+            ret_pos[nn][mm] = std::pow(mu[mm], 2) / 16.;
+          }
+        } // else (N % 2)
+      } // mm
+    } // nn
+    // apply M^{-1} from the right
+    const auto M = std::make_unique<XT::Common::FieldMatrix<RangeFieldType, dimRange, dimRange>>(mass_matrix());
+    MatrixType tmp_mat = ret_neg;
+    for (size_t rr = 0; rr < dimRange; ++rr)
+      M->solve(ret_neg[rr], tmp_mat[rr]);
+    tmp_mat = ret_pos;
+    for (size_t rr = 0; rr < dimRange; ++rr)
+      M->solve(ret_pos[rr], tmp_mat[rr]);
+    return ret;
+  }
+
+  MatrixType reflection_matrix(const DomainType& n) const override final
+  {
+    MatrixType ret(dimRange, dimRange, 0);
+    for (size_t ii = 0; ii < dimDomain; ++ii)
+      if (XT::Common::FloatCmp::ne(n[ii], 0.))
+        if (XT::Common::FloatCmp::ne(std::abs(n[ii]), 1.))
+          DUNE_THROW(NotImplemented, "Implemented only for +-e_i where e_i is the i-th canonical basis vector!");
+    const auto mass_mat = mass_matrix();
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      for (size_t jj = 0; jj < dimRange; ++jj)
+        ret[ii][jj] = mass_mat[ii][num_intervals - jj];
+    ret.rightmultiply(mass_matrix_inverse());
+#ifndef NDEBUG
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      for (size_t jj = 0; jj < dimRange; ++jj)
+        if (std::isnan(ret[ii][jj]) || std::isinf(ret[ii][jj]))
+          DUNE_THROW(Dune::MathError,
+                     "Calculation of reflection matrix failed for normal n = " + XT::Common::to_string(n));
+#endif
+    return ret;
+  }
+
+  static StringifierType stringifier()
+  {
+    return [](const RangeType& val) {
+      RangeFieldType psi(0);
+      for (const auto& entry : val)
+        psi += entry;
+      return XT::Common::to_string(psi, 15);
+    };
+  } // ... stringifier()
+
+  const PartitioningType& partitioning() const
+  {
+    return partitioning_;
+  }
+
+  DynamicRangeType alpha_one() const override final
+  {
+    return DynamicRangeType(dimRange, 1.);
+  }
+
+  RangeFieldType density(const DynamicRangeType& u) const override final
+  {
+    return std::accumulate(u.begin(), u.end(), RangeFieldType(0));
+  }
+
+  using BaseType::u_iso;
+
+  void ensure_min_density(DynamicRangeType& u, const RangeFieldType min_density) const override final
+  {
+    const auto u_iso_min = u_iso() * min_density;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      if (u[ii] < u_iso_min[ii])
+        u[ii] = u_iso_min[ii];
+  }
+
+  void ensure_min_density(RangeType& u, const RangeFieldType min_density) const override final
+  {
+    const auto u_iso_min = u_iso() * min_density;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      if (u[ii] < u_iso_min[ii])
+        u[ii] = u_iso_min[ii];
+  }
+
+  std::string short_id() const override final
+  {
+    return "hf";
+  }
+
+  std::string mn_name() const override final
+  {
+    return "hfm" + XT::Common::to_string(dimRange);
+  }
+
+  std::string pn_name() const override final
+  {
+    return "hfp" + XT::Common::to_string(dimRange);
+  }
+
+  // get indices of all faces that contain point v
+  std::vector<size_t> get_face_indices(const DomainType& v) const
+  {
+    std::vector<size_t> face_indices;
+    for (size_t jj = 0; jj < partitioning_.size() - 1; ++jj)
+      if (XT::Common::FloatCmp::ge(v[0], partitioning_[jj]) && XT::Common::FloatCmp::le(v[0], partitioning_[jj + 1]))
+        face_indices.push_back(jj);
+    assert(face_indices.size());
+    return face_indices;
+  }
+
+private:
+  const PartitioningType partitioning_;
+}; // class HatFunctionMomentBasis<DomainFieldType, 1, ...>
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t rangeDim,
+          size_t rangeDimCols,
+          size_t fluxDim,
+          EntropyType entropy>
+constexpr size_t
+    HatFunctionMomentBasis<DomainFieldType, 1, RangeFieldType, rangeDim, rangeDimCols, fluxDim, entropy>::dimRange;
+
+template <class DomainFieldType, class RangeFieldType, size_t refinements, size_t fluxDim, EntropyType entropy>
+class HatFunctionMomentBasis<DomainFieldType, 3, RangeFieldType, refinements, 1, fluxDim, entropy>
+  : public MomentBasisInterface<DomainFieldType,
+                                3,
+                                RangeFieldType,
+                                OctaederStatistics<refinements>::num_vertices(),
+                                1,
+                                fluxDim,
+                                entropy>
+{
+public:
+  static constexpr size_t dimDomain = 3;
+  static constexpr size_t dimRange = OctaederStatistics<refinements>::num_vertices();
+  static constexpr size_t dimRangeCols = 1;
+  static constexpr size_t dimFlux = fluxDim;
+  static constexpr size_t num_refinements = refinements;
+
+private:
+  using BaseType =
+      MomentBasisInterface<DomainFieldType, dimDomain, RangeFieldType, dimRange, dimRangeCols, dimFlux, entropy>;
+  using ThisType = HatFunctionMomentBasis;
+
+public:
+  using TriangulationType = typename BaseType::SphericalTriangulationType;
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicRangeType;
+  using typename BaseType::MatrixType;
+  using typename BaseType::MergedQuadratureIterator;
+  using typename BaseType::QuadraturesType;
+  using typename BaseType::RangeType;
+  using typename BaseType::StringifierType;
+  using typename BaseType::VisualizerType;
+  using LocalMatrixType = XT::Common::FieldMatrix<RangeFieldType, 3, 3>;
+
+  using BaseType::barycentre_rule;
+  using BaseType::is_negative;
+
+  static size_t default_quad_order()
+  {
+    return refinements == 0 ? 15 : 9;
+  }
+
+  using BaseType::default_quad_refinements;
+
+  HatFunctionMomentBasis(const QuadraturesType& quadratures)
+    : BaseType(refinements, quadratures)
+  {
+    assert(triangulation_.vertices().size() == dimRange);
+    BaseType::initialize_base_values();
+  }
+
+  HatFunctionMomentBasis(const TriangulationType& triangulation, const QuadraturesType& quadratures)
+    : BaseType(triangulation, quadratures)
+  {
+    assert(triangulation_.vertices().size() == dimRange);
+    BaseType::initialize_base_values();
+  }
+
+  HatFunctionMomentBasis(const size_t quad_refinements,
+                         const QuadratureRule<RangeFieldType, 2>& reference_quadrature_rule)
+    : BaseType(refinements)
+  {
+    quadratures_ = triangulation_.quadrature_rules(quad_refinements, reference_quadrature_rule);
+    assert(triangulation_.vertices().size() == dimRange);
+    BaseType::initialize_base_values();
+  }
+
+  HatFunctionMomentBasis(const size_t quad_order = default_quad_order(),
+                         const size_t quad_refinements = default_quad_refinements())
+    : BaseType(refinements)
+  {
+    const QuadratureRule<RangeFieldType, 2> reference_quadrature_rule =
+        XT::Data::FeketeQuadrature<DomainFieldType>::get(quad_order);
+    quadratures_ = triangulation_.quadrature_rules(quad_refinements, reference_quadrature_rule);
+    assert(triangulation_.vertices().size() == dimRange);
+    BaseType::initialize_base_values();
+  }
+
+  DynamicRangeType evaluate(const DomainType& v) const override
+  {
+    DynamicRangeType ret(dimRange, 0);
+    bool success = false;
+    // walk over faces
+    for (const auto& face : triangulation_.faces()) {
+      const auto& vertices = face->vertices();
+      DomainType barycentric_coords(0);
+      success = calculate_barycentric_coordinates(v, vertices, barycentric_coords);
+      if (success) {
+        for (size_t ii = 0; ii < 3; ++ii)
+          ret[vertices[ii]->index()] = barycentric_coords[ii];
+        break;
+      }
+    } // faces
+    assert(success);
+    return ret;
+  } // ... evaluate(...)
+
+  DynamicRangeType evaluate(const DomainType& v, const size_t face_index) const override final
+  {
+    DynamicRangeType ret(dimRange, 0);
+    auto barycentric_coords = evaluate_on_face(v, face_index);
+    const auto& vertices = triangulation_.faces()[face_index]->vertices();
+    for (size_t ii = 0; ii < 3; ++ii)
+      ret[vertices[ii]->index()] = barycentric_coords[ii];
+    return ret;
+  } // ... evaluate(...)
+
+  DomainType evaluate_on_face(const DomainType& v, const size_t face_index) const
+  {
+    DomainType ret(0);
+    const auto& face = triangulation_.faces()[face_index];
+    const auto& vertices = face->vertices();
+    bool success = calculate_barycentric_coordinates(v, vertices, ret);
+    assert(success);
+#ifdef NDEBUG
+    static_cast<void>(success);
+#endif
+    return ret;
+  } // ... evaluate(...)
+
+  static StringifierType stringifier()
+  {
+    return [](const RangeType& val) {
+      RangeFieldType psi(0);
+      for (const auto& entry : val)
+        psi += entry;
+      return XT::Common::to_string(psi, 15);
+    };
+  } // ... stringifier()
+
+  const TriangulationType& triangulation() const
+  {
+    return triangulation_;
+  }
+
+  RangeFieldType unit_ball_volume() const override final
+  {
+    return BaseType::unit_ball_volume_quad();
+  }
+
+  DynamicRangeType alpha_one() const override final
+  {
+    return DynamicRangeType(dimRange, 1.);
+  }
+
+  template <class Vec>
+  std::enable_if_t<XT::Common::is_vector<Vec>::value, void> alpha_one(Vec& ret) const
+  {
+    for (size_t ii = 0; ii < ret.size(); ++ii)
+      XT::Common::VectorAbstraction<Vec>::set_entry(ret, ii, 1.);
+  }
+
+  RangeFieldType density(const DynamicRangeType& u) const override final
+  {
+    return std::accumulate(u.begin(), u.end(), 0.);
+  }
+
+  template <class Vec>
+  std::enable_if_t<XT::Common::is_vector<Vec>::value && !std::is_same<Vec, DynamicRangeType>::value, RangeFieldType>
+  density(const Vec& u) const
+  {
+    RangeFieldType ret(0.);
+    for (size_t ii = 0; ii < u.size(); ++ii)
+      ret += XT::Common::VectorAbstraction<Vec>::get_entry(u, ii);
+    return ret;
+  }
+
+  std::string short_id() const override final
+  {
+    return "hf";
+  }
+
+  std::string mn_name() const override final
+  {
+    return "hfm" + XT::Common::to_string(dimRange);
+  }
+
+  std::string pn_name() const override final
+  {
+    return "hfp" + XT::Common::to_string(dimRange);
+  }
+
+  // get indices of all faces that contain point v
+  std::vector<size_t> get_face_indices(const DomainType& v) const
+  {
+    return triangulation_.get_face_indices(v);
+  }
+
+  // calculates <b(v) dirac(v-dirac_position)>
+  DynamicRangeType integrate_dirac_at(const DomainType& dirac_position) const
+  {
+    return evaluate(dirac_position);
+  }
+
+  using BaseType::u_iso;
+
+  template <class Vec>
+  std::enable_if_t<XT::Common::is_vector<Vec>::value, void> u_iso(Vec& ret) const
+  {
+    auto ret_range = u_iso();
+    using V = XT::Common::VectorAbstraction<Vec>;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      V::set_entry(ret, ii, ret_range[ii]);
+  }
+
+  void ensure_min_density(DynamicRangeType& u, const RangeFieldType min_density) const override final
+  {
+    const auto u_iso_min = u_iso() * min_density;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      if (u[ii] < u_iso_min[ii])
+        u[ii] = u_iso_min[ii];
+  }
+
+  void ensure_min_density(RangeType& u, const RangeFieldType min_density) const override final
+  {
+    const auto u_iso_min = u_iso() * min_density;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      if (u[ii] < u_iso_min[ii])
+        u[ii] = u_iso_min[ii];
+  }
+
+  virtual DynamicRangeType
+  get_moment_vector(const std::function<RangeFieldType(DomainType, bool)>& psi) const override final
+  {
+    DynamicRangeType ret(dimRange, 0.);
+    const auto merged_quads = XT::Data::merged_quadrature(quadratures_);
+    for (auto it = merged_quads.begin(); it != merged_quads.end(); ++it) {
+      const auto face_index = it.first_index();
+      const auto& vertices = triangulation_.faces()[face_index]->vertices();
+      const auto& quad_point = *it;
+      const auto& v = quad_point.position();
+      const auto val = evaluate_on_face(v, face_index);
+      const auto factor = psi(v, is_negative(it)) * quad_point.weight();
+      for (size_t ii = 0; ii < 3; ++ii)
+        ret[vertices[ii]->index()] += val[ii] * factor;
+    }
+    return ret;
+  }
+
+protected:
+  template <class VertexVectorType>
+  bool calculate_barycentric_coordinates(const DomainType& v, const VertexVectorType& vertices, DomainType& ret) const
+  {
+    if (XT::Common::FloatCmp::ne(v.two_norm2(), 1.))
+      DUNE_THROW(Dune::MathError, "Wrong input given!");
+    Dune::FieldMatrix<RangeFieldType, 3, 3> gradients(0);
+    for (size_t ii = 0; ii < 3; ++ii) {
+      // copy vertices to gradients
+      gradients[ii] = vertices[ii]->position();
+      const auto scalar_prod = v * gradients[ii];
+      // if v is not on the same half space of the sphere as the vertices, return false
+      // assumes the triangulation is fine enough that vertices[ii]*vertices[jj] >= 0 for all triangles
+      if (XT::Common::FloatCmp::lt(scalar_prod, 0.))
+        return false;
+      else if (XT::Common::FloatCmp::ge(scalar_prod, 1.)) {
+        ret *= 0.;
+        ret[ii] = 1.;
+        return true;
+      }
+      auto v_scaled = v;
+      v_scaled *= scalar_prod;
+      gradients[ii] -= v_scaled;
+      // scale with factor
+      auto denominator = std::sqrt(1. - std::pow(scalar_prod, 2));
+      if (std::isnan(denominator))
+        DUNE_THROW(Dune::MathError, "NaN in evaluation!");
+      if (std::isnan(std::acos(scalar_prod)))
+        DUNE_THROW(Dune::MathError, "wrong value of scalar_prod!");
+      gradients[ii] *= XT::Common::FloatCmp::eq(denominator, 0.) ? 0. : std::acos(scalar_prod) / denominator;
+    } // ii
+    // Calculate barycentric coordinates for 0 w.r.t to the points g_i = gradients[i]
+    // For that purpose, solve the overdetermined system  A (h0 h1)^T = b
+    // for the matrix A = (g_0-g_2 g_1-g_2) and the right-hand side b = -g_2.
+    // The solution is (A^T A)^{-1} A^T b.
+    // The third coordinate is calculated from the condition h0+h1+h2=1.
+    Dune::XT::Common::FieldMatrix<RangeFieldType, 3, 2> A;
+    Dune::XT::Common::FieldMatrix<RangeFieldType, 2, 3> AT;
+    Dune::XT::Common::FieldVector<RangeFieldType, 2> solution;
+    AT[0] = gradients[0];
+    AT[1] = gradients[1];
+    AT[0] -= gradients[2];
+    AT[1] -= gradients[2];
+    for (size_t ii = 0; ii < 3; ++ii)
+      for (size_t jj = 0; jj < 2; ++jj)
+        A[ii][jj] = AT[jj][ii];
+    Dune::XT::Common::FieldMatrix<RangeFieldType, 2, 2> AT_A = AT.rightmultiplyany(A);
+    gradients[2] *= -1;
+    Dune::XT::Common::FieldVector<RangeFieldType, 2> AT_b;
+    AT.mv(gradients[2], AT_b);
+    AT_A.solve(solution, AT_b);
+    ret[0] = solution[0];
+    ret[1] = solution[1];
+    ret[2] = 1. - ret[0] - ret[1];
+    if (XT::Common::FloatCmp::lt(ret[0], 0., 1e-14, 1e-14) || XT::Common::FloatCmp::lt(ret[1], 0., 1e-14, 1e-14)
+        || XT::Common::FloatCmp::lt(ret[2], 0., 1e-14, 1e-14))
+      return false;
+    // we already checked the values are close to 0, now remove negative values stemming from numerical inaccuracies
+    for (size_t ii = 0; ii < 3; ++ii)
+      if (ret[ii] < 0.)
+        ret[ii] = 0.;
+    return true;
+  } // bool calculate_barycentric_coordinates(...)
+
+  static std::vector<size_t> create_face_decomposition(const size_t num_faces, const size_t num_threads)
+  {
+    std::vector<size_t> ret(num_threads + 1);
+    for (size_t ii = 0; ii < num_threads; ++ii)
+      ret[ii] = num_faces / num_threads * ii;
+    ret[num_threads] = num_faces;
+    return ret;
+  }
+
+  virtual void parallel_quadrature(const QuadraturesType& quadratures,
+                                   MatrixType& matrix,
+                                   const size_t v_index,
+                                   const bool reflecting = false) const override final
+  {
+    const auto& faces = triangulation_.faces();
+    size_t num_threads = std::min(XT::Common::threadManager().max_threads(), faces.size());
+    const auto decomposition = create_face_decomposition(faces.size(), num_threads);
+    std::vector<std::thread> threads(num_threads);
+    // Launch a group of threads
+    std::vector<LocalMatrixType> local_matrices(faces.size(), LocalMatrixType(0.));
+    for (size_t ii = 0; ii < num_threads; ++ii)
+      threads[ii] = std::thread(&ThisType::calculate_in_thread_hat,
+                                this,
+                                std::ref(local_matrices),
+                                std::cref(quadratures),
+                                v_index,
+                                std::cref(decomposition),
+                                ii,
+                                reflecting);
+    // Join the threads with the main thread
+    for (size_t ii = 0; ii < num_threads; ++ii)
+      threads[ii].join();
+    // add local matrices
+    matrix *= 0.;
+    for (size_t ii = 0; ii < num_threads; ++ii) {
+      for (size_t face_index = decomposition[ii]; face_index < decomposition[ii + 1]; ++face_index) {
+        const auto& face = faces[face_index];
+        const auto& vertices = face->vertices();
+        for (size_t nn = 0; nn < 3; ++nn)
+          for (size_t mm = 0; mm < 3; ++mm)
+            matrix[vertices[nn]->index()][vertices[mm]->index()] += local_matrices[face_index][nn][mm];
+      } // faces
+    } // threads
+  } // void parallel_quadrature(...)
+
+  virtual void calculate_in_thread_hat(std::vector<LocalMatrixType>& local_matrices,
+                                       const QuadraturesType& quadratures,
+                                       const size_t v_index,
+                                       const std::vector<size_t>& decomposition,
+                                       const size_t ii,
+                                       const bool reflecting) const
+  {
+    const auto& reflected_indices = triangulation_.reflected_face_indices();
+    for (size_t face_index = decomposition[ii]; face_index < decomposition[ii + 1]; ++face_index) {
+      for (const auto& quad_point : quadratures[face_index]) {
+        const auto& v = quad_point.position();
+        const auto basis_evaluated = evaluate_on_face(v, face_index);
+        auto basis_reflected = basis_evaluated;
+        if (reflecting) {
+          auto v_reflected = v;
+          v_reflected[v_index] *= -1.;
+          const size_t reflected_index = reflected_indices.size() ? reflected_indices[face_index][v_index] : 0;
+          basis_reflected = evaluate_on_face(v_reflected, reflected_index);
+        }
+        const auto& weight = quad_point.weight();
+        const auto factor = (reflecting || v_index == size_t(-1)) ? 1. : v[v_index];
+        for (size_t nn = 0; nn < 3; ++nn)
+          for (size_t mm = 0; mm < 3; ++mm)
+            local_matrices[face_index][nn][mm] +=
+                basis_evaluated[nn] * (reflecting ? basis_reflected[mm] : basis_evaluated[mm]) * factor * weight;
+      } // quad_points
+    } // faces
+  } // void calculate_in_thread(...)
+
+  virtual void integrated_initializer_thread(DynamicRangeType& local_range,
+                                             const std::vector<MergedQuadratureIterator>& decomposition,
+                                             const size_t ii) const override final
+  {
+    const auto& faces = triangulation_.faces();
+    for (auto it = decomposition[ii]; it != decomposition[ii + 1]; ++it) {
+      const auto face_index = it.first_index();
+      const auto& vertices = faces[face_index]->vertices();
+      const auto& quad_point = *it;
+      DomainType basis_evaluated = evaluate_on_face(quad_point.position(), face_index);
+      basis_evaluated *= quad_point.weight();
+      for (size_t nn = 0; nn < 3; ++nn)
+        local_range[vertices[nn]->index()] += basis_evaluated[nn];
+    } // jj
+  } // void calculate_in_thread(...)
+
+  using BaseType::quadratures_;
+  using BaseType::triangulation_;
+}; // class HatFunctionMomentBasis<DomainFieldType, 3, ...>
+
+template <class DomainFieldType, class RangeFieldType, size_t rangeDim, size_t fluxDim, EntropyType entropy>
+constexpr size_t HatFunctionMomentBasis<DomainFieldType, 3, RangeFieldType, rangeDim, 1, fluxDim, entropy>::dimRange;
+
+template <class DomainFieldType, class RangeFieldType, size_t rangeDim, size_t fluxDim, EntropyType entropy>
+constexpr size_t
+    HatFunctionMomentBasis<DomainFieldType, 3, RangeFieldType, rangeDim, 1, fluxDim, entropy>::num_refinements;
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_HATFUNCTIONS_HH
diff --git a/dune/gdt/test/momentmodels/basisfunctions/interface.hh b/dune/gdt/test/momentmodels/basisfunctions/interface.hh
new file mode 100644
index 0000000000000000000000000000000000000000..7502c0c131ea85ec2f7e8566099249edbe77a408
--- /dev/null
+++ b/dune/gdt/test/momentmodels/basisfunctions/interface.hh
@@ -0,0 +1,578 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner (2019)
+
+#ifndef DUNE_GDT_MOMENTMODELS_BASISFUNCTIONS_INTERFACE_HH
+#define DUNE_GDT_MOMENTMODELS_BASISFUNCTIONS_INTERFACE_HH
+
+#include <memory>
+#include <vector>
+#include <string>
+
+#include <dune/xt/common/math.hh>
+#include <dune/xt/common/string.hh>
+#include <dune/xt/common/tuple.hh>
+
+#include <dune/xt/data/quadratures.hh>
+#include <dune/xt/data/spherical_quadratures.hh>
+
+#include <dune/gdt/discretefunction/default.hh>
+#include <dune/gdt/test/momentmodels/triangulation.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+// see https://en.wikipedia.org/wiki/Tridiagonal_matrix#Inversion
+template <class FieldType, int rows>
+Dune::DynamicMatrix<FieldType> tridiagonal_matrix_inverse(const DynamicMatrix<FieldType>& matrix)
+{
+  typedef Dune::DynamicMatrix<FieldType> MatrixType;
+  size_t cols = rows;
+#ifndef NDEBUG
+  for (size_t rr = 0; rr < rows; ++rr)
+    for (size_t cc = 0; cc < cols; ++cc)
+      if ((cc > rr + 1 || cc + 1 < rr) && XT::Common::FloatCmp::ne(matrix[rr][cc], 0.))
+        DUNE_THROW(XT::Common::Exceptions::you_are_using_this_wrong, "Matrix has to be tridiagonal!");
+#endif // NDEBUG
+  MatrixType ret(rows, rows, 0);
+  Dune::FieldVector<FieldType, rows + 1> a(0), b(0), c(0), theta(0);
+  Dune::FieldVector<FieldType, rows + 2> phi(0);
+  for (size_t ii = 1; ii < rows + 1; ++ii) {
+    a[ii] = matrix[ii - 1][ii - 1];
+    if (ii < rows) {
+      b[ii] = matrix[ii - 1][ii];
+      c[ii] = matrix[ii][ii - 1];
+    }
+  }
+  theta[0] = 1;
+  theta[1] = a[1];
+  for (size_t ii = 2; ii < rows + 1; ++ii)
+    theta[ii] = a[ii] * theta[ii - 1] - b[ii - 1] * c[ii - 1] * theta[ii - 2];
+  phi[rows + 1] = 1;
+  phi[rows] = a[rows];
+  for (size_t ii = rows - 1; ii > 0; --ii)
+    phi[ii] = a[ii] * phi[ii + 1] - b[ii] * c[ii] * phi[ii + 2];
+  for (size_t ii = 1; ii < rows + 1; ++ii) {
+    for (size_t jj = 1; jj < cols + 1; ++jj) {
+      if (ii == jj)
+        ret[ii - 1][jj - 1] = theta[ii - 1] * phi[jj + 1] / theta[rows];
+      else if (ii < jj) {
+        ret[ii - 1][jj - 1] = std::pow(-1, ii + jj) * theta[ii - 1] * phi[jj + 1] / theta[rows];
+        for (size_t kk = ii; kk < jj; ++kk)
+          ret[ii - 1][jj - 1] *= b[kk];
+      } else if (ii > jj) {
+        ret[ii - 1][jj - 1] = std::pow(-1, ii + jj) * theta[jj - 1] * phi[ii + 1] / theta[rows];
+        for (size_t kk = jj; kk < ii; ++kk)
+          ret[ii - 1][jj - 1] *= c[kk];
+      }
+    } // jj
+  } // ii
+#ifndef NDEBUG
+  for (size_t ii = 0; ii < rows; ++ii)
+    for (size_t jj = 0; jj < cols; ++jj)
+      if (std::isnan(ret[ii][jj]) || std::isinf(ret[ii][jj]))
+        DUNE_THROW(Dune::MathError, "Inversion of triangular matrix failed!");
+#endif
+  return ret;
+} // ... tridiagonal_matrix_inverse(...)
+
+// After each refinement step:
+// num_vertices_new = num_vertices_old + num_intersections_old
+// num_intersections_new = 2*num_intersections_old + 3*num_faces_old
+// num_faces_new = 4*num_faces_old
+// Initially, there are 6 vertices, 12 intersections and 8 faces.
+template <size_t refinements>
+struct OctaederStatistics
+{
+  static constexpr size_t constexpr_pow(size_t base, size_t exponent)
+  {
+    return (exponent == 0) ? 1 : (base * constexpr_pow(base, exponent - 1));
+  }
+
+  static constexpr size_t num_faces()
+  {
+    return 8 * constexpr_pow(4, refinements);
+  }
+
+  static constexpr size_t num_intersections()
+  {
+    return 2 * OctaederStatistics<refinements - 1>::num_intersections()
+           + 3 * OctaederStatistics<refinements - 1>::num_faces();
+  }
+
+  static constexpr size_t num_vertices()
+  {
+    return OctaederStatistics<refinements - 1>::num_vertices()
+           + OctaederStatistics<refinements - 1>::num_intersections();
+  }
+};
+
+template <>
+struct OctaederStatistics<0>
+{
+  static constexpr size_t num_faces()
+  {
+    return 8;
+  }
+
+  static constexpr size_t num_intersections()
+  {
+    return 12;
+  }
+
+  static constexpr size_t num_vertices()
+  {
+    return 6;
+  }
+};
+
+
+enum class EntropyType
+{
+  MaxwellBoltzmann,
+  BoseEinstein
+};
+
+
+template <class DomainFieldImp,
+          size_t domainDim,
+          class RangeFieldImp,
+          size_t rangeDim,
+          size_t rangeDimCols = 1,
+          size_t fluxDim = domainDim,
+          EntropyType entrpy = EntropyType::MaxwellBoltzmann>
+class MomentBasisInterface
+{
+public:
+  static const size_t dimDomain = domainDim;
+  static const size_t dimRange = rangeDim;
+  static const size_t dimRangeCols = rangeDimCols;
+  static const size_t dimFlux = fluxDim;
+  static const size_t d = domainDim;
+  static const size_t r = rangeDim;
+  static const size_t rC = rangeDimCols;
+  static const size_t d_flux = fluxDim;
+  static const EntropyType entropy = entrpy;
+  using D = DomainFieldImp;
+  using R = RangeFieldImp;
+
+  using DomainFieldType = DomainFieldImp;
+  using DomainType = FieldVector<DomainFieldType, dimDomain>;
+  using RangeFieldType = RangeFieldImp;
+  using MatrixType = DynamicMatrix<RangeFieldType>;
+  using RangeType = typename XT::Functions::RangeTypeSelector<RangeFieldType, dimRange, dimRangeCols>::type;
+  using DynamicRangeType = DynamicVector<RangeFieldType>;
+  using QuadratureType = QuadratureRule<DomainFieldType, dimDomain>;
+  using QuadraturesType = std::vector<QuadratureType>;
+  using VisualizerType = XT::Functions::VisualizerInterface<dimRange, dimRangeCols, RangeFieldType>;
+  using StringifierType = std::function<std::string(const RangeType&)>;
+  using Partitioning1dType = std::vector<RangeFieldType>;
+  using SphericalTriangulationType = SphericalTriangulation<RangeFieldType>;
+  using MergedQuadratureIterator =
+      typename XT::Data::MergedQuadrature<RangeFieldType, dimDomain>::MergedQuadratureIterator;
+
+  static size_t default_quad_order()
+  {
+    DUNE_THROW(Dune::NotImplemented, "Please overwrite this function in derived classes!");
+    return 0;
+  }
+
+  static size_t default_quad_refinements()
+  {
+    return 0;
+  }
+
+  MomentBasisInterface(const QuadraturesType& quadratures = QuadraturesType())
+    : triangulation_(stored_triangulation_)
+    , quadratures_(quadratures)
+  {}
+
+  MomentBasisInterface(const size_t refinements, const QuadraturesType& quadratures = QuadraturesType())
+    : stored_triangulation_(refinements)
+    , triangulation_(stored_triangulation_)
+    , quadratures_(quadratures)
+  {}
+
+  MomentBasisInterface(const SphericalTriangulationType& triangulation, const QuadraturesType& quadratures)
+    : triangulation_(triangulation)
+    , quadratures_(quadratures)
+  {}
+
+  virtual ~MomentBasisInterface() {}
+
+  virtual DynamicRangeType evaluate(const DomainType& v) const = 0;
+
+  virtual DynamicRangeType evaluate(const DomainType& v, const size_t /*face_index*/) const
+  {
+    return evaluate(v);
+  }
+
+  // returns <b>, where b is the basis functions vector
+  virtual DynamicRangeType integrated() const
+  {
+    return integrated_;
+  }
+
+  virtual MatrixType mass_matrix() const
+  {
+    MatrixType M(dimRange, dimRange, 0.);
+    parallel_quadrature(quadratures_, M, size_t(-1));
+    return M;
+  } // ... mass_matrix()
+
+  virtual MatrixType mass_matrix_inverse() const
+  {
+    auto ret = mass_matrix();
+    ret.invert();
+    return ret;
+  }
+
+  virtual DynamicRangeType get_moment_vector(const std::function<RangeFieldType(DomainType, bool)>& psi) const
+  {
+    DynamicRangeType ret(dimRange, 0.);
+    const auto merged_quads = XT::Data::merged_quadrature(quadratures());
+    for (auto it = merged_quads.begin(); it != merged_quads.end(); ++it) {
+      const auto& quad_point = *it;
+      const auto& v = quad_point.position();
+      ret += evaluate(v, it.first_index()) * psi(v, is_negative(it)) * quad_point.weight();
+    }
+    return ret;
+  }
+
+  virtual bool is_negative(const MergedQuadratureIterator& /*it*/) const
+  {
+    return false;
+  }
+
+  virtual FieldVector<MatrixType, dimFlux> flux_matrix() const
+  {
+    FieldVector<MatrixType, dimFlux> B(MatrixType(dimRange, dimRange, 0));
+    for (size_t dd = 0; dd < dimFlux; ++dd)
+      parallel_quadrature(quadratures_, B[dd], dd);
+    return B;
+  }
+
+  virtual std::unique_ptr<VisualizerType> visualizer() const
+  {
+    return std::make_unique<XT::Functions::GenericVisualizer<dimRange, dimRangeCols, RangeFieldType>>(
+        1, [this](const int /*comp*/, const RangeType& val) { return this->density(val); });
+  }
+
+  // returns V M^-1 where the matrix V has entries <v h_i h_j>_- and <v h_i h_j>_+
+  virtual FieldVector<FieldVector<MatrixType, 2>, dimFlux> kinetic_flux_matrices() const
+  {
+    const auto M = std::make_unique<XT::Common::FieldMatrix<RangeFieldType, dimRange, dimRange>>(mass_matrix());
+    FieldVector<FieldVector<MatrixType, 2>, dimFlux> B_kinetic(
+        FieldVector<MatrixType, 2>(MatrixType(dimRange, dimRange, 0.)));
+    MatrixType tmp_mat(dimRange, dimRange, 0.);
+    for (size_t dd = 0; dd < dimFlux; ++dd) {
+      QuadraturesType neg_quadratures(quadratures_.size());
+      QuadraturesType pos_quadratures(quadratures_.size());
+      get_pos_and_neg_quadratures(neg_quadratures, pos_quadratures, dd);
+      parallel_quadrature(neg_quadratures, tmp_mat, dd);
+      for (size_t rr = 0; rr < dimRange; ++rr)
+        M->solve(B_kinetic[dd][0][rr], tmp_mat[rr]);
+      parallel_quadrature(pos_quadratures, tmp_mat, dd);
+      for (size_t rr = 0; rr < dimRange; ++rr)
+        M->solve(B_kinetic[dd][1][rr], tmp_mat[rr]);
+    } // dd
+    return B_kinetic;
+  } // ... kinetic_flux_matrices()
+
+  virtual MatrixType reflection_matrix(const DomainType& n) const
+  {
+    MatrixType ret(dimRange, dimRange, 0);
+    size_t direction;
+    for (size_t ii = 0; ii < dimDomain; ++ii) {
+      if (XT::Common::FloatCmp::ne(n[ii], 0.)) {
+        direction = ii;
+        if (XT::Common::FloatCmp::ne(std::abs(n[ii]), 1.))
+          DUNE_THROW(NotImplemented, "Implemented only for +-e_i where e_i is the i-th canonical basis vector!");
+      }
+    }
+    parallel_quadrature(quadratures_, ret, direction, true);
+    ret.rightmultiply(mass_matrix_inverse());
+    // We need the exact reflection matrix to guarantee Q-realizability, the matrix should only contain 0, +-1, so just
+    // ensure it does
+    for (size_t ii = 0; ii < dimRange; ++ii) {
+      for (size_t jj = 0; jj < dimRange; ++jj) {
+        if (std::abs(ret[ii][jj]) > 0.99 && std::abs(ret[ii][jj]) < 1.01)
+          ret[ii][jj] = ret[ii][jj] / std::abs(ret[ii][jj]);
+        else if (std::abs(ret[ii][jj]) < 0.01)
+          ret[ii][jj] = 0;
+        else
+          DUNE_THROW(Dune::MathError, "Invalid reflection matrix!");
+      }
+    }
+    return ret;
+  }
+
+  // return alpha s.t. alpha_one * b(v) == 1 for all v
+  virtual DynamicRangeType alpha_one() const = 0;
+
+  // returns alpha s.t. the distribution is isotropic and has density rho
+  virtual DynamicRangeType alpha_iso(const RangeFieldType rho = 1.) const
+  {
+    const auto scale_factor = rho / density(integrated());
+    if (entropy == EntropyType::MaxwellBoltzmann)
+      return alpha_one() * std::log(scale_factor);
+    else
+      return alpha_one() * std::log(scale_factor / (scale_factor + 1));
+  }
+
+  virtual RangeFieldType density(const DynamicRangeType& u) const = 0;
+
+  virtual void ensure_min_density(DynamicRangeType& u, const RangeFieldType min_density) const
+  {
+    if (density(u) < min_density)
+      u = u_iso() * min_density;
+  }
+
+  virtual void ensure_min_density(RangeType& u, const RangeFieldType min_density) const
+  {
+    if (density(u) < min_density) {
+      u = u_iso();
+      u *= min_density;
+    }
+  }
+
+  // Volume of integration domain. For the Mn models it is important that u_iso has density 1. If the basis is exactly
+  // integrated, we thus use the exact unit ball volume. If the basis is only integrated by quadrature, we have to use
+  // <1> as volume to get a density of 1.
+  virtual RangeFieldType unit_ball_volume() const
+  {
+    return unit_ball_volume_exact();
+  }
+
+  virtual DynamicRangeType u_iso() const
+  {
+    return u_iso_;
+  }
+
+  virtual std::string short_id() const = 0;
+
+  virtual std::string mn_name() const = 0;
+
+  virtual std::string pn_name() const = 0;
+
+  static QuadratureRule<RangeFieldType, 2> barycentre_rule()
+  {
+    Dune::QuadratureRule<RangeFieldType, 2> ret;
+    ret.push_back(Dune::QuadraturePoint<RangeFieldType, 2>({1. / 3., 1. / 3.}, 0.5));
+    return ret;
+  }
+
+  static Partitioning1dType create_1d_partitioning(const size_t num_intervals)
+  {
+    Partitioning1dType ret(num_intervals + 1);
+    for (size_t ii = 0; ii <= num_intervals; ++ii)
+      ret[ii] = -1. + (2. * ii) / num_intervals;
+    return ret;
+  }
+
+  const QuadraturesType& quadratures() const
+  {
+    return quadratures_;
+  }
+
+  // A Gauss-Lobatto quadrature on each interval
+  template <size_t dD = dimDomain>
+  static std::enable_if_t<dD == 1, QuadraturesType> gauss_lobatto_quadratures(const size_t num_intervals,
+                                                                              const size_t quad_order,
+                                                                              const size_t additional_refinements = 0)
+  {
+    QuadraturesType ret(num_intervals);
+    const auto quads_per_interval = std::pow(2, additional_refinements);
+    const auto quadrature_boundaries = create_1d_partitioning(num_intervals * quads_per_interval);
+    // quadrature on reference interval [0, 1]
+    const auto reference_quadrature = XT::Data::GaussLobattoQuadrature<DomainFieldType>::get(quad_order);
+    // map to quadrature on interval [a, b] by
+    // x_i -> (1-x_i) a + x_i b
+    // w_i -> w_i * (b-a)
+    for (size_t ii = 0; ii < num_intervals; ++ii) {
+      for (size_t jj = 0; jj < quads_per_interval; ++jj) {
+        for (const auto& quad_point : reference_quadrature) {
+          const auto& x = quad_point.position()[0];
+          const auto& a = quadrature_boundaries[ii * quads_per_interval + jj];
+          const auto& b = quadrature_boundaries[ii * quads_per_interval + jj + 1];
+          const auto pos = (1 - x) * a + x * b;
+          const auto weight = quad_point.weight() * (b - a);
+          ret[ii].emplace_back(pos, weight);
+        } // quad_points
+      } // jj
+    } // quad_cells
+    return ret;
+  }
+
+  template <size_t dD = dimDomain>
+  static std::enable_if_t<dD == 3, QuadraturesType> lebedev_quadrature(const size_t quad_order)
+  {
+    return QuadraturesType(1, XT::Data::LebedevQuadrature<DomainFieldType, true>::get(quad_order));
+  }
+
+  static RangeFieldType unit_ball_volume_exact()
+  {
+    if (dimDomain == 1)
+      return 2;
+    else if (dimDomain == 2)
+      return 2 * M_PI;
+    else if (dimDomain == 3)
+      return 4 * M_PI;
+    else {
+      DUNE_THROW(NotImplemented, "");
+      return 0;
+    }
+  }
+
+  RangeFieldType unit_ball_volume_quad() const
+  {
+    RangeFieldType ret(0.);
+    for (const auto& quad_point : XT::Data::merged_quadrature(quadratures_))
+      ret += quad_point.weight();
+    return ret;
+  }
+
+protected:
+  void initialize_base_values()
+  {
+    integrated_ = integrated_initializer(quadratures_);
+    u_iso_ = integrated() / density(integrated());
+  }
+
+  void
+  get_pos_and_neg_quadratures(QuadraturesType& neg_quadratures, QuadraturesType& pos_quadratures, const size_t dd) const
+  {
+    for (size_t ii = 0; ii < quadratures_.size(); ++ii) {
+      for (const auto& quad_point : quadratures_[ii]) {
+        const auto& v = quad_point.position();
+        const auto& weight = quad_point.weight();
+        // if v[dd] = 0 the quad_point does not contribute to the integral
+        if (v[dd] > 0.)
+          pos_quadratures[ii].emplace_back(v, weight);
+        else if (v[dd] < 0.)
+          neg_quadratures[ii].emplace_back(v, weight);
+      } // quad_points
+    } // quadratures
+  }
+
+  static std::vector<MergedQuadratureIterator> create_decomposition(const QuadraturesType& quadratures,
+                                                                    const size_t num_threads)
+  {
+    const size_t size = XT::Data::merged_quadrature(quadratures).size();
+    std::vector<MergedQuadratureIterator> ret(num_threads + 1);
+    for (size_t ii = 0; ii < num_threads; ++ii)
+      ret[ii] = XT::Data::merged_quadrature(quadratures).iterator(size / num_threads * ii);
+    ret[num_threads] = XT::Data::merged_quadrature(quadratures).iterator(size);
+    return ret;
+  }
+
+  virtual void parallel_quadrature(const QuadraturesType& quadratures,
+                                   MatrixType& matrix,
+                                   const size_t v_index,
+                                   const bool reflecting = false) const
+  {
+    size_t num_threads =
+        std::min(XT::Common::threadManager().max_threads(), XT::Data::merged_quadrature(quadratures).size());
+    auto decomposition = create_decomposition(quadratures, num_threads);
+    std::vector<std::thread> threads(num_threads);
+    // Launch a group of threads
+    std::vector<MatrixType> local_matrices(num_threads, MatrixType(matrix.N(), matrix.M(), 0.));
+    for (size_t ii = 0; ii < num_threads; ++ii)
+      threads[ii] = std::thread(&MomentBasisInterface::calculate_in_thread,
+                                this,
+                                std::ref(local_matrices[ii]),
+                                v_index,
+                                std::cref(decomposition),
+                                ii,
+                                reflecting);
+    // Join the threads with the main thread
+    for (size_t ii = 0; ii < num_threads; ++ii)
+      threads[ii].join();
+    // add local matrices
+    matrix *= 0.;
+    for (size_t ii = 0; ii < num_threads; ++ii)
+      matrix += local_matrices[ii];
+  } // void parallel_quadrature(...)
+
+  virtual void calculate_in_thread(MatrixType& local_matrix,
+                                   const size_t v_index,
+                                   const std::vector<MergedQuadratureIterator>& decomposition,
+                                   const size_t ii,
+                                   const bool reflecting) const
+  {
+    const auto& reflected_indices = triangulation_.reflected_face_indices();
+    for (auto it = decomposition[ii]; it != decomposition[ii + 1]; ++it) {
+      const auto& quad_point = *it;
+      const auto& v = quad_point.position();
+      const auto basis_evaluated = evaluate(v, it.first_index());
+      auto basis_reflected = basis_evaluated;
+      if (reflecting) {
+        auto v_reflected = v;
+        v_reflected[v_index] *= -1.;
+        // If the basis functions have a triangulation, get index of reflected triangle. Otherwise set to 0, will be
+        // ignored.
+        const size_t reflected_index = reflected_indices.size() ? reflected_indices[it.first_index()][v_index] : 0;
+        basis_reflected = evaluate(v_reflected, reflected_index);
+      }
+      const auto& weight = quad_point.weight();
+      const auto factor = (reflecting || v_index == size_t(-1)) ? 1. : v[v_index];
+      for (size_t nn = 0; nn < local_matrix.N(); ++nn)
+        for (size_t mm = 0; mm < local_matrix.M(); ++mm)
+          local_matrix[nn][mm] +=
+              basis_evaluated[nn] * (reflecting ? basis_reflected[mm] : basis_evaluated[mm]) * factor * weight;
+    }
+  } // void calculate_in_thread(...)
+
+  virtual DynamicRangeType integrated_initializer(const QuadraturesType& quadratures) const
+  {
+    size_t num_threads =
+        std::min(XT::Common::threadManager().max_threads(), XT::Data::merged_quadrature(quadratures).size());
+    auto decomposition = create_decomposition(quadratures, num_threads);
+    std::vector<std::thread> threads(num_threads);
+    std::vector<DynamicRangeType> local_vectors(num_threads, DynamicRangeType(dimRange, 0.));
+    for (size_t ii = 0; ii < num_threads; ++ii)
+      threads[ii] = std::thread(&MomentBasisInterface::integrated_initializer_thread,
+                                this,
+                                std::ref(local_vectors[ii]),
+                                std::cref(decomposition),
+                                ii);
+    // Join the threads with the main thread
+    for (size_t ii = 0; ii < num_threads; ++ii)
+      threads[ii].join();
+    // add local matrices
+    DynamicRangeType ret(dimRange, 0.);
+    for (size_t ii = 0; ii < num_threads; ++ii)
+      ret += local_vectors[ii];
+    return ret;
+  }
+
+  virtual void integrated_initializer_thread(DynamicRangeType& local_range,
+                                             const std::vector<MergedQuadratureIterator>& decomposition,
+                                             const size_t ii) const
+  {
+    for (auto it = decomposition[ii]; it != decomposition[ii + 1]; ++it) {
+      const auto& quad_point = *it;
+      auto basis_evaluated = evaluate(quad_point.position(), it.first_index());
+      basis_evaluated *= quad_point.weight();
+      local_range += basis_evaluated;
+    } // jj
+  } // void calculate_in_thread(...)
+
+  SphericalTriangulationType stored_triangulation_;
+  const SphericalTriangulationType& triangulation_;
+  QuadraturesType quadratures_;
+  DynamicRangeType integrated_;
+  DynamicRangeType u_iso_;
+};
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_MOMENTMODELS_BASISFUNCTIONS_INTERFACE_HH
diff --git a/dune/gdt/test/momentmodels/basisfunctions/legendre.hh b/dune/gdt/test/momentmodels/basisfunctions/legendre.hh
new file mode 100644
index 0000000000000000000000000000000000000000..2f7fba60e061ef974bb48e5dcb83134c9d51187f
--- /dev/null
+++ b/dune/gdt/test/momentmodels/basisfunctions/legendre.hh
@@ -0,0 +1,254 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_LEGENDRE_HH
+#define DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_LEGENDRE_HH
+
+#include "interface.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t order,
+          size_t dimRangeCols = 1,
+          EntropyType entropy = EntropyType::MaxwellBoltzmann>
+class LegendreMomentBasis
+  : public MomentBasisInterface<DomainFieldType, 1, RangeFieldType, order + 1, dimRangeCols, 1, entropy>
+{
+public:
+  static const size_t dimDomain = 1;
+  static const size_t dimRange = order + 1;
+  static const size_t num_intervals = size_t(-1);
+
+private:
+  typedef MomentBasisInterface<DomainFieldType, dimDomain, RangeFieldType, dimRange, dimRangeCols, 1, entropy> BaseType;
+
+public:
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicRangeType;
+  using typename BaseType::MatrixType;
+  using typename BaseType::QuadraturesType;
+  using typename BaseType::RangeType;
+  using typename BaseType::SphericalTriangulationType;
+  using typename BaseType::StringifierType;
+
+  LegendreMomentBasis(const QuadraturesType& quadratures)
+    : BaseType(quadratures)
+  {
+    BaseType::initialize_base_values();
+  }
+
+  LegendreMomentBasis(const SphericalTriangulationType& /*triangulation*/, const QuadraturesType& quadratures)
+    : LegendreMomentBasis(quadratures)
+  {}
+
+  static size_t default_quad_order()
+  {
+    return 2 * order + 40;
+  }
+
+  static size_t default_quad_refinements()
+  {
+    return 1;
+  }
+
+  LegendreMomentBasis(const size_t quad_order = default_quad_order(),
+                      const size_t quad_refinements = default_quad_refinements())
+    : BaseType(BaseType::gauss_lobatto_quadratures(std::pow(2, quad_refinements), quad_order))
+  {
+    BaseType::initialize_base_values();
+  }
+
+  static std::string static_id()
+  {
+    return "legendre";
+  }
+
+  using BaseType::evaluate;
+
+  DynamicRangeType evaluate(const DomainType& v) const override
+  {
+    DynamicRangeType ret(dimRange);
+    ret[0] = 1.;
+    if (dimRange > 1)
+      ret[1] = v[0];
+    for (size_t ii = 2; ii < dimRange; ++ii)
+      ret[ii] = ((2. * ii - 1.) * v[0] * ret[ii - 1] - (ii - 1.) * ret[ii - 2]) / ii;
+    return ret;
+  } // ... evaluate(...)
+
+  DynamicRangeType integrated_exactly() const
+  {
+    DynamicRangeType ret(dimRange, 0.);
+    ret[0] = 2;
+    return ret;
+  }
+
+  MatrixType mass_matrix() const override
+  {
+    MatrixType M(dimRange, dimRange, 0.);
+    for (size_t rr = 0; rr < dimRange; ++rr)
+      M[rr][rr] = 2. / (2. * rr + 1.);
+    return M;
+  }
+
+  MatrixType mass_matrix_inverse() const override
+  {
+    MatrixType Minv(dimRange, dimRange, 0.);
+    for (size_t rr = 0; rr < dimRange; ++rr)
+      Minv[rr][rr] = (2. * rr + 1.) / 2.;
+    return Minv;
+  }
+
+  FieldVector<MatrixType, dimDomain> flux_matrix() const override
+  {
+    MatrixType B(dimRange, dimRange, 0);
+    for (size_t rr = 0; rr < dimRange; ++rr) {
+      for (size_t cc = 0; cc < dimRange; ++cc) {
+        if (cc == rr - 1)
+          B[rr][cc] = 2. * rr / (4. * rr * rr - 1.);
+        else if (cc == rr + 1)
+          B[rr][cc] = (2. * rr + 2.) / ((2. * rr + 1.) * (2. * rr + 3));
+      }
+    }
+    return B;
+  }
+
+  // returns V M^-1 where the matrix V has entries <v h_i h_j>_- and <v h_i h_j>_+
+  FieldVector<FieldVector<MatrixType, 2>, 1> kinetic_flux_matrices() const override final
+  {
+    FieldVector<FieldVector<MatrixType, 2>, 1> ret(FieldVector<MatrixType, 2>(MatrixType(dimRange, dimRange, 0.)));
+    auto mm_with_v = flux_matrix();
+    auto& ret_neg = ret[0][0];
+    auto& ret_pos = ret[0][1];
+    MatrixType mass_matrix_pos(dimRange + 1, dimRange + 1, 0.); // we need to calculate up to P_N
+    size_t N = dimRange;
+    // calculate <P_n P_m>_+ first
+    for (size_t nn = 0; nn <= N; ++nn) {
+      for (size_t mm = 0; mm <= N; ++mm) {
+        if ((nn + mm) % 2) { // nn and mm have different parity
+          if (nn % 2) // n odd
+            mass_matrix_pos[nn][mm] = fmn(static_cast<int>(mm), static_cast<int>(nn));
+          else // n even
+            mass_matrix_pos[nn][mm] = fmn(static_cast<int>(nn), static_cast<int>(mm));
+        } else { // nn and mm are both odd or both even
+          mass_matrix_pos[nn][mm] = (nn == mm) ? 1. / (2. * nn + 1.) : 0.;
+        }
+      } // mm
+    } // nn
+    // now calculate <v P_n P_m>_+ and <v P_n P_m>_-
+    for (size_t nn = 0; nn < N; ++nn) {
+      for (size_t mm = 0; mm < N; ++mm) {
+        ret_pos[nn][mm] = (nn + 1.) / (2. * nn + 1.) * mass_matrix_pos[nn + 1][mm]
+                          + ((nn > 0) ? nn / (2. * nn + 1.) * mass_matrix_pos[nn - 1][mm] : 0.);
+        ret_neg[nn][mm] = mm_with_v[0][nn][mm] - ret_pos[nn][mm];
+      } // mm
+    } // nn
+    // apply M^{-1} from the right
+    ret_neg.rightmultiply(mass_matrix_inverse());
+    ret_pos.rightmultiply(mass_matrix_inverse());
+    return ret;
+  }
+
+  MatrixType reflection_matrix(const DomainType& n) const override final
+  {
+    MatrixType ret(dimRange, dimRange, 0);
+    for (size_t ii = 0; ii < dimDomain; ++ii)
+      if (XT::Common::FloatCmp::ne(n[ii], 0.))
+        if (XT::Common::FloatCmp::ne(std::abs(n[ii]), 1.))
+          DUNE_THROW(NotImplemented, "Implemented only for +-e_i where e_i is the i-th canonical basis vector!");
+    const auto mass_mat = mass_matrix();
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      for (size_t jj = 0; jj < dimRange; ++jj)
+        ret[ii][jj] = std::pow(-1, ii) * mass_mat[ii][jj];
+    ret.rightmultiply(mass_matrix_inverse());
+    return ret;
+  }
+
+  MatrixType S() const
+  {
+    MatrixType S(dimRange, dimRange, 0);
+    for (size_t rr = 0; rr < dimRange; ++rr)
+      S[rr][rr] = -2. * rr * (rr + 1.) / (2 * rr + 1);
+    return S;
+  }
+
+  static StringifierType stringifier()
+  {
+    return [](const RangeType& val) { return XT::Common::to_string(val[0], 15); };
+  } // ... stringifier()
+
+  DynamicRangeType alpha_one() const override final
+  {
+    DynamicRangeType ret(dimRange, 0.);
+    ret[0] = 1.;
+    return ret;
+  }
+
+  RangeFieldType density(const DynamicRangeType& u) const override final
+  {
+    return u[0];
+  }
+
+  std::string short_id() const override final
+  {
+    return "";
+  }
+
+  std::string mn_name() const override final
+  {
+    return "m" + XT::Common::to_string(order);
+  }
+
+  std::string pn_name() const override final
+  {
+    return "p" + XT::Common::to_string(order);
+  }
+
+private:
+  static RangeFieldType fmn(const int m, const int n)
+  {
+    assert(!(m % 2));
+    assert(n % 2);
+    // The factorials overflow for large m or n, so do it more complicated
+    // return (std::pow(-1., (m + n + 1) / 2) * XT::Common::factorial(m) * XT::Common::factorial(n))
+    //       / (std::pow(2., m + n - 1) * (m - n) * (m + n + 1) * std::pow(XT::Common::factorial(m / 2), 2)
+    //          * std::pow(XT::Common::factorial((n - 1) / 2), 2));
+
+    RangeFieldType ret = 1;
+    int m_factor = m;
+    int n_factor = n;
+    int m_divisor = m / 2;
+    int n_divisor = (n - 1) / 2;
+    size_t max_mn = static_cast<size_t>(std::max(m, n));
+    FieldVector<std::vector<RangeFieldType>, 4> factors((std::vector<RangeFieldType>(max_mn)));
+    assert(std::max(m, n) >= 0);
+    for (size_t ii = 0; ii < max_mn; ++ii) {
+      factors[0][ii] = m_factor > 0 ? m_factor-- : 1.;
+      factors[1][ii] = n_factor > 0 ? n_factor-- : 1.;
+      factors[2][ii] = m_divisor > 0 ? 1. / std::pow(m_divisor--, 2) : 1.;
+      factors[3][ii] = n_divisor > 0 ? 1. / std::pow(n_divisor--, 2) : 1.;
+    }
+    for (size_t ii = 0; ii < max_mn; ++ii) {
+      ret *= factors[0][ii] * factors[1][ii] * factors[2][ii] * factors[3][ii] / 2.;
+    }
+    ret *= std::pow(-1., (m + n + 1) / 2) / ((m - n) * (m + n + 1) * std::pow(2., m + n - 1 - std::max(m, n)));
+    return ret;
+  } // ... fmn(...)
+}; // class LegendreMomentBasis<DomainFieldType, 1, ...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_LEGENDRE_HH
diff --git a/dune/gdt/test/momentmodels/basisfunctions/partial_moments.hh b/dune/gdt/test/momentmodels/basisfunctions/partial_moments.hh
new file mode 100644
index 0000000000000000000000000000000000000000..2c5b7ff8c530e65ad6532ce8c8dc74dc7ab37e8b
--- /dev/null
+++ b/dune/gdt/test/momentmodels/basisfunctions/partial_moments.hh
@@ -0,0 +1,839 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_PARTIALMOMENTS_HH
+#define DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_PARTIALMOMENTS_HH
+
+#include <boost/iostreams/stream.hpp>
+#include <boost/iostreams/device/null.hpp>
+
+#if HAVE_QHULL
+#  include <dune/xt/common/disable_warnings.hh>
+#  include <libqhullcpp/Qhull.h>
+#  include <libqhullcpp/QhullFacetList.h>
+#  include <dune/xt/common/reenable_warnings.hh>
+#endif // HAVE_QHULL
+
+#include <dune/xt/common/fvector.hh>
+
+#include "interface.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+template <class DomainFieldType,
+          size_t dimDomain,
+          class RangeFieldType,
+          size_t dimRange,
+          size_t dimRangeCols = 1,
+          size_t dimFlux = dimDomain,
+          size_t order = 1,
+          EntropyType entropy = EntropyType::MaxwellBoltzmann>
+class PartialMomentBasis
+{
+  //  static_assert(false, "Not implemented for this combination of dimension and order!");
+};
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t rangeDim,
+          size_t rangeDimCols,
+          size_t dimFlux,
+          EntropyType entropy>
+class PartialMomentBasis<DomainFieldType, 1, RangeFieldType, rangeDim, rangeDimCols, dimFlux, 1, entropy>
+  : public MomentBasisInterface<DomainFieldType, 1, RangeFieldType, rangeDim, rangeDimCols, dimFlux, entropy>
+{
+public:
+  static constexpr size_t dimDomain = 1;
+  static constexpr size_t dimRange = rangeDim;
+  static constexpr size_t dimRangeCols = rangeDimCols;
+  static_assert(!(dimRange % 2), "dimRange has to be even!");
+  static constexpr size_t num_intervals = dimRange / 2;
+
+private:
+  using BaseType =
+      MomentBasisInterface<DomainFieldType, dimDomain, RangeFieldType, dimRange, dimRangeCols, dimFlux, entropy>;
+
+public:
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicRangeType;
+  using typename BaseType::MatrixType;
+  using typename BaseType::QuadraturesType;
+  using typename BaseType::RangeType;
+  using typename BaseType::SphericalTriangulationType;
+  using typename BaseType::StringifierType;
+  using typename BaseType::VisualizerType;
+  using LocalVectorType = FieldVector<RangeFieldType, 2>;
+  using PartitioningType = typename BaseType::Partitioning1dType;
+
+  static std::string static_id()
+  {
+    return "pcw";
+  }
+
+  static size_t default_quad_order()
+  {
+    return 15;
+  }
+
+  using BaseType::default_quad_refinements;
+
+  PartialMomentBasis(const QuadraturesType& quadratures)
+    : BaseType(quadratures)
+    , partitioning_(BaseType::create_1d_partitioning(num_intervals))
+  {
+    BaseType::initialize_base_values();
+  }
+
+  PartialMomentBasis(const SphericalTriangulationType& /*triangulation*/, const QuadraturesType& quadratures)
+    : PartialMomentBasis(quadratures)
+  {}
+
+  PartialMomentBasis(const size_t quad_order = default_quad_order(),
+                     const size_t quad_refinements = default_quad_refinements())
+    : BaseType(BaseType::gauss_lobatto_quadratures(num_intervals, quad_order, quad_refinements))
+    , partitioning_(BaseType::create_1d_partitioning(num_intervals))
+  {
+    BaseType::initialize_base_values();
+  }
+
+  DynamicRangeType evaluate(const DomainType& v) const override final
+  {
+    for (size_t ii = 0; ii < num_intervals; ++ii)
+      if (XT::Common::FloatCmp::ge(v[0], partitioning_[ii]) && XT::Common::FloatCmp::le(v[0], partitioning_[ii + 1]))
+        return evaluate(v, ii);
+    DUNE_THROW(Dune::MathError, "v has to be between -1 and 1!");
+    return DynamicRangeType();
+  } // ... evaluate(...)
+
+  // evaluate on interval ii
+  DynamicRangeType evaluate(const DomainType& v, const size_t ii) const override final
+  {
+    DynamicRangeType ret(dimRange, 0);
+    const auto local_ret = evaluate_on_interval(v, ii);
+    ret[2 * ii] = local_ret[0];
+    ret[2 * ii + 1] = local_ret[1];
+    return ret;
+  } // ... evaluate(...)
+
+  LocalVectorType evaluate_on_interval(const DomainType& v, const size_t /*ii*/) const
+  {
+    return LocalVectorType{{1, v[0]}};
+  } // ... evaluate(...)
+
+  LocalVectorType evaluate_on_face(const DomainType& v, const size_t ii) const
+  {
+    return evaluate_on_interval(v, ii);
+  } // ... evaluate(...)
+
+  DynamicRangeType integrated() const override final
+  {
+    DynamicRangeType ret(dimRange, 0);
+    for (size_t ii = 0; ii < num_intervals; ++ii) {
+      ret[2 * ii] = partitioning_[ii + 1] - partitioning_[ii];
+      ret[2 * ii + 1] = (std::pow(partitioning_[ii + 1], 2) - std::pow(partitioning_[ii], 2)) / 2.;
+    }
+    return ret;
+  }
+
+  virtual bool
+  is_negative(const typename XT::Data::MergedQuadrature<RangeFieldType, dimDomain>::MergedQuadratureIterator& it)
+      const override final
+  {
+    return it.first_index() < num_intervals / 2;
+  }
+
+  // returns matrix with entries <h_i h_j>
+  MatrixType mass_matrix() const override final
+  {
+    MatrixType M(dimRange, dimRange, 0.);
+    for (size_t ii = 0; ii < num_intervals; ++ii) {
+      M[2 * ii][2 * ii] = partitioning_[ii + 1] - partitioning_[ii];
+      M[2 * ii + 1][2 * ii + 1] = (std::pow(partitioning_[ii + 1], 3) - std::pow(partitioning_[ii], 3)) / 3.;
+      M[2 * ii][2 * ii + 1] = (std::pow(partitioning_[ii + 1], 2) - std::pow(partitioning_[ii], 2)) / 2.;
+      M[2 * ii + 1][2 * ii] = M[2 * ii][2 * ii + 1];
+    }
+    return M;
+  }
+
+  MatrixType mass_matrix_inverse() const override final
+  {
+    return tridiagonal_matrix_inverse<RangeFieldType, dimRange>(mass_matrix());
+  }
+
+  // returns matrix with entries <v h_i h_j>
+  FieldVector<MatrixType, dimDomain> flux_matrix() const override final
+  {
+    MatrixType B(dimRange, dimRange, 0.);
+    for (size_t ii = 0; ii < num_intervals; ++ii) {
+      B[2 * ii][2 * ii] = (std::pow(partitioning_[ii + 1], 2) - std::pow(partitioning_[ii], 2)) / 2.;
+      B[2 * ii + 1][2 * ii + 1] = (std::pow(partitioning_[ii + 1], 4) - std::pow(partitioning_[ii], 4)) / 4.;
+      B[2 * ii][2 * ii + 1] = (std::pow(partitioning_[ii + 1], 3) - std::pow(partitioning_[ii], 3)) / 3.;
+      B[2 * ii + 1][2 * ii] = B[2 * ii][2 * ii + 1];
+    }
+    return FieldVector<MatrixType, dimDomain>(B);
+  }
+
+  // returns V M^-1 where the matrix V has entries <v h_i h_j>_- and <v h_i h_j>_+
+  FieldVector<FieldVector<MatrixType, 2>, 1> kinetic_flux_matrices() const override final
+  {
+    FieldVector<FieldVector<MatrixType, 2>, 1> ret(FieldVector<MatrixType, 2>(MatrixType(dimRange, dimRange, 0.)));
+    auto mm_with_v = flux_matrix();
+    auto& ret_neg = ret[0][0];
+    auto& ret_pos = ret[0][1];
+    for (size_t ii = 0; ii < num_intervals; ++ii) {
+      if (num_intervals % 2) {
+        // if there is an odd number of intervals, the middle interval is split in 2 parts
+        // copy other intervals
+        for (size_t nn = 0; nn < num_intervals - 1; ++nn)
+          for (size_t mm = 0; mm < num_intervals - 1; ++mm)
+            ret_neg[nn][mm] = mm_with_v[0][nn][mm];
+        for (size_t nn = num_intervals + 1; nn < dimRange; ++nn)
+          for (size_t mm = num_intervals + 1; mm < dimRange; ++mm)
+            ret_pos[nn][mm] = mm_with_v[0][nn][mm];
+        // treat middle interval
+        // mixed integrals are symmetric, so ret_pos and ret_neg both get half of it
+        ret_neg[num_intervals - 1][num_intervals] = mm_with_v[0][num_intervals - 1][num_intervals] / 2;
+        ret_neg[num_intervals][num_intervals - 1] = mm_with_v[0][num_intervals][num_intervals - 1] / 2;
+        ret_pos[num_intervals - 1][num_intervals] = mm_with_v[0][num_intervals - 1][num_intervals] / 2;
+        ret_pos[num_intervals][num_intervals - 1] = mm_with_v[0][num_intervals][num_intervals - 1] / 2;
+        // integral corresponding to constant basis function
+        ret_neg[num_intervals - 1][num_intervals - 1] = -std::pow(partitioning_[num_intervals / 2], 2) / 2;
+        ret_pos[num_intervals - 1][num_intervals - 1] = std::pow(partitioning_[num_intervals / 2], 2) / 2;
+        // integral corresponding to v basis function
+        ret_neg[num_intervals][num_intervals] = -std::pow(partitioning_[num_intervals / 2], 4) / 4;
+        ret_pos[num_intervals][num_intervals] = std::pow(partitioning_[num_intervals / 2], 4) / 4;
+      } else {
+        // if there is an even number of intervals, the matrix is just split up in upper and lower part
+        for (size_t nn = 0; nn < num_intervals; ++nn)
+          for (size_t mm = 0; mm < num_intervals; ++mm)
+            ret_neg[nn][mm] = mm_with_v[0][nn][mm];
+        for (size_t nn = num_intervals; nn < dimRange; ++nn)
+          for (size_t mm = num_intervals; mm < dimRange; ++mm)
+            ret_pos[nn][mm] = mm_with_v[0][nn][mm];
+      }
+    } // nn
+    // apply M^{-1} from the right
+    const auto M = std::make_unique<XT::Common::FieldMatrix<RangeFieldType, dimRange, dimRange>>(mass_matrix());
+    MatrixType tmp_mat = ret_neg;
+    for (size_t rr = 0; rr < dimRange; ++rr)
+      M->solve(ret_neg[rr], tmp_mat[rr]);
+    tmp_mat = ret_pos;
+    for (size_t rr = 0; rr < dimRange; ++rr)
+      M->solve(ret_pos[rr], tmp_mat[rr]);
+    return ret;
+  }
+
+  MatrixType reflection_matrix(const DomainType& n) const override final
+  {
+    MatrixType ret(dimRange, dimRange, 0);
+    for (size_t ii = 0; ii < dimDomain; ++ii)
+      if (XT::Common::FloatCmp::ne(n[ii], 0.))
+        if (XT::Common::FloatCmp::ne(std::abs(n[ii]), 1.))
+          DUNE_THROW(NotImplemented, "Implemented only for +-e_i where e_i is the i-th canonical basis vector!");
+    const auto mass_mat = mass_matrix();
+    for (size_t ii = 0; ii < dimRange; ++ii) {
+      for (size_t jj = 0; jj < num_intervals; ++jj) {
+        ret[ii][2 * jj] = mass_mat[ii][dimRange - 1 - (2 * jj + 1)];
+        ret[ii][2 * jj + 1] = -mass_mat[ii][dimRange - 1 - 2 * jj];
+      }
+    }
+    ret.rightmultiply(mass_matrix_inverse());
+    return ret;
+  }
+
+  static StringifierType stringifier()
+  {
+    return [](const RangeType& val) {
+      RangeFieldType psi(0);
+      for (size_t ii = 0; ii < dimRange; ii += 2)
+        psi += val[ii];
+      return XT::Common::to_string(psi, 15);
+    };
+  } // ... stringifier()
+
+  const PartitioningType& partitioning() const
+  {
+    return partitioning_;
+  }
+
+  DynamicRangeType alpha_one() const override final
+  {
+    DynamicRangeType ret(dimRange, 0);
+    for (size_t ii = 0; ii < dimRange; ii += 2)
+      ret[ii] = 1.;
+    return ret;
+  }
+
+  RangeFieldType density(const DynamicRangeType& u) const override final
+  {
+    RangeFieldType ret(0.);
+    for (size_t ii = 0; ii < dimRange; ii += 2) {
+      ret += u[ii];
+    }
+    return ret;
+  }
+
+  RangeFieldType density(const RangeType& u) const
+  {
+    RangeFieldType ret(0.);
+    for (size_t ii = 0; ii < dimRange; ii += 2) {
+      ret += u[ii];
+    }
+    return ret;
+  }
+
+  RangeFieldType density(const XT::Common::BlockedFieldVector<RangeFieldType, num_intervals, 2>& u) const
+  {
+    RangeFieldType ret(0.);
+    for (size_t jj = 0; jj < num_intervals; ++jj)
+      ret += u.block(jj)[0];
+    return ret;
+  }
+
+  RangeFieldType min_density(const XT::Common::BlockedFieldVector<RangeFieldType, num_intervals, 2>& u) const
+  {
+    RangeFieldType ret(u.block(0)[0]);
+    for (size_t jj = 1; jj < num_intervals; ++jj)
+      ret = std::min(ret, u.block(jj)[0]);
+    return ret;
+  }
+
+  using BaseType::u_iso;
+
+  // For the partial moments, we might not be able to solve the optimization problem for some moments where the density
+  // on one interval/spherical triangle is very low. The overall density might be much higher than the density on that
+  // triangle, so we specialize this function.
+  void ensure_min_density(DynamicRangeType& u, const RangeFieldType min_density) const override final
+  {
+    const auto u_iso_min = u_iso() * min_density;
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      if (u[2 * jj] < u_iso_min[2 * jj]) {
+        u[2 * jj] = u_iso_min[2 * jj];
+        u[2 * jj + 1] = u_iso_min[2 * jj + 1];
+      }
+    }
+  }
+
+  void ensure_min_density(RangeType& u, const RangeFieldType min_density) const override final
+  {
+    const auto u_iso_min = u_iso() * min_density;
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      if (u[2 * jj] < u_iso_min[2 * jj]) {
+        u[2 * jj] = u_iso_min[2 * jj];
+        u[2 * jj + 1] = u_iso_min[2 * jj + 1];
+      }
+    }
+  }
+
+  std::string short_id() const override final
+  {
+    return "pm";
+  }
+
+  std::string mn_name() const override final
+  {
+    return "pmm" + XT::Common::to_string(dimRange);
+  }
+
+  std::string pn_name() const override final
+  {
+    return "pmp" + XT::Common::to_string(dimRange);
+  }
+
+  // get indices of all faces that contain point
+  std::vector<size_t> get_face_indices(const DomainType& v) const
+  {
+    std::vector<size_t> face_indices;
+    for (size_t jj = 0; jj < partitioning_.size() - 1; ++jj)
+      if (XT::Common::FloatCmp::ge(v[0], partitioning_[jj]) && XT::Common::FloatCmp::le(v[0], partitioning_[jj + 1]))
+        face_indices.push_back(jj);
+    assert(face_indices.size());
+    return face_indices;
+  }
+
+private:
+  const PartitioningType partitioning_;
+  using BaseType::quadratures_;
+}; // class PartialMomentBasis<DomainFieldType, 1, ...>
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t rangeDim,
+          size_t rangeDimCols,
+          size_t dimFlux,
+          EntropyType entropy>
+constexpr size_t
+    PartialMomentBasis<DomainFieldType, 1, RangeFieldType, rangeDim, rangeDimCols, dimFlux, 1, entropy>::dimRange;
+
+template <class DomainFieldType, class RangeFieldType, size_t refinements, size_t dimFlux, EntropyType entropy>
+class PartialMomentBasis<DomainFieldType, 3, RangeFieldType, refinements, 1, dimFlux, 1, entropy>
+  : public MomentBasisInterface<DomainFieldType,
+                                3,
+                                RangeFieldType,
+                                OctaederStatistics<refinements>::num_faces() * 4,
+                                1,
+                                dimFlux,
+                                entropy>
+{
+public:
+  static const size_t dimDomain = 3;
+  static const size_t dimRange = OctaederStatistics<refinements>::num_faces() * 4;
+  static const size_t dimRangeCols = 1;
+  static constexpr size_t block_size = 4;
+  static constexpr size_t num_blocks = dimRange / block_size;
+  static constexpr size_t num_refinements = refinements;
+
+private:
+  using BaseType =
+      MomentBasisInterface<DomainFieldType, dimDomain, RangeFieldType, dimRange, dimRangeCols, dimFlux, entropy>;
+  using ThisType = PartialMomentBasis;
+
+public:
+  using TriangulationType = typename BaseType::SphericalTriangulationType;
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicRangeType;
+  using typename BaseType::MatrixType;
+  using typename BaseType::QuadraturesType;
+  using typename BaseType::QuadratureType;
+  using typename BaseType::RangeType;
+  using typename BaseType::StringifierType;
+  using typename BaseType::VisualizerType;
+  using BlockRangeType = XT::Common::FieldVector<RangeFieldType, block_size>;
+  using BlockPlaneCoefficientsType = typename std::vector<std::pair<BlockRangeType, RangeFieldType>>;
+  using PlaneCoefficientsType = XT::Common::FieldVector<BlockPlaneCoefficientsType, num_blocks>;
+  using BlockMatrixType = XT::Common::BlockedFieldMatrix<RangeFieldType, num_blocks, block_size, block_size>;
+  using LocalMatrixType = typename BlockMatrixType::BlockType;
+  using LocalVectorType = XT::Common::FieldVector<RangeFieldType, block_size>;
+
+  using BaseType::barycentre_rule;
+
+  static size_t default_quad_order()
+  {
+    return refinements == 0 ? 15 : 9;
+  }
+
+  using BaseType::default_quad_refinements;
+
+  PartialMomentBasis(const QuadraturesType& quadratures)
+    : BaseType(refinements, quadratures)
+  {
+    assert(4 * triangulation_.faces().size() == dimRange);
+    BaseType::initialize_base_values();
+  }
+
+  PartialMomentBasis(const size_t quad_refinements, const QuadratureRule<RangeFieldType, 2>& reference_quadrature_rule)
+    : BaseType(refinements)
+  {
+    quadratures_ = triangulation_.quadrature_rules(quad_refinements, reference_quadrature_rule);
+    assert(4 * triangulation_.faces().size() == dimRange);
+    BaseType::initialize_base_values();
+  }
+
+  PartialMomentBasis(const TriangulationType& triangulation, const QuadraturesType& quadratures)
+    : BaseType(triangulation, quadratures)
+  {
+    assert(4 * triangulation_.faces().size() == dimRange);
+    BaseType::initialize_base_values();
+  }
+
+  PartialMomentBasis(const size_t quad_order = default_quad_order(),
+                     const size_t quad_refinements = default_quad_refinements())
+    : BaseType(refinements)
+  {
+    const QuadratureRule<RangeFieldType, 2> reference_quadrature_rule =
+        XT::Data::FeketeQuadrature<DomainFieldType>::get(quad_order);
+    quadratures_ = triangulation_.quadrature_rules(quad_refinements, reference_quadrature_rule);
+    assert(4 * triangulation_.faces().size() == dimRange);
+    BaseType::initialize_base_values();
+  }
+
+  DynamicRangeType evaluate(const DomainType& v) const override final
+  {
+    DynamicRangeType ret(dimRange, 0);
+    const auto face_indices = triangulation_.get_face_indices(v);
+    if (face_indices.size() != 1)
+      DUNE_THROW(Dune::MathError,
+                 "Either v is not on the unit sphere or on a boundary between triangles where pointwise evaluation is "
+                 "not defined for this basis!");
+    return evaluate(v, face_indices[0]);
+  } // ... evaluate(...)
+
+  // evaluate on spherical triangle face_index
+  DynamicRangeType evaluate(const DomainType& v, const size_t face_index) const override final
+  {
+    DynamicRangeType ret(dimRange, 0);
+    const auto local_eval = evaluate_on_face(v, face_index);
+    assert(4 * face_index + 3 < ret.size());
+    for (size_t ii = 0; ii < 4; ++ii)
+      ret[4 * face_index + ii] = local_eval[ii];
+    return ret;
+  } // ... evaluate(...)
+
+  // evaluate on spherical triangle face_index
+  LocalVectorType evaluate_on_face(const DomainType& v, const size_t /*face_index*/) const
+  {
+    LocalVectorType ret;
+    ret[0] = 1.;
+    for (size_t ii = 1; ii < 4; ++ii)
+      ret[ii] = v[ii - 1];
+    return ret;
+  } // ... evaluate(...)
+
+  static StringifierType stringifier()
+  {
+    return [](const DynamicRangeType& val) {
+      RangeFieldType psi(0);
+      for (size_t ii = 0; ii < dimRange; ii += 4)
+        psi += val[ii];
+      return XT::Common::to_string(psi, 15);
+    };
+  } // ... stringifier()
+
+  RangeFieldType unit_ball_volume() const override final
+  {
+    return BaseType::unit_ball_volume_quad();
+  }
+
+  DynamicRangeType alpha_one() const override final
+  {
+    DynamicRangeType ret(dimRange, 0.);
+    for (size_t ii = 0; ii < dimRange; ii += 4)
+      ret[ii] = 1.;
+    return ret;
+  }
+
+  virtual RangeFieldType density(const RangeType& u) const
+  {
+    RangeFieldType ret(0.);
+    for (size_t ii = 0; ii < dimRange; ii += 4)
+      ret += u[ii];
+    return ret;
+  }
+
+  RangeFieldType density(const DynamicRangeType& u) const override final
+  {
+    RangeFieldType ret(0.);
+    for (size_t ii = 0; ii < dimRange; ii += 4)
+      ret += u[ii];
+    return ret;
+  }
+
+  RangeFieldType density(const XT::Common::BlockedFieldVector<RangeFieldType, dimRange / 4, 4>& u) const
+  {
+    RangeFieldType ret(0.);
+    for (size_t jj = 0; jj < dimRange / 4; ++jj)
+      ret += u.block(jj)[0];
+    return ret;
+  }
+
+  RangeFieldType min_density(const XT::Common::BlockedFieldVector<RangeFieldType, dimRange / 4, 4>& u) const
+  {
+    RangeFieldType ret(u.block(0)[0]);
+    for (size_t jj = 1; jj < dimRange / 4; ++jj)
+      ret = std::min(ret, u.block(jj)[0]);
+    return ret;
+  }
+
+  using BaseType::u_iso;
+
+  // For the partial moments, we might not be able to solve the optimization problem for some moments where the density
+  // on one interval/spherical triangle is very low. The overall density might be much higher than the density on that
+  // triangle, so we specialize this function.
+  void ensure_min_density(DynamicRangeType& u, const RangeFieldType min_density) const override final
+  {
+    const auto u_iso_min = u_iso() * min_density;
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      const auto block_density = u[4 * jj];
+      if (block_density < u_iso_min[4 * jj]) {
+        for (size_t ii = 0; ii < block_size; ++ii)
+          u[4 * jj + ii] = u_iso_min[4 * jj + ii];
+      }
+    }
+  }
+
+  // For the partial moments, we might not be able to solve the optimization problem for some moments where the density
+  // on one interval/spherical triangle is very low. The overall density might be much higher than the density on that
+  // triangle, so we specialize this function.
+  void ensure_min_density(RangeType& u, const RangeFieldType min_density) const override final
+  {
+    const auto u_iso_min = u_iso() * min_density;
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      const auto block_density = u[4 * jj];
+      if (block_density < u_iso_min[4 * jj]) {
+        for (size_t ii = 0; ii < block_size; ++ii)
+          u[4 * jj + ii] = u_iso_min[4 * jj + ii];
+      }
+    }
+  }
+
+  std::string short_id() const override final
+  {
+    return "pm";
+  }
+
+  std::string mn_name() const override final
+  {
+    return "pmm" + XT::Common::to_string(dimRange);
+  }
+
+  std::string pn_name() const override final
+  {
+    return "pmp" + XT::Common::to_string(dimRange);
+  }
+
+  std::vector<size_t> get_face_indices(const DomainType& v) const
+  {
+    return triangulation_.get_face_indices(v);
+  }
+
+  const TriangulationType& triangulation() const
+  {
+    return triangulation_;
+  }
+
+  // calculates <b(v) dirac(v-dirac_position)>
+  DynamicRangeType integrate_dirac_at(const DomainType& dirac_position) const
+  {
+    size_t num_faces;
+    auto ret = evaluate(dirac_position, true, num_faces);
+    if (dirac_position == DomainType{1, 0, 0})
+      DXT_ASSERT(num_faces == 4);
+    return ret;
+  }
+
+  const PlaneCoefficientsType& plane_coefficients() const
+  {
+    return plane_coefficients_;
+  }
+
+  // calculate half space representation of realizable set
+  void calculate_plane_coefficients() const
+  {
+    XT::Common::FieldVector<std::vector<XT::Common::FieldVector<RangeFieldType, block_size>>, num_blocks> points;
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      points[jj].resize(quadratures_[jj].size() + 1);
+      for (size_t ll = 0; ll < quadratures_[jj].size(); ++ll) {
+        const auto val = evaluate(quadratures_[jj][ll].position(), jj);
+        for (size_t ii = 0; ii < block_size; ++ii)
+          points[jj][ll][ii] = val[block_size * jj + ii];
+      } // ll
+      points[jj][quadratures_[jj].size()] = XT::Common::FieldVector<RangeFieldType, block_size>(0.);
+    }
+    std::vector<std::thread> threads(num_blocks);
+    // Calculate plane coefficients for each block in a separate thread
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      threads[jj] = std::thread(&ThisType::calculate_plane_coefficients_block, this, std::ref(points[jj]), jj);
+    // Join the threads with the main thread
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      threads[jj].join();
+  }
+
+  virtual DynamicRangeType
+  get_moment_vector(const std::function<RangeFieldType(DomainType, bool)>& psi) const override final
+  {
+    DynamicRangeType ret(dimRange, 0);
+    for (size_t jj = 0; jj < quadratures_.size(); ++jj) {
+      const size_t offset = jj * block_size;
+      for (const auto& quad_point : quadratures_[jj]) {
+        const auto& v = quad_point.position();
+        const auto basis_val = evaluate_on_face(v, jj);
+        const auto psi_val = psi(v, false);
+        const auto factor = psi_val * quad_point.weight();
+        for (size_t ii = 0; ii < block_size; ++ii)
+          ret[offset + ii] += basis_val[ii] * factor;
+      }
+    }
+    return ret;
+  }
+
+  std::unique_ptr<BlockMatrixType> block_mass_matrix() const
+  {
+    auto block_matrix = std::make_unique<BlockMatrixType>();
+    parallel_quadrature_blocked(quadratures_, *block_matrix, size_t(-1));
+    return block_matrix;
+  } // ... mass_matrix()
+
+  MatrixType mass_matrix() const override final
+  {
+    return block_mass_matrix()->convert_to_dynamic_matrix();
+  } // ... mass_matrix()
+
+  FieldVector<MatrixType, dimFlux> flux_matrix() const override final
+  {
+    FieldVector<MatrixType, dimFlux> B(MatrixType(dimRange, dimRange, 0));
+    BlockMatrixType block_matrix;
+    for (size_t dd = 0; dd < dimFlux; ++dd) {
+      parallel_quadrature_blocked(quadratures_, block_matrix, dd);
+      B[dd] = block_matrix.convert_to_dynamic_matrix();
+    }
+    return B;
+  }
+
+  // returns V M^-1 where V has entries <v h_i h_j>_- and <v h_i h_j>_+
+  FieldVector<FieldVector<MatrixType, 2>, dimFlux> kinetic_flux_matrices() const override final
+  {
+    FieldVector<FieldVector<MatrixType, 2>, dimFlux> B_kinetic(
+        FieldVector<MatrixType, 2>(MatrixType(dimRange, dimRange, 0.)));
+    BlockMatrixType block_matrix;
+    const auto mass_matrix = block_mass_matrix();
+    for (size_t dd = 0; dd < dimFlux; ++dd) {
+      QuadraturesType neg_quadratures(quadratures_.size());
+      QuadraturesType pos_quadratures(quadratures_.size());
+      BaseType::get_pos_and_neg_quadratures(neg_quadratures, pos_quadratures, dd);
+      parallel_quadrature_blocked(neg_quadratures, block_matrix, dd);
+      apply_invM_from_right(*mass_matrix, block_matrix, B_kinetic[dd][0]);
+      parallel_quadrature_blocked(pos_quadratures, block_matrix, dd);
+      apply_invM_from_right(*mass_matrix, block_matrix, B_kinetic[dd][1]);
+    } // dd
+    return B_kinetic;
+  } // ... kinetic_flux_matrices()
+
+
+private:
+  void calculate_plane_coefficients_block(std::vector<XT::Common::FieldVector<RangeFieldType, block_size>>& points,
+                                          const size_t jj) const
+  {
+#if HAVE_QHULL
+    orgQhull::Qhull qhull;
+    // ignore output
+    boost::iostreams::stream<boost::iostreams::null_sink> null_ostream((boost::iostreams::null_sink()));
+    qhull.setOutputStream(&null_ostream);
+    qhull.setErrorStream(&null_ostream);
+    // calculate convex hull
+    assert(points.size() < std::numeric_limits<int>::max());
+    qhull.runQhull(
+        "Realizable set", static_cast<int>(block_size), static_cast<int>(points.size()), &(points[0][0]), "Qt T1");
+    const auto facet_end = qhull.endFacet();
+    BlockPlaneCoefficientsType block_plane_coefficients(qhull.facetList().count());
+    //    std::cout << "num_vertices: " << qhull.vertexList().count() << std::endl;
+    size_t ll = 0;
+    for (auto facet = qhull.beginFacet(); facet != facet_end; facet = facet.next(), ++ll) {
+      for (size_t ii = 0; ii < block_size; ++ii)
+        block_plane_coefficients[ll].first[ii] = *(facet.hyperplane().coordinates() + ii);
+      block_plane_coefficients[ll].second = -facet.hyperplane().offset();
+    } // ii
+    // discard duplicate facets (qhull triangulates output, so there may be several facets on the same hyperplane)
+    using CoeffType = typename BlockPlaneCoefficientsType::value_type;
+    std::sort(block_plane_coefficients.begin(),
+              block_plane_coefficients.end(),
+              [](const CoeffType& first, const CoeffType& second) {
+                // Check component-wise if first.a[ii] < second.a[ii]. If they are equal, check next component.
+                for (size_t ii = 0; ii < block_size; ++ii) {
+                  if (XT::Common::FloatCmp::lt(first.first[ii], second.first[ii]))
+                    return true;
+                  else if (XT::Common::FloatCmp::gt(first.first[ii], second.first[ii]))
+                    return false;
+                }
+                // first.a and second.a are equal, check first.b and second.b
+                if (XT::Common::FloatCmp::lt(first.second, second.second))
+                  return true;
+                return false;
+              });
+    static const auto pair_float_cmp = [](const CoeffType& first, const CoeffType& second) {
+      return XT::Common::FloatCmp::eq(first.first, second.first)
+             && XT::Common::FloatCmp::eq(first.second, second.second);
+    };
+    block_plane_coefficients.erase(
+        std::unique(block_plane_coefficients.begin(), block_plane_coefficients.end(), pair_float_cmp),
+        block_plane_coefficients.end());
+    // discard facets belonging to the condition u0 < 1, i.e. with a = (1, 0, 0, ...) and b = 1
+    CoeffType coeff_to_erase{BlockRangeType(0), 1.};
+    coeff_to_erase.first[0] = 1.;
+    auto coeff_to_erase_it =
+        std::find_if(block_plane_coefficients.begin(),
+                     block_plane_coefficients.end(),
+                     [&coeff_to_erase](const CoeffType& coeff) { return pair_float_cmp(coeff, coeff_to_erase); });
+    if (coeff_to_erase_it == block_plane_coefficients.end())
+      DUNE_THROW(Dune::MathError, "There should be such a coefficient!");
+    block_plane_coefficients.erase(coeff_to_erase_it);
+    plane_coefficients_[jj] = block_plane_coefficients;
+#else // HAVE_QHULL
+    DUNE_UNUSED_PARAMETER(points);
+    DUNE_UNUSED_PARAMETER(jj);
+    DUNE_THROW(Dune::NotImplemented, "You are missing Qhull!");
+#endif // HAVE_QHULL
+  }
+
+  void
+  parallel_quadrature_blocked(const QuadraturesType& quadratures, BlockMatrixType& matrix, const size_t v_index) const
+  {
+    const size_t num_threads = std::min(XT::Common::threadManager().max_threads(), num_blocks);
+    std::vector<std::thread> threads(num_threads);
+    // Launch a group of threads
+    size_t blocks_done = 0;
+    while (blocks_done < num_blocks) {
+      size_t inner_loop_count = std::min(num_threads, num_blocks - blocks_done);
+      for (size_t jj = 0; jj < inner_loop_count; ++jj)
+        threads[jj] = std::thread(&ThisType::calculate_block_in_thread,
+                                  this,
+                                  std::cref(quadratures[blocks_done + jj]),
+                                  std::ref(matrix.block(blocks_done + jj)),
+                                  v_index,
+                                  blocks_done + jj);
+      // Join the threads with the main thread
+      for (size_t jj = 0; jj < inner_loop_count; ++jj)
+        threads[jj].join();
+      blocks_done += inner_loop_count;
+    }
+  } // void parallel_quadrature(...)
+
+  void calculate_block_in_thread(const QuadratureType& quadrature,
+                                 LocalMatrixType& local_matrix,
+                                 const size_t v_index,
+                                 const size_t jj) const
+  {
+    local_matrix *= 0.;
+    for (const auto& quad_point : quadrature) {
+      const auto& v = quad_point.position();
+      const auto basis_evaluated = evaluate(v, jj);
+      const auto& weight = quad_point.weight();
+      const auto factor = v_index == size_t(-1) ? 1. : v[v_index];
+      for (size_t nn = 0; nn < local_matrix.N(); ++nn)
+        for (size_t mm = 0; mm < local_matrix.M(); ++mm)
+          local_matrix[nn][mm] +=
+              basis_evaluated[jj * block_size + nn] * basis_evaluated[jj * block_size + mm] * factor * weight;
+    }
+  } // void calculate_in_thread(...)
+
+  void apply_invM_from_right(const BlockMatrixType& M, BlockMatrixType& V, MatrixType& ret) const
+  {
+    LocalVectorType local_vec;
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      for (size_t rr = 0; rr < block_size; ++rr) {
+        M.block(jj).solve(local_vec, V.block(jj)[rr]);
+        V.block(jj)[rr] = local_vec;
+      } // rr
+    } // jj
+    ret = V.convert_to_dynamic_matrix();
+  }
+
+  using BaseType::quadratures_;
+  using BaseType::triangulation_;
+  mutable PlaneCoefficientsType plane_coefficients_;
+}; // class PartialMomentBasis<DomainFieldType, 3, ...>
+
+template <class DomainFieldType, class RangeFieldType, size_t refinements, size_t dimFlux, EntropyType entropy>
+constexpr size_t PartialMomentBasis<DomainFieldType, 3, RangeFieldType, refinements, 1, dimFlux, 1, entropy>::dimRange;
+
+template <class DomainFieldType, class RangeFieldType, size_t refinements, size_t dimFlux, EntropyType entropy>
+constexpr size_t
+    PartialMomentBasis<DomainFieldType, 3, RangeFieldType, refinements, 1, dimFlux, 1, entropy>::num_blocks;
+
+template <class DomainFieldType, class RangeFieldType, size_t refinements, size_t dimFlux, EntropyType entropy>
+constexpr size_t
+    PartialMomentBasis<DomainFieldType, 3, RangeFieldType, refinements, 1, dimFlux, 1, entropy>::num_refinements;
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_PARTIALMOMENTS_HH
diff --git a/dune/gdt/test/momentmodels/basisfunctions/spherical_harmonics.hh b/dune/gdt/test/momentmodels/basisfunctions/spherical_harmonics.hh
new file mode 100644
index 0000000000000000000000000000000000000000..f2507df61b570c7a0cfc9860cb7a80f1eecd0e83
--- /dev/null
+++ b/dune/gdt/test/momentmodels/basisfunctions/spherical_harmonics.hh
@@ -0,0 +1,623 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_BASISFUNCTIONS_SPHERICALHARMONICS_HH
+#define DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_BASISFUNCTIONS_SPHERICALHARMONICS_HH
+
+#include <boost/math/special_functions/legendre.hpp>
+#include <boost/math/special_functions/spherical_harmonic.hpp>
+
+#include <dune/xt/common/coordinates.hh>
+
+#include "interface.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+// TODO: use complex arithmetic, currently only usable for Pn Models in 2D, test for only_positive = false
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t order,
+          size_t fluxDim = 3,
+          bool only_positive = true,
+          EntropyType entropy = EntropyType::MaxwellBoltzmann>
+class SphericalHarmonicsMomentBasis
+  : public MomentBasisInterface<DomainFieldType,
+                                3,
+                                RangeFieldType,
+                                only_positive ? ((order + 1) * (order + 2)) / 2 : (order + 1) * (order + 1),
+                                1,
+                                fluxDim,
+                                entropy>
+{
+public:
+  static const size_t dimDomain = 3;
+  static const size_t dimRange = only_positive ? ((order + 1) * (order + 2)) / 2 : (order + 1) * (order + 1);
+  static const size_t dimRangeCols = 1;
+  static const size_t dimFlux = fluxDim;
+  static const size_t num_refinements = 0;
+
+private:
+  using BaseType = MomentBasisInterface<DomainFieldType, dimDomain, RangeFieldType, dimRange, 1, dimFlux, entropy>;
+
+public:
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicRangeType;
+  using typename BaseType::MatrixType;
+  using typename BaseType::QuadraturesType;
+  using typename BaseType::RangeType;
+  using typename BaseType::SphericalTriangulationType;
+  using typename BaseType::StringifierType;
+  static_assert(order <= std::numeric_limits<int>::max(), "");
+
+  static size_t default_quad_order()
+  {
+    return 2 * order + 8;
+  }
+
+  using BaseType::default_quad_refinements;
+
+  SphericalHarmonicsMomentBasis(const QuadraturesType& quadratures)
+    : BaseType(quadratures)
+  {
+    BaseType::initialize_base_values();
+  }
+
+  SphericalHarmonicsMomentBasis(const SphericalTriangulationType& triangulation, const QuadraturesType& quadratures)
+    : BaseType(triangulation, quadratures)
+  {
+    BaseType::initialize_base_values();
+  }
+
+  SphericalHarmonicsMomentBasis(const size_t quad_order = default_quad_order(),
+                                const size_t DXTC_DEBUG_ONLY(quad_refinements) = default_quad_refinements())
+    : BaseType(XT::Data::OctantQuadratures<DomainFieldType>::get(quad_order))
+  {
+    assert(quad_refinements == 0 && "Refinement of the quadrature intervals not implemented for this basis!");
+    BaseType::initialize_base_values();
+  }
+
+  using BaseType::evaluate;
+
+  DynamicRangeType evaluate(const DomainType& v) const override
+  {
+    const auto v_spherical = XT::Common::CoordinateConverter<DomainFieldType>::to_spherical(v);
+    return evaluate_in_spherical_coords(v_spherical);
+  } // ... evaluate(...)
+
+  DynamicRangeType evaluate_in_spherical_coords(const FieldVector<DomainFieldType, 2>& coords) const
+  {
+    const DomainFieldType theta = coords[0];
+    const DomainFieldType phi = coords[1];
+    DynamicRangeType ret(dimRange, 0.);
+    // TODO: use complex arithmetic, remove real() call
+    for (unsigned int ll = 0; ll <= static_cast<unsigned int>(order); ++ll)
+      for (int mm = only_positive ? 0 : -static_cast<int>(ll); mm <= static_cast<int>(ll); ++mm)
+        ret[helper<only_positive>::pos(ll, mm)] = boost::math::spherical_harmonic(ll, mm, theta, phi).real();
+    return ret;
+  } // ... evaluate(...)
+
+  DynamicRangeType integrated() const override final
+  {
+    DynamicRangeType ret(dimRange, 0.);
+    ret[0] = std::sqrt(4. * M_PI);
+    return ret;
+  }
+
+  MatrixType mass_matrix() const override
+  {
+    MatrixType M(dimRange, dimRange, 0);
+    for (size_t rr = 0; rr < dimRange; ++rr)
+      M[rr][rr] = 1;
+    return M;
+  }
+
+  MatrixType mass_matrix_inverse() const override
+  {
+    return mass_matrix();
+  }
+
+  FieldVector<MatrixType, dimFlux> flux_matrix() const override
+  {
+    FieldVector<MatrixType, dimFlux> ret(MatrixType(dimRange, dimRange, 0));
+    ret[0] = create_Bx();
+    ret[1] = create_Bz();
+    //    if (dimFlux == 3)
+    //      ret[2] = create_By();
+    return ret;
+  } // ... flux_matrix()
+
+  static StringifierType stringifier()
+  {
+    return [](const RangeType& val) { return XT::Common::to_string(val[0] * std::sqrt(4 * M_PI), 15); };
+  } // ... stringifier()
+
+  DynamicRangeType alpha_one() const override final
+  {
+    DynamicRangeType ret(dimRange, 0.);
+    ret[0] = std::sqrt(4. * M_PI);
+    return ret;
+  }
+
+  RangeFieldType density(const DynamicRangeType& u) const override final
+  {
+    return u[0] * std::sqrt(4 * M_PI);
+  }
+
+  std::string short_id() const override final
+  {
+    return "";
+  }
+
+  std::string mn_name() const override final
+  {
+    return "m" + XT::Common::to_string(order);
+  }
+
+  std::string pn_name() const override final
+  {
+    return "p" + XT::Common::to_string(order);
+  }
+
+private:
+  static RangeFieldType A_lm(const int l, const int m)
+  {
+    assert(std::abs(m) <= l);
+    return std::sqrt((l + m) * (l - m) / ((2. * l + 1.) * (2. * l - 1.)));
+  }
+
+  static RangeFieldType B_lm(const int l, const int m)
+  {
+    assert(std::abs(m) <= l);
+    return std::sqrt((l + m) * (l + m - 1.) / ((2. * l + 1.) * (2. * l - 1.)));
+  }
+
+  static MatrixType create_Bx()
+  {
+    MatrixType Bx(dimRange, dimRange, 0);
+    const auto& pos = helper<only_positive>::pos;
+    for (int l1 = 0; l1 <= static_cast<int>(order); ++l1) {
+      for (int m1 = only_positive ? 0 : -int(l1); std::abs(m1) <= l1; ++m1) {
+        for (int l2 = 0; l2 <= static_cast<int>(order); ++l2) {
+          for (int m2 = -int(l2); std::abs(m2) <= l2; ++m2) {
+            size_t row = pos(l1, m1);
+            size_t col = pos(l2, only_positive ? std::abs(m2) : m2);
+            RangeFieldType factor = !only_positive ? 1. : (m2 < 0 ? std::pow(-1., m2) : 1.);
+            if (l1 == l2 + 1 && m1 == m2 + 1)
+              Bx[row][col] += -0.5 * factor * B_lm(l2 + 1, m2 + 1);
+            if (l1 == l2 - 1 && m1 == m2 + 1)
+              Bx[row][col] += 0.5 * factor * B_lm(l2, -m2);
+            if (l1 == l2 + 1 && m1 == m2 - 1)
+              Bx[row][col] += 0.5 * factor * B_lm(l2 + 1, -m2 - 1);
+            if (l1 == l2 - 1 && m1 == m2 - 1)
+              Bx[row][col] += -0.5 * factor * B_lm(l2, m2);
+          } // m2
+        } // l2
+      } // m1
+    } // l1
+    return Bx;
+  } // ... create_Bx()
+
+  //    static MatrixType create_By()
+  //    {
+  //      MatrixType By(dimRange, dimRange, 0);
+  //      const auto& pos = helper<only_positive>::pos;
+  //      for (size_t l1 = 0; l1 <= order; ++l1) {
+  //        for (int m1 = only_positive ? 0 : -l1; size_t(std::abs(m1)) <= l1; ++m1) {
+  //          for (size_t l2 = 0; l2 <= order; ++l2) {
+  //            for (int m2 = -int(l2); size_t(std::abs(m2)) <= l2; ++m2) {
+  //              size_t row = pos(l1, m1);
+  //              size_t col = pos(l2, only_positive ? std::abs(m2) : m2);
+  //              RangeFieldType factor = !only_positive ? 1. : (m2 < 0 ? std::pow(-1., m2) : 1.);
+  //              if (l1 == l2 + 1 && m1 == m2 + 1)
+  //                By[row][col] += 0.5 * factor * std::complex<RangeFieldType>(0, 1) * B_lm(l2 + 1, m2 + 1);
+  //              if (l1 == l2 - 1 && m1 == m2 + 1)
+  //                By[row][col] += -0.5 * factor * std::complex<RangeFieldType>(0, 1) * B_lm(l2, -m2);
+  //              if (l1 == l2 + 1 && m1 == m2 - 1)
+  //                By[row][col] += 0.5 * factor * std::complex<RangeFieldType>(0, 1) * B_lm(l2 + 1, -m2 - 1);
+  //              if (l1 == l2 - 1 && m1 == m2 - 1)
+  //                By[row][col] += -0.5 * factor * std::complex<RangeFieldType>(0, 1) * B_lm(l2, m2);
+  //            } // m2
+  //          } // l2
+  //        } // m1
+  //      } // l1
+  //      return By;
+  //    } // ... create_By()
+
+  static MatrixType create_Bz()
+  {
+    MatrixType Bz(dimRange, dimRange, 0);
+    const auto& pos = helper<only_positive>::pos;
+    for (int l1 = 0; l1 <= static_cast<int>(order); ++l1) {
+      for (int m1 = only_positive ? 0. : -l1; std::abs(m1) <= l1; ++m1) {
+        for (int l2 = 0; l2 <= static_cast<int>(order); ++l2) {
+          size_t row = pos(l1, m1);
+          if (l1 == l2 + 1 && std::abs(m1) < l1) {
+            size_t col = pos(l2, m1); // m1 == m2, else matrix entry is 0
+            Bz[row][col] += A_lm(l2 + 1, m1);
+          }
+          if (l1 == l2 - 1) {
+            size_t col = pos(l2, m1); // m1 == m2, else matrix entry is 0
+            Bz[row][col] += A_lm(l2, m1);
+          }
+        } // l2
+      } // m1
+    } // l1
+    return Bz;
+  }
+
+  template <bool positive, class anything = void>
+  struct helper
+  {
+    // Converts a pair (l, m) to a vector index. The vector is ordered by l first, then by m.
+    // Each l has 2l+1 values of m, so (l, m) has position
+    // (\sum_{k=0}^{l-1} (2k+1)) + (m+l) = l^2 + m + l
+    static size_t pos(const int l, const int m)
+    {
+      const int ret = (l * l + m + l);
+      assert(ret >= 0 && std::abs(m) <= l);
+      return static_cast<size_t>(ret);
+    }
+  };
+
+  template <class anything>
+  struct helper<true, anything>
+  {
+    // Converts a pair (l, m) to a vector index. The vector is ordered by l first, then by m.
+    // Each l has l+1 non-negative values of m, so (l, m) has position
+    // (\sum_{k=0}^{l-1} (l+1)) + m = l(l+1)/2 + m
+    static size_t pos(const int l, const int m)
+    {
+      const int ret = l * (l + 1) / 2 + m;
+      assert(std::abs(m) <= l && ret >= 0);
+      return static_cast<size_t>(ret);
+    }
+  };
+}; // class SphericalHarmonicsMomentBasis<DomainFieldType, 3, ...>
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t order,
+          size_t fluxDim,
+          bool only_positive,
+          EntropyType entropy>
+const size_t SphericalHarmonicsMomentBasis<DomainFieldType, RangeFieldType, order, fluxDim, only_positive, entropy>::
+    num_refinements;
+
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t order,
+          size_t fluxDim = 3,
+          bool only_even = false,
+          EntropyType entropy = EntropyType::MaxwellBoltzmann>
+class RealSphericalHarmonicsMomentBasis
+  : public MomentBasisInterface<DomainFieldType,
+                                3,
+                                RangeFieldType,
+                                only_even ? ((order + 1) * (order + 2)) / 2 : (order + 1) * (order + 1),
+                                1,
+                                fluxDim,
+                                entropy>
+{
+public:
+  static const size_t dimDomain = 3;
+  static const size_t dimFlux = fluxDim;
+  static const size_t dimRange = only_even ? ((order + 1) * (order + 2)) / 2 : (order + 1) * (order + 1);
+  static const size_t dimRangeCols = 1;
+  static const size_t num_refinements = 0;
+
+private:
+  using BaseType = MomentBasisInterface<DomainFieldType, dimDomain, RangeFieldType, dimRange, 1, dimFlux, entropy>;
+
+public:
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicRangeType;
+  using typename BaseType::MatrixType;
+  using typename BaseType::QuadraturesType;
+  using typename BaseType::RangeType;
+  using typename BaseType::SphericalTriangulationType;
+  using typename BaseType::StringifierType;
+
+  static size_t default_quad_order()
+  {
+    return 2 * order + 8;
+  }
+
+  using BaseType::default_quad_refinements;
+
+  RealSphericalHarmonicsMomentBasis(const QuadraturesType& quadratures)
+    : BaseType(quadratures)
+  {
+    BaseType::initialize_base_values();
+  }
+
+  RealSphericalHarmonicsMomentBasis(const SphericalTriangulationType& triangulation, const QuadraturesType& quadratures)
+    : BaseType(triangulation, quadratures)
+  {
+    BaseType::initialize_base_values();
+  }
+
+  RealSphericalHarmonicsMomentBasis(const size_t quad_order = default_quad_order(),
+                                    const size_t DXTC_DEBUG_ONLY(quad_refinements) = default_quad_refinements())
+    : BaseType(XT::Data::OctantQuadratures<DomainFieldType>::get(quad_order))
+  {
+    assert(quad_refinements == 0 && "Refinement of the quadrature intervals not implemented for this basis!");
+    BaseType::initialize_base_values();
+  }
+
+  using BaseType::evaluate;
+
+  DynamicRangeType evaluate(const DomainType& v) const override
+  {
+    const auto v_spherical = XT::Common::CoordinateConverter<DomainFieldType>::to_spherical(v);
+    return evaluate_in_spherical_coords(v_spherical);
+  } // ... evaluate(...)
+
+  DynamicRangeType evaluate_in_spherical_coords(const FieldVector<DomainFieldType, 2>& coords) const
+  {
+    const DomainFieldType theta = coords[0];
+    const DomainFieldType phi = coords[1];
+    DynamicRangeType ret(dimRange, 0.);
+    for (int ll = 0; ll <= static_cast<int>(order); ++ll)
+      for (int mm = -ll; mm <= ll; ++mm)
+        if (!only_even || !((mm + ll) % 2))
+          ret[helper<only_even>::pos(ll, mm)] = evaluate_lm(theta, phi, ll, mm);
+    return ret;
+  } // ... evaluate(...)
+
+  DynamicRangeType integrated_exactly() const
+  {
+    DynamicRangeType ret(dimRange, 0.);
+    ret[0] = std::sqrt(4. * M_PI);
+    return ret;
+  }
+
+  MatrixType mass_matrix() const override
+  {
+    MatrixType M(dimRange, dimRange, 0.);
+    for (size_t rr = 0; rr < dimRange; ++rr)
+      M[rr][rr] = 1;
+    return M;
+  }
+
+  MatrixType mass_matrix_inverse() const override
+  {
+    return mass_matrix();
+  }
+
+  FieldVector<MatrixType, dimFlux> flux_matrix() const override
+  {
+    FieldVector<MatrixType, dimFlux> ret(MatrixType(dimRange, dimRange, 0));
+    ret[0] = create_Bx();
+    ret[1] = create_By();
+    if (dimFlux == 3)
+      ret[2] = create_Bz();
+    return ret;
+  } // ... flux_matrix()
+
+  static StringifierType stringifier()
+  {
+    return [](const RangeType& val) { return XT::Common::to_string(val[0] * std::sqrt(4 * M_PI), 15); };
+  } // ... stringifier()
+
+  DynamicRangeType alpha_one() const override final
+  {
+    DynamicRangeType ret(dimRange, 0.);
+    ret[0] = std::sqrt(4. * M_PI);
+    return ret;
+  }
+
+  RangeFieldType density(const DynamicRangeType& u) const override final
+  {
+    return u[0] * std::sqrt(4 * M_PI);
+  }
+
+  std::string short_id() const override final
+  {
+    return "";
+  }
+
+  std::string mn_name() const override final
+  {
+    return "m" + XT::Common::to_string(order);
+  }
+
+  std::string pn_name() const override final
+  {
+    return "p" + XT::Common::to_string(order);
+  }
+
+  DynamicRangeType integrate_dirac_at(const DomainType& dirac_position) const
+  {
+    return evaluate(dirac_position);
+  }
+
+private:
+  static RangeFieldType A_lm(const int l, const int m)
+  {
+    assert(std::abs(m) <= l);
+    return std::sqrt((l + m) * (l - m) / ((2. * l + 1.) * (2. * l - 1.)));
+  }
+
+  static RangeFieldType B_lm(const int l, const int m)
+  {
+    assert(std::abs(m) <= l);
+    return std::sqrt((l + m) * (l + m - 1.) / ((2. * l + 1.) * (2. * l - 1.)));
+  }
+
+  static MatrixType create_Bx()
+  {
+    MatrixType Bx(dimRange, dimRange, 0.);
+    const auto& pos = helper<only_even>::pos;
+    for (int l1 = 0; l1 <= static_cast<int>(order); ++l1) {
+      for (int m1 = -int(l1); std::abs(m1) <= l1; ++m1) {
+        for (int l2 = 0; l2 <= static_cast<int>(order); ++l2) {
+          for (int m2 = -int(l2); std::abs(m2) <= l2; ++m2) {
+            if (!only_even || (!((m1 + l1) % 2) && !((m2 + l2) % 2))) {
+              if (l1 == l2 - 1 && m1 == m2 - 1 && m2 > 0)
+                Bx[pos(l1, m1)][pos(l2, m2)] += 0.5 * std::sqrt(1. + (m2 == 1)) * B_lm(l2, m2);
+              if (l1 == l2 + 1 && m1 == m2 - 1 && m2 > 0)
+                Bx[pos(l1, m1)][pos(l2, m2)] += -0.5 * std::sqrt(1. + (m2 == 1)) * B_lm(l2 + 1, -m2 + 1);
+              if (l1 == l2 - 1 && m1 == m2 + 1 && m2 > 0)
+                Bx[pos(l1, m1)][pos(l2, m2)] += -0.5 * B_lm(l2, -m2);
+              if (l1 == l2 + 1 && m1 == m2 + 1 && m2 > 0)
+                Bx[pos(l1, m1)][pos(l2, m2)] += 0.5 * B_lm(l2 + 1, m2 + 1);
+              if (l1 == l2 - 1 && m1 == m2 + 1 && m2 < 0)
+                Bx[pos(l1, m1)][pos(l2, m2)] += 0.5 * (1. - (-m2 == 1)) * B_lm(l2, -m2);
+              if (l1 == l2 + 1 && m1 == m2 + 1 && m2 < 0)
+                Bx[pos(l1, m1)][pos(l2, m2)] += -0.5 * (1. - (-m2 == 1)) * B_lm(l2 + 1, m2 + 1);
+              if (l1 == l2 - 1 && m1 == m2 - 1 && m2 < 0)
+                Bx[pos(l1, m1)][pos(l2, m2)] += -0.5 * B_lm(l2, m2);
+              if (l1 == l2 + 1 && m1 == m2 - 1 && m2 < 0)
+                Bx[pos(l1, m1)][pos(l2, m2)] += 0.5 * B_lm(l2 + 1, -m2 + 1);
+              if (l1 == l2 - 1 && m1 == 1 && m2 == 0)
+                Bx[pos(l1, m1)][pos(l2, m2)] += -1. / std::sqrt(2.) * B_lm(l2, 0);
+              if (l1 == l2 + 1 && m1 == 1 && m2 == 0)
+                Bx[pos(l1, m1)][pos(l2, m2)] += 1. / std::sqrt(2.) * B_lm(l2 + 1, 1);
+            }
+          } // m2
+        } // l2
+      } // m1
+    } // l1
+    return Bx;
+  }
+
+  static MatrixType create_By()
+  {
+    MatrixType By(dimRange, dimRange, 0.);
+    const auto& pos = helper<only_even>::pos;
+    for (int l1 = 0; l1 <= static_cast<int>(order); ++l1) {
+      for (int m1 = -int(l1); std::abs(m1) <= l1; ++m1) {
+        for (int l2 = 0; l2 <= static_cast<int>(order); ++l2) {
+          for (int m2 = -int(l2); std::abs(m2) <= l2; ++m2) {
+            if (!only_even || (!((m1 + l1) % 2) && !((m2 + l2) % 2))) {
+              if (l1 == l2 + 1 && m1 == -m2 + 1 && m2 > 0)
+                By[pos(l1, m1)][pos(l2, m2)] += 0.5 * (1. - (m2 == 1)) * B_lm(l2 + 1, -m2 + 1);
+              if (l1 == l2 - 1 && m1 == -m2 + 1 && m2 > 0)
+                By[pos(l1, m1)][pos(l2, m2)] += -0.5 * (1. - (m2 == 1)) * B_lm(l2, m2);
+              if (l1 == l2 - 1 && m1 == -m2 - 1 && m2 > 0)
+                By[pos(l1, m1)][pos(l2, m2)] += -0.5 * B_lm(l2, -m2);
+              if (l1 == l2 + 1 && m1 == -m2 - 1 && m2 > 0)
+                By[pos(l1, m1)][pos(l2, m2)] += 0.5 * B_lm(l2 + 1, m2 + 1);
+              if (l1 == l2 - 1 && m1 == -m2 - 1 && m2 < 0)
+                By[pos(l1, m1)][pos(l2, m2)] += 0.5 * std::sqrt(1. + (-m2 == 1)) * B_lm(l2, -m2);
+              if (l1 == l2 + 1 && m1 == -m2 - 1 && m2 < 0)
+                By[pos(l1, m1)][pos(l2, m2)] += -0.5 * std::sqrt(1. + (-m2 == 1)) * B_lm(l2 + 1, m2 + 1);
+              if (l1 == l2 - 1 && m1 == -m2 + 1 && m2 < 0)
+                By[pos(l1, m1)][pos(l2, m2)] += 0.5 * B_lm(l2, m2);
+              if (l1 == l2 + 1 && m1 == -m2 + 1 && m2 < 0)
+                By[pos(l1, m1)][pos(l2, m2)] += -0.5 * B_lm(l2 + 1, -m2 + 1);
+              if (l1 == l2 - 1 && m1 == -1 && m2 == 0)
+                By[pos(l1, m1)][pos(l2, m2)] += -1. / std::sqrt(2.) * B_lm(l2, 0);
+              if (l1 == l2 + 1 && m1 == -1 && m2 == 0)
+                By[pos(l1, m1)][pos(l2, m2)] += 1. / std::sqrt(2.) * B_lm(l2 + 1, 1);
+            }
+          } // m2
+        } // l2
+      } // m1
+    } // l1
+    return By;
+  } // ... create_By()
+
+  static MatrixType create_Bz()
+  {
+    MatrixType Bz(dimRange, dimRange, 0);
+    const auto& pos = helper<only_even>::pos;
+    for (int l1 = 0; l1 <= static_cast<int>(order); ++l1) {
+      for (int m1 = -l1; std::abs(m1) <= l1; ++m1) {
+        for (int l2 = 0; l2 <= static_cast<int>(order); ++l2) {
+          for (int m2 = -l2; std::abs(m2) <= l2; ++m2) {
+            if (!only_even || (!((m1 + l1) % 2) && !((m2 + l2) % 2))) {
+              if (m1 == m2 && l1 == l2 + 1)
+                Bz[pos(l1, m1)][pos(l2, m2)] += A_lm(l2 + 1, m2);
+              if (m1 == m2 && l1 == l2 - 1)
+                Bz[pos(l1, m1)][pos(l2, m2)] += A_lm(l2, m2);
+            }
+          } // m2
+        } // l2
+      } // m1
+    } // l1
+    return Bz;
+  } // ... create_Bz()
+
+  template <bool even, class anything = void>
+  struct helper
+  {
+    // Converts a pair (l, m) to a vector index. The vector is ordered by l first, then by m.
+    // Each l has 2l+1 values of m, so (l, m) has position
+    // (\sum_{k=0}^{l-1} (2k+1)) + (m+l) = l^2 + m + l
+    static size_t pos(const int l, const int m)
+    {
+      const int ret = l * l + m + l;
+      assert(ret >= 0 && std::abs(m) <= l);
+      return static_cast<size_t>(ret);
+    }
+  };
+
+  template <class anything>
+  struct helper<true, anything>
+  {
+    // Converts a pair (l, m) to a vector index. The vector is ordered by l first, then by m.
+    // Each l has l+1 values of m (as only m s.t. m+l is even are considered), so (l, m) has position
+    // (\sum_{k=0}^{l-1} (k+1)) + (m+l)/2 = l(l+1)/2 + (l+m)/2
+    static size_t pos(const int l, const int m)
+    {
+      const int ret = l * (l + 1) / 2 + (m + l) / 2;
+      assert(std::abs(m) <= l && ret >= 0);
+      return static_cast<size_t>(ret);
+    }
+  };
+
+  // Notation from Garrett, Hauck, "A Comparison of Moment Closures for Linear Kinetic Transport Equations: The Line
+  // Source Benchmark",
+  // http://www.tandfonline.com/doi/full/10.1080/00411450.2014.910226?src=recsys&, Section 4.1
+  RangeFieldType N_lm(const int l, const int m) const
+  {
+    static constexpr auto frac_4pi = 1. / (4. * M_PI);
+    assert(l >= 0 && m >= 0 && m <= l);
+    // return std::sqrt((2. * l + 1.) * XT::Common::factorial(l - m) / (XT::Common::factorial(l + m) * 4. * M_PI));
+    auto factor = 1.;
+    for (int ii = l - m + 1; ii <= l + m; ++ii)
+      factor *= ii;
+    factor = 1. / factor;
+    return std::sqrt((2. * l + 1.) * factor * frac_4pi);
+  }
+
+  RangeFieldType evaluate_lm(const DomainFieldType theta, const DomainFieldType phi, const int l, const int m) const
+  {
+    const auto cos_theta = std::cos(theta);
+    assert(l >= 0 && std::abs(m) <= l);
+    if (m < 0)
+      return std::sqrt(2) * N_lm(l, -m) * boost::math::legendre_p(l, -m, cos_theta) * std::sin(-m * phi);
+    else if (m == 0)
+      return N_lm(l, 0) * boost::math::legendre_p(l, 0, cos_theta);
+    else
+      return std::sqrt(2) * N_lm(l, m) * boost::math::legendre_p(l, m, cos_theta) * std::cos(m * phi);
+  }
+
+  using BaseType::quadratures_;
+}; // class RealSphericalHarmonicsMomentBasis<DomainFieldType, 3, ...>
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t order,
+          size_t fluxDim,
+          bool only_even,
+          EntropyType entropy>
+const size_t RealSphericalHarmonicsMomentBasis<DomainFieldType, RangeFieldType, order, fluxDim, only_even, entropy>::
+    num_refinements;
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_BASISFUNCTIONS_SPHERICALHARMONICS_HH
diff --git a/dune/gdt/test/momentmodels/density_evaluations.hh b/dune/gdt/test/momentmodels/density_evaluations.hh
new file mode 100644
index 0000000000000000000000000000000000000000..ff863a98f33d8520c67ada453c52eb1ca61aa87b
--- /dev/null
+++ b/dune/gdt/test/momentmodels/density_evaluations.hh
@@ -0,0 +1,177 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner (2018)
+
+#ifndef DUNE_GDT_MOMENTMODELS_DENSITYEVALUATOR_HH
+#define DUNE_GDT_MOMENTMODELS_DENSITYEVALUATOR_HH
+
+#include <string>
+#include <functional>
+
+#include <dune/xt/grid/functors/interfaces.hh>
+#include <dune/xt/common/parameter.hh>
+
+#include <dune/gdt/discretefunction/default.hh>
+#include <dune/gdt/test/momentmodels/entropyflux_kineticcoords.hh>
+#include <dune/gdt/operators/interfaces.hh>
+#include <dune/gdt/type_traits.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+template <class SpaceType, class VectorType, class MomentBasis, SlopeLimiterType slope>
+class LocalDensityEvaluator : public XT::Grid::ElementFunctor<typename SpaceType::GridViewType>
+{
+  using BaseType = XT::Grid::ElementFunctor<typename SpaceType::GridViewType>;
+
+public:
+  using GridViewType = typename SpaceType::GridViewType;
+  using EntityType = typename GridViewType::template Codim<0>::Entity;
+  using IndexSetType = typename GridViewType::IndexSet;
+  using EntropyFluxType = EntropyBasedFluxEntropyCoordsFunction<GridViewType, MomentBasis, slope>;
+  using RangeFieldType = typename EntropyFluxType::RangeFieldType;
+  static const size_t dimFlux = EntropyFluxType::dimFlux;
+  static const size_t dimRange = EntropyFluxType::basis_dimRange;
+  using DiscreteFunctionType = DiscreteFunction<VectorType, GridViewType, dimRange, 1, RangeFieldType>;
+  using ConstDiscreteFunctionType = ConstDiscreteFunction<VectorType, GridViewType, dimRange, 1, RangeFieldType>;
+  using DomainType = FieldVector<RangeFieldType, dimFlux>;
+  using BoundaryDistributionType = std::function<std::function<RangeFieldType(const DomainType&)>(const DomainType&)>;
+
+  explicit LocalDensityEvaluator(const SpaceType& space,
+                                 const VectorType& alpha_dofs,
+                                 EntropyFluxType& analytical_flux,
+                                 const BoundaryDistributionType& boundary_distribution,
+                                 const RangeFieldType min_acceptable_density,
+                                 const XT::Common::Parameter& param)
+    : space_(space)
+    , alpha_(space_, alpha_dofs, "alpha")
+    , local_alpha_(alpha_.local_discrete_function())
+    , analytical_flux_(analytical_flux)
+    , boundary_distribution_(boundary_distribution)
+    , min_acceptable_density_(min_acceptable_density)
+    , param_(param)
+    , index_set_(space_.grid_view().indexSet())
+  {}
+
+  explicit LocalDensityEvaluator(LocalDensityEvaluator& other)
+    : BaseType(other)
+    , space_(other.space_)
+    , alpha_(space_, other.alpha_.dofs().vector(), "source")
+    , local_alpha_(alpha_.local_discrete_function())
+    , analytical_flux_(other.analytical_flux_)
+    , boundary_distribution_(other.boundary_distribution_)
+    , min_acceptable_density_(other.min_acceptable_density_)
+    , param_(other.param_)
+    , index_set_(space_.grid_view().indexSet())
+  {}
+
+  XT::Grid::ElementFunctor<GridViewType>* copy() override final
+  {
+    return new LocalDensityEvaluator(*this);
+  }
+
+  void apply_local(const EntityType& entity) override final
+  {
+    local_alpha_->bind(entity);
+    const auto entity_index = index_set_.index(entity);
+    XT::Common::FieldVector<RangeFieldType, dimRange> alpha;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      alpha[ii] = local_alpha_->dofs().get_entry(ii);
+    analytical_flux_.store_evaluations(entity_index, alpha);
+    for (auto&& intersection : Dune::intersections(space_.grid_view(), entity))
+      if (intersection.boundary())
+        analytical_flux_.store_boundary_evaluations(
+            boundary_distribution_(intersection.geometry().center()), entity_index, intersection.indexInInside());
+    analytical_flux_.set_eta_ast_pointers();
+  } // void apply_local(...)
+
+private:
+  const SpaceType& space_;
+  const ConstDiscreteFunctionType alpha_;
+  std::unique_ptr<typename ConstDiscreteFunctionType::ConstLocalDiscreteFunctionType> local_alpha_;
+  EntropyFluxType& analytical_flux_;
+  const BoundaryDistributionType& boundary_distribution_;
+  const RangeFieldType min_acceptable_density_;
+  const XT::Common::Parameter& param_;
+  const typename SpaceType::GridViewType::IndexSet& index_set_;
+}; // class LocalDensityEvaluator<...>
+
+template <class MomentBasisImp,
+          class SpaceImp,
+          SlopeLimiterType slope,
+          class MatrixType = typename XT::LA::Container<typename MomentBasisImp::RangeFieldType>::MatrixType>
+class DensityEvaluator
+  : public OperatorInterface<MatrixType, typename SpaceImp::GridViewType, MomentBasisImp::dimRange, 1>
+{
+  using BaseType = OperatorInterface<MatrixType, typename SpaceImp::GridViewType, MomentBasisImp::dimRange, 1>;
+
+public:
+  using typename BaseType::VectorType;
+  using MomentBasis = MomentBasisImp;
+  using SpaceType = SpaceImp;
+  using SourceSpaceType = SpaceImp;
+  using RangeSpaceType = SpaceImp;
+  using EntropyFluxType = EntropyBasedFluxEntropyCoordsFunction<typename SpaceType::GridViewType, MomentBasis, slope>;
+  using RangeFieldType = typename MomentBasis::RangeFieldType;
+  using LocalDensityEvaluatorType = LocalDensityEvaluator<SpaceType, VectorType, MomentBasis, slope>;
+  using BoundaryDistributionType = typename LocalDensityEvaluatorType::BoundaryDistributionType;
+
+  DensityEvaluator(EntropyFluxType& analytical_flux,
+                   const SpaceType& space,
+                   const BoundaryDistributionType& boundary_distribution,
+                   const RangeFieldType min_acceptable_density)
+    : analytical_flux_(analytical_flux)
+    , space_(space)
+    , boundary_distribution_(boundary_distribution)
+    , min_acceptable_density_(min_acceptable_density)
+  {}
+
+  bool linear() const override final
+  {
+    return false;
+  }
+
+  const SourceSpaceType& source_space() const override final
+  {
+    return space_;
+  }
+
+  const RangeSpaceType& range_space() const override final
+  {
+    return space_;
+  }
+
+  void
+  apply(const VectorType& alpha, VectorType& /*range*/, const XT::Common::Parameter& param = {}) const override final
+  {
+    analytical_flux_.exp_evaluations().resize(space_.grid_view().size(0));
+    if (EntropyFluxType::entropy != EntropyType::MaxwellBoltzmann) {
+      analytical_flux_.eta_ast_prime_evaluations().resize(space_.grid_view().size(0));
+      analytical_flux_.eta_ast_twoprime_evaluations().resize(space_.grid_view().size(0));
+    }
+    analytical_flux_.boundary_distribution_evaluations().resize(space_.grid_view().size(0));
+    LocalDensityEvaluatorType local_density_evaluator(
+        space_, alpha, analytical_flux_, boundary_distribution_, min_acceptable_density_, param);
+    auto walker = XT::Grid::Walker<typename SpaceType::GridViewType>(space_.grid_view());
+    walker.append(local_density_evaluator);
+    walker.walk(true);
+  } // void apply(...)
+
+private:
+  EntropyFluxType& analytical_flux_;
+  const SpaceType& space_;
+  const BoundaryDistributionType& boundary_distribution_;
+  const RangeFieldType min_acceptable_density_;
+}; // class DensityEvaluator<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_MOMENTMODELS_DENSITYEVALUATOR_HH
diff --git a/dune/gdt/test/momentmodels/entropic-coords-mn-discretization.hh b/dune/gdt/test/momentmodels/entropic-coords-mn-discretization.hh
new file mode 100644
index 0000000000000000000000000000000000000000..9ea77ad2e2ac1c2baab6f9aca873b94b71c9d671
--- /dev/null
+++ b/dune/gdt/test/momentmodels/entropic-coords-mn-discretization.hh
@@ -0,0 +1,376 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
+// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+// Authors:
+//   Tobias Leibner  (2016)
+
+#ifndef DUNE_GDT_TEST_HYPERBOLIC_ENTROPIC_COORDS_MN_DISCRETIZATION_HH
+#define DUNE_GDT_TEST_HYPERBOLIC_ENTROPIC_COORDS_MN_DISCRETIZATION_HH
+
+#include <chrono>
+
+#include <dune/xt/common/parallel/threadmanager.hh>
+#include <dune/xt/common/string.hh>
+#include <dune/xt/common/test/gtest/gtest.h>
+
+#include <dune/xt/grid/information.hh>
+#include <dune/xt/grid/gridprovider.hh>
+
+#include <dune/xt/la/container.hh>
+
+#include <dune/gdt/discretefunction/default.hh>
+#include <dune/gdt/interpolations/default.hh>
+#include <dune/gdt/local/numerical-fluxes/kinetic.hh>
+#include <dune/gdt/local/operators/advection-fv.hh>
+#include <dune/gdt/local/operators/generic.hh>
+#include <dune/gdt/operators/advection-fv.hh>
+#include <dune/gdt/operators/advection-fv-entropybased.hh>
+#include <dune/gdt/operators/localizable-operator.hh>
+#include <dune/gdt/operators/reconstruction/linear_kinetic.hh>
+#include <dune/gdt/spaces/l2/finite-volume.hh>
+#include <dune/gdt/test/momentmodels/entropyflux_kineticcoords.hh>
+#include <dune/gdt/test/momentmodels/entropyflux.hh>
+#include <dune/gdt/test/momentmodels/hessianinverter.hh>
+#include <dune/gdt/test/momentmodels/density_evaluations.hh>
+#include <dune/gdt/tools/timestepper/adaptive-rungekutta.hh>
+#include <dune/gdt/tools/timestepper/explicit-rungekutta.hh>
+#include <dune/gdt/tools/timestepper/fractional-step.hh>
+#include <dune/gdt/tools/timestepper/matrix-exponential-kinetic-isotropic.hh>
+
+#include <dune/gdt/test/momentmodels/kineticequation.hh>
+
+#include "pn-discretization.hh"
+
+template <class TestCaseType>
+struct HyperbolicEntropicCoordsMnDiscretization
+{
+  // returns: (l1norm, l2norm, linfnorm, MPI rank)
+  static std::pair<Dune::FieldVector<double, 3>, int> run(size_t num_save_steps = 1,
+                                                          size_t num_output_steps = 0,
+                                                          size_t quad_order = size_t(-1),
+                                                          size_t quad_refinements = size_t(-1),
+                                                          std::string grid_size = "",
+                                                          size_t overlap_size = 2,
+                                                          double t_end = 0.,
+                                                          std::string filename = "",
+                                                          bool /*disable_thread_cache*/ = false)
+  {
+    using namespace Dune;
+    using namespace Dune::GDT;
+
+    //******************* get typedefs and constants from ProblemType **********************//
+    using MomentBasis = typename TestCaseType::MomentBasis;
+    using DiscreteFunctionType = typename TestCaseType::DiscreteFunctionType;
+    using GridType = typename TestCaseType::GridType;
+    using SpaceType = typename TestCaseType::SpaceType;
+    using AdvectionSourceSpaceType = typename TestCaseType::AdvectionSourceSpaceType;
+    using GV = typename TestCaseType::GridViewType;
+    //    using E = XT::Grid::extract_entity_t<GV>;
+    using I = XT::Grid::extract_intersection_t<GV>;
+    using ProblemType = typename TestCaseType::ProblemType;
+    using RangeFieldType = typename MomentBasis::RangeFieldType;
+    using BoundaryValueType = typename ProblemType::BoundaryValueType;
+    static constexpr size_t dimDomain = MomentBasis::dimDomain;
+    static constexpr size_t dimRange = MomentBasis::dimRange;
+    using MatrixType = typename XT::LA::Container<RangeFieldType>::MatrixType;
+    using VectorType = typename XT::LA::Container<RangeFieldType>::VectorType;
+    using GenericFunctionType = XT::Functions::GenericFunction<dimDomain, dimRange, 1, RangeFieldType>;
+    using DomainType = FieldVector<RangeFieldType, dimDomain>;
+    using RangeType = FieldVector<RangeFieldType, dimRange>;
+    //        static const RangeFieldType scale_factor = 1e2;
+
+    //******************* create grid and FV space ***************************************
+    auto grid_config = ProblemType::default_grid_cfg();
+    if (!grid_size.empty())
+      grid_config["num_elements"] = grid_size;
+    grid_config["overlap_size"] = XT::Common::to_string(overlap_size);
+    const auto grid_ptr =
+        Dune::XT::Grid::CubeGridProviderFactory<GridType>::create(grid_config, MPIHelper::getCommunicator()).grid_ptr();
+    assert(grid_ptr->comm().size() == 1 || grid_ptr->overlapSize(0) > 0);
+    const GV grid_view(grid_ptr->leafGridView());
+    const SpaceType fv_space(grid_view);
+    const AdvectionSourceSpaceType advection_source_space(grid_view);
+
+    //******************* create EquationType object ***************************************
+    std::shared_ptr<const MomentBasis> basis_functions = std::make_shared<const MomentBasis>(
+        quad_order == size_t(-1) ? MomentBasis::default_quad_order() : quad_order,
+        quad_refinements == size_t(-1) ? MomentBasis::default_quad_refinements() : quad_refinements);
+    const std::unique_ptr<ProblemType> problem_ptr =
+        XT::Common::make_unique<ProblemType>(*basis_functions, grid_view, grid_config);
+    const auto& problem = *problem_ptr;
+    const auto initial_values_u = problem.initial_values();
+    const auto boundary_values_u = problem.boundary_values();
+    const auto boundary_distribution = problem.boundary_distribution();
+
+    using AnalyticalFluxType = typename ProblemType::FluxType;
+    constexpr SlopeLimiterType slope =
+        TestCaseType::reconstruction ? SlopeLimiterType::minmod : SlopeLimiterType::no_slope;
+    using EntropyFluxType = EntropyBasedFluxEntropyCoordsFunction<GV, MomentBasis, slope>;
+    using OldEntropyFluxType = EntropyBasedFluxFunction<GV, MomentBasis>;
+    auto flux = problem.flux();
+    auto* entropy_flux = dynamic_cast<OldEntropyFluxType*>(flux.get());
+    auto analytical_flux = std::make_unique<EntropyFluxType>(*entropy_flux);
+    const RangeFieldType CFL = problem.CFL();
+
+    // calculate boundary values for alpha
+    std::map<DomainType, RangeType, XT::Common::FieldVectorFloatLess> alpha_boundary_vals;
+    for (const auto& element : Dune::elements(grid_view))
+      for (const auto& intersection : Dune::intersections(grid_view, element))
+        if (intersection.boundary()) {
+          const auto x = intersection.geometry().center();
+          const auto u = boundary_values_u->evaluate(x);
+          alpha_boundary_vals.insert(std::make_pair(x, analytical_flux->get_alpha(u)));
+        }
+    GenericFunctionType boundary_values_alpha(
+        1, [&](const DomainType& x, const XT::Common::Parameter&) { return alpha_boundary_vals[x]; });
+
+
+    // ***************** project initial values to discrete function *********************
+    // create a discrete function for the solution
+    DiscreteFunctionType u(fv_space, "solution");
+    DiscreteFunctionType alpha(fv_space, "solution");
+    // project initial values
+    default_interpolation(*initial_values_u, u, grid_view);
+
+    // convert initial values to alpha
+    const auto u_local_func = u.local_discrete_function();
+    const auto alpha_local_func = alpha.local_discrete_function();
+    XT::Common::FieldVector<RangeFieldType, dimRange> u_local;
+    for (auto&& element : Dune::elements(grid_view)) {
+      u_local_func->bind(element);
+      alpha_local_func->bind(element);
+      for (size_t ii = 0; ii < dimRange; ++ii)
+        u_local[ii] = u_local_func->dofs().get_entry(ii);
+      const auto alpha_local = analytical_flux->get_alpha(u_local);
+      for (size_t ii = 0; ii < dimRange; ++ii)
+        alpha_local_func->dofs().set_entry(ii, alpha_local[ii]);
+    }
+
+    // ******************** choose flux and rhs operator and timestepper ******************************************
+
+    using AdvectionOperatorType = AdvectionFvOperator<MatrixType, GV, dimRange>;
+    using HessianInverterType = EntropicHessianInverter<MomentBasis, SpaceType, slope>;
+#if 0
+    using ReconstructionOperatorType = PointwiseLinearReconstructionNoCharOperator<
+                                                                             GV,
+                                                                             BoundaryValueType,
+        EntropyFluxType,
+                                                                             VectorType,
+                                                                             RangeType>;
+#else
+    using ReconstructionOperatorType =
+        PointwiseLinearKineticReconstructionOperator<GV, BoundaryValueType, EntropyFluxType, VectorType, RangeType>;
+#endif
+    using ReconstructionAdvectionOperatorType =
+        AdvectionWithPointwiseReconstructionOperator<AdvectionOperatorType, ReconstructionOperatorType>;
+    using NonEntropicAdvectionOperatorType = ReconstructionAdvectionOperatorType;
+    //    using FvOperatorType = EntropicCoordinatesOperator<
+    //        NonEntropicAdvectionOperatorType,
+    //        HessianInverterType>;
+    using NonEntropicRhsOperatorType = LocalizableOperator<MatrixType, GV, dimRange>;
+    //    using RhsOperatorType = EntropicCoordinatesOperator<NonEntropicRhsOperatorType, HessianInverterType>;
+    using DensityOperatorType = DensityEvaluator<MomentBasis, SpaceType, slope>;
+    using CombinedOperatorType = EntropicCoordinatesCombinedOperator<DensityOperatorType,
+                                                                     NonEntropicAdvectionOperatorType,
+                                                                     NonEntropicRhsOperatorType,
+                                                                     HessianInverterType>;
+
+    //    using OperatorTimeStepperType =
+    //        ExplicitRungeKuttaTimeStepper<FvOperatorType,
+    //                                      DiscreteFunctionType,
+    //                                      TimeStepperMethods::explicit_euler>;
+    //    using RhsTimeStepperType =
+    //        ExplicitRungeKuttaTimeStepper<RhsOperatorType,
+    //                                      DiscreteFunctionType,
+    //                                      TimeStepperMethods::explicit_euler>;
+
+    //    using OperatorTimeStepperType =
+    //        AdaptiveRungeKuttaTimeStepper<FvOperatorType,
+    //                                      DiscreteFunctionType>;
+    //    using RhsTimeStepperType =
+    //        AdaptiveRungeKuttaTimeStepper<RhsOperatorType,
+    //                                      DiscreteFunctionType>;
+
+    //    using TimeStepperType = FractionalTimeStepper<OperatorTimeStepperType, RhsTimeStepperType>;
+
+    using TimeStepperType =
+        AdaptiveRungeKuttaTimeStepper<CombinedOperatorType, DiscreteFunctionType, TimeStepperMethods::dormand_prince>;
+
+    // *************** Calculate dx and initial dt **************************************
+    Dune::XT::Grid::Dimensions<GV> dimensions(grid_view);
+    RangeFieldType dx = dimensions.entity_width.max();
+    if (dimDomain == 2)
+      dx /= std::sqrt(2);
+    if (dimDomain == 3)
+      dx /= std::sqrt(3);
+    RangeFieldType dt = CFL * dx;
+
+    // *********************** create operators and timesteppers ************************************
+    NumericalKineticFlux<GV, MomentBasis, EntropyFluxType> numerical_flux(*analytical_flux, *basis_functions);
+    AdvectionOperatorType advection_operator(grid_view, numerical_flux, advection_source_space, fv_space);
+
+    // boundary treatment
+    using BoundaryOperator =
+        LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator<I, VectorType, GV, dimRange>;
+    using LambdaType = typename BoundaryOperator::LambdaType;
+    using StateType = typename BoundaryOperator::StateType;
+
+    // calculate boundary kinetic fluxes
+    // apply density_operator first to store boundary_evaluations
+    const double min_acceptable_density = problem.psi_vac() / 10;
+    DensityOperatorType density_operator(*analytical_flux, fv_space, boundary_distribution, min_acceptable_density);
+    density_operator.apply(alpha.dofs().vector(), alpha.dofs().vector());
+
+    // store boundary fluxes
+    std::map<DomainType, RangeType, XT::Common::FieldVectorFloatLess> boundary_fluxes;
+    for (const auto& element : Dune::elements(grid_view))
+      for (const auto& intersection : Dune::intersections(grid_view, element))
+        if (intersection.boundary()) {
+          const auto x = intersection.geometry().center();
+          const auto dd = intersection.indexInInside() / 2;
+          const auto boundary_flux = problem.kinetic_boundary_flux(x, intersection.centerUnitOuterNormal()[dd], dd);
+          boundary_fluxes.insert(std::make_pair(x, boundary_flux));
+        }
+    GenericFunctionType boundary_kinetic_fluxes(
+        1, [&](const DomainType& x, const XT::Common::Parameter&) { return boundary_fluxes[x]; });
+
+
+    LambdaType boundary_lambda =
+        [&](const I& intersection,
+            const FieldVector<RangeFieldType, dimDomain - 1>& xx_in_reference_intersection_coordinates,
+            const AnalyticalFluxType& /*flux*/,
+            const StateType& /*u*/,
+            const XT::Common::Parameter& /*param*/) {
+          return boundary_kinetic_fluxes.evaluate(
+              intersection.geometry().global(xx_in_reference_intersection_coordinates));
+        };
+    XT::Grid::ApplyOn::NonPeriodicBoundaryIntersections<GV> filter;
+    advection_operator.append(boundary_lambda, {}, filter);
+
+    ReconstructionOperatorType reconstruction_operator(boundary_values_alpha, fv_space, *analytical_flux);
+    ReconstructionAdvectionOperatorType reconstruction_advection_operator(advection_operator, reconstruction_operator);
+
+    if (XT::Common::is_zero(t_end))
+      t_end = problem.t_end();
+
+    if (!filename.empty())
+      filename += "_";
+    filename += "minmod_";
+    filename += ProblemType::static_id();
+    filename += "_grid_" + grid_config["num_elements"];
+    filename += "_tend_" + XT::Common::to_string(t_end);
+    filename += "_quad_" + XT::Common::to_string(quad_order);
+    filename += TestCaseType::reconstruction ? "_ord2" : "_ord1";
+    filename += "_" + basis_functions->mn_name();
+
+    HessianInverterType hessian_inverter(*analytical_flux, fv_space);
+    auto& non_entropic_advection_operator = reconstruction_advection_operator;
+
+    const auto u_iso = basis_functions->u_iso();
+    const auto basis_integrated = basis_functions->integrated();
+    const auto sigma_a = problem.sigma_a();
+    const auto sigma_s = problem.sigma_s();
+    const auto Q = problem.Q();
+    auto rhs_func = [&](const auto& /*source*/,
+                        const auto& /*local_source*/,
+                        auto& local_range,
+                        const Dune::XT::Common::Parameter& /*param*/) {
+      const auto& element = local_range.element();
+      const auto center = element.geometry().center();
+      const auto u_elem = analytical_flux->get_u(fv_space.grid_view().indexSet().index(element));
+      const auto sigma_a_value = sigma_a->evaluate(center)[0];
+      const auto sigma_s_value = sigma_s->evaluate(center)[0];
+      const auto sigma_t_value = sigma_a_value + sigma_s_value;
+      const auto Q_value = Q->evaluate(center)[0];
+      auto ret = u_elem * (-sigma_t_value);
+      ret += u_iso * basis_functions->density(u_elem) * sigma_s_value;
+      ret += basis_integrated * Q_value;
+      for (size_t ii = 0; ii < local_range.dofs().size(); ++ii)
+        local_range.dofs()[ii] += ret[ii];
+    };
+    NonEntropicRhsOperatorType non_entropic_rhs_operator(grid_view, fv_space, fv_space);
+    non_entropic_rhs_operator.append(GenericLocalElementOperator<VectorType, GV, dimRange>(rhs_func));
+    //    RhsOperatorType rhs_operator(non_entropic_rhs_operator, hessian_inverter);
+    CombinedOperatorType combined_operator(
+        density_operator, non_entropic_advection_operator, non_entropic_rhs_operator, hessian_inverter);
+
+    // ******************************** do the time steps ***********************************************************
+    //    OperatorTimeStepperType timestepper_op(fv_operator, alpha, -1.0);
+    //    RhsTimeStepperType timestepper_rhs(rhs_operator, alpha, 1.0);
+    //    TimeStepperType timestepper(timestepper_op, timestepper_rhs);
+    TimeStepperType timestepper(combined_operator, alpha, 1.);
+
+    auto begin_time = std::chrono::steady_clock::now();
+    auto visualizer = std::make_unique<XT::Functions::GenericVisualizer<dimRange, 1, double>>(
+        1, [&basis_functions, &analytical_flux](const int /*comp*/, const auto& val) {
+          return basis_functions->density(analytical_flux->get_u(val));
+        });
+    timestepper.solve(t_end,
+                      dt,
+                      num_save_steps,
+                      num_output_steps,
+                      false,
+                      true,
+                      true,
+                      false,
+                      filename,
+                      *visualizer,
+                      basis_functions->stringifier());
+    auto end_time = std::chrono::steady_clock::now();
+    std::chrono::duration<double> time_diff = end_time - begin_time;
+    if (grid_view.comm().rank() == 0)
+      std::cout << "Solving took: " << XT::Common::to_string(time_diff.count(), 15) << " s" << std::endl;
+
+    auto ret = std::make_pair(FieldVector<double, 3>(0.), int(0));
+    double& l1norm = ret.first[0];
+    double& l2norm = ret.first[1];
+    double& linfnorm = ret.first[2];
+    ret.second = grid_view.comm().rank();
+    const auto& current_sol = timestepper.current_solution();
+    const auto local_sol = current_sol.local_function();
+    for (const auto& entity : elements(grid_view, Dune::Partitions::interior)) {
+      local_sol->bind(entity);
+      const auto val = local_sol->evaluate(entity.geometry().local(entity.geometry().center()));
+      RangeFieldType psi = basis_functions->density(val);
+      l1norm += std::abs(psi) * entity.geometry().volume();
+      l2norm += std::pow(psi, 2) * entity.geometry().volume();
+      linfnorm = std::max(std::abs(psi), linfnorm);
+    }
+    l1norm = grid_view.comm().sum(l1norm);
+    l2norm = grid_view.comm().sum(l2norm);
+    linfnorm = grid_view.comm().max(linfnorm);
+    l2norm = std::sqrt(l2norm);
+    return ret;
+  }
+};
+
+template <class TestCaseType>
+struct HyperbolicEntropicCoordsMnTest
+  : public HyperbolicEntropicCoordsMnDiscretization<TestCaseType>
+  , public ::testing::Test
+{
+  void run()
+  {
+    auto norms = HyperbolicEntropicCoordsMnDiscretization<TestCaseType>::run(
+                     1,
+                     0,
+                     TestCaseType::quad_order,
+                     TestCaseType::quad_refinements,
+                     "",
+                     2,
+                     TestCaseType::t_end,
+                     "test_kinetic",
+                     Dune::GDT::is_full_moment_basis<typename TestCaseType::MomentBasis>::value)
+                     .first;
+    const double l1norm = norms[0];
+    const double l2norm = norms[1];
+    const double linfnorm = norms[2];
+    using ResultsType = typename TestCaseType::ExpectedResultsType;
+    EXPECT_NEAR(ResultsType::l1norm, l1norm, ResultsType::l1norm * ResultsType::tol);
+    EXPECT_NEAR(ResultsType::l2norm, l2norm, ResultsType::l2norm * ResultsType::tol);
+    EXPECT_NEAR(ResultsType::linfnorm, linfnorm, ResultsType::linfnorm * ResultsType::tol);
+  }
+};
+
+#endif // DUNE_GDT_TEST_HYPERBOLIC_ENTROPIC_COORDS_MN_DISCRETIZATION_HH
diff --git a/dune/gdt/test/momentmodels/entropyflux.hh b/dune/gdt/test/momentmodels/entropyflux.hh
new file mode 100644
index 0000000000000000000000000000000000000000..e10a013de0dc4bf2225b651a554e3d0aea6bb396
--- /dev/null
+++ b/dune/gdt/test/momentmodels/entropyflux.hh
@@ -0,0 +1,362 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2017 - 2018)
+//   Tobias Leibner (2016 - 2017)
+//
+// Contributors: Tobias Leibner
+
+#ifndef DUNE_GDT_LOCAL_FLUXES_ENTROPYBASED_HH
+#define DUNE_GDT_LOCAL_FLUXES_ENTROPYBASED_HH
+
+#include <list>
+#include <memory>
+
+#include <dune/xt/common/float_cmp.hh>
+#include <dune/xt/common/vector_less.hh>
+
+#include <dune/xt/functions/interfaces/flux-function.hh>
+
+#include <dune/gdt/test/momentmodels/basisfunctions.hh>
+#include <dune/gdt/test/momentmodels/entropyflux_implementations.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+// Caches a specified number of (u, alpha) pairs. If the cache is full and another pair is added, the oldest existing
+// pair is dropped.
+template <class KeyVectorType, class ValueVectorType>
+class EntropyLocalCache
+{
+public:
+  using MapType = typename std::map<KeyVectorType, ValueVectorType, XT::Common::VectorLess>;
+  using IteratorType = typename MapType::iterator;
+  using ConstIteratorType = typename MapType::const_iterator;
+  using RangeFieldType = typename XT::Common::VectorAbstraction<KeyVectorType>::ScalarType;
+
+  EntropyLocalCache(const size_t capacity = 0)
+    : capacity_(capacity)
+  {}
+
+  void insert(const KeyVectorType& u, const ValueVectorType& alpha)
+  {
+    cache_.insert(std::make_pair(u, alpha));
+    keys_.push_back(u);
+    if (cache_.size() > capacity_) {
+      cache_.erase(keys_.front());
+      keys_.pop_front();
+    }
+  }
+
+  std::pair<RangeFieldType, ConstIteratorType> find_closest(const KeyVectorType& u) const
+  {
+    ConstIteratorType ret = cache_.begin();
+    if (ret == end())
+      return std::make_pair(std::numeric_limits<RangeFieldType>::max(), ret);
+    auto diff = u - ret->first;
+    // use infinity_norm as distance
+    RangeFieldType distance = infinity_norm(diff);
+    auto it = ret;
+    while (++it != end()) {
+      if (XT::Common::FloatCmp::eq(distance, 0.))
+        break;
+      diff = u - it->first;
+      RangeFieldType new_distance = infinity_norm(diff);
+      if (new_distance < distance) {
+        distance = new_distance;
+        ret = it;
+      }
+    }
+    return std::make_pair(distance, ret);
+  }
+
+  IteratorType begin()
+  {
+    return cache_.begin();
+  }
+
+  ConstIteratorType begin() const
+  {
+    return cache_.begin();
+  }
+
+  IteratorType end()
+  {
+    return cache_.end();
+  }
+
+  ConstIteratorType end() const
+  {
+    return cache_.end();
+  }
+
+private:
+  static RangeFieldType infinity_norm(const KeyVectorType& vec)
+  {
+    RangeFieldType ret = std::abs(vec[0]);
+    for (size_t ii = 1; ii < vec.size(); ++ii)
+      ret = std::max(ret, std::abs(vec[ii]));
+    return ret;
+  }
+
+  size_t capacity_;
+  MapType cache_;
+  std::list<KeyVectorType> keys_;
+};
+
+
+// This flux function only does the caching, which is used for providing a good initial guess and to avoid solving the
+// same optimization problem twice. The actual implementations of the optimization algorithm are in
+// entropyflux_implementations.hh
+template <class GridViewImp, class MomentBasisImp>
+class EntropyBasedFluxFunction
+  : public XT::Functions::FluxFunctionInterface<XT::Grid::extract_entity_t<GridViewImp>,
+                                                MomentBasisImp::dimRange,
+                                                MomentBasisImp::dimFlux,
+                                                MomentBasisImp::dimRange,
+                                                typename MomentBasisImp::R>
+{
+  using BaseType = typename XT::Functions::FluxFunctionInterface<XT::Grid::extract_entity_t<GridViewImp>,
+                                                                 MomentBasisImp::dimRange,
+                                                                 MomentBasisImp::dimFlux,
+                                                                 MomentBasisImp::dimRange,
+                                                                 typename MomentBasisImp::R>;
+  using ThisType = EntropyBasedFluxFunction;
+
+public:
+  using GridViewType = GridViewImp;
+  using MomentBasis = MomentBasisImp;
+  using IndexSetType = typename GridViewType::IndexSet;
+  static const size_t dimFlux = MomentBasis::dimFlux;
+  static const size_t basis_dimRange = MomentBasis::dimRange;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::LocalFunctionType;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::StateType;
+  using ImplementationType = EntropyBasedFluxImplementation<MomentBasis>;
+  using AlphaReturnType = typename ImplementationType::AlphaReturnType;
+  using VectorType = typename ImplementationType::VectorType;
+  using LocalCacheType = EntropyLocalCache<StateType, VectorType>;
+  static const size_t cache_size = 4 * dimFlux + 2;
+
+  explicit EntropyBasedFluxFunction(
+      const GridViewType& grid_view,
+      const MomentBasis& basis_functions,
+      const RangeFieldType tau = 1e-9,
+      const bool disable_realizability_check = false,
+      const RangeFieldType epsilon_gamma = 0.01,
+      const RangeFieldType chi = 0.5,
+      const RangeFieldType xi = 1e-3,
+      const std::vector<RangeFieldType> r_sequence = {0, 1e-8, 1e-6, 1e-4, 1e-3, 1e-2, 5e-2, 0.1, 0.5, 1},
+      const size_t k_0 = 500,
+      const size_t k_max = 1000,
+      const RangeFieldType epsilon = std::pow(2, -52))
+    : index_set_(grid_view.indexSet())
+    , use_thread_cache_(true)
+    , use_entity_cache_(true)
+    , entity_caches_(index_set_.size(0), LocalCacheType(cache_size))
+    , mutexes_(index_set_.size(0))
+    , implementation_(std::make_shared<ImplementationType>(
+          basis_functions, tau, disable_realizability_check, epsilon_gamma, chi, xi, r_sequence, k_0, k_max, epsilon))
+  {}
+
+  void enable_thread_cache()
+  {
+    use_thread_cache_ = true;
+  }
+
+  void disable_thread_cache()
+  {
+    use_thread_cache_ = false;
+  }
+
+  void enable_entity_cache()
+  {
+    use_entity_cache_ = true;
+  }
+
+  void disable_entity_cache()
+  {
+    use_entity_cache_ = false;
+  }
+
+  static const constexpr bool available = true;
+
+  class Localfunction : public LocalFunctionType
+  {
+    using BaseType = LocalFunctionType;
+
+  public:
+    using typename BaseType::DynamicJacobianRangeType;
+    using typename BaseType::E;
+    using typename BaseType::RangeReturnType;
+
+    Localfunction(const IndexSetType& index_set,
+                  std::vector<LocalCacheType>& entity_caches,
+                  const bool use_thread_cache,
+                  const bool use_entity_cache,
+                  std::vector<std::mutex>& mutexes,
+                  const ImplementationType& implementation)
+      : index_set_(index_set)
+      , thread_cache_(cache_size)
+      , entity_caches_(entity_caches)
+      , use_thread_cache_(use_thread_cache)
+      , use_entity_cache_(use_entity_cache)
+      , mutexes_(mutexes)
+      , implementation_(implementation)
+    {}
+
+    void post_bind(const E& element) override final
+    {
+      const auto index = index_set_.index(element);
+      entity_cache_ = &(entity_caches_[index]);
+      mutex_ = &(mutexes_[index]);
+    }
+
+    int order(const XT::Common::Parameter&) const override final
+    {
+      return 1.;
+    }
+
+    std::unique_ptr<AlphaReturnType> get_alpha(const StateType& u, const bool regularize) const
+    {
+      // find starting point. Candidates: alpha_iso and the entries in the two caches
+      std::lock_guard<std::mutex> DUNE_UNUSED(guard)(*mutex_);
+      const auto& basis_functions = implementation_.basis_functions();
+      static const auto u_iso = basis_functions.u_iso();
+      const auto density = basis_functions.density(u);
+      const auto alpha_iso = basis_functions.alpha_iso(density);
+      const auto u_iso_scaled = u_iso * density;
+      // calculate (inf-norm) distance to isotropic moment with same density
+      RangeFieldType distance = (u - u_iso_scaled).infinity_norm();
+      VectorType alpha_start = XT::Common::convert_to<VectorType>(alpha_iso);
+      if (!XT::Common::FloatCmp::eq(distance, 0.) && use_entity_cache_) {
+        // calculate distance to closest moment in entity_cache
+        const auto entity_cache_dist_and_it = entity_cache_->find_closest(u);
+        const auto& entity_cache_dist = entity_cache_dist_and_it.first;
+        if (entity_cache_dist < distance) {
+          distance = entity_cache_dist;
+          alpha_start = entity_cache_dist_and_it.second->second;
+        }
+        if (!XT::Common::FloatCmp::eq(distance, 0.) && use_thread_cache_) {
+          // calculate distance to closest moment in thread_cache
+          const auto thread_cache_dist_and_it = thread_cache_.find_closest(u);
+          const auto& thread_cache_dist = thread_cache_dist_and_it.first;
+          if (thread_cache_dist < distance) {
+            distance = thread_cache_dist;
+            alpha_start = thread_cache_dist_and_it.second->second;
+          }
+        }
+      }
+      // If alpha_start is already the solution, we are finished. Else start optimization.
+      if (XT::Common::FloatCmp::eq(distance, 0.)) {
+        return std::make_unique<AlphaReturnType>(std::make_pair(alpha_start, std::make_pair(u, 0.)));
+      } else {
+        auto ret = implementation_.get_alpha(u, alpha_start, regularize);
+        if (use_entity_cache_)
+          entity_cache_->insert(ret->second.first, ret->first);
+        if (use_thread_cache_)
+          thread_cache_.insert(ret->second.first, ret->first);
+        return ret;
+      }
+    }
+
+    virtual RangeReturnType evaluate(const DomainType& /*point_in_reference_element*/,
+                                     const StateType& u,
+                                     const XT::Common::Parameter& /*param*/ = {}) const override final
+    {
+      const auto alpha = get_alpha(u, true)->first;
+      return implementation_.evaluate_with_alpha(alpha);
+    }
+
+    virtual void jacobian(const DomainType& /*point_in_reference_element*/,
+                          const StateType& u,
+                          DynamicJacobianRangeType& result,
+                          const XT::Common::Parameter& /*param*/ = {}) const override final
+    {
+      const auto alpha = get_alpha(u, true)->first;
+      implementation_.jacobian_with_alpha(alpha, result);
+    } // ... jacobian(...)
+
+  private:
+    const IndexSetType& index_set_;
+    mutable LocalCacheType thread_cache_;
+    std::vector<LocalCacheType>& entity_caches_;
+    const bool use_thread_cache_;
+    const bool use_entity_cache_;
+    std::vector<std::mutex>& mutexes_;
+    const ImplementationType& implementation_;
+    LocalCacheType* entity_cache_;
+    std::mutex* mutex_;
+  }; // class Localfunction
+
+  bool x_dependent() const override final
+  {
+    return false;
+  }
+
+  std::unique_ptr<LocalFunctionType> local_function() const override final
+  {
+    return std::make_unique<Localfunction>(
+        index_set_, entity_caches_, use_thread_cache_, use_entity_cache_, mutexes_, *implementation_);
+  }
+
+  virtual std::unique_ptr<Localfunction> derived_local_function() const
+  {
+    return std::make_unique<Localfunction>(
+        index_set_, entity_caches_, use_thread_cache_, use_entity_cache_, mutexes_, *implementation_);
+  }
+
+  StateType evaluate_kinetic_flux(const E& inside_entity,
+                                  const E& outside_entity,
+                                  const StateType& u_i,
+                                  const StateType& u_j,
+                                  const DomainType& n_ij,
+                                  const size_t dd) const
+  {
+    // calculate \sum_{i=1}^d < \omega_i m G_\alpha(u) > n_i
+    const auto local_func = derived_local_function();
+    local_func->bind(inside_entity);
+    const auto alpha_i = local_func->get_alpha(u_i, true)->first;
+    local_func->bind(outside_entity);
+    const auto alpha_j = local_func->get_alpha(u_j, true)->first;
+    return implementation_->evaluate_kinetic_flux_with_alphas(alpha_i, alpha_j, n_ij, dd);
+  } // StateType evaluate_kinetic_flux(...)
+
+  // Returns alpha(u), starting from alpha_iso. To get better performance when calculating several alphas, use
+  // Localfunction's get_alpha
+  std::unique_ptr<AlphaReturnType> get_alpha(const StateType& u, const bool regularize) const
+  {
+    const auto& basis_functions = implementation_->basis_functions();
+    const auto density = basis_functions.density(u);
+    auto alpha_iso = implementation_->get_isotropic_alpha(density);
+    return implementation_->get_alpha(u, *alpha_iso, regularize);
+  }
+
+  const MomentBasis& basis_functions() const
+  {
+    return implementation_->basis_functions();
+  }
+
+  const IndexSetType& index_set_;
+  bool use_thread_cache_;
+  bool use_entity_cache_;
+  mutable std::vector<LocalCacheType> entity_caches_;
+  mutable std::vector<std::mutex> mutexes_;
+  std::shared_ptr<ImplementationType> implementation_;
+};
+
+template <class GridViewImp, class MomentBasisImp>
+const size_t EntropyBasedFluxFunction<GridViewImp, MomentBasisImp>::cache_size;
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_LOCAL_FLUXES_ENTROPYBASED_HH
diff --git a/dune/gdt/test/momentmodels/entropyflux_implementations.hh b/dune/gdt/test/momentmodels/entropyflux_implementations.hh
new file mode 100644
index 0000000000000000000000000000000000000000..bfaabadb6ec1218add42a7dcc23891e9ffe7aa6f
--- /dev/null
+++ b/dune/gdt/test/momentmodels/entropyflux_implementations.hh
@@ -0,0 +1,4613 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2017 - 2018)
+//   Tobias Leibner (2016 - 2017)
+//
+// Contributors: Tobias Leibner
+
+#ifndef DUNE_GDT_MOMENTMODELS_ENTROPYFLUX_IMPLEMENTATIONS_HH
+#define DUNE_GDT_MOMENTMODELS_ENTROPYFLUX_IMPLEMENTATIONS_HH
+
+#include <algorithm>
+#include <cmath>
+#include <list>
+#include <memory>
+
+#include <boost/align/aligned_allocator.hpp>
+
+#include <dune/geometry/quadraturerules.hh>
+
+#include <dune/xt/common/debug.hh>
+#include <dune/xt/common/fmatrix.hh>
+#include <dune/xt/common/fvector.hh>
+#include <dune/xt/common/lapacke.hh>
+#include <dune/xt/common/cblas.hh>
+#include <dune/xt/common/mkl.hh>
+#include <dune/xt/common/math.hh>
+#include <dune/xt/common/memory.hh>
+#include <dune/xt/common/parallel/threadstorage.hh>
+#include <dune/xt/common/vector_less.hh>
+
+#include <dune/xt/la/algorithms/cholesky.hh>
+#include <dune/xt/la/algorithms/solve_sym_tridiag_posdef.hh>
+#include <dune/xt/la/container/common.hh>
+#include <dune/xt/la/container/conversion.hh>
+#include <dune/xt/la/container/eye-matrix.hh>
+#include <dune/xt/la/container/pattern.hh>
+
+#include <dune/xt/functions/interfaces/function.hh>
+
+#include <dune/gdt/test/momentmodels/basisfunctions.hh>
+#include <dune/gdt/type_traits.hh>
+
+#include "config.h"
+
+#if HAVE_CLP
+#  include <coin/ClpSimplex.hpp>
+#endif // HAVE_CLP
+
+namespace Dune {
+namespace GDT {
+
+
+template <class T>
+std::enable_if_t<std::is_arithmetic<T>::value, T> superbee(const T first_slope, const T second_slope)
+{
+  return XT::Common::maxmod(XT::Common::minmod(first_slope, 2. * second_slope),
+                            XT::Common::minmod(2. * first_slope, second_slope));
+}
+
+
+// choose specializations
+#ifndef ENTROPY_FLUX_UNSPECIALIZED_USE_ADAPTIVE_CHANGE_OF_BASIS
+#  define ENTROPY_FLUX_UNSPECIALIZED_USE_ADAPTIVE_CHANGE_OF_BASIS 1
+#endif
+
+#ifndef ENTROPY_FLUX_USE_PARTIAL_MOMENTS_SPECIALIZATION
+#  define ENTROPY_FLUX_USE_PARTIAL_MOMENTS_SPECIALIZATION 1
+#endif
+
+#ifndef ENTROPY_FLUX_USE_3D_HATFUNCTIONS_SPECIALIZATION
+#  define ENTROPY_FLUX_USE_3D_HATFUNCTIONS_SPECIALIZATION 1
+#endif
+
+#ifndef ENTROPY_FLUX_USE_1D_HATFUNCTIONS_SPECIALIZATION
+#  define ENTROPY_FLUX_USE_1D_HATFUNCTIONS_SPECIALIZATION 1
+#endif
+
+#ifndef ENTROPY_FLUX_1D_HATFUNCTIONS_USE_ANALYTICAL_INTEGRALS
+#  define ENTROPY_FLUX_1D_HATFUNCTIONS_USE_ANALYTICAL_INTEGRALS 0
+#endif
+
+
+enum class SlopeLimiterType
+{
+  minmod,
+  superbee,
+  mc,
+  no_slope
+};
+
+
+/**
+ * Unspecialized implementation, should work with all bases
+ */
+template <class MomentBasisImp>
+class EntropyBasedFluxImplementationUnspecializedBase
+  : public XT::Functions::FunctionInterface<MomentBasisImp::dimRange,
+                                            MomentBasisImp::dimFlux,
+                                            MomentBasisImp::dimRange,
+                                            typename MomentBasisImp::R>
+{
+  using BaseType = typename XT::Functions::FunctionInterface<MomentBasisImp::dimRange,
+                                                             MomentBasisImp::dimFlux,
+                                                             MomentBasisImp::dimRange,
+                                                             typename MomentBasisImp::R>;
+  using ThisType = EntropyBasedFluxImplementationUnspecializedBase;
+
+public:
+  using MomentBasis = MomentBasisImp;
+  static const size_t dimFlux = MomentBasis::dimFlux;
+  static const size_t basis_dimRange = MomentBasis::dimRange;
+  using typename BaseType::DomainFieldType;
+  using BasisDomainType = typename MomentBasis::DomainType;
+  using FluxDomainType = FieldVector<DomainFieldType, dimFlux>;
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicDerivativeRangeType;
+  using typename BaseType::DynamicRowDerivativeRangeType;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::RangeReturnType;
+  // make matrices a little larger to align to 64 byte boundary
+  static constexpr size_t matrix_num_cols =
+      basis_dimRange % 8 ? basis_dimRange : basis_dimRange + (8 - basis_dimRange % 8);
+  using MatrixType = XT::Common::FieldMatrix<RangeFieldType, basis_dimRange, basis_dimRange>;
+  using VectorType = XT::Common::FieldVector<RangeFieldType, basis_dimRange>;
+  using DynamicRangeType = DynamicVector<RangeFieldType>;
+  using BasisValuesMatrixType = XT::LA::CommonDenseMatrix<RangeFieldType>;
+  using AlphaReturnType = std::pair<VectorType, std::pair<DomainType, RangeFieldType>>;
+  using QuadraturePointsType = std::vector<BasisDomainType, boost::alignment::aligned_allocator<BasisDomainType, 64>>;
+  using QuadratureWeightsType = std::vector<RangeFieldType, boost::alignment::aligned_allocator<RangeFieldType, 64>>;
+  static const EntropyType entropy = MomentBasis::entropy;
+
+  explicit EntropyBasedFluxImplementationUnspecializedBase(const MomentBasis& basis_functions,
+                                                           const RangeFieldType tau,
+                                                           const bool disable_realizability_check,
+                                                           const RangeFieldType epsilon_gamma,
+                                                           const RangeFieldType chi,
+                                                           const RangeFieldType xi,
+                                                           const std::vector<RangeFieldType> r_sequence,
+                                                           const size_t k_0,
+                                                           const size_t k_max,
+                                                           const RangeFieldType epsilon)
+    : basis_functions_(basis_functions)
+    , quad_points_(XT::Data::merged_quadrature(basis_functions_.quadratures()).size())
+    , quad_weights_(quad_points_.size())
+    , M_(quad_points_.size(), matrix_num_cols, 0., 0)
+    , tau_(tau)
+    , disable_realizability_check_(disable_realizability_check)
+    , epsilon_gamma_(epsilon_gamma)
+    , chi_(chi)
+    , xi_(xi)
+    , r_sequence_(r_sequence)
+    , k_0_(k_0)
+    , k_max_(k_max)
+    , epsilon_(epsilon)
+    , realizability_helper_(basis_functions_,
+                            quad_points_,
+                            disable_realizability_check_
+#if HAVE_CLP
+                            ,
+                            lp_
+#endif
+      )
+  {
+    size_t ll = 0;
+    for (const auto& quad_point : XT::Data::merged_quadrature(basis_functions_.quadratures())) {
+      quad_points_[ll] = quad_point.position();
+      quad_weights_[ll] = quad_point.weight();
+      ++ll;
+    }
+    // Join duplicate quad_points. For that purpose, first sort the vectors
+    const auto permutation = get_sort_permutation(quad_points_, XT::Common::VectorFloatLess{});
+    apply_permutation_in_place(quad_points_, permutation);
+    apply_permutation_in_place(quad_weights_, permutation);
+    // Now join duplicate quad_points by removing all quad_points with the same position except one and adding the
+    // weights of the removed points to the remaining point
+    join_duplicate_quadpoints(quad_points_, quad_weights_);
+    assert(quad_points_.size() == quad_weights_.size());
+    // evaluate basis functions and store in matrix
+    M_.resize(quad_points_.size(), matrix_num_cols);
+    for (ll = 0; ll < quad_points_.size(); ++ll) {
+      const auto val = basis_functions_.evaluate(quad_points_[ll]);
+      for (size_t ii = 0; ii < basis_dimRange; ++ii)
+        M_.set_entry(ll, ii, val[ii]);
+    }
+  }
+
+
+  // ============================================================================================
+  // ============================= FunctionInterface methods ====================================
+  // ============================================================================================
+
+
+  int order(const XT::Common::Parameter& /*param*/ = {}) const override
+  {
+    return 1;
+  }
+
+  virtual RangeReturnType evaluate(const DomainType& u,
+                                   const XT::Common::Parameter& /*param*/ = {}) const override final
+  {
+    const auto alpha = get_alpha(u, *get_isotropic_alpha(u), true)->first;
+    return evaluate_with_alpha(alpha);
+  }
+
+  virtual RangeReturnType evaluate_with_alpha(const DomainType& alpha) const
+  {
+    RangeReturnType ret(0.);
+    auto& eta_ast_prime_vals = working_storage();
+    evaluate_eta_ast_prime(alpha, M_, eta_ast_prime_vals);
+    for (size_t dd = 0; dd < dimFlux; ++dd) {
+      // calculate ret[dd] = < omega[dd] m G_\alpha(u) >
+      for (size_t ll = 0; ll < quad_weights_.size(); ++ll) {
+        const auto factor = eta_ast_prime_vals[ll] * quad_weights_[ll] * quad_points_[ll][dd];
+        for (size_t ii = 0; ii < basis_dimRange; ++ii)
+          ret[dd][ii] += M_.get_entry(ll, ii) * factor;
+      } // ll
+    } // dd
+    return ret;
+  } // void evaluate(...)
+
+  virtual void jacobian(const DomainType& u,
+                        DynamicDerivativeRangeType& result,
+                        const XT::Common::Parameter& /*param*/ = {}) const override final
+  {
+    const auto alpha = get_alpha(u, *get_isotropic_alpha(u), true)->first;
+    jacobian_with_alpha(alpha, result);
+  }
+
+  virtual void jacobian_with_alpha(const DomainType& alpha, DynamicDerivativeRangeType& result) const
+  {
+    thread_local auto H = XT::Common::make_unique<MatrixType>();
+    calculate_hessian(alpha, M_, *H);
+    for (size_t dd = 0; dd < dimFlux; ++dd)
+      row_jacobian(dd, M_, *H, result[dd], dd > 0);
+  }
+
+  void row_jacobian(const size_t row,
+                    const BasisValuesMatrixType& M,
+                    MatrixType& H,
+                    DynamicRowDerivativeRangeType& ret,
+                    bool L_calculated = false) const
+  {
+    assert(row < dimFlux);
+    calculate_J(M, ret, row);
+    calculate_J_Hinv(ret, H, L_calculated);
+  } // void partial_u_col(...)
+
+
+  // ============================================================================================
+  // ============ Evaluations of ansatz distribution, moments, hessian etc. =====================
+  // ============================================================================================
+
+
+  // Solves the minimum entropy optimization problem for u.
+  // returns (alpha, (actual_u, r)), where r is the regularization parameter and actual_u the regularized u
+  virtual std::unique_ptr<AlphaReturnType>
+  get_alpha(const DomainType& u, const DomainType& alpha_in, const bool regularize) const = 0;
+
+  std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u) const
+  {
+    return get_alpha(u, *get_isotropic_alpha(u), true);
+  }
+
+  // returns density rho = < eta_ast_prime(beta_in * b(v)) >
+  RangeFieldType get_rho(const DomainType& beta_in, const BasisValuesMatrixType& M) const
+  {
+    auto& eta_ast_prime_vals = working_storage();
+    evaluate_eta_ast_prime(beta_in, M, eta_ast_prime_vals);
+    return std::inner_product(
+        quad_weights_.begin(), quad_weights_.end(), eta_ast_prime_vals.begin(), RangeFieldType(0.));
+  }
+
+  // returns < eta_ast(beta_in * b(v)) >
+  RangeFieldType get_eta_ast_integrated(const DomainType& beta_in, const BasisValuesMatrixType& M) const
+  {
+    auto& eta_ast_vals = working_storage();
+    evaluate_eta_ast(beta_in, M, eta_ast_vals);
+    return std::inner_product(quad_weights_.begin(), quad_weights_.end(), eta_ast_vals.begin(), RangeFieldType(0.));
+  }
+
+  // returns < b \eta_{\ast}^{\prime}(\alpha^T b(v)) >
+  DomainType get_u(const DomainType& alpha) const
+  {
+    DomainType ret;
+    calculate_u(alpha, M_, ret);
+    return ret;
+  }
+
+  DomainType get_u(const QuadratureWeightsType& eta_ast_prime_vals) const
+  {
+    DomainType ret(0.);
+    const size_t num_quad_points = quad_weights_.size();
+    for (size_t ll = 0; ll < num_quad_points; ++ll) {
+      const auto factor_ll = eta_ast_prime_vals[ll] * quad_weights_[ll];
+      const auto* basis_ll = &(M_.get_entry_ref(ll, 0.));
+      for (size_t ii = 0; ii < basis_dimRange; ++ii)
+        ret[ii] += basis_ll[ii] * factor_ll;
+    } // ll
+    return ret;
+  }
+
+  // calculate ret = < b eta_ast_prime(beta_in * b) >
+  void calculate_u(const DomainType& beta_in,
+                   const BasisValuesMatrixType& M,
+                   DomainType& ret,
+                   bool same_beta = false,
+                   bool only_first_component = false) const
+  {
+    auto& eta_ast_prime_vals = working_storage();
+    if (!same_beta)
+      evaluate_eta_ast_prime(beta_in, M, eta_ast_prime_vals);
+    std::fill(ret.begin(), ret.end(), 0.);
+    const size_t num_quad_points = quad_weights_.size();
+    for (size_t ll = 0; ll < num_quad_points; ++ll) {
+      const auto factor_ll = eta_ast_prime_vals[ll] * quad_weights_[ll];
+      const auto* basis_ll = &(M.get_entry_ref(ll, 0.));
+      for (size_t ii = 0; ii < (only_first_component ? 1 : basis_dimRange); ++ii)
+        ret[ii] += basis_ll[ii] * factor_ll;
+    } // ll
+  }
+
+  void calculate_hessian(const DomainType& alpha,
+                         const BasisValuesMatrixType& M,
+                         MatrixType& H,
+                         const bool use_stored_data = false) const
+  {
+    auto& eta_ast_twoprime_values = working_storage();
+    if (!use_stored_data)
+      evaluate_eta_ast_twoprime(alpha, M, eta_ast_twoprime_values);
+    calculate_hessian(eta_ast_twoprime_values, M, H);
+  } // void calculate_hessian(...)
+
+  void calculate_hessian(const QuadratureWeightsType& eta_ast_twoprime_vals,
+                         const BasisValuesMatrixType& M,
+                         MatrixType& H) const
+  {
+    std::fill(H.begin(), H.end(), 0.);
+    const size_t num_quad_points = quad_weights_.size();
+    // matrix is symmetric, we only use lower triangular part
+    for (size_t ll = 0; ll < num_quad_points; ++ll) {
+      auto factor_ll = eta_ast_twoprime_vals[ll] * quad_weights_[ll];
+      const auto* basis_ll = &(M.get_entry_ref(ll, 0.));
+      for (size_t ii = 0; ii < basis_dimRange; ++ii) {
+        auto* H_row = &(H[ii][0]);
+        const auto factor_ll_ii = basis_ll[ii] * factor_ll;
+        for (size_t kk = 0; kk <= ii; ++kk) {
+          H_row[kk] += basis_ll[kk] * factor_ll_ii;
+        } // kk
+      } // ii
+    } // ll
+  } // void calculate_hessian(...)
+
+  void apply_inverse_hessian(const QuadratureWeightsType& eta_ast_twoprime_vals,
+                             const DomainType& u,
+                             DomainType& Hinv_u) const
+  {
+    thread_local auto H = XT::Common::make_unique<MatrixType>();
+    calculate_hessian(eta_ast_twoprime_vals, M_, *H);
+    XT::LA::cholesky(*H);
+    thread_local DomainType tmp_vec;
+    XT::LA::solve_lower_triangular(*H, tmp_vec, u);
+    XT::LA::solve_lower_triangular_transposed(*H, Hinv_u, tmp_vec);
+  }
+
+  // J = df/dalpha is the derivative of the flux with respect to alpha.
+  // As F = (f_1, f_2, f_3) is matrix-valued
+  // (div f = \sum_{i=1}^d \partial_{x_i} f_i  = \sum_{i=1}^d \partial_{x_i} < v_i m \hat{psi}(alpha) > is
+  // vector-valued),
+  // the derivative is the vector of matrices (df_1/dalpha, df_2/dalpha, ...)
+  // this function returns the dd-th matrix df_dd/dalpha of J
+  // assumes eta_ast_twoprime_values already contains the needed \eta_{\ast}^{\prime \prime} (\alpha * m) values
+  void calculate_J(const BasisValuesMatrixType& M, DynamicRowDerivativeRangeType& J_dd, const size_t dd) const
+  {
+    assert(dd < dimFlux);
+    const auto& eta_ast_twoprime_values = working_storage();
+    J_dd.set_all_entries(0.);
+    const size_t num_quad_points = quad_points_.size();
+    for (size_t ll = 0; ll < num_quad_points; ++ll) {
+      const auto factor_ll = eta_ast_twoprime_values[ll] * quad_points_[ll][dd] * quad_weights_[ll];
+      const auto* basis_ll = &(M.get_entry_ref(ll, 0.));
+      for (size_t ii = 0; ii < basis_dimRange; ++ii) {
+        const auto factor_ll_ii = factor_ll * basis_ll[ii];
+        if (!XT::Common::is_zero(factor_ll_ii)) {
+          for (size_t kk = 0; kk <= ii; ++kk)
+            J_dd.unsafe_add_to_entry(ii, kk, basis_ll[kk] * factor_ll_ii);
+        }
+      } // ii
+    } // ll
+    // symmetric update for upper triangular part of J
+    for (size_t mm = 0; mm < basis_dimRange; ++mm)
+      for (size_t nn = mm + 1; nn < basis_dimRange; ++nn)
+        J_dd.set_entry(mm, nn, J_dd.get_entry(nn, mm));
+  } // void calculate_J(...)
+
+  // calculates J = J H^{-1}. H is assumed to be symmetric positive definite.
+  static void calculate_J_Hinv(DynamicRowDerivativeRangeType& A, MatrixType& B, bool L_calculated = false)
+  {
+    // if B = LL^T, then we have to calculate ret = A (L^T)^{-1} L^{-1} = C L^{-1}
+    // calculate B = LL^T first
+    if (!L_calculated)
+      XT::LA::cholesky(B);
+    VectorType tmp_vec;
+    for (size_t ii = 0; ii < basis_dimRange; ++ii) {
+      // calculate C = A (L^T)^{-1} and store in B
+      auto&& row_view = A[ii];
+      XT::LA::solve_lower_triangular(B, tmp_vec, row_view);
+      // calculate ret = C L^{-1}
+      XT::LA::solve_lower_triangular_transposed(B, row_view, tmp_vec);
+    } // ii
+  } // void calculate_J_Hinv(...)
+
+
+  // ============================================================================================
+  // ============================= Entropy evaluations ==========================================
+  // ============================================================================================
+
+
+  // evaluates \eta_{\ast}(\alpha^T b(v_i)) for all quadrature points v_i
+  void evaluate_eta_ast(const DomainType& alpha, const BasisValuesMatrixType& M, QuadratureWeightsType& ret) const
+  {
+    calculate_scalar_products(alpha, M, ret);
+    apply_exponential(ret);
+    evaluate_eta_ast(ret);
+  }
+
+  // evaluates \eta_{\ast}(\alpha^T b(v_i)) for all quadrature points v_i, assumes that ret already contains
+  // exp(alpha^T b(v_i))
+  void evaluate_eta_ast(QuadratureWeightsType& ret) const
+  {
+    if (entropy == EntropyType::BoseEinstein)
+      for (size_t ll = 0; ll < ret.size(); ++ll)
+        ret[ll] = -std::log(1 - ret[ll]);
+  }
+
+  // evaluates \eta_{\ast}^{\prime}(\alpha^T b(v_i)) for all quadrature points v_i
+  void evaluate_eta_ast_prime(const DomainType& alpha, const BasisValuesMatrixType& M, QuadratureWeightsType& ret) const
+  {
+    calculate_scalar_products(alpha, M, ret);
+    apply_exponential(ret);
+    evaluate_eta_ast_prime(ret);
+  }
+
+  // evaluates \eta_{\ast}^{\prime}(\alpha^T b(v_i)) for all quadrature points v_i, assumes that ret already contains
+  // exp(alpha^T b(v_i))
+  void evaluate_eta_ast_prime(QuadratureWeightsType& ret) const
+  {
+    if (entropy == EntropyType::BoseEinstein)
+      for (size_t ll = 0; ll < ret.size(); ++ll)
+        ret[ll] /= (1 - ret[ll]);
+  }
+
+  // evaluates \eta_{\ast}^{\prime\prime}(\alpha^T b(v_i)) for all quadrature points v_i
+  void
+  evaluate_eta_ast_twoprime(const DomainType& alpha, const BasisValuesMatrixType& M, QuadratureWeightsType& ret) const
+  {
+    calculate_scalar_products(alpha, M, ret);
+    apply_exponential(ret);
+    evaluate_eta_ast_twoprime(ret);
+  }
+
+  // evaluates \eta_{\ast}^{\prime\prime}(\alpha^T b(v_i)) for all quadrature points v_i, assumes that ret already
+  // contains exp(alpha^T b(v_i))
+  void evaluate_eta_ast_twoprime(QuadratureWeightsType& ret) const
+  {
+    if (entropy == EntropyType::BoseEinstein)
+      for (size_t ll = 0; ll < ret.size(); ++ll)
+        ret[ll] /= std::pow(1 - ret[ll], 2);
+  }
+
+  // stores evaluations of exp(alpha^T b(v_i)) for all quadrature points v_i
+  void store_exp_evaluations(QuadratureWeightsType& exp_evaluations, const DomainType& alpha) const
+  {
+    exp_evaluations.resize(quad_points_.size());
+    this->calculate_scalar_products(alpha, M_, exp_evaluations);
+    this->apply_exponential(exp_evaluations);
+  }
+
+  void store_eta_ast_prime_vals(const QuadratureWeightsType& exp_evaluations, QuadratureWeightsType& eta_ast_prime_vals)
+  {
+    eta_ast_prime_vals = exp_evaluations;
+    evaluate_eta_ast_prime(eta_ast_prime_vals);
+  }
+
+  void store_eta_ast_twoprime_vals(const QuadratureWeightsType& exp_evaluations,
+                                   QuadratureWeightsType& eta_ast_twoprime_vals)
+  {
+    eta_ast_twoprime_vals = exp_evaluations;
+    evaluate_eta_ast_twoprime(eta_ast_twoprime_vals);
+  }
+
+  // stores evaluations of a given boundary distribution psi(v) at all quadrature points v_i
+  void store_boundary_distribution_evaluations(
+      QuadratureWeightsType& boundary_distribution_evaluations,
+      const std::function<RangeFieldType(const FluxDomainType&)>& boundary_distribution) const
+  {
+    boundary_distribution_evaluations.resize(quad_points_.size());
+    for (size_t ll = 0; ll < quad_points_.size(); ++ll)
+      boundary_distribution_evaluations[ll] = boundary_distribution(quad_points_[ll]);
+  }
+
+
+  // ============================================================================================
+  // =============================== Kinetic fluxes =============================================
+  // ============================================================================================
+
+
+  // calculate \sum_{i=1}^d < v_i m \psi > n_i, where n is the unit outer normal,
+  // m is the basis function vector, \psi is the ansatz corresponding to u
+  // and x, v, t are the space, velocity and time variable, respectively
+  // As we are using cartesian grids, n_i == 0 in all but one dimension, so only evaluate for i == dd
+  DomainType
+  evaluate_kinetic_flux(const DomainType& u_i, const DomainType& u_j, const FluxDomainType& n_ij, const size_t dd) const
+  {
+    // calculate \sum_{i=1}^d < \omega_i m G_\alpha(u) > n_i
+    const auto alpha_i = get_alpha(u_i, *get_isotropic_alpha(u_i), true)->first;
+    const auto alpha_j = get_alpha(u_j, *get_isotropic_alpha(u_j), true)->first;
+    evaluate_kinetic_flux_with_alphas(alpha_i, alpha_j, n_ij, dd);
+  } // DomainType evaluate_kinetic_flux(...)
+
+  DomainType evaluate_kinetic_flux_with_alphas(const DomainType& alpha_i,
+                                               const DomainType& alpha_j,
+                                               const FluxDomainType& n_ij,
+                                               const size_t dd) const
+
+  {
+    thread_local FieldVector<QuadratureWeightsType, 2> eta_ast_prime_vals;
+    eta_ast_prime_vals[0].resize(quad_points_.size());
+    eta_ast_prime_vals[1].resize(quad_points_.size());
+    evaluate_eta_ast_prime(alpha_i, M_, eta_ast_prime_vals[0]);
+    evaluate_eta_ast_prime(alpha_j, M_, eta_ast_prime_vals[1]);
+    DomainType ret(0);
+    for (size_t ll = 0; ll < quad_points_.size(); ++ll) {
+      const auto position = quad_points_[ll][dd];
+      RangeFieldType factor = position * n_ij[dd] > 0. ? eta_ast_prime_vals[0][ll] : eta_ast_prime_vals[1][ll];
+      factor *= quad_weights_[ll] * position;
+      const auto* basis_ll = &(M_.get_entry_ref(ll, 0.));
+      for (size_t ii = 0; ii < basis_dimRange; ++ii)
+        ret[ii] += basis_ll[ii] * factor;
+    } // ll
+    ret *= n_ij[dd];
+    return ret;
+  } // DomainType evaluate_kinetic_flux_with_alphas(...)
+
+  // Calculates left and right kinetic flux with reconstructed densities. Ansatz distribution values contains
+  // evaluations of the ansatz distribution at each quadrature point for a stencil of three entities. The distributions
+  // are reconstructed pointwise for each quadrature point and the resulting (part of) the kinetic flux is <
+  // psi_reconstr * b * v>_{+/-}.
+  template <SlopeLimiterType slope_type, class FluxesMapType>
+  void calculate_reconstructed_fluxes(const FieldVector<const QuadratureWeightsType*, 3>& ansatz_distribution_values,
+                                      FluxesMapType& flux_values,
+                                      const size_t dd) const
+  {
+    // get left and right reconstructed values for each quadrature point v_i
+    thread_local XT::Common::FieldVector<QuadratureWeightsType, 2> reconstructed_values(
+        QuadratureWeightsType(quad_points_.size()));
+    auto& vals_left = reconstructed_values[0];
+    auto& vals_right = reconstructed_values[1];
+    if (slope_type == SlopeLimiterType::no_slope) {
+      for (size_t ll = 0; ll < quad_points_.size(); ++ll)
+        vals_left[ll] = vals_right[ll] = (*ansatz_distribution_values[1])[ll];
+    } else {
+      const auto slope_func =
+          (slope_type == SlopeLimiterType::minmod) ? XT::Common::minmod<RangeFieldType> : superbee<RangeFieldType>;
+      for (size_t ll = 0; ll < quad_points_.size(); ++ll) {
+        const auto slope = slope_func((*ansatz_distribution_values[1])[ll] - (*ansatz_distribution_values[0])[ll],
+                                      (*ansatz_distribution_values[2])[ll] - (*ansatz_distribution_values[1])[ll]);
+        vals_left[ll] = (*ansatz_distribution_values[1])[ll] - 0.5 * slope;
+        vals_right[ll] = (*ansatz_distribution_values[1])[ll] + 0.5 * slope;
+      } // ll
+    }
+
+    BasisDomainType coord(0.5);
+    coord[dd] = 0;
+    auto& left_flux_value = flux_values[coord];
+    coord[dd] = 1;
+    auto& right_flux_value = flux_values[coord];
+    right_flux_value = left_flux_value = DomainType(0.);
+
+    for (size_t ll = 0; ll < quad_points_.size(); ++ll) {
+      const auto position = quad_points_[ll][dd];
+      RangeFieldType factor = position > 0. ? vals_right[ll] : vals_left[ll];
+      factor *= quad_weights_[ll] * position;
+      auto& val = position > 0. ? right_flux_value : left_flux_value;
+      const auto* basis_ll = &(M_.get_entry_ref(ll, 0.));
+      for (size_t ii = 0; ii < basis_dimRange; ++ii)
+        val[ii] += basis_ll[ii] * factor;
+    } // ll
+  } // void calculate_reconstructed_fluxes(...)
+
+
+  // ============================================================================================
+  // ================================== Helper functions ========================================
+  // ============================================================================================
+
+
+  // get permutation instead of sorting directly to be able to sort two vectors the same way
+  // see
+  // https://stackoverflow.com/questions/17074324/how-can-i-sort-two-vectors-in-the-same-way-with-criteria-that-uses-only-one-of
+  template <class T, class Alloc, class Compare>
+  static std::vector<std::size_t> get_sort_permutation(const std::vector<T, Alloc>& vec, const Compare& compare)
+  {
+    std::vector<std::size_t> p(vec.size());
+    std::iota(p.begin(), p.end(), 0);
+    std::sort(p.begin(), p.end(), [&](std::size_t i, std::size_t j) { return compare(vec[i], vec[j]); });
+    return p;
+  }
+
+  template <class T, class Alloc>
+  static void apply_permutation_in_place(std::vector<T, Alloc>& vec, const std::vector<std::size_t>& p)
+  {
+    std::vector<bool> done(vec.size());
+    for (std::size_t i = 0; i < vec.size(); ++i) {
+      if (done[i]) {
+        continue;
+      }
+      done[i] = true;
+      std::size_t prev_j = i;
+      std::size_t j = p[i];
+      while (i != j) {
+        std::swap(vec[prev_j], vec[j]);
+        done[j] = true;
+        prev_j = j;
+        j = p[j];
+      }
+    }
+  }
+
+  // Joins duplicate quadpoints, vectors have to be sorted!
+  static void join_duplicate_quadpoints(QuadraturePointsType& quad_points, QuadratureWeightsType& quad_weights)
+  {
+    // Index of first quad_point of several quad_points with the same position
+    size_t curr_index = 0;
+    std::vector<size_t> indices_to_remove;
+    for (size_t ll = 1; ll < quad_weights.size(); ++ll) {
+      if (XT::Common::FloatCmp::eq(quad_points[curr_index], quad_points[ll])) {
+        quad_weights[curr_index] += quad_weights[ll];
+        indices_to_remove.push_back(ll);
+      } else {
+        curr_index = ll;
+      }
+    } // ll
+    assert(indices_to_remove.size() < std::numeric_limits<int>::max());
+    // remove duplicate points, from back to front to avoid invalidating indices
+    for (int ll = static_cast<int>(indices_to_remove.size()) - 1; ll >= 0; --ll) {
+      quad_points.erase(quad_points.begin() + indices_to_remove[ll]);
+      quad_weights.erase(quad_weights.begin() + indices_to_remove[ll]);
+    }
+  }
+
+  // temporary vectors to store inner products and exponentials
+  QuadratureWeightsType& working_storage() const
+  {
+    thread_local QuadratureWeightsType work_vec;
+    work_vec.resize(quad_points_.size());
+    return work_vec;
+  }
+
+  bool all_positive(const QuadratureWeightsType& vals) const
+  {
+    for (size_t ll = 0; ll < quad_points_.size(); ++ll) {
+      const auto val = vals[ll];
+      if (val < 0. || std::isinf(val) || std::isnan(val))
+        return false;
+    }
+    return true;
+  }
+
+  void copy_transposed(const MatrixType& T_k, MatrixType& T_k_trans) const
+  {
+    for (size_t ii = 0; ii < basis_dimRange; ++ii)
+      for (size_t jj = 0; jj <= ii; ++jj)
+        T_k_trans[jj][ii] = T_k[ii][jj];
+  }
+
+  // calculates alpha^T b(v_i) for all quadrature points v_i
+  void calculate_scalar_products(const DomainType& beta_in,
+                                 const BasisValuesMatrixType& M,
+                                 QuadratureWeightsType& scalar_products) const
+  {
+#if HAVE_MKL
+    XT::Common::Cblas::dgemv(XT::Common::Cblas::row_major(),
+                             XT::Common::Cblas::no_trans(),
+                             static_cast<int>(quad_points_.size()),
+                             basis_dimRange,
+                             1.,
+                             M.data(),
+                             matrix_num_cols,
+                             &(beta_in[0]),
+                             1,
+                             0.,
+                             scalar_products.data(),
+                             1);
+#else
+    const size_t num_quad_points = quad_points_.size();
+    for (size_t ll = 0; ll < num_quad_points; ++ll) {
+      const auto* basis_ll = &(M.get_entry_ref(ll, 0.));
+      scalar_products[ll] = std::inner_product(beta_in.begin(), beta_in.end(), basis_ll, 0.);
+    }
+#endif
+  }
+
+  // calculates exp(val) for all vals in values
+  void apply_exponential(QuadratureWeightsType& values) const
+  {
+    assert(values.size() < std::numeric_limits<int>::max());
+    XT::Common::Mkl::exp(static_cast<int>(values.size()), values.data(), values.data());
+  }
+
+  const MomentBasis& basis_functions() const
+  {
+    return basis_functions_;
+  }
+
+  std::unique_ptr<VectorType> get_isotropic_alpha(const RangeFieldType density) const
+  {
+    return std::make_unique<VectorType>(basis_functions_.alpha_iso(density));
+  }
+
+  std::unique_ptr<VectorType> get_isotropic_alpha(const DomainType& u) const
+  {
+    return get_isotropic_alpha(basis_functions_.density(u));
+  }
+
+#if HAVE_CLP
+  template <class BasisFuncImp = MomentBasis, bool anything = true>
+  struct RealizabilityHelper
+  {
+    static_assert(std::is_same<BasisFuncImp, MomentBasis>::value, "BasisFuncImp has to be MomentBasis!");
+
+    RealizabilityHelper(const MomentBasis& basis_functions,
+                        const QuadraturePointsType& quad_points,
+                        const bool /*disable_realizability_check*/,
+                        XT::Common::PerThreadValue<std::unique_ptr<ClpSimplex>>& lp)
+      : basis_functions_(basis_functions)
+      , quad_points_(quad_points)
+      , lp_(lp)
+    {}
+
+    // The ClpSimplex structure seems to get corrupted sometimes (maybe some problems with infs/NaNs?), so we
+    // reinitialize it if the stopping conditions is always false
+    void setup_linear_program(const bool reinitialize) const
+    {
+      if (!*lp_ || reinitialize) {
+        // We start with creating a model with basis_dimRange rows and num_quad_points columns */
+        constexpr int num_rows = static_cast<int>(basis_dimRange);
+        assert(quad_points_.size() < std::numeric_limits<int>::max());
+        int num_cols = static_cast<int>(quad_points_.size()); /* variables are x_1, ..., x_{num_quad_points} */
+        *lp_ = std::make_unique<ClpSimplex>(false);
+        auto& lp = **lp_;
+        // set number of rows
+        lp.resize(num_rows, 0);
+
+        // Clp wants the row indices that are non-zero in each column. We have a dense matrix, so provide all indices
+        // 0..num_rows
+        std::array<int, num_rows> row_indices;
+        for (int ii = 0; ii < num_rows; ++ii)
+          row_indices[static_cast<size_t>(ii)] = ii;
+
+        // set columns for quadrature points
+        for (int ii = 0; ii < num_cols; ++ii) {
+          const auto v_i = basis_functions_.evaluate(quad_points_[static_cast<size_t>(ii)]);
+          // First argument: number of elements in column
+          // Second/Third argument: indices/values of column entries
+          // Fourth/Fifth argument: lower/upper column bound, i.e. lower/upper bound for x_i. As all x_i should be
+          // positive, set to 0/inf, which is the default.
+          // Sixth argument: Prefactor in objective for x_i, this is 0 for all x_i, which is also the default;
+          lp.addColumn(num_rows, row_indices.data(), &(v_i[0]));
+        }
+
+        // silence lp
+        lp.setLogLevel(0);
+      } // if (!lp_)
+    }
+
+    bool is_realizable(const DomainType& u, const bool reinitialize) const
+    {
+      const auto density = basis_functions_.density(u);
+      if (!(density > 0.) || std::isinf(density))
+        return false;
+      const auto phi = u / density;
+      setup_linear_program(reinitialize);
+      auto& lp = **lp_;
+      constexpr int num_rows = static_cast<int>(basis_dimRange);
+      // set rhs (equality constraints, so set both bounds equal
+      for (int ii = 0; ii < num_rows; ++ii) {
+        size_t uii = static_cast<size_t>(ii);
+        lp.setRowLower(ii, phi[uii]);
+        lp.setRowUpper(ii, phi[uii]);
+      }
+      // set maximal wall time. If this is not set, in rare cases the primal method never returns
+      lp.setMaximumWallSeconds(60);
+      // Now check solvability
+      lp.primal();
+      return lp.primalFeasible();
+    }
+
+    const MomentBasis& basis_functions_;
+    const QuadraturePointsType& quad_points_;
+    XT::Common::PerThreadValue<std::unique_ptr<ClpSimplex>>& lp_;
+  }; // struct RealizabilityHelper<...>
+#else // HAVE_CLP
+  template <class BasisFuncImp = MomentBasis, bool anything = true>
+  struct RealizabilityHelper
+  {
+    RealizabilityHelper(const MomentBasis& /*basis_functions*/,
+                        const QuadraturePointsType& /*quad_points*/,
+                        const bool disable_realizability_check)
+    {
+      if (!disable_realizability_check)
+        std::cerr << "Warning: You are missing Clp, realizability stopping condition will not be checked!" << std::endl;
+    }
+
+    bool is_realizable(const DomainType& /*u*/, const bool /*reinitialize*/) const
+    {
+      return true;
+    }
+  }; // struct RealizabilityHelper<...>
+#endif // HAVE_CLP
+
+  // specialization for hatfunctions
+  template <size_t dimRange_or_refinements, bool anything>
+  struct RealizabilityHelper<
+      HatFunctionMomentBasis<DomainFieldType, dimFlux, RangeFieldType, dimRange_or_refinements, 1, dimFlux>,
+      anything>
+  {
+    RealizabilityHelper(const MomentBasis& /*basis_functions*/,
+                        const std::vector<BasisDomainType>& /*quad_points*/,
+                        const bool /*disable_realizability_check*/
+#if HAVE_CLP
+                        ,
+                        XT::Common::PerThreadValue<std::unique_ptr<ClpSimplex>>& /*lp*/)
+#else
+    )
+#endif
+    {}
+
+    static bool is_realizable(const DomainType& u, const bool /*reinitialize*/)
+    {
+      for (const auto& u_i : u)
+        if (!(u_i > 0.) || std::isinf(u_i))
+          return false;
+      return true;
+    }
+  }; // struct RealizabilityHelper<Hatfunctions, ...>
+
+  // For each basis evaluation b, calculates T_k^{-1} b. As the basis evaluations are the rows of M, we want to
+  // calculate (T_k^{-1} M^T)^T = M T_k^{-T}
+  void apply_inverse_matrix(const MatrixType& T_k, BasisValuesMatrixType& M) const
+  {
+#if HAVE_MKL
+    // Calculate the transpose here first as this is much faster than passing the matrix to dtrsm and using CblasTrans
+    thread_local auto T_k_trans = std::make_unique<MatrixType>(0.);
+    copy_transposed(T_k, *T_k_trans);
+    assert(quad_points_.size() < std::numeric_limits<int>::max());
+    XT::Common::Cblas::dtrsm(XT::Common::Cblas::row_major(),
+                             XT::Common::Cblas::right(),
+                             XT::Common::Cblas::upper(),
+                             XT::Common::Cblas::no_trans(),
+                             XT::Common::Cblas::non_unit(),
+                             static_cast<int>(quad_points_.size()),
+                             basis_dimRange,
+                             1.,
+                             &((*T_k_trans)[0][0]),
+                             basis_dimRange,
+                             M.data(),
+                             matrix_num_cols);
+#else
+    assert(quad_points_.size() == M.rows());
+    VectorType tmp_vec, tmp_vec2;
+    for (size_t ll = 0; ll < quad_points_.size(); ++ll) {
+      auto* M_row = &(M.get_entry_ref(ll, 0.));
+      std::copy_n(M_row, basis_dimRange, tmp_vec.begin());
+      XT::LA::solve_lower_triangular(T_k, tmp_vec2, tmp_vec);
+      std::copy_n(tmp_vec2.begin(), basis_dimRange, M_row);
+    }
+#endif
+  }
+
+  const MomentBasis& basis_functions_;
+  QuadraturePointsType quad_points_;
+  QuadratureWeightsType quad_weights_;
+  BasisValuesMatrixType M_;
+  const RangeFieldType tau_;
+  const bool disable_realizability_check_;
+  const RangeFieldType epsilon_gamma_;
+  const RangeFieldType chi_;
+  const RangeFieldType xi_;
+  const std::vector<RangeFieldType> r_sequence_;
+  const size_t k_0_;
+  const size_t k_max_;
+  const RangeFieldType epsilon_;
+  const RealizabilityHelper<> realizability_helper_;
+#if HAVE_CLP
+  mutable XT::Common::PerThreadValue<std::unique_ptr<ClpSimplex>> lp_;
+#endif
+};
+
+#if ENTROPY_FLUX_UNSPECIALIZED_USE_ADAPTIVE_CHANGE_OF_BASIS
+/** Analytical flux \mathbf{f}(\mathbf{u}) = < \mu \mathbf{m} G_{\hat{\alpha}(\mathbf{u})} >,
+ * for the notation see
+ * Alldredge, Hauck, O'Leary, Tits, "Adaptive change of basis in entropy-based moment closures for linear kinetic
+ * equations"
+ */
+template <class MomentBasisImp>
+class EntropyBasedFluxImplementation : public EntropyBasedFluxImplementationUnspecializedBase<MomentBasisImp>
+{
+  using BaseType = EntropyBasedFluxImplementationUnspecializedBase<MomentBasisImp>;
+  using ThisType = EntropyBasedFluxImplementation;
+
+public:
+  using BaseType::basis_dimRange;
+  using BaseType::dimFlux;
+  using BaseType::entropy;
+  using typename BaseType::AlphaReturnType;
+  using typename BaseType::BasisDomainType;
+  using typename BaseType::BasisValuesMatrixType;
+  using typename BaseType::DomainType;
+  using typename BaseType::FluxDomainType;
+  using typename BaseType::MatrixType;
+  using typename BaseType::MomentBasis;
+  using typename BaseType::QuadratureWeightsType;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::VectorType;
+
+  explicit EntropyBasedFluxImplementation(const MomentBasis& basis_functions,
+                                          const RangeFieldType tau,
+                                          const bool disable_realizability_check,
+                                          const RangeFieldType epsilon_gamma,
+                                          const RangeFieldType chi,
+                                          const RangeFieldType xi,
+                                          const std::vector<RangeFieldType> r_sequence,
+                                          const size_t k_0,
+                                          const size_t k_max,
+                                          const RangeFieldType epsilon)
+    : BaseType(
+          basis_functions, tau, disable_realizability_check, epsilon_gamma, chi, xi, r_sequence, k_0, k_max, epsilon)
+    , T_minus_one_(std::make_unique<MatrixType>())
+  {
+    XT::LA::eye_matrix(*T_minus_one_);
+  }
+
+  using BaseType::get_alpha;
+
+  virtual std::unique_ptr<AlphaReturnType>
+  get_alpha(const DomainType& u, const DomainType& alpha_in, const bool regularize) const override final
+  {
+    auto ret = std::make_unique<AlphaReturnType>();
+
+    // rescale u such that the density <psi> is 1
+    RangeFieldType density = basis_functions_.density(u);
+    static const auto alpha_one = basis_functions_.alpha_one();
+    if (!(density > 0.) || std::isinf(density))
+      DUNE_THROW(Dune::MathError, "Negative, inf or NaN density!");
+
+    constexpr bool rescale = (entropy == EntropyType::MaxwellBoltzmann);
+
+    VectorType phi = rescale ? u / density : u;
+    VectorType alpha_initial = rescale ? alpha_in - alpha_one * std::log(density) : alpha_in;
+    VectorType beta_in = alpha_initial;
+    VectorType v, u_eps_diff, g_k, beta_out;
+    RangeFieldType first_error_cond, second_error_cond, tau_prime;
+
+    const auto u_iso = rescale ? basis_functions_.u_iso() : basis_functions_.u_iso() * density;
+    const RangeFieldType dim_factor = is_full_moment_basis<MomentBasis>::value ? 1. : std::sqrt(dimFlux);
+    tau_prime =
+        rescale ? std::min(tau_ / ((1 + dim_factor * phi.two_norm()) * density + dim_factor * tau_), tau_) : tau_;
+
+    thread_local auto T_k = XT::Common::make_unique<MatrixType>();
+
+    const auto& r_sequence = regularize ? r_sequence_ : std::vector<RangeFieldType>{0.};
+    const auto r_max = r_sequence.back();
+    for (const auto& rr : r_sequence) {
+      // regularize u
+      v = phi;
+      if (rr > 0) {
+        beta_in = *get_isotropic_alpha(v);
+        VectorType r_times_u_iso = u_iso;
+        r_times_u_iso *= rr;
+        v *= 1 - rr;
+        v += r_times_u_iso;
+      }
+      *T_k = *T_minus_one_;
+      // calculate T_k u
+      VectorType v_k = v;
+      // calculate values of basis p = S_k m
+      thread_local BasisValuesMatrixType P_k(M_.backend(), false, 0., 0);
+      std::copy_n(M_.data(), M_.rows() * M_.cols(), P_k.data());
+      // calculate f_0
+      RangeFieldType f_k = get_eta_ast_integrated(beta_in, P_k);
+      f_k -= beta_in * v_k;
+
+      thread_local auto H = XT::Common::make_unique<MatrixType>(0.);
+
+      int backtracking_failed = 0;
+      for (size_t kk = 0; kk < k_max_; ++kk) {
+        // exit inner for loop to increase rr if too many iterations are used or cholesky decomposition fails
+        if (kk > k_0_ && rr < r_max)
+          break;
+        try {
+          change_basis(beta_in, v_k, P_k, *T_k, g_k, beta_out, *H);
+        } catch (const Dune::MathError&) {
+          if (rr < r_max)
+            break;
+          const std::string err_msg =
+              "Failed to converge for " + XT::Common::to_string(u) + " with density " + XT::Common::to_string(density)
+              + " and multiplier " + XT::Common::to_string(beta_in)
+              + " due to errors in change_basis! Last u_eps_diff = " + XT::Common::to_string(u_eps_diff)
+              + ", first_error_cond = " + XT::Common::to_string(first_error_cond) + ", second_error_cond = "
+              + XT::Common::to_string(second_error_cond) + ", tau_prime = " + XT::Common::to_string(tau_prime);
+          DUNE_THROW(MathError, err_msg);
+        }
+        // calculate descent direction d_k;
+        VectorType d_k = g_k;
+        d_k *= -1;
+
+        // Calculate values for stopping criteria (in original basis).
+        VectorType alpha_tilde, d_alpha_tilde;
+        XT::LA::solve_lower_triangular_transposed(*T_k, alpha_tilde, beta_out);
+        XT::LA::solve_lower_triangular_transposed(*T_k, d_alpha_tilde, d_k);
+        VectorType u_alpha_tilde;
+        calculate_u(alpha_tilde, M_, u_alpha_tilde);
+        VectorType g_alpha_tilde = u_alpha_tilde - v;
+        auto density_tilde = basis_functions_.density(u_alpha_tilde);
+        if (!(density_tilde > 0.) || std::isinf(density_tilde))
+          break;
+
+        // if rescale == true, we ensure that the ansatz density exactly matches the density of u
+        const auto alpha_prime = rescale ? alpha_tilde - alpha_one * std::log(density_tilde) : alpha_tilde;
+        VectorType u_alpha_prime;
+        if (rescale)
+          calculate_u(alpha_prime, M_, u_alpha_prime);
+        else
+          u_alpha_prime = u_alpha_tilde;
+
+        // calculate stopping criteria
+        u_eps_diff = v - u_alpha_prime * (1 - epsilon_gamma_);
+        first_error_cond = g_alpha_tilde.two_norm();
+        second_error_cond = std::exp(
+            -(rescale ? d_alpha_tilde.one_norm() + std::abs(std::log(density_tilde)) : d_alpha_tilde.one_norm()));
+        // working_storage contains eta_ast_prime evaluations due to the calculate_u call above
+        const auto& eta_ast_prime_vals = working_storage();
+        if (first_error_cond < tau_prime && 1 - epsilon_gamma_ < second_error_cond
+            && (entropy == EntropyType::MaxwellBoltzmann || all_positive(eta_ast_prime_vals))
+            && (disable_realizability_check_
+                || realizability_helper_.is_realizable(u_eps_diff, kk == static_cast<size_t>(0.8 * k_0_)))) {
+          ret->first = rescale ? alpha_prime + alpha_one * std::log(density) : alpha_prime;
+          ret->second = std::make_pair(rescale ? v * density : v, rr);
+          return ret;
+        } else {
+          RangeFieldType zeta_k = 1;
+          beta_in = beta_out;
+          // backtracking line search
+          while (backtracking_failed >= 2 || zeta_k > epsilon_ * beta_out.two_norm() / d_k.two_norm()) {
+            VectorType beta_new = d_k;
+            beta_new *= zeta_k;
+            beta_new += beta_out;
+            RangeFieldType f = get_eta_ast_integrated(beta_new, P_k);
+            f -= beta_new * v_k;
+            if (backtracking_failed >= 2 || XT::Common::FloatCmp::le(f, f_k + xi_ * zeta_k * (g_k * d_k))) {
+              beta_in = beta_new;
+              f_k = f;
+              backtracking_failed = 0;
+              break;
+            }
+            zeta_k = chi_ * zeta_k;
+          } // backtracking linesearch while
+          if (zeta_k <= epsilon_ * beta_out.two_norm() / d_k.two_norm())
+            ++backtracking_failed;
+        } // else (stopping conditions)
+      } // k loop (Newton iterations)
+    } // rr loop (Regularization parameter)
+    const std::string err_msg = "Failed to converge for " + XT::Common::to_string(u) + " with density "
+                                + XT::Common::to_string(density) + " and multiplier " + XT::Common::to_string(beta_in)
+                                + " due to too many iterations! Last u_eps_diff = " + XT::Common::to_string(u_eps_diff)
+                                + ", first_error_cond = " + XT::Common::to_string(first_error_cond)
+                                + ", second_error_cond = " + XT::Common::to_string(second_error_cond)
+                                + ", tau_prime = " + XT::Common::to_string(tau_prime);
+    DUNE_THROW(MathError, err_msg);
+
+    return ret;
+  }
+
+  using BaseType::all_positive;
+  using BaseType::apply_inverse_matrix;
+  using BaseType::calculate_hessian;
+  using BaseType::calculate_u;
+  using BaseType::get_eta_ast_integrated;
+  using BaseType::get_isotropic_alpha;
+  using BaseType::working_storage;
+
+  void change_basis(const DomainType& beta_in,
+                    DomainType& v_k,
+                    BasisValuesMatrixType& P_k,
+                    MatrixType& T_k,
+                    DomainType& g_k,
+                    DomainType& beta_out,
+                    MatrixType& H) const
+  {
+    calculate_hessian(beta_in, P_k, H);
+    XT::LA::cholesky(H);
+    const auto& L = H;
+    thread_local std::unique_ptr<MatrixType> tmp_mat = std::make_unique<MatrixType>();
+    *tmp_mat = T_k;
+    rightmultiply(T_k, *tmp_mat, L);
+    L.mtv(beta_in, beta_out);
+    VectorType tmp_vec;
+    XT::LA::solve_lower_triangular(L, tmp_vec, v_k);
+    v_k = tmp_vec;
+    apply_inverse_matrix(L, P_k);
+    calculate_u(beta_out, P_k, g_k, false);
+    g_k -= v_k;
+  } // void change_basis(...)
+
+  using BaseType::basis_functions_;
+  using BaseType::chi_;
+  using BaseType::disable_realizability_check_;
+  using BaseType::epsilon_;
+  using BaseType::epsilon_gamma_;
+  using BaseType::k_0_;
+  using BaseType::k_max_;
+  using BaseType::M_;
+  using BaseType::quad_points_;
+  using BaseType::quad_weights_;
+  using BaseType::r_sequence_;
+  using BaseType::realizability_helper_;
+  using BaseType::tau_;
+  using BaseType::xi_;
+  const std::unique_ptr<MatrixType> T_minus_one_;
+};
+
+#else // ENTROPY_FLUX_UNSPECIALIZED_USE_ADAPTIVE_CHANGE_OF_BASIS
+
+/** Analytical flux \mathbf{f}(\mathbf{u}) = < \mu \mathbf{m} G_{\hat{\alpha}(\mathbf{u})} >,
+ * Simple backtracking Newton without change of basis
+ */
+template <class MomentBasisImp>
+class EntropyBasedFluxImplementation : public EntropyBasedFluxImplementationUnspecializedBase<MomentBasisImp>
+{
+  using BaseType = EntropyBasedFluxImplementationUnspecializedBase<MomentBasisImp>;
+  using ThisType = EntropyBasedFluxImplementation;
+
+public:
+  using BaseType::dimFlux;
+  using BaseType::entropy;
+  using typename BaseType::AlphaReturnType;
+  using typename BaseType::BasisValuesMatrixType;
+  using typename BaseType::DomainType;
+  using typename BaseType::MatrixType;
+  using typename BaseType::MomentBasis;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::VectorType;
+
+  explicit EntropyBasedFluxImplementation(const MomentBasis& basis_functions,
+                                          const RangeFieldType tau,
+                                          const bool disable_realizability_check,
+                                          const RangeFieldType epsilon_gamma,
+                                          const RangeFieldType chi,
+                                          const RangeFieldType xi,
+                                          const std::vector<RangeFieldType> r_sequence,
+                                          const size_t k_0,
+                                          const size_t k_max,
+                                          const RangeFieldType epsilon)
+    : BaseType(
+          basis_functions, tau, disable_realizability_check, epsilon_gamma, chi, xi, r_sequence, k_0, k_max, epsilon)
+  {}
+
+  std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u) const
+  {
+    return get_alpha(u, *get_isotropic_alpha(u), true);
+  }
+
+  // returns (alpha, (actual_u, r)), where r is the regularization parameter and actual_u the regularized u
+  virtual std::unique_ptr<AlphaReturnType>
+  get_alpha(const DomainType& u, const DomainType& alpha_in, const bool regularize) const override final
+  {
+    auto ret = std::make_unique<AlphaReturnType>();
+    RangeFieldType density = basis_functions_.density(u);
+    static const auto alpha_one = basis_functions_.alpha_one();
+    if (!(density > 0.) || std::isinf(density))
+      DUNE_THROW(Dune::MathError, "Negative, inf or NaN density!");
+
+    constexpr bool rescale = (entropy == EntropyType::MaxwellBoltzmann);
+
+    VectorType phi = rescale ? u / density : u;
+    VectorType alpha_initial = rescale ? alpha_in - alpha_one * std::log(density) : alpha_in;
+    VectorType v, g_k, d_k, tmp_vec, alpha_prime;
+    RangeFieldType first_error_cond, second_error_cond, tau_prime;
+    auto u_iso = rescale ? basis_functions_.u_iso() : basis_functions_.u_iso() * density;
+    const RangeFieldType dim_factor = is_full_moment_basis<MomentBasis>::value ? 1. : std::sqrt(dimFlux);
+    tau_prime =
+        rescale ? std::min(tau_ / ((1 + dim_factor * phi.two_norm()) * density + dim_factor * tau_), tau_) : tau_;
+    VectorType alpha_k = alpha_initial;
+    const auto& r_sequence = regularize ? r_sequence_ : std::vector<RangeFieldType>{0.};
+    const auto r_max = r_sequence.back();
+    for (const auto& rr : r_sequence) {
+      // regularize u
+      v = phi;
+      if (rr > 0) {
+        alpha_k = *get_isotropic_alpha(v);
+        VectorType r_times_u_iso = u_iso;
+        r_times_u_iso *= rr;
+        v *= 1 - rr;
+        v += r_times_u_iso;
+      }
+      // calculate T_k u
+      VectorType v_k = v;
+      // calculate f_0
+      RangeFieldType f_k = get_eta_ast_integrated(alpha_k, M_);
+      f_k -= alpha_k * v_k;
+
+      thread_local auto H = XT::Common::make_unique<MatrixType>(0.);
+
+      int backtracking_failed = 0;
+      for (size_t kk = 0; kk < k_max_; ++kk) {
+        // exit inner for loop to increase rr if too many iterations are used
+        if (kk > k_0_ && rr < r_max)
+          break;
+        // calculate gradient g
+        calculate_u(alpha_k, M_, g_k);
+        g_k -= v_k;
+        // calculate Hessian H
+        calculate_hessian(alpha_k, M_, *H, entropy == EntropyType::MaxwellBoltzmann);
+        // calculate descent direction d_k;
+        d_k = g_k;
+        d_k *= -1;
+        try {
+          // if H = LL^T, then we have to calculate d_k = - L^{-T} L^{-1} g_k
+          // calculate H = LL^T first
+          XT::LA::cholesky(*H);
+          // calculate d_tmp = -L^{-1} g_k and store in B
+          XT::LA::solve_lower_triangular(*H, tmp_vec, d_k);
+          // calculate d_k = L^{-T} d_tmp
+          XT::LA::solve_lower_triangular_transposed(*H, d_k, tmp_vec);
+        } catch (const Dune::MathError&) {
+          if (rr < r_max)
+            break;
+          const std::string err_msg =
+              "Failed to converge for " + XT::Common::to_string(u) + " with density " + XT::Common::to_string(density);
+          DUNE_THROW(MathError, err_msg);
+        }
+
+        const auto& alpha_tilde = alpha_k;
+        auto& u_alpha_tilde = tmp_vec;
+        u_alpha_tilde = g_k + v;
+        auto density_tilde = basis_functions_.density(u_alpha_tilde);
+        if (!(density_tilde > 0.) || std::isinf(density_tilde))
+          break;
+        auto& u_eps_diff = tmp_vec;
+        alpha_prime = alpha_tilde;
+        if (rescale) {
+          alpha_prime -= alpha_one * std::log(density_tilde);
+          calculate_u(alpha_prime, M_, u_eps_diff);
+        } else {
+          u_eps_diff = u_alpha_tilde;
+        }
+        u_eps_diff *= -(1 - epsilon_gamma_);
+        u_eps_diff += v;
+
+        first_error_cond = g_k.two_norm();
+        second_error_cond = std::exp(-(rescale ? d_k.one_norm() + std::abs(std::log(density_tilde)) : d_k.one_norm()));
+        auto& eta_ast_prime_vals = working_storage();
+        // if rescale is true, working storage already contains the eta_ast_prime evaluations due to the call to
+        // calculate_u above
+        if (!rescale)
+          evaluate_eta_ast_prime(alpha_prime, M_, eta_ast_prime_vals);
+        if (first_error_cond < tau_prime && 1 - epsilon_gamma_ < second_error_cond
+            && (entropy == EntropyType::MaxwellBoltzmann || all_positive(eta_ast_prime_vals))
+            && (disable_realizability_check_
+                || realizability_helper_.is_realizable(u_eps_diff, kk == static_cast<size_t>(0.8 * k_0_)))) {
+          ret->first = rescale ? alpha_prime + alpha_one * std::log(density) : alpha_prime;
+          ret->second = std::make_pair(rescale ? v * density : v, rr);
+          return ret;
+        } else {
+          RangeFieldType zeta_k = 1;
+          // backtracking line search
+          auto& alpha_new = tmp_vec;
+          while (backtracking_failed >= 2 || zeta_k > epsilon_ * alpha_k.two_norm() / d_k.two_norm()) {
+            // calculate alpha_new = alpha_k + zeta_k d_k
+            alpha_new = d_k;
+            alpha_new *= zeta_k;
+            alpha_new += alpha_k;
+            // calculate f(alpha_new)
+            RangeFieldType f_new = get_eta_ast_integrated(alpha_new, M_);
+            f_new -= alpha_new * v_k;
+            if (backtracking_failed >= 2 || XT::Common::FloatCmp::le(f_new, f_k + xi_ * zeta_k * (g_k * d_k))) {
+              alpha_k = alpha_new;
+              f_k = f_new;
+              backtracking_failed = 0;
+              break;
+            }
+            zeta_k = chi_ * zeta_k;
+          } // backtracking linesearch while
+          // if (zeta_k <= epsilon_ * alpha_k.two_norm() / d_k.two_norm() * 100.)
+          if (zeta_k <= epsilon_ * alpha_k.two_norm() / d_k.two_norm())
+            ++backtracking_failed;
+        } // else (stopping conditions)
+      } // k loop (Newton iterations)
+    } // rr loop (Regularization parameter)
+    const std::string err_msg =
+        "Failed to converge for " + XT::Common::to_string(u) + " with density " + XT::Common::to_string(density);
+    DUNE_THROW(MathError, err_msg);
+    return ret;
+  }
+
+  using BaseType::all_positive;
+  using BaseType::calculate_hessian;
+  using BaseType::calculate_u;
+  using BaseType::evaluate_eta_ast_prime;
+  using BaseType::get_eta_ast_integrated;
+  using BaseType::get_isotropic_alpha;
+  using BaseType::working_storage;
+
+  using BaseType::basis_functions_;
+  using BaseType::chi_;
+  using BaseType::disable_realizability_check_;
+  using BaseType::epsilon_;
+  using BaseType::epsilon_gamma_;
+  using BaseType::k_0_;
+  using BaseType::k_max_;
+  using BaseType::M_;
+  using BaseType::quad_points_;
+  using BaseType::quad_weights_;
+  using BaseType::r_sequence_;
+  using BaseType::realizability_helper_;
+  using BaseType::tau_;
+  using BaseType::xi_;
+};
+#endif
+
+#if ENTROPY_FLUX_USE_PARTIAL_MOMENTS_SPECIALIZATION
+/**
+ * Specialization for DG basis
+ */
+template <class D, size_t d, class R, size_t dimRange_or_refinements, size_t fluxDim, EntropyType entropy>
+class EntropyBasedFluxImplementation<PartialMomentBasis<D, d, R, dimRange_or_refinements, 1, fluxDim, 1, entropy>>
+  : public XT::Functions::FunctionInterface<
+        PartialMomentBasis<D, d, R, dimRange_or_refinements, 1, fluxDim, 1, entropy>::dimRange,
+        fluxDim,
+        PartialMomentBasis<D, d, R, dimRange_or_refinements, 1, fluxDim, 1, entropy>::dimRange,
+        R>
+{
+public:
+  using MomentBasis = PartialMomentBasis<D, d, R, dimRange_or_refinements, 1, fluxDim, 1, entropy>;
+  using BaseType =
+      typename XT::Functions::FunctionInterface<MomentBasis::dimRange, MomentBasis::dimFlux, MomentBasis::dimRange, R>;
+  using ThisType = EntropyBasedFluxImplementation;
+  static const size_t dimFlux = MomentBasis::dimFlux;
+  static const size_t basis_dimRange = MomentBasis::dimRange;
+  using typename BaseType::DomainFieldType;
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicDerivativeRangeType;
+  using typename BaseType::DynamicRowDerivativeRangeType;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::RangeReturnType;
+  using BasisDomainType = typename MomentBasis::DomainType;
+  using FluxDomainType = FieldVector<DomainFieldType, dimFlux>;
+  static const size_t block_size = (dimFlux == 1) ? 2 : 4;
+  static const size_t num_blocks = basis_dimRange / block_size;
+  using BlockMatrixType = XT::Common::BlockedFieldMatrix<RangeFieldType, num_blocks, block_size>;
+  using LocalMatrixType = typename BlockMatrixType::BlockType;
+  using BlockVectorType = XT::Common::BlockedFieldVector<RangeFieldType, num_blocks, block_size>;
+  using VectorType = BlockVectorType;
+  using LocalVectorType = typename BlockVectorType::BlockType;
+  using BasisValuesMatrixType = std::vector<XT::LA::CommonDenseMatrix<RangeFieldType>>;
+  using QuadraturePointsType =
+      std::vector<std::vector<BasisDomainType, boost::alignment::aligned_allocator<BasisDomainType, 64>>>;
+  using BlockQuadratureWeightsType =
+      std::vector<RangeFieldType, boost::alignment::aligned_allocator<RangeFieldType, 64>>;
+  using QuadratureWeightsType = std::vector<BlockQuadratureWeightsType>;
+  using AlphaReturnType = std::pair<BlockVectorType, std::pair<DomainType, RangeFieldType>>;
+
+  explicit EntropyBasedFluxImplementation(const MomentBasis& basis_functions,
+                                          const RangeFieldType tau,
+                                          const bool disable_realizability_check,
+                                          const RangeFieldType epsilon_gamma,
+                                          const RangeFieldType chi,
+                                          const RangeFieldType xi,
+                                          const std::vector<RangeFieldType> r_sequence,
+                                          const size_t k_0,
+                                          const size_t k_max,
+                                          const RangeFieldType epsilon)
+    : basis_functions_(basis_functions)
+    , quad_points_(num_blocks)
+    , quad_weights_(num_blocks)
+    , M_(num_blocks, XT::LA::CommonDenseMatrix<RangeFieldType>())
+    , tau_(tau)
+    , disable_realizability_check_(disable_realizability_check)
+    , epsilon_gamma_(epsilon_gamma)
+    , chi_(chi)
+    , xi_(xi)
+    , r_sequence_(r_sequence)
+    , k_0_(k_0)
+    , k_max_(k_max)
+    , epsilon_(epsilon)
+  {
+    XT::LA::eye_matrix(T_minus_one_);
+    if (!disable_realizability_check_)
+      helper<dimFlux>::calculate_plane_coefficients(basis_functions_);
+    const auto& quadratures = basis_functions_.quadratures();
+    assert(quadratures.size() == num_blocks);
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      for (const auto& quad_point : quadratures[jj]) {
+        quad_points_[jj].emplace_back(quad_point.position());
+        quad_weights_[jj].emplace_back(quad_point.weight());
+      }
+    } // jj
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      M_[jj] = XT::LA::CommonDenseMatrix<RangeFieldType>(quad_points_[jj].size(), block_size, 0., 0);
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll) {
+        const auto val = basis_functions_.evaluate_on_face(quad_points_[jj][ll], jj);
+        for (size_t ii = 0; ii < block_size; ++ii)
+          M_[jj].set_entry(ll, ii, val[ii]);
+      } // ll
+    } // jj
+  }
+
+
+  // ============================================================================================
+  // ============================= FunctionInterface methods ====================================
+  // ============================================================================================
+
+
+  int order(const XT::Common::Parameter& /*param*/) const override
+  {
+    return 1;
+  }
+
+  virtual RangeReturnType evaluate(const DomainType& u,
+                                   const XT::Common::Parameter& /*param*/ = {}) const override final
+  {
+    const auto alpha = std::make_unique<BlockVectorType>(get_alpha(u, *get_isotropic_alpha(u), true)->first);
+    return evaluate_with_alpha(*alpha);
+  }
+
+  virtual RangeReturnType evaluate_with_alpha(const BlockVectorType& alpha) const
+  {
+    RangeReturnType ret(0.);
+    auto& eta_ast_prime_vals = working_storage();
+    evaluate_eta_ast_prime(alpha, M_, eta_ast_prime_vals);
+    for (size_t dd = 0; dd < dimFlux; ++dd) {
+      // calculate ret[dd] = < omega[dd] m G_\alpha(u) >
+      for (size_t jj = 0; jj < num_blocks; ++jj) {
+        const auto offset = block_size * jj;
+        for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+          const auto factor = eta_ast_prime_vals[jj][ll] * quad_weights_[jj][ll] * quad_points_[jj][ll][dd];
+          for (size_t ii = 0; ii < block_size; ++ii)
+            ret[dd][offset + ii] += M_[jj].get_entry(ll, ii) * factor;
+        } // ll
+      } // jj
+    } // dd
+    return ret;
+  } // void evaluate(...)
+
+  virtual void jacobian(const DomainType& u,
+                        DynamicDerivativeRangeType& result,
+                        const XT::Common::Parameter& /*param*/ = {}) const override final
+  {
+    const auto alpha = std::make_unique<BlockVectorType>(get_alpha(u, *get_isotropic_alpha(u), true)->first);
+    jacobian_with_alpha(*alpha, result);
+  }
+
+  virtual void jacobian_with_alpha(const BlockVectorType& alpha, DynamicDerivativeRangeType& result) const
+  {
+    thread_local auto H = XT::Common::make_unique<BlockMatrixType>();
+    calculate_hessian(alpha, M_, *H);
+    for (size_t dd = 0; dd < dimFlux; ++dd)
+      row_jacobian(dd, M_, *H, result[dd], dd > 0);
+  }
+
+  void row_jacobian(const size_t row,
+                    const BasisValuesMatrixType& M,
+                    BlockMatrixType& H,
+                    DynamicRowDerivativeRangeType& ret,
+                    bool L_calculated = false) const
+  {
+    assert(row < dimFlux);
+    calculate_J(M, ret, row);
+    calculate_J_Hinv(ret, H, L_calculated);
+  } // void partial_u_col(...)
+
+
+  // ============================================================================================
+  // ============ Evaluations of ansatz distribution, moments, hessian etc. =====================
+  // ============================================================================================
+
+
+  std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u) const
+  {
+    return get_alpha(u, *get_isotropic_alpha(u), true);
+  }
+
+  // Solves the minimum entropy optimization problem for u.
+  // returns (alpha, (actual_u, r)), where r is the regularization parameter and actual_u the regularized u
+  std::unique_ptr<AlphaReturnType>
+  get_alpha(const DomainType& u, const VectorType& alpha_in, const bool regularize) const
+  {
+    auto ret = std::make_unique<AlphaReturnType>();
+
+    constexpr bool rescale = (entropy == EntropyType::MaxwellBoltzmann);
+
+    // rescale u such that the density <psi> is 1
+    RangeFieldType density = basis_functions_.density(u);
+    static const auto alpha_one = std::make_unique<BlockVectorType>(basis_functions_.alpha_one());
+    auto alpha_initial = std::make_unique<BlockVectorType>(*alpha_one);
+    if (rescale) {
+      *alpha_initial *= -std::log(density);
+      *alpha_initial += alpha_in;
+    } else {
+      *alpha_initial = alpha_in;
+    }
+
+    if (!(density > 0. || !(basis_functions_.min_density(u) > 0.)) || std::isinf(density))
+      DUNE_THROW(Dune::MathError, "Negative, inf or NaN density!");
+    auto phi = std::make_unique<BlockVectorType>(u);
+    if (rescale)
+      *phi *= 1. / density;
+
+    // if value has already been calculated for these values, skip computation
+    RangeFieldType tau_prime = rescale ? std::min(tau_
+                                                      / ((1 + std::sqrt(basis_dimRange) * phi->two_norm()) * density
+                                                         + std::sqrt(basis_dimRange) * tau_),
+                                                  tau_)
+                                       : tau_;
+
+    // calculate moment vector for isotropic distribution
+    auto u_iso = std::make_unique<BlockVectorType>(basis_functions_.u_iso());
+    if (!rescale)
+      *u_iso *= density;
+
+    // define further variables
+    auto g_k = std::make_unique<BlockVectorType>();
+    auto beta_out = std::make_unique<BlockVectorType>();
+    auto v = std::make_unique<BlockVectorType>();
+    thread_local auto T_k = XT::Common::make_unique<BlockMatrixType>();
+    auto beta_in = std::make_unique<BlockVectorType>(*alpha_initial);
+
+    const auto& r_sequence = regularize ? r_sequence_ : std::vector<RangeFieldType>{0.};
+    const auto r_max = r_sequence.back();
+    for (const auto& rr : r_sequence) {
+      // regularize u
+      *v = *phi;
+      if (rr > 0.) {
+        *beta_in = *get_isotropic_alpha(*v);
+        // calculate v = (1-r) u + r u_iso
+        // use beta_out as storage for u_iso_in * r
+        *v *= (1 - rr);
+        *beta_out = *u_iso;
+        *beta_out *= rr;
+        *v += *beta_out;
+      }
+      for (size_t jj = 0; jj < num_blocks; ++jj)
+        T_k->block(jj) = T_minus_one_;
+      // calculate T_k u
+      auto v_k = std::make_unique<BlockVectorType>(*v);
+      // calculate values of basis p = S_k m
+      thread_local BasisValuesMatrixType P_k(num_blocks, XT::LA::CommonDenseMatrix<RangeFieldType>(0, 0, 0., 0));
+      copy_basis_matrix(M_, P_k);
+      // calculate f_0
+      RangeFieldType f_k = get_eta_ast_integrated(*beta_in, P_k) - *beta_in * *v_k;
+
+      thread_local auto H = XT::Common::make_unique<BlockMatrixType>(0.);
+
+      int backtracking_failed = 0;
+      for (size_t kk = 0; kk < k_max_; ++kk) {
+        // exit inner for loop to increase r if too many iterations are used or cholesky decomposition fails
+        if (kk > k_0_ && rr < r_max)
+          break;
+        try {
+          change_basis(*beta_in, *v_k, P_k, *T_k, *g_k, *beta_out, *H);
+        } catch (const Dune::MathError&) {
+          if (rr < r_max)
+            break;
+          DUNE_THROW(Dune::MathError, "Failure to converge!");
+        }
+
+        // calculate descent direction d_k;
+        thread_local auto d_k = std::make_unique<BlockVectorType>();
+        *d_k = *g_k;
+        *d_k *= -1;
+
+        // Calculate stopping criteria (in original basis). Variables with _k are in current basis, without k in
+        // original basis.
+        thread_local auto alpha_tilde = std::make_unique<BlockVectorType>();
+        thread_local auto alpha_prime = std::make_unique<BlockVectorType>();
+        thread_local auto u_alpha_tilde = std::make_unique<BlockVectorType>();
+        thread_local auto u_alpha_prime = std::make_unique<BlockVectorType>();
+        thread_local auto d_alpha_tilde = std::make_unique<BlockVectorType>();
+        thread_local auto g_alpha_tilde = std::make_unique<BlockVectorType>();
+        thread_local auto u_eps_diff = std::make_unique<BlockVectorType>();
+        // convert everything to original basis
+        for (size_t jj = 0; jj < num_blocks; ++jj) {
+          XT::LA::solve_lower_triangular_transposed(T_k->block(jj), alpha_tilde->block(jj), beta_out->block(jj));
+          XT::LA::solve_lower_triangular_transposed(T_k->block(jj), d_alpha_tilde->block(jj), d_k->block(jj));
+        } // jj
+        calculate_u(*alpha_tilde, M_, *u_alpha_tilde);
+        *g_alpha_tilde = *u_alpha_tilde;
+        *g_alpha_tilde -= *v;
+        auto density_tilde = basis_functions_.density(*u_alpha_tilde);
+        if (!(density_tilde > 0.) || !(basis_functions_.min_density(*u_alpha_tilde) > 0.) || std::isinf(density_tilde))
+          break;
+
+        // if rescale == true, we ensure that the ansatz density exactly matches the density of u
+        if (rescale) {
+          *alpha_prime = *alpha_one;
+          *alpha_prime *= -std::log(density_tilde);
+          *alpha_prime += *alpha_tilde;
+          calculate_u(*alpha_prime, M_, *u_alpha_prime);
+        } else {
+          *alpha_prime = *alpha_tilde;
+          *u_alpha_prime = *u_alpha_tilde;
+        }
+
+        // calculate stopping criteria
+        *u_eps_diff = *u_alpha_prime;
+        *u_eps_diff *= -(1 - epsilon_gamma_);
+        *u_eps_diff += *v;
+
+        // working_storage contains eta_ast_prime evaluations due to the calculate_u call above
+        auto& eta_ast_prime_vals = working_storage();
+        if (g_alpha_tilde->two_norm() < tau_prime
+            && 1 - epsilon_gamma_ < std::exp(-(rescale ? d_alpha_tilde->one_norm() + std::abs(std::log(density_tilde))
+                                                       : d_alpha_tilde->one_norm()))
+            && (entropy == EntropyType::MaxwellBoltzmann || all_positive(eta_ast_prime_vals))
+            && (disable_realizability_check_ || helper<dimFlux>::is_realizable(*u_eps_diff, basis_functions_))) {
+          ret->second.first = *v;
+          ret->second.second = rr;
+          if (rescale) {
+            ret->first = *alpha_one;
+            ret->first *= std::log(density);
+            ret->first += *alpha_prime;
+            ret->second.first *= density;
+          } else {
+            ret->first = *alpha_prime;
+          }
+          return ret;
+        } else {
+          RangeFieldType zeta_k = 1;
+          *beta_in = *beta_out;
+          // backtracking line search
+          while (backtracking_failed >= 2 || zeta_k > epsilon_ * beta_out->two_norm() / d_k->two_norm()) {
+            thread_local auto beta_new = std::make_unique<BlockVectorType>();
+            *beta_new = *d_k;
+            *beta_new *= zeta_k;
+            *beta_new += *beta_out;
+            RangeFieldType f = get_eta_ast_integrated(*beta_new, P_k) - *beta_new * *v_k;
+            if (backtracking_failed >= 2 || f <= f_k + xi_ * zeta_k * (*g_k * *d_k)) {
+              *beta_in = *beta_new;
+              f_k = f;
+              backtracking_failed = 0;
+              break;
+            }
+            zeta_k = chi_ * zeta_k;
+          } // backtracking linesearch while
+          if (zeta_k <= epsilon_ * beta_out->two_norm() / d_k->two_norm())
+            ++backtracking_failed;
+        } // else (stopping conditions)
+      } // k loop (Newton iterations)
+    } // rr loop (Regularization parameter)
+    DUNE_THROW(MathError, "Failed to converge");
+
+    return ret;
+  }
+
+  void change_basis(const BlockVectorType& beta_in,
+                    BlockVectorType& v_k,
+                    BasisValuesMatrixType& P_k,
+                    BlockMatrixType& T_k,
+                    BlockVectorType& g_k,
+                    BlockVectorType& beta_out,
+                    BlockMatrixType& H) const
+  {
+    calculate_hessian(beta_in, P_k, H);
+    FieldVector<RangeFieldType, block_size> tmp_vec;
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      XT::LA::cholesky(H.block(jj));
+    const auto& L = H;
+    T_k.rightmultiply(L);
+    L.mtv(beta_in, beta_out);
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      XT::LA::solve_lower_triangular(L.block(jj), tmp_vec, v_k.block(jj));
+      v_k.block(jj) = tmp_vec;
+    } // jj
+    apply_inverse_matrix(L, P_k);
+    calculate_u(beta_out, P_k, g_k);
+    g_k -= v_k;
+  } // void change_basis(...)
+
+  // returns density rho = < eta_ast_prime(beta_in * b(v)) >
+  RangeFieldType get_rho(const BlockVectorType& beta_in, const BasisValuesMatrixType& M) const
+  {
+    auto& eta_ast_prime_vals = working_storage();
+    evaluate_eta_ast_prime(beta_in, M, eta_ast_prime_vals);
+    RangeFieldType ret(0.);
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      ret += std::inner_product(
+          quad_weights_[jj].begin(), quad_weights_[jj].end(), eta_ast_prime_vals[jj].begin(), RangeFieldType(0.));
+    return ret;
+  }
+
+  // returns < eta_ast(beta_in * b(v)) >
+  RangeFieldType get_eta_ast_integrated(const BlockVectorType& beta_in, const BasisValuesMatrixType& M) const
+  {
+    auto& eta_ast_vals = working_storage();
+    evaluate_eta_ast(beta_in, M, eta_ast_vals);
+    RangeFieldType ret(0.);
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      ret += std::inner_product(
+          quad_weights_[jj].begin(), quad_weights_[jj].end(), eta_ast_vals[jj].begin(), RangeFieldType(0.));
+    return ret;
+  }
+
+  // returns < b \eta_{\ast}^{\prime}(\alpha^T b(v)) >
+  DomainType get_u(const DomainType& alpha) const
+  {
+    BlockVectorType u_block;
+    calculate_u(alpha, M_, u_block);
+    return u_block;
+  }
+
+  DomainType get_u(const QuadratureWeightsType& eta_ast_prime_vals) const
+  {
+    DomainType ret;
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      const auto offset = block_size * jj;
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto factor = eta_ast_prime_vals[jj][ll] * quad_weights_[jj][ll];
+        const auto* basis_ll = &(M_[jj].get_entry_ref(ll, 0.));
+        for (size_t ii = 0; ii < block_size; ++ii)
+          ret[offset + ii] += basis_ll[ii] * factor;
+      } // ll
+    } // jj (intervals)
+    return ret;
+  }
+
+  // calculate ret = \int (b eta_ast_prime(beta_in * m))
+  void calculate_u(const BlockVectorType& beta_in, const BasisValuesMatrixType& M, BlockVectorType& ret) const
+  {
+    auto& eta_ast_prime_vals = working_storage();
+    evaluate_eta_ast_prime(beta_in, M, eta_ast_prime_vals);
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      auto& ret_jj = ret.block(jj);
+      std::fill(ret_jj.begin(), ret_jj.end(), 0.);
+      const size_t num_quad_points = quad_weights_[jj].size();
+      for (size_t ll = 0; ll < num_quad_points; ++ll) {
+        const auto factor = eta_ast_prime_vals[jj][ll] * quad_weights_[jj][ll];
+        const auto* basis_ll = &(M[jj].get_entry_ref(ll, 0.));
+        for (size_t ii = 0; ii < block_size; ++ii)
+          ret_jj[ii] += basis_ll[ii] * factor;
+      } // ll
+    } // jj
+  }
+
+  void calculate_hessian(const BlockVectorType& alpha, const BasisValuesMatrixType& M, BlockMatrixType& H) const
+  {
+    auto& eta_ast_twoprime_vals = working_storage();
+    evaluate_eta_ast_twoprime(alpha, M, eta_ast_twoprime_vals);
+    calculate_hessian(eta_ast_twoprime_vals, M, H);
+  } // void calculate_hessian(...)
+
+  void calculate_hessian(const QuadratureWeightsType& eta_ast_twoprime_vals,
+                         const BasisValuesMatrixType& M,
+                         BlockMatrixType& H) const
+  {
+    // matrix is symmetric, we only use lower triangular part
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      std::fill(H.block(jj).begin(), H.block(jj).end(), 0.);
+      const size_t num_quad_points = quad_weights_[jj].size();
+      for (size_t ll = 0; ll < num_quad_points; ++ll) {
+        auto factor_ll = eta_ast_twoprime_vals[jj][ll] * quad_weights_[jj][ll];
+        const auto* basis_ll = &(M[jj].get_entry_ref(ll, 0.));
+        for (size_t ii = 0; ii < block_size; ++ii) {
+          auto* H_row = &(H.block(jj)[ii][0]);
+          const auto factor_ll_ii = basis_ll[ii] * factor_ll;
+          for (size_t kk = 0; kk <= ii; ++kk)
+            H_row[kk] += basis_ll[kk] * factor_ll_ii;
+        } // ii
+      } // ll
+    } // jj
+  } // void calculate_hessian(...)
+
+  void apply_inverse_hessian(const QuadratureWeightsType& eta_ast_twoprime_vals,
+                             const DomainType& u,
+                             DomainType& Hinv_u) const
+  {
+    thread_local auto H = XT::Common::make_unique<BlockMatrixType>();
+    calculate_hessian(eta_ast_twoprime_vals, M_, *H);
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      XT::LA::cholesky(H->block(jj));
+    FieldVector<RangeFieldType, block_size> tmp_vec;
+    FieldVector<RangeFieldType, block_size> block_u;
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      // calculate C = A (L^T)^{-1}
+      const auto offset = block_size * jj;
+      for (size_t ii = 0; ii < block_size; ++ii)
+        block_u[ii] = u[offset + ii];
+      XT::LA::solve_lower_triangular(H->block(jj), tmp_vec, block_u);
+      XT::LA::solve_lower_triangular_transposed(H->block(jj), block_u, tmp_vec);
+      for (size_t ii = 0; ii < block_size; ++ii)
+        Hinv_u[offset + ii] = block_u[ii];
+    } // jj
+  }
+
+  // J = df/dalpha is the derivative of the flux with respect to alpha.
+  // As F = (f_1, f_2, f_3) is matrix-valued
+  // (div f = \sum_{i=1}^d \partial_{x_i} f_i  = \sum_{i=1}^d \partial_{x_i} < v_i m \hat{psi}(alpha) > is
+  // vector-valued),
+  // the derivative is the vector of matrices (df_1/dalpha, df_2/dalpha, ...)
+  // this function returns the dd-th matrix df_dd/dalpha of J
+  // assumes work_vecs already contains the needed eta_ast_twoprime(alpha * m) values
+  void calculate_J(const BasisValuesMatrixType& M, DynamicRowDerivativeRangeType& J_dd, const size_t dd) const
+  {
+    assert(dd < dimFlux);
+    const auto& eta_ast_twoprime_values = working_storage();
+    // matrix is symmetric, we only use lower triangular part
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      const auto offset = jj * block_size;
+      // set entries in this block to zero
+      for (size_t ii = 0; ii < block_size; ++ii)
+        std::fill_n(&(J_dd.get_entry_ref(offset + ii, offset)), ii + 1, 0.);
+      // now calculate integral
+      const size_t num_quad_points = quad_weights_[jj].size();
+      for (size_t ll = 0; ll < num_quad_points; ++ll) {
+        auto factor_ll = eta_ast_twoprime_values[jj][ll] * quad_weights_[jj][ll] * quad_points_[jj][ll][dd];
+        const auto* basis_ll = &(M[jj].get_entry_ref(ll, 0.));
+        for (size_t ii = 0; ii < block_size; ++ii) {
+          auto* J_row = &(J_dd[offset + ii][offset]);
+          const auto factor_ll_ii = basis_ll[ii] * factor_ll;
+          for (size_t kk = 0; kk <= ii; ++kk)
+            J_row[kk] += basis_ll[kk] * factor_ll_ii;
+        } // ii
+      } // ll
+    } // jj
+    // symmetric update for upper triangular part of J
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      const auto offset = block_size * jj;
+      for (size_t mm = 0; mm < block_size; ++mm)
+        for (size_t nn = mm + 1; nn < block_size; ++nn)
+          J_dd.set_entry(offset + mm, offset + nn, J_dd.get_entry(offset + nn, offset + mm));
+    }
+  } // void calculate_J(...)
+
+  // calculates A = A B^{-1}. B is assumed to be symmetric positive definite.
+  static void calculate_J_Hinv(DynamicRowDerivativeRangeType& A, BlockMatrixType& B, bool L_calculated = false)
+  {
+    // if B = LL^T, then we have to calculate ret = A (L^T)^{-1} L^{-1} = C L^{-1}
+    // calculate B = LL^T first
+    if (!L_calculated) {
+      for (size_t jj = 0; jj < num_blocks; ++jj)
+        XT::LA::cholesky(B.block(jj));
+    }
+    FieldVector<RangeFieldType, block_size> tmp_vec;
+    FieldVector<RangeFieldType, block_size> tmp_A_row;
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      // calculate C = A (L^T)^{-1}
+      const auto offset = block_size * jj;
+      for (size_t ii = 0; ii < block_size; ++ii) {
+        for (size_t kk = 0; kk < block_size; ++kk)
+          tmp_A_row[kk] = A[offset + ii][offset + kk];
+        XT::LA::solve_lower_triangular(B.block(jj), tmp_vec, tmp_A_row);
+        // calculate ret = C L^{-1}
+        XT::LA::solve_lower_triangular_transposed(B.block(jj), tmp_A_row, tmp_vec);
+        for (size_t kk = 0; kk < block_size; ++kk)
+          A[offset + ii][offset + kk] = tmp_A_row[kk];
+      } // ii
+    } // jj
+  } // void calculate_J_Hinv(...)
+
+
+  // ============================================================================================
+  // ============================= Entropy evaluations ==========================================
+  // ============================================================================================
+
+
+  // evaluates \eta_{\ast}(\alpha^T b(v_i)) for all quadrature points v_i
+  void evaluate_eta_ast(const DomainType& alpha, const BasisValuesMatrixType& M, QuadratureWeightsType& ret) const
+  {
+    calculate_scalar_products(alpha, M, ret);
+    apply_exponential(ret);
+    evaluate_eta_ast(ret);
+  }
+
+  // evaluates \eta_{\ast}(\alpha^T b(v_i)) for all quadrature points v_i, assumes that ret already contains
+  // exp(alpha^T b(v_i))
+  void evaluate_eta_ast(QuadratureWeightsType& ret) const
+  {
+    if (entropy == EntropyType::BoseEinstein)
+      for (size_t jj = 0; jj < num_blocks; ++jj)
+        for (size_t ll = 0; ll < ret[jj].size(); ++ll)
+          ret[jj][ll] = -std::log(1 - ret[jj][ll]);
+  }
+
+  // evaluates \eta_{\ast}^{\prime}(\alpha^T b(v_i)) for all quadrature points v_i
+  void evaluate_eta_ast_prime(const DomainType& alpha, const BasisValuesMatrixType& M, QuadratureWeightsType& ret) const
+  {
+    calculate_scalar_products(alpha, M, ret);
+    apply_exponential(ret);
+    evaluate_eta_ast_prime(ret);
+  }
+
+  // evaluates \eta_{\ast}^{\prime}(\alpha^T b(v_i)) for all quadrature points v_i, assumes that ret already contains
+  // exp(alpha^T b(v_i))
+  void evaluate_eta_ast_prime(QuadratureWeightsType& ret) const
+  {
+    if (entropy == EntropyType::BoseEinstein)
+      for (size_t jj = 0; jj < num_blocks; ++jj)
+        for (size_t ll = 0; ll < ret[jj].size(); ++ll)
+          ret[jj][ll] /= (1 - ret[jj][ll]);
+  }
+
+  // evaluates \eta_{\ast}^{\prime\prime}(\alpha^T b(v_i)) for all quadrature points v_i
+  void
+  evaluate_eta_ast_twoprime(const DomainType& alpha, const BasisValuesMatrixType& M, QuadratureWeightsType& ret) const
+  {
+    calculate_scalar_products(alpha, M, ret);
+    apply_exponential(ret);
+    evaluate_eta_ast_twoprime(ret);
+  }
+
+  // evaluates \eta_{\ast}^{\prime\prime}(\alpha^T b(v_i)) for all quadrature points v_i, assumes that ret already
+  // contains exp(alpha^T b(v_i))
+  void evaluate_eta_ast_twoprime(QuadratureWeightsType& ret) const
+  {
+    if (entropy == EntropyType::BoseEinstein)
+      for (size_t jj = 0; jj < num_blocks; ++jj)
+        for (size_t ll = 0; ll < ret[jj].size(); ++ll)
+          ret[jj][ll] /= std::pow(1 - ret[jj][ll], 2);
+  }
+
+  // stores evaluations of exp(alpha^T b(v_i)) for all quadrature points v_i
+  void store_exp_evaluations(QuadratureWeightsType& exp_evaluations, const DomainType& alpha) const
+  {
+    this->calculate_scalar_products(alpha, M_, exp_evaluations);
+    this->apply_exponential(exp_evaluations);
+  }
+
+  void store_eta_ast_prime_vals(const QuadratureWeightsType& exp_evaluations, QuadratureWeightsType& eta_ast_prime_vals)
+  {
+    eta_ast_prime_vals = exp_evaluations;
+    evaluate_eta_ast_prime(eta_ast_prime_vals);
+  }
+
+  void store_eta_ast_twoprime_vals(const QuadratureWeightsType& exp_evaluations,
+                                   QuadratureWeightsType& eta_ast_twoprime_vals)
+  {
+    eta_ast_twoprime_vals = exp_evaluations;
+    evaluate_eta_ast_twoprime(eta_ast_twoprime_vals);
+  }
+
+  // stores evaluations of a given boundary distribution psi(v) at all quadrature points v_i
+  void store_boundary_distribution_evaluations(
+      QuadratureWeightsType& boundary_distribution_evaluations,
+      const std::function<RangeFieldType(const FluxDomainType&)>& boundary_distribution) const
+  {
+    boundary_distribution_evaluations.resize(num_blocks);
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      boundary_distribution_evaluations[jj].resize(quad_points_[jj].size());
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll)
+        boundary_distribution_evaluations[jj][ll] = boundary_distribution(quad_points_[jj][ll]);
+    }
+  }
+
+
+  // ============================================================================================
+  // =============================== Kinetic fluxes =============================================
+  // ============================================================================================
+
+
+  // calculate \sum_{i=1}^d < v_i m \psi > n_i, where n is the unit outer normal,
+  // m is the basis function vector, \psi is the ansatz corresponding to u
+  // and x, v, t are the space, velocity and time variable, respectively
+  // As we are using cartesian grids, n_i == 0 in all but one dimension, so only evaluate for i == dd
+  DomainType
+  evaluate_kinetic_flux(const DomainType& u_i, const DomainType& u_j, const FluxDomainType& n_ij, const size_t dd) const
+  {
+    // calculate \sum_{i=1}^d < \omega_i m G_\alpha(u) > n_i
+    const auto alpha_i = std::make_unique<BlockVectorType>(get_alpha(u_i, *get_isotropic_alpha(u_i), true)->first);
+    const auto alpha_j = std::make_unique<BlockVectorType>(get_alpha(u_j, *get_isotropic_alpha(u_j), true)->first);
+    evaluate_kinetic_flux_with_alphas(*alpha_i, *alpha_j, n_ij, dd);
+  } // DomainType evaluate_kinetic_flux(...)
+
+  DomainType evaluate_kinetic_flux_with_alphas(const BlockVectorType& alpha_i,
+                                               const BlockVectorType& alpha_j,
+                                               const FluxDomainType& n_ij,
+                                               const size_t dd) const
+  {
+    // calculate \sum_{i=1}^d < \omega_i m G_\alpha(u) > n_i
+    thread_local FieldVector<QuadratureWeightsType, 2> eta_ast_prime_vals{
+        {QuadratureWeightsType(num_blocks), QuadratureWeightsType(num_blocks)}};
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      eta_ast_prime_vals[0][jj].resize(quad_points_[jj].size());
+      eta_ast_prime_vals[1][jj].resize(quad_points_[jj].size());
+    }
+    evaluate_eta_ast_prime(alpha_i, M_, eta_ast_prime_vals[0]);
+    evaluate_eta_ast_prime(alpha_j, M_, eta_ast_prime_vals[1]);
+    DomainType ret(0);
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      const auto offset = block_size * jj;
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll) {
+        const auto position = quad_points_[jj][ll][dd];
+        RangeFieldType factor =
+            position * n_ij[dd] > 0. ? eta_ast_prime_vals[0][jj][ll] : eta_ast_prime_vals[1][jj][ll];
+        factor *= quad_weights_[jj][ll] * position;
+        for (size_t ii = 0; ii < block_size; ++ii)
+          ret[offset + ii] += M_[jj].get_entry(ll, ii) * factor;
+      } // ll
+    } // jj
+    ret *= n_ij[dd];
+    return ret;
+  } // DomainType evaluate_kinetic_flux(...)
+
+  // Calculates left and right kinetic flux with reconstructed densities. Ansatz distribution values contains
+  // evaluations of the ansatz distribution at each quadrature point for a stencil of three entities. The distributions
+  // are reconstructed pointwise for each quadrature point and the resulting (part of) the kinetic flux is <
+  // psi_reconstr * b * v>_{+/-}.
+  template <SlopeLimiterType slope_type, class FluxesMapType>
+  void calculate_reconstructed_fluxes(const FieldVector<const QuadratureWeightsType*, 3>& ansatz_distribution_values,
+                                      FluxesMapType& flux_values,
+                                      const size_t dd) const
+  {
+    // get flux storage
+    BasisDomainType coord(0.5);
+    coord[dd] = 0;
+    auto& left_flux_value = flux_values[coord];
+    coord[dd] = 1;
+    auto& right_flux_value = flux_values[coord];
+    right_flux_value = left_flux_value = DomainType(0.);
+
+    // evaluate exp(alpha^T b(v_i)) at all quadratures points v_i for all three alphas
+    thread_local XT::Common::FieldVector<BlockQuadratureWeightsType, 2> reconstructed_values(
+        BlockQuadratureWeightsType(quad_points_[0].size()));
+
+    // get left and right reconstructed values for each quadrature point v_i
+    auto& vals_left = reconstructed_values[0];
+    auto& vals_right = reconstructed_values[1];
+
+    const auto slope_func =
+        (slope_type == SlopeLimiterType::minmod) ? XT::Common::minmod<RangeFieldType> : superbee<RangeFieldType>;
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      // reconstruct densities
+      if (slope_type == SlopeLimiterType::no_slope) {
+        for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll)
+          vals_left[ll] = vals_right[ll] = (*ansatz_distribution_values[1])[jj][ll];
+      } else {
+        for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll) {
+          const auto slope =
+              slope_func((*ansatz_distribution_values[1])[jj][ll] - (*ansatz_distribution_values[0])[jj][ll],
+                         (*ansatz_distribution_values[2])[jj][ll] - (*ansatz_distribution_values[1])[jj][ll]);
+          vals_left[ll] = (*ansatz_distribution_values[1])[jj][ll] - 0.5 * slope;
+          vals_right[ll] = (*ansatz_distribution_values[1])[jj][ll] + 0.5 * slope;
+        } // ll
+      }
+      // calculate fluxes
+      const auto offset = block_size * jj;
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll) {
+        const auto position = quad_points_[jj][ll][dd];
+        RangeFieldType factor = position > 0. ? vals_right[ll] : vals_left[ll];
+        factor *= quad_weights_[jj][ll] * position;
+        auto& val = position > 0. ? right_flux_value : left_flux_value;
+        for (size_t ii = 0; ii < block_size; ++ii)
+          val[offset + ii] += M_[jj].get_entry(ll, ii) * factor;
+      } // ll
+    } // jj
+  } // void calculate_reconstructed_fluxes(...)
+
+
+  // ============================================================================================
+  // ================================== Helper functions ========================================
+  // ============================================================================================
+
+
+  const MomentBasis& basis_functions() const
+  {
+    return basis_functions_;
+  }
+
+  // temporary vectors to store inner products and exponentials
+  QuadratureWeightsType& working_storage() const
+  {
+    thread_local QuadratureWeightsType work_vecs(num_blocks);
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      work_vecs[jj].resize(quad_points_[jj].size());
+    return work_vecs;
+  }
+
+  bool all_positive(const QuadratureWeightsType& vals) const
+  {
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll) {
+        const auto val = vals[jj][ll];
+        if (val < 0. || std::isinf(val) || std::isnan(val))
+          return false;
+      }
+    return true;
+  }
+
+  std::unique_ptr<BlockVectorType> get_isotropic_alpha(const RangeFieldType density) const
+  {
+    return std::make_unique<BlockVectorType>(basis_functions_.alpha_iso(density));
+  }
+
+  std::unique_ptr<BlockVectorType> get_isotropic_alpha(const DomainType& u) const
+  {
+    return get_isotropic_alpha(basis_functions_.density(u));
+  }
+
+  void copy_basis_matrix(const BasisValuesMatrixType& source_mat, BasisValuesMatrixType& range_mat) const
+  {
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      range_mat[jj].backend() = source_mat[jj].backend();
+  }
+
+  void calculate_scalar_products_block(const size_t jj,
+                                       const LocalVectorType& beta_in,
+                                       const XT::LA::CommonDenseMatrix<RangeFieldType>& M,
+                                       BlockQuadratureWeightsType& scalar_products) const
+  {
+    const size_t num_quad_points = quad_points_[jj].size();
+    for (size_t ll = 0; ll < num_quad_points; ++ll) {
+      const auto* basis_ll = &(M.get_entry_ref(ll, 0.));
+      scalar_products[ll] = std::inner_product(beta_in.begin(), beta_in.end(), basis_ll, 0.);
+    } // ll
+  }
+
+  void calculate_scalar_products(const BlockVectorType& beta_in,
+                                 const BasisValuesMatrixType& M,
+                                 QuadratureWeightsType& scalar_products) const
+  {
+    scalar_products.resize(num_blocks);
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      scalar_products[jj].resize(quad_weights_[jj].size());
+      calculate_scalar_products_block(jj, beta_in.block(jj), M[jj], scalar_products[jj]);
+    }
+  }
+
+  void apply_exponential(BlockQuadratureWeightsType& values) const
+  {
+    assert(values.size() < std::numeric_limits<int>::max());
+    XT::Common::Mkl::exp(static_cast<int>(values.size()), values.data(), values.data());
+  }
+
+  void apply_exponential(QuadratureWeightsType& values) const
+  {
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      apply_exponential(values[jj]);
+  }
+
+  void copy_transposed(const LocalMatrixType& T_k, LocalMatrixType& T_k_trans) const
+  {
+    for (size_t ii = 0; ii < block_size; ++ii)
+      for (size_t kk = 0; kk <= ii; ++kk)
+        T_k_trans[kk][ii] = T_k[ii][kk];
+  }
+
+  void apply_inverse_matrix_block(const size_t jj,
+                                  const LocalMatrixType& T_k,
+                                  XT::LA::CommonDenseMatrix<RangeFieldType>& M) const
+  {
+    const size_t num_quad_points = quad_points_[jj].size();
+    if (block_size == 2) {
+      const auto T_00_inv = 1 / T_k[0][0];
+      const auto T_11_inv = 1 / T_k[1][1];
+      for (size_t ll = 0; ll < num_quad_points; ++ll) {
+        auto* basis_ll = &(M.get_entry_ref(ll, 0.));
+        basis_ll[0] *= T_00_inv;
+        basis_ll[1] = (basis_ll[1] - T_k[1][0] * basis_ll[0]) * T_11_inv;
+      }
+    } else if (block_size == 4) {
+      FieldVector<RangeFieldType, 4> diag_inv;
+      for (size_t ii = 0; ii < 4; ++ii)
+        diag_inv[ii] = 1. / T_k[ii][ii];
+      for (size_t ll = 0; ll < num_quad_points; ++ll) {
+        auto* basis_ll = &(M.get_entry_ref(ll, 0.));
+        basis_ll[0] *= diag_inv[0];
+        basis_ll[1] = (basis_ll[1] - T_k[1][0] * basis_ll[0]) * diag_inv[1];
+        basis_ll[2] = (basis_ll[2] - T_k[2][0] * basis_ll[0] - T_k[2][1] * basis_ll[1]) * diag_inv[2];
+        basis_ll[3] =
+            (basis_ll[3] - T_k[3][0] * basis_ll[0] - T_k[3][1] * basis_ll[1] - T_k[3][2] * basis_ll[2]) * diag_inv[3];
+      }
+    } else {
+#  if HAVE_MKL
+      thread_local LocalMatrixType T_k_trans(0.);
+      assert(num_quad_points < std::numeric_limits<int>::max());
+      // Calculate the transpose here first as this is much faster than passing the matrix to dtrsm and using
+      // CblasTrans
+      copy_transposed(T_k, T_k_trans);
+      XT::Common::Cblas::dtrsm(XT::Common::Cblas::row_major(),
+                               XT::Common::Cblas::right(),
+                               XT::Common::Cblas::upper(),
+                               XT::Common::Cblas::no_trans(),
+                               XT::Common::Cblas::non_unit(),
+                               static_cast<int>(num_quad_points),
+                               block_size,
+                               1.,
+                               &(T_k_trans[0][0]),
+                               block_size,
+                               M.data(),
+                               block_size);
+#  else
+      LocalVectorType tmp_vec, tmp_vec2;
+      for (size_t ll = 0; ll < num_quad_points; ++ll) {
+        auto* M_row = &(M.get_entry_ref(ll, 0.));
+        std::copy_n(M_row, block_size, tmp_vec.begin());
+        XT::LA::solve_lower_triangular(T_k, tmp_vec2, tmp_vec);
+        std::copy_n(tmp_vec2.begin(), block_size, M_row);
+      }
+#  endif
+    }
+  }
+
+  void apply_inverse_matrix(const BlockMatrixType& T_k, BasisValuesMatrixType& M) const
+  {
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      apply_inverse_matrix_block(jj, T_k.block(jj), M[jj]);
+  }
+
+  template <size_t domainDim = dimFlux, class anything = void>
+  struct helper
+  {
+#  if HAVE_QHULL
+    static void calculate_plane_coefficients(const MomentBasis& basis_functions)
+    {
+      if (!basis_functions.plane_coefficients()[0].size())
+        basis_functions.calculate_plane_coefficients();
+    }
+
+    static bool is_realizable(const BlockVectorType& u, const MomentBasis& basis_functions)
+    {
+      for (size_t jj = 0; jj < num_blocks; ++jj)
+        for (const auto& coeff : basis_functions.plane_coefficients()[jj])
+          if (!(u.block(jj) * coeff.first < coeff.second))
+            return false;
+      return true;
+    }
+#  else
+    static void calculate_plane_coefficients(const MomentBasis& /*basis_functions*/)
+    {
+      std::cerr << "Warning: You are missing Qhull, realizability stopping condition will not be checked!" << std::endl;
+    }
+
+    static bool is_realizable(const BlockVectorType& /*u*/, const MomentBasis& /*basis_functions*/)
+    {
+      return true;
+    }
+#  endif
+  }; // class helper<...>
+
+  template <class anything>
+  struct helper<1, anything>
+  {
+    static void calculate_plane_coefficients(const MomentBasis& /*basis_functions*/) {}
+
+    static bool is_realizable(const BlockVectorType& u, const MomentBasis& basis_functions)
+    {
+      for (size_t jj = 0; jj < num_blocks; ++jj) {
+        const auto& u0 = u.block(jj)[0];
+        const auto& u1 = u.block(jj)[1];
+        const auto& v0 = basis_functions.partitioning()[jj];
+        const auto& v1 = basis_functions.partitioning()[jj + 1];
+        bool ret = (u0 >= 0) && (u1 <= v1 * u0) && (v0 * u0 <= u1);
+        if (!ret)
+          return false;
+      } // jj
+      return true;
+    }
+  }; // class helper<1, ...>
+
+  const MomentBasis& basis_functions_;
+  QuadraturePointsType quad_points_;
+  QuadratureWeightsType quad_weights_;
+  BasisValuesMatrixType M_;
+  const RangeFieldType tau_;
+  const bool disable_realizability_check_;
+  const RangeFieldType epsilon_gamma_;
+  const RangeFieldType chi_;
+  const RangeFieldType xi_;
+  const std::vector<RangeFieldType> r_sequence_;
+  const size_t k_0_;
+  const size_t k_max_;
+  const RangeFieldType epsilon_;
+  LocalMatrixType T_minus_one_;
+};
+#endif // ENTROPY_FLUX_USE_PARTIAL_MOMENTS_SPECIALIZATION
+
+#if ENTROPY_FLUX_USE_3D_HATFUNCTIONS_SPECIALIZATION
+/**
+ * Specialization of EntropyBasedFluxImplementation for 3D Hatfunctions
+ */
+template <class D, class R, size_t dimRange_or_refinements, size_t fluxDim, EntropyType entropy>
+class EntropyBasedFluxImplementation<HatFunctionMomentBasis<D, 3, R, dimRange_or_refinements, 1, fluxDim, entropy>>
+  : public XT::Functions::FunctionInterface<
+        HatFunctionMomentBasis<D, 3, R, dimRange_or_refinements, 1, fluxDim, entropy>::dimRange,
+        fluxDim,
+        HatFunctionMomentBasis<D, 3, R, dimRange_or_refinements, 1, fluxDim, entropy>::dimRange,
+        R>
+{
+public:
+  using MomentBasis = HatFunctionMomentBasis<D, 3, R, dimRange_or_refinements, 1, fluxDim, entropy>;
+  using BaseType =
+      typename XT::Functions::FunctionInterface<MomentBasis::dimRange, MomentBasis::dimFlux, MomentBasis::dimRange, R>;
+  using ThisType = EntropyBasedFluxImplementation;
+  static const size_t dimFlux = MomentBasis::dimFlux;
+  static const size_t basis_dimRange = MomentBasis::dimRange;
+  using typename BaseType::DomainFieldType;
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicDerivativeRangeType;
+  using typename BaseType::DynamicRowDerivativeRangeType;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::RangeReturnType;
+  using BasisDomainType = typename MomentBasis::DomainType;
+  using FluxDomainType = FieldVector<DomainFieldType, dimFlux>;
+  using DynamicRangeType = DynamicVector<RangeFieldType>;
+  using LocalVectorType = XT::Common::FieldVector<RangeFieldType, 3>;
+  using LocalMatrixType = XT::Common::FieldMatrix<RangeFieldType, 3, 3>;
+  using BasisValuesMatrixType = std::vector<std::vector<LocalVectorType>>;
+  using QuadraturePointsType = std::vector<std::vector<BasisDomainType>>;
+  using QuadratureWeightsType = std::vector<std::vector<RangeFieldType>>;
+#  if HAVE_EIGEN
+  using SparseMatrixType = typename XT::LA::Container<RangeFieldType, XT::LA::Backends::eigen_sparse>::MatrixType;
+  using VectorType = typename XT::LA::Container<RangeFieldType, XT::LA::Backends::eigen_sparse>::VectorType;
+#  else
+  using SparseMatrixType = typename XT::LA::Container<RangeFieldType, XT::LA::default_sparse_backend>::MatrixType;
+  using VectorType = typename XT::LA::Container<RangeFieldType, XT::LA::default_sparse_backend>::VectorType;
+#  endif
+  using AlphaReturnType = std::pair<VectorType, std::pair<DomainType, RangeFieldType>>;
+
+  explicit EntropyBasedFluxImplementation(const MomentBasis& basis_functions,
+                                          const RangeFieldType tau,
+                                          const bool /*disable_realizability_check*/,
+                                          const RangeFieldType epsilon_gamma,
+                                          const RangeFieldType chi,
+                                          const RangeFieldType xi,
+                                          const std::vector<RangeFieldType> r_sequence,
+                                          const size_t k_0,
+                                          const size_t k_max,
+                                          const RangeFieldType epsilon)
+    : basis_functions_(basis_functions)
+    , quad_points_(basis_functions_.triangulation().faces().size())
+    , quad_weights_(basis_functions_.triangulation().faces().size())
+    , M_(basis_functions_.triangulation().faces().size())
+    , tau_(tau)
+    , epsilon_gamma_(epsilon_gamma)
+    , chi_(chi)
+    , xi_(xi)
+    , r_sequence_(r_sequence)
+    , k_0_(k_0)
+    , k_max_(k_max)
+    , epsilon_(epsilon)
+    , num_faces_(basis_functions_.triangulation().faces().size())
+  {
+    const auto& triangulation = basis_functions_.triangulation();
+    const auto& faces = triangulation.faces();
+    assert(triangulation.vertices().size() == basis_dimRange);
+    // create pattern
+    XT::LA::SparsityPatternDefault pattern(basis_dimRange);
+    for (size_t vertex_index = 0; vertex_index < basis_dimRange; ++vertex_index) {
+      const auto& vertex = triangulation.vertices()[vertex_index];
+      const auto& adjacent_faces = triangulation.get_face_indices(vertex->position());
+      for (const auto& face_index : adjacent_faces) {
+        const auto& face_vertices = faces[face_index]->vertices();
+        assert(face_vertices.size() == 3);
+        for (size_t jj = 0; jj < 3; ++jj)
+          pattern.insert(vertex_index, face_vertices[jj]->index());
+      }
+    }
+    pattern.sort();
+    pattern_ = pattern;
+    // store basis evaluations
+    const auto& quadratures = basis_functions_.quadratures();
+    assert(quadratures.size() == num_faces_);
+    for (size_t jj = 0; jj < num_faces_; ++jj) {
+      quad_points_[jj].resize(quadratures[jj].size());
+      quad_weights_[jj].resize(quadratures[jj].size());
+      for (size_t ll = 0; ll < quadratures[jj].size(); ++ll) {
+        const auto& quad_point = quadratures[jj][ll];
+        quad_points_[jj][ll] = quad_point.position();
+        quad_weights_[jj][ll] = quad_point.weight();
+      }
+    } // jj
+    for (size_t jj = 0; jj < num_faces_; ++jj) {
+      M_[jj].resize(quad_points_[jj].size());
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll)
+        M_[jj][ll] = basis_functions_.evaluate_on_face(quad_points_[jj][ll], jj);
+    } // jj
+
+  } // constructor
+
+
+  // ============================================================================================
+  // ============================= FunctionInterface methods ====================================
+  // ============================================================================================
+
+
+  int order(const XT::Common::Parameter& /*param*/ = {}) const override
+  {
+    return 1;
+  }
+
+  virtual RangeReturnType evaluate(const DomainType& u,
+                                   const XT::Common::Parameter& /*param*/ = {}) const override final
+  {
+    const auto alpha = get_alpha(u, *get_isotropic_alpha(u), true)->first;
+    return evaluate_with_alpha(alpha);
+  }
+
+  RangeReturnType evaluate_with_alpha(const DomainType& alpha) const
+  {
+    const auto alpha_vec = XT::LA::convert_to<VectorType>(alpha);
+    return evaluate_with_alpha(alpha_vec);
+  }
+
+  virtual RangeReturnType evaluate_with_alpha(const VectorType& alpha) const
+  {
+    RangeReturnType ret(0.);
+    LocalVectorType local_ret;
+    auto& eta_ast_prime_vals = working_storage();
+    evaluate_eta_ast_prime(alpha, eta_ast_prime_vals);
+    const auto& faces = basis_functions_.triangulation().faces();
+    for (size_t dd = 0; dd < dimFlux; ++dd) {
+      // calculate ret[dd] = < omega[dd] m G_\alpha(u) >
+      for (size_t jj = 0; jj < num_faces_; ++jj) {
+        local_ret *= 0.;
+        const auto& vertices = faces[jj]->vertices();
+        for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+          const auto& basis_ll = M_[jj][ll];
+          auto factor_ll = eta_ast_prime_vals[jj][ll] * quad_points_[jj][ll][dd] * quad_weights_[jj][ll];
+          for (size_t ii = 0; ii < 3; ++ii)
+            local_ret[ii] += basis_ll[ii] * factor_ll;
+        } // ll (quad points)
+        for (size_t ii = 0; ii < 3; ++ii)
+          ret[dd][vertices[ii]->index()] += local_ret[ii];
+      } // jj (faces)
+    } // dd
+    return ret;
+  } // void evaluate(...)
+
+  virtual void jacobian(const DomainType& u,
+                        DynamicDerivativeRangeType& result,
+                        const XT::Common::Parameter& /*param*/ = {}) const override final
+  {
+    const auto alpha = get_alpha(u, *get_isotropic_alpha(u), true)->first;
+    jacobian_with_alpha(alpha, result);
+  }
+
+  void jacobian_with_alpha(const DomainType& alpha, DynamicDerivativeRangeType& result) const
+  {
+    const auto alpha_vec = XT::LA::convert_to<VectorType>(alpha);
+    jacobian_with_alpha(alpha_vec, result);
+  }
+
+  void jacobian_with_alpha(const VectorType& alpha, DynamicDerivativeRangeType& result) const
+  {
+    thread_local SparseMatrixType H(basis_dimRange, basis_dimRange, pattern_, 0);
+    thread_local SparseMatrixType J(basis_dimRange, basis_dimRange, pattern_, 0);
+    calculate_hessian(alpha, M_, H);
+    for (size_t dd = 0; dd < dimFlux; ++dd) {
+      calculate_J(M_, J, dd);
+      calculate_J_Hinv(J, H, result[dd]);
+    }
+  } // ... jacobian(...)
+
+
+  // ============================================================================================
+  // ============ Evaluations of ansatz distribution, moments, hessian etc. =====================
+  // ============================================================================================
+
+
+  std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u) const
+  {
+    return get_alpha(u, *get_isotropic_alpha(u), true);
+  }
+
+  // Solves the minimum entropy optimization problem for u.
+  // returns (alpha, (actual_u, r)), where r is the regularization parameter and actual_u the regularized u
+  std::unique_ptr<AlphaReturnType>
+  get_alpha(const DomainType& u, const VectorType& alpha_in, const bool regularize) const
+  {
+    auto ret = std::make_unique<AlphaReturnType>();
+
+    RangeFieldType density = basis_functions_.density(u);
+    if (!(density > 0.) || std::isinf(density))
+      DUNE_THROW(Dune::MathError, "Negative, inf or NaN density!");
+
+    constexpr bool rescale = (entropy == EntropyType::MaxwellBoltzmann);
+
+    // rescale u such that the density <psi> is 1 if rescale is true
+    VectorType phi(basis_dimRange, 0., 0);
+    for (size_t ii = 0; ii < basis_dimRange; ++ii)
+      phi.set_entry(ii, rescale ? u[ii] / density : u[ii]);
+    VectorType alpha_one(basis_dimRange, 0., 0);
+    basis_functions_.alpha_one(alpha_one);
+
+    RangeFieldType tau_prime =
+        rescale
+            ? std::min(
+                  tau_ / ((1 + std::sqrt(basis_dimRange) * phi.l2_norm()) * density + std::sqrt(basis_dimRange) * tau_),
+                  tau_)
+            : tau_;
+    thread_local SparseMatrixType H(basis_dimRange, basis_dimRange, pattern_, 0);
+#  if HAVE_EIGEN
+    typedef ::Eigen::SparseMatrix<RangeFieldType, ::Eigen::ColMajor> ColMajorBackendType;
+    typedef ::Eigen::SimplicialLDLT<ColMajorBackendType> SolverType;
+    thread_local SolverType solver;
+    ColMajorBackendType colmajor_copy(H.backend());
+#  else // HAVE_EIGEN
+    thread_local auto solver = XT::LA::make_solver(H);
+#  endif // HAVE_EIGEN
+
+    // calculate moment vector for isotropic distribution
+    VectorType u_iso(basis_dimRange, 0., 0);
+    basis_functions_.u_iso(u_iso);
+    if (!rescale)
+      u_iso *= density;
+    VectorType alpha_k = alpha_in;
+    if (rescale)
+      alpha_k -= alpha_one * std::log(density);
+    VectorType v(basis_dimRange, 0., 0), g_k(basis_dimRange, 0., 0), d_k(basis_dimRange, 0., 0),
+        tmp_vec(basis_dimRange, 0., 0), alpha_prime(basis_dimRange);
+    const auto& r_sequence = regularize ? r_sequence_ : std::vector<RangeFieldType>{0.};
+    const auto r_max = r_sequence.back();
+    for (const auto& rr : r_sequence_) {
+      // regularize u
+      v = phi;
+      if (rr > 0) {
+        alpha_k = *get_isotropic_alpha(v);
+        tmp_vec = u_iso;
+        tmp_vec *= rr;
+        v *= 1 - rr;
+        v += tmp_vec;
+      }
+
+      // calculate f_0
+      RangeFieldType f_k = calculate_f(alpha_k, v);
+
+      int backtracking_failed = 0;
+      for (size_t kk = 0; kk < k_max_; ++kk) {
+        // exit inner for loop to increase r if too many iterations are used
+        if (kk > k_0_ && rr < r_max)
+          break;
+        // calculate gradient g
+        calculate_gradient(alpha_k, v, g_k);
+        // calculate Hessian H
+        calculate_hessian(alpha_k, M_, H, entropy == EntropyType::MaxwellBoltzmann);
+        // calculate descent direction d_k;
+        tmp_vec = g_k;
+        tmp_vec *= -1;
+        try {
+#  if HAVE_EIGEN
+          colmajor_copy = H.backend();
+          solver.analyzePattern(colmajor_copy);
+          solver.factorize(colmajor_copy);
+          d_k.backend() = solver.solve(tmp_vec.backend());
+#  else // HAVE_EIGEN
+          solver.apply(tmp_vec, d_k);
+#  endif // HAVE_EIGEN
+        } catch (const XT::LA::Exceptions::linear_solver_failed& error) {
+          if (rr < r_max) {
+            break;
+          } else {
+            DUNE_THROW(XT::LA::Exceptions::linear_solver_failed,
+                       "Failure to converge, solver error was: " << error.what());
+          }
+        }
+
+        const auto& alpha_tilde = alpha_k;
+        auto& u_alpha_tilde = tmp_vec;
+        u_alpha_tilde = g_k;
+        u_alpha_tilde += v;
+        auto density_tilde = basis_functions_.density(u_alpha_tilde);
+        if (!(density_tilde > 0.) || std::isinf(density_tilde))
+          break;
+        auto& u_eps_diff = tmp_vec;
+        if (rescale) {
+          alpha_prime = alpha_one;
+          alpha_prime *= -std::log(density_tilde);
+          alpha_prime += alpha_tilde;
+          calculate_u(alpha_prime, u_eps_diff);
+        } else {
+          alpha_prime = alpha_tilde;
+          u_eps_diff = u_alpha_tilde;
+        }
+        u_eps_diff *= -(1 - epsilon_gamma_);
+        u_eps_diff += v;
+        // checking realizability is cheap so we do not need the second stopping criterion
+        if (g_k.l2_norm() < tau_prime && is_realizable(u_eps_diff)) {
+          if (rescale) {
+            ret->first = alpha_one;
+            ret->first *= std::log(density);
+            ret->first += alpha_prime;
+          } else {
+            ret->first = alpha_prime;
+          }
+          const auto v_ret_eig = rescale ? v * density : v;
+          ret->second = std::make_pair(XT::LA::convert_to<DomainType>(v_ret_eig), rr);
+          return ret;
+        } else {
+          RangeFieldType zeta_k = 1;
+          // backtracking line search
+          auto& alpha_new = tmp_vec;
+          while (backtracking_failed >= 2 || zeta_k > epsilon_ * alpha_k.l2_norm() / d_k.l2_norm()) {
+            // calculate alpha_new = alpha_k + zeta_k d_k
+            alpha_new = d_k;
+            alpha_new *= zeta_k;
+            alpha_new += alpha_k;
+            // calculate f(alpha_new)
+            RangeFieldType f_new = calculate_f(alpha_new, v);
+            if (backtracking_failed >= 2 || XT::Common::FloatCmp::le(f_new, f_k + xi_ * zeta_k * (g_k * d_k))) {
+              alpha_k = alpha_new;
+              f_k = f_new;
+              backtracking_failed = 0;
+              break;
+            }
+            zeta_k = chi_ * zeta_k;
+          } // backtracking linesearch while
+          // if (zeta_k <= epsilon_ * alpha_k.two_norm() / d_k.two_norm() * 100.)
+          if (zeta_k <= epsilon_ * alpha_k.l2_norm() / d_k.l2_norm())
+            ++backtracking_failed;
+        } // else (stopping conditions)
+      } // k loop (Newton iterations)
+    } // rr loop (Regularization parameter)
+    DUNE_THROW(MathError, "Failed to converge");
+    return ret;
+  } // ... get_alpha(...)
+
+  // returns density rho = < eta_ast_prime(alpha * b(v)) >
+  RangeFieldType get_rho(const DomainType& alpha) const
+  {
+    auto& eta_ast_prime_vals = working_storage();
+    evaluate_eta_ast_prime(alpha, eta_ast_prime_vals);
+    RangeFieldType ret(0.);
+    for (size_t jj = 0; jj < num_faces_; ++jj)
+      ret += std::inner_product(
+          quad_weights_[jj].begin(), quad_weights_[jj].end(), eta_ast_prime_vals[jj].begin(), RangeFieldType(0.));
+    return ret;
+  }
+
+  // returns < eta_ast(alpha * b(v)) >
+  RangeFieldType get_eta_ast_integrated(const DomainType& alpha) const
+  {
+    return get_eta_ast_integrated(XT::LA::convert_to<VectorType>(alpha));
+  }
+
+  // returns < eta_ast(alpha * b(v)) >
+  RangeFieldType get_eta_ast_integrated(const VectorType& alpha) const
+  {
+    auto& eta_ast_vals = working_storage();
+    evaluate_eta_ast(alpha, eta_ast_vals);
+    RangeFieldType ret(0.);
+    for (size_t jj = 0; jj < num_faces_; ++jj)
+      ret += std::inner_product(
+          quad_weights_[jj].begin(), quad_weights_[jj].end(), eta_ast_vals[jj].begin(), RangeFieldType(0.));
+    return ret;
+  }
+
+  DomainType get_u(const DomainType& alpha) const
+  {
+    VectorType u(basis_dimRange, 0., 0);
+    calculate_u(XT::LA::convert_to<VectorType>(alpha), u);
+    return XT::LA::convert_to<DomainType>(u);
+  }
+
+  DomainType get_u(const QuadratureWeightsType& eta_ast_prime_vals) const
+  {
+    VectorType u(basis_dimRange, 0., 0);
+    calculate_u(eta_ast_prime_vals, u);
+    return XT::LA::convert_to<DomainType>(u);
+  }
+
+  // calculate ret = < b eta_ast_prime(alpha * b) >
+  void calculate_u(const VectorType& alpha, VectorType& u) const
+  {
+    auto& eta_ast_prime_vals = working_storage();
+    evaluate_eta_ast_prime(alpha, eta_ast_prime_vals);
+    calculate_u(eta_ast_prime_vals, u);
+  } // void calculate_u(...)
+
+  // calculate ret = < b eta_ast_prime(alpha * b) >
+  void calculate_u(const QuadratureWeightsType& eta_ast_prime_vals, VectorType& u) const
+  {
+    u *= 0.;
+    LocalVectorType local_u;
+    const auto& faces = basis_functions_.triangulation().faces();
+    for (size_t jj = 0; jj < num_faces_; ++jj) {
+      const auto& vertices = faces[jj]->vertices();
+      std::fill(local_u.begin(), local_u.end(), 0.);
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto factor = eta_ast_prime_vals[jj][ll] * quad_weights_[jj][ll];
+        for (size_t ii = 0; ii < 3; ++ii)
+          local_u[ii] += M_[jj][ll][ii] * factor;
+      } // ll (quad points)
+      for (size_t ii = 0; ii < 3; ++ii)
+        u.add_to_entry(vertices[ii]->index(), local_u[ii]);
+    } // jj (faces)
+  } // void calculate_u(...)
+
+  RangeFieldType calculate_f(const VectorType& alpha, const VectorType& v) const
+  {
+    return get_eta_ast_integrated(alpha) - alpha * v;
+  } // void calculate_f(...)
+
+  void calculate_gradient(const VectorType& alpha, const VectorType& v, VectorType& g_k) const
+  {
+    calculate_u(alpha, g_k);
+    g_k -= v;
+  }
+
+  void calculate_hessian(const QuadratureWeightsType& eta_ast_twoprime_vals,
+                         const BasisValuesMatrixType& M,
+                         SparseMatrixType& H) const
+  {
+    H *= 0.;
+    LocalMatrixType H_local(0.);
+    const auto& faces = basis_functions_.triangulation().faces();
+    for (size_t jj = 0; jj < num_faces_; ++jj) {
+      H_local *= 0.;
+      const auto& vertices = faces[jj]->vertices();
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto& basis_ll = M[jj][ll];
+        const auto factor = eta_ast_twoprime_vals[jj][ll] * quad_weights_[jj][ll];
+        for (size_t ii = 0; ii < 3; ++ii)
+          for (size_t kk = 0; kk < 3; ++kk)
+            H_local[ii][kk] += basis_ll[ii] * basis_ll[kk] * factor;
+      } // ll (quad points)
+      for (size_t ii = 0; ii < 3; ++ii)
+        for (size_t kk = 0; kk < 3; ++kk)
+          H.add_to_entry(vertices[ii]->index(), vertices[kk]->index(), H_local[ii][kk]);
+    } // jj (faces)
+  } // void calculate_hessian(...)
+
+  void calculate_hessian(const VectorType& alpha,
+                         const BasisValuesMatrixType& M,
+                         SparseMatrixType& H,
+                         const bool use_working_storage = false) const
+  {
+    auto& eta_ast_twoprime_vals = working_storage();
+    if (!use_working_storage)
+      evaluate_eta_ast_twoprime(alpha, eta_ast_twoprime_vals);
+    calculate_hessian(eta_ast_twoprime_vals, M, H);
+  } // void calculate_hessian(...)
+
+  void apply_inverse_hessian(const QuadratureWeightsType& eta_ast_twoprime_vals,
+                             const DomainType& u,
+                             DomainType& Hinv_u) const
+  {
+    thread_local SparseMatrixType H(basis_dimRange, basis_dimRange, pattern_, 0);
+    calculate_hessian(eta_ast_twoprime_vals, M_, H);
+#  if HAVE_EIGEN
+    thread_local VectorType u_vec(basis_dimRange, 0., 0), Hinv_u_vec(basis_dimRange, 0., 0);
+    std::copy(u.begin(), u.end(), u_vec.begin());
+    typedef ::Eigen::SparseMatrix<RangeFieldType, ::Eigen::ColMajor> ColMajorBackendType;
+    ColMajorBackendType colmajor_copy(H.backend());
+    typedef ::Eigen::SimplicialLDLT<ColMajorBackendType> SolverType;
+    SolverType solver;
+    solver.analyzePattern(colmajor_copy);
+    solver.factorize(colmajor_copy);
+    Hinv_u_vec.backend() = solver.solve(u_vec.backend());
+    std::copy(Hinv_u_vec.begin(), Hinv_u_vec.end(), Hinv_u.begin());
+#  else // HAVE_EIGEN
+    auto solver = XT::LA::make_solver(H);
+    VectorType Hinv_u_la(basis_dimRange);
+    VectorType u_la = XT::Common::convert_to<VectorType>(u);
+    solver.apply(u_la, Hinv_u_la);
+    Hinv_u = XT::Common::convert_to<DomainType>(Hinv_u_la);
+#  endif
+  } // void apply_inverse_hessian(..)
+
+  // J = df/dalpha is the derivative of the flux with respect to alpha.
+  // As F = (f_1, f_2, f_3) is matrix-valued
+  // (div f = \sum_{i=1}^d \partial_{x_i} f_i  = \sum_{i=1}^d \partial_{x_i} < v_i m \hat{psi}(alpha) > is
+  // vector-valued),
+  // the derivative is the vector of matrices (df_1/dalpha, df_2/dalpha, ...)
+  // this function returns the dd-th matrix df_dd/dalpha of J
+  // assumes work_vecs already contains the needed \eta_{\ast}^{\prime \prime} (\alpha * b) values
+  void calculate_J(const BasisValuesMatrixType& M, SparseMatrixType& J_dd, const size_t dd) const
+  {
+    assert(dd < dimFlux);
+    J_dd *= 0.;
+    LocalMatrixType J_local(0.);
+    auto& eta_ast_twoprime_vals = working_storage();
+    const auto& faces = basis_functions_.triangulation().faces();
+    for (size_t jj = 0; jj < num_faces_; ++jj) {
+      J_local *= 0.;
+      const auto& vertices = faces[jj]->vertices();
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto& basis_ll = M[jj][ll];
+        const auto factor = eta_ast_twoprime_vals[jj][ll] * quad_points_[jj][ll][dd] * quad_weights_[jj][ll];
+        for (size_t ii = 0; ii < 3; ++ii)
+          for (size_t kk = 0; kk < 3; ++kk)
+            J_local[ii][kk] += basis_ll[ii] * basis_ll[kk] * factor;
+      } // ll (quad points)
+      for (size_t ii = 0; ii < 3; ++ii)
+        for (size_t kk = 0; kk < 3; ++kk)
+          J_dd.add_to_entry(vertices[ii]->index(), vertices[kk]->index(), J_local[ii][kk]);
+    } // jj (faces)
+  } // void calculate_J(...)
+
+  // calculates ret = J H^{-1}. H is assumed to be symmetric positive definite, which gives ret^T = H^{-T} J^T =
+  // H^{-1} J^T, so we just have to solve y = H^{-1} x for each row x of J
+  void calculate_J_Hinv(SparseMatrixType& J, const SparseMatrixType& H, DynamicRowDerivativeRangeType& ret) const
+  {
+    thread_local VectorType solution(basis_dimRange, 0., 0), tmp_rhs(basis_dimRange, 0., 0);
+#  if HAVE_EIGEN
+    typedef ::Eigen::SparseMatrix<RangeFieldType, ::Eigen::ColMajor> ColMajorBackendType;
+    ColMajorBackendType colmajor_copy(H.backend());
+    typedef ::Eigen::SimplicialLDLT<ColMajorBackendType> SolverType;
+    SolverType solver;
+    solver.analyzePattern(colmajor_copy);
+    solver.factorize(colmajor_copy);
+#  else // HAVE_EIGEN
+    auto solver = XT::LA::make_solver(H);
+#  endif // HAVE_EIGEN
+    for (size_t ii = 0; ii < basis_dimRange; ++ii) {
+      // copy row to VectorType
+      for (size_t kk = 0; kk < basis_dimRange; ++kk)
+        tmp_rhs.set_entry(kk, J.get_entry(ii, kk));
+        // solve
+#  if HAVE_EIGEN
+      solution.backend() = solver.solve(tmp_rhs.backend());
+#  else // HAVE_EIGEN
+      solver.apply(tmp_rhs, solution);
+#  endif
+      // copy result to C
+      for (size_t kk = 0; kk < basis_dimRange; ++kk)
+        ret.set_entry(ii, kk, solution.get_entry(kk));
+    } // ii
+  } // void calculate_J_Hinv(...)
+
+
+  // ============================================================================================
+  // ============================= Entropy evaluations ==========================================
+  // ============================================================================================
+
+
+  // evaluates \eta_{\ast}(\alpha^T b(v_i)) for all quadrature points v_i
+  void evaluate_eta_ast(const VectorType& alpha, QuadratureWeightsType& ret) const
+  {
+    calculate_scalar_products(alpha, ret);
+    apply_exponential(ret);
+    evaluate_eta_ast(ret);
+  }
+
+  // evaluates \eta_{\ast}(\alpha^T b(v_i)) for all quadrature points v_i, assumes that ret already contains
+  // exp(alpha^T b(v_i))
+  void evaluate_eta_ast(QuadratureWeightsType& ret) const
+  {
+    if (entropy == EntropyType::BoseEinstein)
+      for (size_t jj = 0; jj < num_faces_; ++jj)
+        for (size_t ll = 0; ll < ret[jj].size(); ++ll)
+          ret[jj][ll] = -std::log(1 - ret[jj][ll]);
+  }
+
+  // evaluates \eta_{\ast}^{\prime}(\alpha^T b(v_i)) for all quadrature points v_i
+  void evaluate_eta_ast_prime(const VectorType& alpha, QuadratureWeightsType& ret) const
+  {
+    calculate_scalar_products(alpha, ret);
+    apply_exponential(ret);
+    evaluate_eta_ast_prime(ret);
+  }
+
+  // evaluates \eta_{\ast}^{\prime}(\alpha^T b(v_i)) for all quadrature points v_i, assumes that ret already contains
+  // exp(alpha^T b(v_i))
+  void evaluate_eta_ast_prime(QuadratureWeightsType& ret) const
+  {
+    if (entropy == EntropyType::BoseEinstein)
+      for (size_t jj = 0; jj < num_faces_; ++jj)
+        for (size_t ll = 0; ll < ret[jj].size(); ++ll)
+          ret[jj][ll] /= (1 - ret[jj][ll]);
+  }
+
+  // evaluates \eta_{\ast}^{\prime\prime}(\alpha^T b(v_i)) for all quadrature points v_i
+  void evaluate_eta_ast_twoprime(const VectorType& alpha, QuadratureWeightsType& ret) const
+  {
+    calculate_scalar_products(alpha, ret);
+    apply_exponential(ret);
+    evaluate_eta_ast_twoprime(ret);
+  }
+
+  // evaluates \eta_{\ast}^{\prime\prime}(\alpha^T b(v_i)) for all quadrature points v_i, assumes that ret already
+  // contains exp(alpha^T b(v_i))
+  void evaluate_eta_ast_twoprime(QuadratureWeightsType& ret) const
+  {
+    if (entropy == EntropyType::BoseEinstein)
+      for (size_t jj = 0; jj < num_faces_; ++jj)
+        for (size_t ll = 0; ll < ret[jj].size(); ++ll)
+          ret[jj][ll] /= std::pow(1 - ret[jj][ll], 2);
+  }
+
+  // stores evaluations of exp(alpha^T b(v_i)) for all quadrature points v_i
+  void store_exp_evaluations(QuadratureWeightsType& exp_evaluations, const DomainType& alpha) const
+  {
+    this->calculate_scalar_products(XT::LA::convert_to<VectorType>(alpha), exp_evaluations);
+    this->apply_exponential(exp_evaluations);
+  }
+
+  void store_eta_ast_prime_vals(const QuadratureWeightsType& exp_evaluations, QuadratureWeightsType& eta_ast_prime_vals)
+  {
+    eta_ast_prime_vals = exp_evaluations;
+    evaluate_eta_ast_prime(eta_ast_prime_vals);
+  }
+
+  void store_eta_ast_twoprime_vals(const QuadratureWeightsType& exp_evaluations,
+                                   QuadratureWeightsType& eta_ast_twoprime_vals)
+  {
+    eta_ast_twoprime_vals = exp_evaluations;
+    evaluate_eta_ast_twoprime(eta_ast_twoprime_vals);
+  }
+
+  // stores evaluations of a given boundary distribution psi(v) at all quadrature points v_i
+  void store_boundary_distribution_evaluations(
+      QuadratureWeightsType& boundary_distribution_evaluations,
+      const std::function<RangeFieldType(const FluxDomainType&)>& boundary_distribution) const
+  {
+    boundary_distribution_evaluations.resize(num_faces_);
+    for (size_t jj = 0; jj < num_faces_; ++jj) {
+      boundary_distribution_evaluations[jj].resize(quad_points_[jj].size());
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll)
+        boundary_distribution_evaluations[jj][ll] = boundary_distribution(quad_points_[jj][ll]);
+    }
+  }
+
+
+  // ============================================================================================
+  // =============================== Kinetic fluxes =============================================
+  // ============================================================================================
+
+
+  DomainType
+  evaluate_kinetic_flux(const DomainType& u_i, const DomainType& u_j, const FluxDomainType& n_ij, const size_t dd) const
+  {
+    const auto alpha_i = get_alpha(u_i, *get_isotropic_alpha(u_i), true)->first;
+    const auto alpha_j = get_alpha(u_j, *get_isotropic_alpha(u_j), true)->first;
+    return evaluate_kinetic_flux_with_alphas(alpha_i, alpha_j, n_ij, dd);
+  } // DomainType evaluate_kinetic_flux(...)
+
+  DomainType evaluate_kinetic_flux_with_alphas(const DomainType& alpha_i,
+                                               const DomainType& alpha_j,
+                                               const FluxDomainType& n_ij,
+                                               const size_t dd) const
+  {
+    return evaluate_kinetic_flux_with_alphas(
+        XT::LA::convert_to<VectorType>(alpha_i), XT::LA::convert_to<VectorType>(alpha_j), n_ij, dd);
+  }
+
+  DomainType evaluate_kinetic_flux_with_alphas(const VectorType& alpha_i,
+                                               const VectorType& alpha_j,
+                                               const FluxDomainType& n_ij,
+                                               const size_t dd) const
+  {
+    thread_local FieldVector<QuadratureWeightsType, 2> eta_ast_prime_vals;
+    eta_ast_prime_vals[0].resize(num_faces_);
+    eta_ast_prime_vals[1].resize(num_faces_);
+    for (size_t jj = 0; jj < num_faces_; ++jj) {
+      eta_ast_prime_vals[0][jj].resize(quad_points_[jj].size());
+      eta_ast_prime_vals[1][jj].resize(quad_points_[jj].size());
+    }
+    evaluate_eta_ast_prime(alpha_i, eta_ast_prime_vals[0]);
+    evaluate_eta_ast_prime(alpha_j, eta_ast_prime_vals[1]);
+    // calculate \sum_{i=1}^d < \omega_i m G_\alpha(u) > n_i
+    DomainType ret(0);
+    const auto& faces = basis_functions_.triangulation().faces();
+    LocalVectorType local_ret;
+    for (size_t jj = 0; jj < num_faces_; ++jj) {
+      local_ret *= 0.;
+      const auto& vertices = faces[jj]->vertices();
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto position = quad_points_[jj][ll][dd];
+        RangeFieldType factor =
+            position * n_ij[dd] > 0. ? eta_ast_prime_vals[0][jj][ll] : eta_ast_prime_vals[1][jj][ll];
+        factor *= quad_weights_[jj][ll] * position;
+        for (size_t ii = 0; ii < 3; ++ii)
+          local_ret[ii] += M_[jj][ll][ii] * factor;
+      } // ll (quad points)
+      for (size_t ii = 0; ii < 3; ++ii)
+        ret[vertices[ii]->index()] += local_ret[ii];
+    } // jj (faces)
+    ret *= n_ij[dd];
+    return ret;
+  } // DomainType evaluate_kinetic_flux(...)
+
+  // Calculates left and right kinetic flux with reconstructed densities. Ansatz distribution values contains
+  // evaluations of the ansatz distribution at each quadrature point for a stencil of three entities. The distributions
+  // are reconstructed pointwise for each quadrature point and the resulting (part of) the kinetic flux is <
+  // psi_reconstr * b * v>_{+/-}.
+  template <SlopeLimiterType slope_type, class FluxesMapType>
+  void calculate_reconstructed_fluxes(const FieldVector<const QuadratureWeightsType*, 3>& ansatz_distribution_values,
+                                      FluxesMapType& flux_values,
+                                      const size_t dd) const
+  {
+    // get flux storage
+    BasisDomainType coord(0.5);
+    coord[dd] = 0;
+    auto& left_flux_value = flux_values[coord];
+    coord[dd] = 1;
+    auto& right_flux_value = flux_values[coord];
+    right_flux_value = left_flux_value = DomainType(0.);
+    thread_local XT::Common::FieldVector<std::vector<RangeFieldType>, 2> reconstructed_values(
+        std::vector<RangeFieldType>(quad_points_[0].size()));
+    const auto& faces = basis_functions_.triangulation().faces();
+    const auto slope_func =
+        (slope_type == SlopeLimiterType::minmod) ? XT::Common::minmod<RangeFieldType> : superbee<RangeFieldType>;
+    auto& vals_left = reconstructed_values[0];
+    auto& vals_right = reconstructed_values[1];
+    for (size_t jj = 0; jj < num_faces_; ++jj) {
+      const auto& vertices = faces[jj]->vertices();
+      // reconstruct densities
+      if (slope_type == SlopeLimiterType::no_slope) {
+        for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll)
+          vals_left[ll] = vals_right[ll] = (*ansatz_distribution_values[1])[jj][ll];
+      } else {
+        for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+          const auto slope =
+              slope_func((*ansatz_distribution_values[1])[jj][ll] - (*ansatz_distribution_values[0])[jj][ll],
+                         (*ansatz_distribution_values[2])[jj][ll] - (*ansatz_distribution_values[1])[jj][ll]);
+          vals_left[ll] = (*ansatz_distribution_values[1])[jj][ll] - 0.5 * slope;
+          vals_right[ll] = (*ansatz_distribution_values[1])[jj][ll] + 0.5 * slope;
+        } // ll (quad points)
+      }
+      // calculate fluxes
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto& position = quad_points_[jj][ll][dd];
+        RangeFieldType factor = position > 0. ? vals_right[ll] : vals_left[ll];
+        factor *= quad_weights_[jj][ll] * position;
+        auto& val = position > 0. ? right_flux_value : left_flux_value;
+        const auto& basis_ll = M_[jj][ll];
+        for (size_t ii = 0; ii < 3; ++ii)
+          val[vertices[ii]->index()] += basis_ll[ii] * factor;
+      } // ll (quad points)
+    } // jj
+  } // void calculate_reconstructed_fluxes(...)
+
+
+  // ============================================================================================
+  // ================================== Helper functions ========================================
+  // ============================================================================================
+
+
+  std::unique_ptr<VectorType> get_isotropic_alpha(const RangeFieldType density) const
+  {
+    const auto alpha_iso_dynvector = basis_functions_.alpha_iso(density);
+    auto ret = std::make_unique<VectorType>(alpha_iso_dynvector.size(), 0., 0);
+    for (size_t ii = 0; ii < ret->size(); ++ii)
+      (*ret)[ii] = alpha_iso_dynvector[ii];
+    return ret;
+  }
+
+  std::unique_ptr<VectorType> get_isotropic_alpha(const DomainType& u) const
+  {
+    return get_isotropic_alpha(basis_functions_.density(u));
+  }
+
+  std::unique_ptr<VectorType> get_isotropic_alpha(const VectorType& u) const
+  {
+    auto u_domain = std::make_unique<DomainType>();
+    for (size_t ii = 0; ii < dimFlux; ++ii)
+      (*u_domain)[ii] = u.get_entry(ii);
+    return get_isotropic_alpha(*u_domain);
+  }
+
+  const MomentBasis& basis_functions() const
+  {
+    return basis_functions_;
+  }
+
+  static bool is_realizable(const VectorType& u)
+  {
+    for (const auto& u_i : u)
+      if (!(u_i > 0.) || std::isinf(u_i))
+        return false;
+    return true;
+  }
+
+  // temporary vectors to store inner products and exponentials
+  std::vector<std::vector<RangeFieldType>>& working_storage() const
+  {
+    thread_local std::vector<std::vector<RangeFieldType>> work_vecs;
+    work_vecs.resize(num_faces_);
+    for (size_t jj = 0; jj < num_faces_; ++jj)
+      work_vecs[jj].resize(quad_points_[jj].size());
+    return work_vecs;
+  }
+
+  bool all_positive(const QuadratureWeightsType& vals) const
+  {
+    for (size_t jj = 0; jj < num_faces_; ++jj)
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll) {
+        const auto val = vals[jj][ll];
+        if (val < 0. || std::isinf(val) || std::isnan(val))
+          return false;
+      }
+    return true;
+  }
+
+  void calculate_scalar_products(const VectorType& alpha, QuadratureWeightsType& scalar_products) const
+  {
+    LocalVectorType local_alpha;
+    const auto& faces = basis_functions_.triangulation().faces();
+    scalar_products.resize(num_faces_);
+    for (size_t jj = 0; jj < num_faces_; ++jj) {
+      scalar_products[jj].resize(quad_weights_[jj].size());
+      const auto& vertices = faces[jj]->vertices();
+      for (size_t ii = 0; ii < 3; ++ii)
+        local_alpha[ii] = alpha.get_entry(vertices[ii]->index());
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll)
+        scalar_products[jj][ll] = local_alpha * M_[jj][ll];
+    } // jj
+  }
+
+  void apply_exponential(QuadratureWeightsType& values) const
+  {
+    for (size_t jj = 0; jj < num_faces_; ++jj) {
+      assert(values[jj].size() < std::numeric_limits<int>::max());
+      XT::Common::Mkl::exp(static_cast<int>(values[jj].size()), values[jj].data(), values[jj].data());
+    }
+  }
+
+  const MomentBasis& basis_functions_;
+  QuadraturePointsType quad_points_;
+  QuadratureWeightsType quad_weights_;
+  BasisValuesMatrixType M_;
+  const RangeFieldType tau_;
+  const RangeFieldType epsilon_gamma_;
+  const RangeFieldType chi_;
+  const RangeFieldType xi_;
+  const std::vector<RangeFieldType> r_sequence_;
+  const size_t k_0_;
+  const size_t k_max_;
+  const RangeFieldType epsilon_;
+  const size_t num_faces_;
+  XT::LA::SparsityPatternDefault pattern_;
+};
+#endif // ENTROPY_FLUX_USE_3D_HATFUNCTIONS_SPECIALIZATION
+
+#if ENTROPY_FLUX_USE_1D_HATFUNCTIONS_SPECIALIZATION
+#  if ENTROPY_FLUX_1D_HATFUNCTIONS_USE_ANALYTICAL_INTEGRALS
+/**
+ * Specialization of EntropyBasedFluxImplementation for 1D Hatfunctions with MaxwellBoltzmann entropy
+ * (no change of basis, analytic integrals + Taylor)
+ */
+template <class D, class R, size_t dimRange, EntropyType entropy>
+class EntropyBasedFluxImplementation<HatFunctionMomentBasis<D, 1, R, dimRange, 1, 1, entropy>>
+  : public XT::Functions::FunctionInterface<dimRange, 1, dimRange, R>
+{
+  using BaseType = typename XT::Functions::FunctionInterface<dimRange, 1, dimRange, R>;
+  using ThisType = EntropyBasedFluxImplementation;
+
+public:
+  using MomentBasis = HatFunctionMomentBasis<D, 1, R, dimRange, 1, 1, entropy>;
+  static const size_t dimFlux = MomentBasis::dimFlux;
+  static const size_t basis_dimRange = dimRange;
+  using typename BaseType::DomainFieldType;
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicDerivativeRangeType;
+  using typename BaseType::DynamicRowDerivativeRangeType;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::RangeReturnType;
+  using BasisDomainType = typename MomentBasis::DomainType;
+  using FluxDomainType = FieldVector<DomainFieldType, dimFlux>;
+  using VectorType = XT::Common::FieldVector<RangeFieldType, basis_dimRange>;
+  using AlphaReturnType = std::pair<VectorType, std::pair<DomainType, RangeFieldType>>;
+  static_assert(entropy == EntropyType::MaxwellBoltzmann, "Not implemented for other entropies");
+
+  explicit EntropyBasedFluxImplementation(const MomentBasis& basis_functions,
+                                          const RangeFieldType tau,
+                                          const bool /*disable_realizability_check*/,
+                                          const RangeFieldType epsilon_gamma,
+                                          const RangeFieldType chi,
+                                          const RangeFieldType xi,
+                                          const std::vector<RangeFieldType> r_sequence,
+                                          const size_t k_0,
+                                          const size_t k_max,
+                                          const RangeFieldType epsilon,
+                                          const RangeFieldType taylor_tol = 0.1,
+                                          const size_t max_taylor_order = 200)
+    : basis_functions_(basis_functions)
+    , v_points_(basis_functions_.partitioning())
+    , tau_(tau)
+    , epsilon_gamma_(epsilon_gamma)
+    , chi_(chi)
+    , xi_(xi)
+    , r_sequence_(r_sequence)
+    , k_0_(k_0)
+    , k_max_(k_max)
+    , epsilon_(epsilon)
+    , taylor_tol_(taylor_tol)
+    , max_taylor_order_(max_taylor_order)
+  {}
+
+
+  // ============================================================================================
+  // ============================= FunctionInterface methods ====================================
+  // ============================================================================================
+
+
+  int order(const XT::Common::Parameter& /*param*/) const override
+  {
+    return 1;
+  }
+
+  virtual RangeReturnType evaluate(const DomainType& u,
+                                   const XT::Common::Parameter& /*param*/ = {}) const override final
+  {
+    const auto alpha = get_alpha(u, *get_isotropic_alpha(u), true)->first;
+    return evaluate_with_alpha(alpha);
+  }
+
+  virtual RangeReturnType evaluate_with_alpha(const VectorType& alpha) const
+  {
+    RangeReturnType ret(0.);
+    // calculate < \mu m G_\alpha(u) >
+    for (size_t nn = 0; nn < dimRange; ++nn) {
+      if (nn > 0) {
+        if (std::abs(alpha[nn] - alpha[nn - 1]) > taylor_tol_) {
+          ret[0][nn] +=
+              2. * std::pow(v_points_[nn] - v_points_[nn - 1], 2) / std::pow(alpha[nn] - alpha[nn - 1], 3)
+                  * (std::exp(alpha[nn]) - std::exp(alpha[nn - 1]))
+              + (v_points_[nn] - v_points_[nn - 1]) / std::pow(alpha[nn] - alpha[nn - 1], 2)
+                    * (v_points_[nn - 1] * (std::exp(alpha[nn]) + std::exp(alpha[nn - 1]))
+                       - 2 * v_points_[nn] * std::exp(alpha[nn]))
+              + v_points_[nn] * (v_points_[nn] - v_points_[nn - 1]) / (alpha[nn] - alpha[nn - 1]) * std::exp(alpha[nn]);
+        } else {
+          RangeFieldType update = 1.;
+          RangeFieldType result = 0.;
+          RangeFieldType base = alpha[nn] - alpha[nn - 1];
+          size_t ll = 0;
+          auto pow_frac = 1. / 6.;
+          while (ll <= max_taylor_order_ - 3 && XT::Common::FloatCmp::ne(update, 0.)) {
+            update = pow_frac * ((ll * ll + 3 * ll + 2) * v_points_[nn] + (ll + 1) * v_points_[nn - 1]);
+            result += update;
+            ++ll;
+            pow_frac *= base / (ll + 3);
+          } // ll
+          assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+          ret[0][nn] += result * (v_points_[nn] - v_points_[nn - 1]) * std::exp(alpha[nn - 1]);
+        }
+      }
+      if (nn < dimRange - 1) {
+        if (std::abs(alpha[nn + 1] - alpha[nn]) > taylor_tol_) {
+          ret[0][nn] +=
+              -2. * std::pow(v_points_[nn + 1] - v_points_[nn], 2) / std::pow(alpha[nn + 1] - alpha[nn], 3)
+                  * (std::exp(alpha[nn + 1]) - std::exp(alpha[nn]))
+              + (v_points_[nn + 1] - v_points_[nn]) / std::pow(alpha[nn + 1] - alpha[nn], 2)
+                    * (v_points_[nn + 1] * (std::exp(alpha[nn + 1]) + std::exp(alpha[nn]))
+                       - 2 * v_points_[nn] * std::exp(alpha[nn]))
+              - v_points_[nn] * (v_points_[nn + 1] - v_points_[nn]) / (alpha[nn + 1] - alpha[nn]) * std::exp(alpha[nn]);
+        } else {
+          RangeFieldType update = 1.;
+          RangeFieldType result = 0.;
+          RangeFieldType base = alpha[nn + 1] - alpha[nn];
+          size_t ll = 0;
+          auto pow_frac = 1. / 6.;
+          while (ll < 3 || (ll <= max_taylor_order_ - 3 && XT::Common::FloatCmp::ne(update, 0.))) {
+            update = pow_frac * (2 * v_points_[nn] + (ll + 1) * v_points_[nn + 1]);
+            result += update;
+            ++ll;
+            pow_frac *= base / (ll + 3);
+          } // ll
+          assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+          ret[0][nn] += result * (v_points_[nn + 1] - v_points_[nn]) * std::exp(alpha[nn]);
+        }
+      } // if (nn < dimRange - 1)
+    } // nn
+    return ret;
+  } // void evaluate_with_alpha(...)
+
+  virtual void jacobian(const DomainType& u,
+                        DynamicDerivativeRangeType& result,
+                        const XT::Common::Parameter& /*param*/ = {}) const override final
+  {
+    const auto alpha = get_alpha(u, *get_isotropic_alpha(u), true)->first;
+    jacobian_with_alpha(alpha, result);
+  }
+
+  virtual void jacobian_with_alpha(const VectorType& alpha, DynamicDerivativeRangeType& result) const
+  {
+    VectorType H_diag, J_diag;
+    XT::Common::FieldVector<RangeFieldType, dimRange - 1> H_subdiag, J_subdiag;
+    calculate_hessian(alpha, H_diag, H_subdiag);
+    calculate_J(alpha, J_diag, J_subdiag);
+    calculate_J_Hinv(result[0], J_diag, J_subdiag, H_diag, H_subdiag);
+  }
+
+
+  // ============================================================================================
+  // =============================== Kinetic fluxes =============================================
+  // ============================================================================================
+
+
+  // calculate \sum_{i=1}^d < v_i m \psi > n_i, where n is the unit outer normal,
+  // m is the basis function vector, \psi is the ansatz corresponding to u
+  // and x, v, t are the space, velocity and time variable, respectively
+  // As we are using cartesian grids, n_i == 0 in all but one dimension, so only evaluate for i == dd
+  DomainType
+  evaluate_kinetic_flux(const DomainType& u_i, const DomainType& u_j, const FluxDomainType& n_ij, const size_t dd) const
+  {
+    // calculate \sum_{i=1}^d < \omega_i m G_\alpha(u) > n_i
+    const auto alpha_i = get_alpha(u_i, *get_isotropic_alpha(u_i), true)->first;
+    const auto alpha_j = get_alpha(u_j, *get_isotropic_alpha(u_j), true)->first;
+    evaluate_kinetic_flux_with_alphas(alpha_i, alpha_j, n_ij, dd);
+  } // DomainType evaluate_kinetic_flux(...)
+
+  DomainType evaluate_kinetic_flux_with_alphas(const VectorType& alpha_i,
+                                               const VectorType& alpha_j,
+                                               const FluxDomainType& n_ij,
+                                               const size_t dd) const
+  {
+    assert(dd == 0);
+    // calculate < \mu m G_\alpha(u) > * n_ij
+    DomainType ret(0);
+    for (size_t nn = 0; nn < dimRange; ++nn) {
+      if (nn > 0) {
+        if (dimRange % 2 || nn != dimRange / 2) {
+          const auto& alpha = (n_ij[0] * (v_points_[nn - 1] + v_points_[nn]) / 2. > 0.) ? alpha_i : alpha_j;
+          if (std::abs(alpha[nn] - alpha[nn - 1]) > taylor_tol_) {
+            ret[nn] += 2. * std::pow(v_points_[nn] - v_points_[nn - 1], 2) / std::pow(alpha[nn] - alpha[nn - 1], 3)
+                           * (std::exp(alpha[nn]) - std::exp(alpha[nn - 1]))
+                       + (v_points_[nn] - v_points_[nn - 1]) / std::pow(alpha[nn] - alpha[nn - 1], 2)
+                             * (v_points_[nn - 1] * (std::exp(alpha[nn]) + std::exp(alpha[nn - 1]))
+                                - 2 * v_points_[nn] * std::exp(alpha[nn]))
+                       + v_points_[nn] * (v_points_[nn] - v_points_[nn - 1]) / (alpha[nn] - alpha[nn - 1])
+                             * std::exp(alpha[nn]);
+          } else {
+            RangeFieldType update = 1.;
+            RangeFieldType result = 0.;
+            RangeFieldType base = alpha[nn - 1] - alpha[nn];
+            size_t ll = 0;
+            auto pow_frac = 1. / 6.;
+            while (ll < 3 || (ll <= max_taylor_order_ - 3 && XT::Common::FloatCmp::ne(update, 0.))) {
+              update = pow_frac * (2 * v_points_[nn] + (ll + 1) * v_points_[nn - 1]);
+              result += update;
+              ++ll;
+              pow_frac *= base / (ll + 3);
+            } // ll
+            assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+            ret[nn] += result * (v_points_[nn] - v_points_[nn - 1]) * std::exp(alpha[nn]);
+          }
+        } else { //  if (dimRange % 2 || nn != dimRange/2)
+          const auto& alpha_pos = n_ij[0] > 0. ? alpha_i : alpha_j;
+          const auto& alpha_neg = n_ij[0] > 0. ? alpha_j : alpha_i;
+          if (std::abs(alpha_neg[nn] - alpha_neg[nn - 1]) > taylor_tol_) {
+            ret[nn] += -2. * std::pow(v_points_[nn], 2)
+                       * (4. / std::pow(alpha_neg[nn - 1] - alpha_neg[nn], 3)
+                              * (std::exp((alpha_neg[nn] + alpha_neg[nn - 1]) / 2.) - std::exp(alpha_neg[nn - 1]))
+                          + 1. / std::pow(alpha_neg[nn - 1] - alpha_neg[nn], 2)
+                                * (std::exp((alpha_neg[nn] + alpha_neg[nn - 1]) / 2.) + std::exp(alpha_neg[nn - 1])));
+
+          } else {
+            RangeFieldType update = 1.;
+            RangeFieldType result = 0.;
+            RangeFieldType base = alpha_neg[nn] - alpha_neg[nn - 1];
+            size_t ll = 2;
+            auto pow_frac = 1. / 24.;
+            while (ll <= max_taylor_order_ - 1 && XT::Common::FloatCmp::ne(update, 0.)) {
+              update = pow_frac * (ll - 1.);
+              result += update;
+              ++ll;
+              pow_frac *= base / (2. * (ll + 1));
+            } // ll
+            assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+            ret[nn] += result * -2. * std::pow(v_points_[nn], 2) * std::exp(alpha_neg[nn - 1]);
+          }
+          if (std::abs(alpha_pos[nn] - alpha_pos[nn - 1]) > taylor_tol_) {
+            ret[nn] += 2. * std::pow(v_points_[nn], 2)
+                       * (4. / std::pow(alpha_pos[nn - 1] - alpha_pos[nn], 3)
+                              * (std::exp((alpha_pos[nn] + alpha_pos[nn - 1]) / 2.) - std::exp(alpha_pos[nn]))
+                          + 1. / std::pow(alpha_pos[nn - 1] - alpha_pos[nn], 2)
+                                * (std::exp((alpha_pos[nn] + alpha_pos[nn - 1]) / 2.) - 3. * std::exp(alpha_pos[nn]))
+                          - 1. / (alpha_pos[nn - 1] - alpha_pos[nn]) * std::exp(alpha_pos[nn]));
+          } else {
+            RangeFieldType update = 1.;
+            RangeFieldType result = 0.;
+            RangeFieldType base = alpha_pos[nn - 1] - alpha_pos[nn];
+            auto pow_frac = 1. / 24.;
+            size_t ll = 2;
+            while (ll <= max_taylor_order_ - 1 && XT::Common::FloatCmp::ne(update, 0.)) {
+              update = pow_frac * (ll + 3);
+              result += update;
+              ++ll;
+              pow_frac *= base / (2. * (ll + 1));
+            } // ll
+            assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+            ret[nn] += result * 2. * std::pow(v_points_[nn], 2) * std::exp(alpha_pos[nn]);
+          } // else (alpha_n - alpha_{n-1} != 0)
+        } // else (dimRange % 2 || nn != dimRange/2)
+      } // if (nn > 0)
+      if (nn < dimRange - 1) {
+        if (dimRange % 2 || nn != dimRange / 2 - 1) {
+          const auto& alpha = (n_ij[0] * (v_points_[nn] + v_points_[nn + 1]) / 2. > 0.) ? alpha_i : alpha_j;
+          if (XT::Common::FloatCmp::ne(alpha[nn + 1], alpha[nn], 0., taylor_tol_)) {
+            ret[nn] += -2. * std::pow(v_points_[nn + 1] - v_points_[nn], 2) / std::pow(alpha[nn + 1] - alpha[nn], 3)
+                           * (std::exp(alpha[nn + 1]) - std::exp(alpha[nn]))
+                       + (v_points_[nn + 1] - v_points_[nn]) / std::pow(alpha[nn + 1] - alpha[nn], 2)
+                             * (v_points_[nn + 1] * (std::exp(alpha[nn + 1]) + std::exp(alpha[nn]))
+                                - 2 * v_points_[nn] * std::exp(alpha[nn]))
+                       - v_points_[nn] * (v_points_[nn + 1] - v_points_[nn]) / (alpha[nn + 1] - alpha[nn])
+                             * std::exp(alpha[nn]);
+          } else {
+            RangeFieldType update = 1.;
+            RangeFieldType result = 0.;
+            RangeFieldType base = alpha[nn + 1] - alpha[nn];
+            size_t ll = 0;
+            auto pow_frac = 1. / 6.;
+            while (ll < 3
+                   || (ll <= max_taylor_order_ - 3 && XT::Common::FloatCmp::ne(result, result + update, 1e-16, 0.))) {
+              update = pow_frac * (2 * v_points_[nn] + (ll + 1) * v_points_[nn + 1]);
+              result += update;
+              ++ll;
+              pow_frac *= base / (ll + 3);
+            } // ll
+            ret[nn] += result * (v_points_[nn + 1] - v_points_[nn]) * std::exp(alpha[nn]);
+          }
+        } else { // if (dimRange % 2 || nn != dimRange / 2 - 1)
+          const auto& alpha_pos = n_ij[0] > 0. ? alpha_i : alpha_j;
+          const auto& alpha_neg = n_ij[0] > 0. ? alpha_j : alpha_i;
+          if (std::abs(alpha_neg[nn + 1] - alpha_neg[nn]) > taylor_tol_) {
+            ret[nn] += -2. * std::pow(v_points_[nn + 1], 2)
+                       * (-4. / std::pow(alpha_neg[nn + 1] - alpha_neg[nn], 3)
+                              * (std::exp(alpha_neg[nn]) - std::exp((alpha_neg[nn + 1] + alpha_neg[nn]) / 2.))
+                          - 1. / std::pow(alpha_neg[nn + 1] - alpha_neg[nn], 2)
+                                * (3 * std::exp(alpha_neg[nn]) - std::exp((alpha_neg[nn + 1] + alpha_neg[nn]) / 2.))
+                          - 1. / (alpha_neg[nn + 1] - alpha_neg[nn]) * std::exp(alpha_neg[nn]));
+          } else {
+            RangeFieldType update = 1.;
+            RangeFieldType result = 0.;
+            RangeFieldType base = alpha_neg[nn + 1] - alpha_neg[nn];
+            auto pow_frac = 1. / 24.;
+            size_t ll = 2;
+            while (ll <= max_taylor_order_ - 1 && XT::Common::FloatCmp::ne(update, 0.)) {
+              update = pow_frac * (ll + 3);
+              result += update;
+              ++ll;
+              pow_frac *= base / (2. * (ll + 1));
+            } // ll
+            assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+            ret[nn] += result * -2. * std::pow(v_points_[nn + 1], 2) * std::exp(alpha_neg[nn]);
+          }
+          if (std::abs(alpha_pos[nn + 1] - alpha_pos[nn]) > taylor_tol_) {
+            ret[nn] += 2. * std::pow(v_points_[nn + 1], 2)
+                       * (4. / std::pow(alpha_pos[nn + 1] - alpha_pos[nn], 3)
+                              * (std::exp((alpha_pos[nn + 1] + alpha_pos[nn]) / 2.) - std::exp(alpha_pos[nn + 1]))
+                          + 1. / std::pow(alpha_pos[nn + 1] - alpha_pos[nn], 2)
+                                * (std::exp((alpha_pos[nn + 1] + alpha_pos[nn]) / 2.) + std::exp(alpha_pos[nn + 1])));
+          } else {
+            RangeFieldType update = 1.;
+            RangeFieldType result = 0.;
+            RangeFieldType base = alpha_pos[nn] - alpha_pos[nn + 1];
+            auto pow_frac = 1. / 24.;
+            size_t ll = 2;
+            while (ll <= max_taylor_order_ - 1 && XT::Common::FloatCmp::ne(update, 0.)) {
+              update = pow_frac * (ll - 1.);
+              result += update;
+              ++ll;
+              pow_frac *= base / (2. * (ll + 1));
+            } // ll
+            assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+            ret[nn] += result * 2. * std::pow(v_points_[nn + 1], 2) * std::exp(alpha_pos[nn + 1]);
+          } // else (alpha_n - alpha_{n-1} != 0)
+        } // else (dimRange % 2 || nn != dimRange / 2 - 1)
+      } // if (nn < dimRange - 1)
+    } // nn
+    ret *= n_ij[0];
+    return ret;
+  } // DomainType evaluate_kinetic_flux(...)
+
+
+  // ============================================================================================
+  // ============ Evaluations of ansatz distribution, moments, hessian etc. =====================
+  // ============================================================================================
+
+
+  std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u) const
+  {
+    return get_alpha(u, *get_isotropic_alpha(u), true);
+  }
+
+  // returns (alpha, (actual_u, r)), where r is the regularization parameter and actual_u the regularized u
+  std::unique_ptr<AlphaReturnType>
+  get_alpha(const DomainType& u, const VectorType& alpha_in, const bool regularize) const
+  {
+    auto ret = std::make_unique<AlphaReturnType>();
+    // rescale u such that the density <psi> is 1
+    RangeFieldType density = basis_functions_.density(u);
+    if (!(density > 0.) || std::isinf(density))
+      DUNE_THROW(Dune::MathError, "Negative, inf or NaN density!");
+    static const auto alpha_one = basis_functions_.alpha_one();
+    VectorType phi = u / density;
+    VectorType alpha_initial = alpha_in - alpha_one * std::log(density);
+    RangeFieldType tau_prime =
+        std::min(tau_ / ((1 + std::sqrt(dimRange) * phi.two_norm()) * density + std::sqrt(dimRange) * tau_), tau_);
+    // The hessian H is always symmetric and tridiagonal, so we only need to store the diagonal and subdiagonal
+    // elements
+    VectorType H_diag;
+    FieldVector<RangeFieldType, dimRange - 1> H_subdiag;
+
+    // calculate moment vector for isotropic distribution
+    VectorType u_iso = basis_functions_.u_iso();
+    VectorType v;
+    VectorType alpha_k = alpha_initial;
+    const auto& r_sequence = regularize ? r_sequence_ : std::vector<RangeFieldType>{0.};
+    const auto r_max = r_sequence.back();
+    for (const auto& rr : r_sequence_) {
+      // regularize u
+      v = phi;
+      if (rr > 0) {
+        alpha_k = *get_isotropic_alpha(v);
+        VectorType r_times_u_iso(u_iso);
+        r_times_u_iso *= rr;
+        v *= 1 - rr;
+        v += r_times_u_iso;
+      }
+
+      // calculate f_0
+      RangeFieldType f_k = calculate_f(alpha_k, v);
+
+      int backtracking_failed = 0;
+      for (size_t kk = 0; kk < k_max_; ++kk) {
+        // exit inner for loop to increase r if too many iterations are used
+        if (kk > k_0_ && rr < r_max)
+          break;
+        // calculate gradient g
+        VectorType g_k = calculate_gradient(alpha_k, v);
+        // calculate Hessian H
+        calculate_hessian(alpha_k, H_diag, H_subdiag);
+        // calculate descent direction d_k;
+        VectorType d_k(0), minus_g_k(g_k);
+        minus_g_k *= -1;
+        try {
+          d_k = minus_g_k;
+          XT::LA::solve_sym_tridiag_posdef(H_diag, H_subdiag, d_k);
+        } catch (const Dune::MathError&) {
+          if (rr < r_max)
+            break;
+          else
+            DUNE_THROW(Dune::MathError, "Failure to converge!");
+        }
+
+        const auto& alpha_tilde = alpha_k;
+        const auto u_alpha_tilde = g_k + v;
+        auto density_tilde = basis_functions_.density(u_alpha_tilde);
+        if (!(density_tilde > 0.) || std::isinf(density_tilde))
+          break;
+        const auto alpha_prime = alpha_tilde - alpha_one * std::log(density_tilde);
+        const auto u_alpha_prime = calculate_u(alpha_prime);
+        auto u_eps_diff = v - u_alpha_prime * (1 - epsilon_gamma_);
+        // checking realizability is cheap so we do not need the second stopping criterion
+        if (g_k.two_norm() < tau_prime && is_realizable(u_eps_diff)) {
+          ret->first = alpha_prime + alpha_one * std::log(density);
+          ret->second = std::make_pair(v * density, rr);
+          return ret;
+        } else {
+          RangeFieldType zeta_k = 1;
+          // backtracking line search
+          while (backtracking_failed >= 2 || zeta_k > epsilon_ * alpha_k.two_norm() / d_k.two_norm()) {
+            // while (backtracking_failed >= 2 || zeta_k > epsilon_ * alpha_k.two_norm() / d_k.two_norm() * 100.) {
+            // calculate alpha_new = alpha_k + zeta_k d_k
+            auto alpha_new = d_k;
+            alpha_new *= zeta_k;
+            alpha_new += alpha_k;
+            // calculate f(alpha_new)
+            RangeFieldType f_new = calculate_f(alpha_new, v);
+            if (backtracking_failed >= 2 || XT::Common::FloatCmp::le(f_new, f_k + xi_ * zeta_k * (g_k * d_k))) {
+              alpha_k = alpha_new;
+              f_k = f_new;
+              backtracking_failed = 0.;
+              break;
+            }
+            zeta_k = chi_ * zeta_k;
+          } // backtracking linesearch while
+          // if (zeta_k <= epsilon_ * alpha_k.two_norm() / d_k.two_norm() * 100.)
+          if (zeta_k <= epsilon_ * alpha_k.two_norm() / d_k.two_norm())
+            ++backtracking_failed;
+        } // else (stopping conditions)
+      } // k loop (Newton iterations)
+    } // rr loop (Regularization parameter)
+    DUNE_THROW(MathError, "Failed to converge");
+
+    return ret;
+  } // ... get_alpha(...)
+
+  DomainType get_u(const DomainType& alpha) const
+  {
+    return calculate_u(alpha);
+  }
+
+  VectorType calculate_u(const VectorType& alpha_k) const
+  {
+    VectorType u(0);
+    for (size_t nn = 0; nn < dimRange; ++nn) {
+      if (nn > 0) {
+        if (std::abs(alpha_k[nn] - alpha_k[nn - 1]) > taylor_tol_) {
+          u[nn] += -(v_points_[nn] - v_points_[nn - 1]) / std::pow(alpha_k[nn] - alpha_k[nn - 1], 2)
+                       * (std::exp(alpha_k[nn]) - std::exp(alpha_k[nn - 1]))
+                   + (v_points_[nn] - v_points_[nn - 1]) / (alpha_k[nn] - alpha_k[nn - 1]) * std::exp(alpha_k[nn]);
+        } else {
+          RangeFieldType result = 0.;
+          RangeFieldType base = alpha_k[nn - 1] - alpha_k[nn];
+          size_t ll = 0;
+          RangeFieldType update = 1;
+          RangeFieldType pow_frac = 0.5;
+          while (ll <= max_taylor_order_ - 2 && XT::Common::FloatCmp::ne(update, 0.)) {
+            update = pow_frac;
+            result += update;
+            ++ll;
+            pow_frac *= base / (ll + 2);
+          }
+          assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+          u[nn] += result * (v_points_[nn] - v_points_[nn - 1]) * std::exp(alpha_k[nn]);
+        }
+      } // if (nn > 0)
+      if (nn < dimRange - 1) {
+        if (std::abs(alpha_k[nn + 1] - alpha_k[nn]) > taylor_tol_) {
+          u[nn] += (v_points_[nn + 1] - v_points_[nn]) / std::pow(alpha_k[nn + 1] - alpha_k[nn], 2)
+                       * (std::exp(alpha_k[nn + 1]) - std::exp(alpha_k[nn]))
+                   - (v_points_[nn + 1] - v_points_[nn]) / (alpha_k[nn + 1] - alpha_k[nn]) * std::exp(alpha_k[nn]);
+        } else {
+          RangeFieldType update = 1.;
+          RangeFieldType result = 0.;
+          size_t ll = 0;
+          RangeFieldType base = alpha_k[nn + 1] - alpha_k[nn];
+          auto pow_frac = 0.5;
+          while (ll <= max_taylor_order_ - 2 && XT::Common::FloatCmp::ne(update, 0.)) {
+            update = pow_frac;
+            result += update;
+            ++ll;
+            pow_frac *= base / (ll + 2);
+          }
+          assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+          u[nn] += result * (v_points_[nn + 1] - v_points_[nn]) * std::exp(alpha_k[nn]);
+        }
+      } // if (nn < dimRange-1)
+    } // nn
+    return u;
+  } // VectorType calculate_u(...)
+
+
+  RangeFieldType calculate_f(const VectorType& alpha_k, const VectorType& v) const
+  {
+    RangeFieldType ret(0);
+    for (size_t ii = 0; ii < dimRange - 1; ++ii) {
+      if (std::abs(alpha_k[ii + 1] - alpha_k[ii]) > taylor_tol_) {
+        ret += (v_points_[ii + 1] - v_points_[ii]) / (alpha_k[ii + 1] - alpha_k[ii])
+               * (std::exp(alpha_k[ii + 1]) - std::exp(alpha_k[ii]));
+      } else {
+        RangeFieldType update = 1.;
+        RangeFieldType result = 0.;
+        size_t ll = 1;
+        RangeFieldType base = alpha_k[ii + 1] - alpha_k[ii];
+        auto pow_frac = 1.;
+        while (ll <= max_taylor_order_ && XT::Common::FloatCmp::ne(update, 0.)) {
+          update = pow_frac;
+          result += update;
+          ++ll;
+          pow_frac *= base / ll;
+        }
+        assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+        ret += result * (v_points_[ii + 1] - v_points_[ii]) * std::exp(alpha_k[ii]);
+      }
+    } // ii
+    ret -= alpha_k * v;
+    return ret;
+  } // .. calculate_f(...)
+
+  VectorType calculate_gradient(const VectorType& alpha_k, const VectorType& v) const
+  {
+    return calculate_u(alpha_k) - v;
+  }
+
+  void calculate_hessian(const VectorType& alpha_k,
+                         VectorType& diag,
+                         FieldVector<RangeFieldType, dimRange - 1>& subdiag) const
+  {
+    std::fill(diag.begin(), diag.end(), 0.);
+    std::fill(subdiag.begin(), subdiag.end(), 0.);
+    for (size_t nn = 0; nn < dimRange; ++nn) {
+      if (nn > 0) {
+        if (std::abs(alpha_k[nn] - alpha_k[nn - 1]) > taylor_tol_) {
+          subdiag[nn - 1] =
+              (v_points_[nn] - v_points_[nn - 1])
+              * ((std::exp(alpha_k[nn]) + std::exp(alpha_k[nn - 1])) / std::pow(alpha_k[nn] - alpha_k[nn - 1], 2)
+                 - 2. * (std::exp(alpha_k[nn]) - std::exp(alpha_k[nn - 1]))
+                       / std::pow(alpha_k[nn] - alpha_k[nn - 1], 3));
+          diag[nn] = (v_points_[nn] - v_points_[nn - 1])
+                     * ((-2. / std::pow(alpha_k[nn] - alpha_k[nn - 1], 2) + 1. / (alpha_k[nn] - alpha_k[nn - 1]))
+                            * std::exp(alpha_k[nn])
+                        + 2. / std::pow(alpha_k[nn] - alpha_k[nn - 1], 3)
+                              * (std::exp(alpha_k[nn]) - std::exp(alpha_k[nn - 1])));
+
+        } else {
+          RangeFieldType update = 1.;
+          RangeFieldType result = 0.;
+          RangeFieldType base = alpha_k[nn - 1] - alpha_k[nn];
+          RangeFieldType factor = (v_points_[nn] - v_points_[nn - 1]) * std::exp(alpha_k[nn]);
+          size_t ll = 2;
+          auto pow_frac = 1. / 6.;
+          while (ll <= max_taylor_order_ - 1 && XT::Common::FloatCmp::ne(update, 0.)) {
+            update = pow_frac * (ll - 1.);
+            result += update;
+            ++ll;
+            pow_frac *= base / (ll + 1);
+          } // ll
+          assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+          subdiag[nn - 1] += result * factor;
+
+          result = 0.;
+          update = 1;
+          ll = 3;
+          pow_frac = 2. / 6.;
+          while (ll <= max_taylor_order_ && XT::Common::FloatCmp::ne(update, 0.)) {
+            update = pow_frac;
+            result += update;
+            ++ll;
+            pow_frac *= base / ll;
+          } // ll
+          assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+          diag[nn] += result * factor;
+        }
+      } // if (nn > 0)
+      if (nn < dimRange - 1) {
+        if (std::abs(alpha_k[nn + 1] - alpha_k[nn]) > taylor_tol_) {
+          diag[nn] += (v_points_[nn + 1] - v_points_[nn])
+                      * ((-2. / std::pow(alpha_k[nn + 1] - alpha_k[nn], 2) - 1. / (alpha_k[nn + 1] - alpha_k[nn]))
+                             * std::exp(alpha_k[nn])
+                         + 2. / std::pow(alpha_k[nn + 1] - alpha_k[nn], 3)
+                               * (std::exp(alpha_k[nn + 1]) - std::exp(alpha_k[nn])));
+        } else {
+          RangeFieldType update = 1.;
+          RangeFieldType result = 0.;
+          RangeFieldType base = alpha_k[nn + 1] - alpha_k[nn];
+          size_t ll = 3;
+          auto pow_frac = 2. / 6.;
+          while (ll <= max_taylor_order_ && XT::Common::FloatCmp::ne(update, 0.)) {
+            update = pow_frac;
+            result += update;
+            ++ll;
+            pow_frac *= base / ll;
+          } // ll
+          assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+          diag[nn] += result * (v_points_[nn + 1] - v_points_[nn]) * std::exp(alpha_k[nn]);
+        }
+      } // if (nn < dimRange - 1)
+    } // nn
+  } // void calculate_hessian(...)
+
+  void
+  calculate_J(const VectorType& alpha_k, VectorType& diag, FieldVector<RangeFieldType, dimRange - 1>& subdiag) const
+  {
+    std::fill(diag.begin(), diag.end(), 0.);
+    std::fill(subdiag.begin(), subdiag.end(), 0.);
+    for (size_t nn = 0; nn < dimRange; ++nn) {
+      if (nn > 0) {
+        if (std::abs(alpha_k[nn] - alpha_k[nn - 1]) > taylor_tol_) {
+          subdiag[nn - 1] =
+              (v_points_[nn] - v_points_[nn - 1])
+                  * ((v_points_[nn] * std::exp(alpha_k[nn]) + v_points_[nn - 1] * std::exp(alpha_k[nn - 1]))
+                         / std::pow(alpha_k[nn - 1] - alpha_k[nn], 2)
+                     + 2.
+                           * ((2 * v_points_[nn] - v_points_[nn - 1]) * std::exp(alpha_k[nn])
+                              - (2 * v_points_[nn - 1] - v_points_[nn]) * std::exp(alpha_k[nn - 1]))
+                           / std::pow(alpha_k[nn - 1] - alpha_k[nn], 3))
+              + 6. * std::pow(v_points_[nn] - v_points_[nn - 1], 2)
+                    * (std::exp(alpha_k[nn]) - std::exp(alpha_k[nn - 1])) / std::pow(alpha_k[nn - 1] - alpha_k[nn], 4);
+          diag[nn] = 6 * std::pow(v_points_[nn - 1] - v_points_[nn], 2)
+                         * (std::exp(alpha_k[nn - 1]) - std::exp(alpha_k[nn]))
+                         / std::pow(alpha_k[nn - 1] - alpha_k[nn], 4)
+                     + 2. * (v_points_[nn] - v_points_[nn - 1])
+                           * (v_points_[nn - 1] * std::exp(alpha_k[nn - 1])
+                              - (3 * v_points_[nn] - 2 * v_points_[nn - 1]) * std::exp(alpha_k[nn]))
+                           / std::pow(alpha_k[nn - 1] - alpha_k[nn], 3)
+                     - v_points_[nn] * (v_points_[nn] - v_points_[nn - 1]) * std::exp(alpha_k[nn])
+                           / (alpha_k[nn - 1] - alpha_k[nn])
+                     - (std::pow(v_points_[nn - 1], 2) - 4 * v_points_[nn] * v_points_[nn - 1]
+                        + 3. * std::pow(v_points_[nn], 2))
+                           * std::exp(alpha_k[nn]) / std::pow(alpha_k[nn - 1] - alpha_k[nn], 2);
+        } else {
+          RangeFieldType update = 1.;
+          RangeFieldType result = 0.;
+          RangeFieldType base = alpha_k[nn - 1] - alpha_k[nn];
+          RangeFieldType factor = (v_points_[nn] - v_points_[nn - 1]) * std::exp(alpha_k[nn]);
+          size_t ll = 0;
+          auto pow_frac = 1. / 24.;
+          while (ll < 2 || (ll <= max_taylor_order_ - 4 && XT::Common::FloatCmp::ne(update, 0.))) {
+            update = pow_frac * ((ll * ll + 3 * ll + 2) * v_points_[nn - 1] + (2 * ll + 2) * v_points_[nn]);
+            result += update;
+            ++ll;
+            pow_frac *= base / (ll + 4);
+          } // ll
+          subdiag[nn - 1] += result * factor;
+          assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+
+          result = 0.;
+          update = 1;
+          ll = 0;
+          pow_frac = 1. / 24.;
+          while (ll < 4 || (ll <= max_taylor_order_ - 4 && XT::Common::FloatCmp::ne(update, 0.))) {
+            update = pow_frac * (6 * v_points_[nn] + (2 * ll + 2) * v_points_[nn - 1]);
+            result += update;
+            ++ll;
+            pow_frac *= base / (ll + 4);
+          } // ll
+          assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+          diag[nn] += result * factor;
+        }
+      } // if (nn > 0)
+      if (nn < dimRange - 1) {
+        if (std::abs(alpha_k[nn + 1] - alpha_k[nn]) > taylor_tol_) {
+          diag[nn] += 6 * std::pow(v_points_[nn] - v_points_[nn + 1], 2)
+                          * (std::exp(alpha_k[nn]) - std::exp(alpha_k[nn + 1]))
+                          / std::pow(alpha_k[nn] - alpha_k[nn + 1], 4)
+                      + 2. * (v_points_[nn] - v_points_[nn + 1])
+                            * (v_points_[nn + 1] * std::exp(alpha_k[nn + 1])
+                               - (3 * v_points_[nn] - 2 * v_points_[nn + 1]) * std::exp(alpha_k[nn]))
+                            / std::pow(alpha_k[nn] - alpha_k[nn + 1], 3)
+                      - v_points_[nn] * (v_points_[nn] - v_points_[nn + 1]) * std::exp(alpha_k[nn])
+                            / (alpha_k[nn] - alpha_k[nn + 1])
+                      + (std::pow(v_points_[nn + 1], 2) - 4 * v_points_[nn] * v_points_[nn + 1]
+                         + 3. * std::pow(v_points_[nn], 2))
+                            * std::exp(alpha_k[nn]) / std::pow(alpha_k[nn] - alpha_k[nn + 1], 2);
+        } else {
+          RangeFieldType update = 1.;
+          RangeFieldType result = 0.;
+          RangeFieldType base = alpha_k[nn + 1] - alpha_k[nn];
+          size_t ll = 0;
+          auto pow_frac = 1. / 24.;
+          while (ll < 4 || (ll <= max_taylor_order_ - 4 && XT::Common::FloatCmp::ne(update, 0.))) {
+            update = pow_frac * (6 * v_points_[nn] + (2 * ll + 2) * v_points_[nn + 1]);
+            result += update;
+            ++ll;
+            pow_frac *= base / (ll + 4);
+          } // ll
+          assert(!(std::isinf(pow_frac) || std::isnan(pow_frac)));
+          diag[nn] += result * (v_points_[nn + 1] - v_points_[nn]) * std::exp(alpha_k[nn]);
+        }
+      } // if (nn < dimRange - 1)
+    } // nn
+  } // void calculate_J(...)
+
+  // calculates ret = J H^{-1}. Both J and H are symmetric tridiagonal, H is positive definite.
+  static void calculate_J_Hinv(DynamicRowDerivativeRangeType& ret,
+                               const VectorType& J_diag,
+                               const FieldVector<RangeFieldType, dimRange - 1>& J_subdiag,
+                               VectorType& H_diag,
+                               FieldVector<RangeFieldType, dimRange - 1>& H_subdiag)
+  {
+    // factorize H = LDL^T, where L is unit lower bidiagonal and D is diagonal
+    // H_diag is overwritten by the diagonal elements of D
+    // H_subdiag is overwritten by the subdiagonal elements of L
+    XT::LA::tridiagonal_ldlt(H_diag, H_subdiag);
+
+    // copy J to dense matrix
+    ret.set_all_entries(0.);
+    for (size_t ii = 0; ii < dimRange - 1; ++ii) {
+      ret[ii][ii] = J_diag[ii];
+      ret[ii + 1][ii] = J_subdiag[ii];
+      ret[ii][ii + 1] = J_subdiag[ii];
+    }
+    ret[dimRange - 1][dimRange - 1] = J_diag[dimRange - 1];
+
+    // Solve ret H = J which is equivalent to (as H and J are symmetric) to H ret^T = J;
+    XT::LA::solve_tridiagonal_ldlt_factorized(H_diag, H_subdiag, ret);
+    // transpose ret
+    RangeFieldType* ret_ptr = &(ret[0][0]);
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      for (size_t jj = 0; jj < ii; ++jj)
+        std::swap(ret_ptr[jj * dimRange + ii], ret_ptr[ii * dimRange + jj]);
+  } // void calculate_J_Hinv(...)
+
+
+  // ============================================================================================
+  // ================================== Helper functions ========================================
+  // ============================================================================================
+
+
+  const MomentBasis& basis_functions() const
+  {
+    return basis_functions_;
+  }
+
+  static bool is_realizable(const DomainType& u)
+  {
+    for (const auto& u_i : u)
+      if (!(u_i > 0.) || std::isinf(u_i))
+        return false;
+    return true;
+  }
+
+  std::unique_ptr<VectorType> get_isotropic_alpha(const RangeFieldType density) const
+  {
+    return std::make_unique<VectorType>(basis_functions_.alpha_iso(density));
+  }
+
+  std::unique_ptr<VectorType> get_isotropic_alpha(const DomainType& u) const
+  {
+    return get_isotropic_alpha(basis_functions_.density(u));
+  }
+
+  const MomentBasis& basis_functions_;
+  const std::vector<RangeFieldType>& v_points_;
+  const RangeFieldType tau_;
+  const RangeFieldType epsilon_gamma_;
+  const RangeFieldType chi_;
+  const RangeFieldType xi_;
+  const std::vector<RangeFieldType> r_sequence_;
+  const size_t k_0_;
+  const size_t k_max_;
+  const RangeFieldType epsilon_;
+  const RangeFieldType taylor_tol_;
+  const size_t max_taylor_order_;
+};
+
+#  else // ENTROPY_FLUX_1D_HATFUNCTIONS_USE_ANALYTICAL_INTEGRALS
+
+/**
+ * Specialization of EntropyBasedFluxImplementation for 1D Hatfunctions (no change of basis, use structure)
+ */
+template <class D, class R, size_t dimRange, EntropyType entropy>
+class EntropyBasedFluxImplementation<HatFunctionMomentBasis<D, 1, R, dimRange, 1, 1, entropy>>
+  : public XT::Functions::FunctionInterface<dimRange, 1, dimRange, R>
+{
+  using BaseType = typename XT::Functions::FunctionInterface<dimRange, 1, dimRange, R>;
+  using ThisType = EntropyBasedFluxImplementation;
+
+public:
+  using MomentBasis = HatFunctionMomentBasis<D, 1, R, dimRange, 1, 1, entropy>;
+  static const size_t dimFlux = MomentBasis::dimFlux;
+  static const size_t basis_dimRange = dimRange;
+  using typename BaseType::DomainFieldType;
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicDerivativeRangeType;
+  using typename BaseType::DynamicRowDerivativeRangeType;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::RangeReturnType;
+  using BasisDomainType = typename MomentBasis::DomainType;
+  using FluxDomainType = FieldVector<DomainFieldType, dimFlux>;
+  using VectorType = XT::Common::FieldVector<RangeFieldType, basis_dimRange>;
+  using AlphaReturnType = std::pair<VectorType, std::pair<DomainType, RangeFieldType>>;
+  static const size_t num_intervals = dimRange - 1;
+  static const size_t block_size = 2;
+  using LocalVectorType = XT::Common::FieldVector<RangeFieldType, block_size>;
+  using BasisValuesMatrixType = FieldVector<std::vector<LocalVectorType>, num_intervals>;
+  using QuadraturePointsType = FieldVector<std::vector<RangeFieldType>, num_intervals>;
+  using QuadratureWeightsType = QuadraturePointsType;
+
+  explicit EntropyBasedFluxImplementation(const MomentBasis& basis_functions,
+                                          const RangeFieldType tau,
+                                          const bool /*disable_realizability_check*/,
+                                          const RangeFieldType epsilon_gamma,
+                                          const RangeFieldType chi,
+                                          const RangeFieldType xi,
+                                          const std::vector<RangeFieldType> r_sequence,
+                                          const size_t k_0,
+                                          const size_t k_max,
+                                          const RangeFieldType epsilon)
+    : basis_functions_(basis_functions)
+    , grid_points_(basis_functions_.partitioning())
+    , tau_(tau)
+    , epsilon_gamma_(epsilon_gamma)
+    , chi_(chi)
+    , xi_(xi)
+    , r_sequence_(r_sequence)
+    , k_0_(k_0)
+    , k_max_(k_max)
+    , epsilon_(epsilon)
+  {
+    const auto& quadratures = basis_functions_.quadratures();
+    assert(quadratures.size() == grid_points_.size() - 1);
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      for (const auto& quad_point : quadratures[jj]) {
+        quad_points_[jj].emplace_back(quad_point.position()[0]);
+        quad_weights_[jj].emplace_back(quad_point.weight());
+      }
+    } // jj
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      M_[jj].resize(quad_points_[jj].size());
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll)
+        M_[jj][ll] = basis_functions_.evaluate_on_interval(quad_points_[jj][ll], jj);
+    } // jj
+  }
+
+
+  // ============================================================================================
+  // ============================= FunctionInterface methods ====================================
+  // ============================================================================================
+
+
+  int order(const XT::Common::Parameter& /*param*/) const override
+  {
+    return 1;
+  }
+
+  virtual RangeReturnType evaluate(const DomainType& u,
+                                   const XT::Common::Parameter& /*param*/ = {}) const override final
+  {
+    const auto alpha = get_alpha(u, *get_isotropic_alpha(u), true)->first;
+    return evaluate_with_alpha(alpha);
+  }
+
+  virtual RangeReturnType evaluate_with_alpha(const VectorType& alpha) const
+  {
+    RangeReturnType ret(0.);
+    // calculate ret[ii] = < omega[ii] m G_\alpha(u) >
+    auto& eta_ast_prime_vals = working_storage();
+    evaluate_eta_ast_prime(alpha, eta_ast_prime_vals);
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto& basis_ll = M_[jj][ll];
+        auto factor_ll = eta_ast_prime_vals[jj][ll] * quad_points_[jj][ll] * quad_weights_[jj][ll];
+        for (size_t ii = 0; ii < 2; ++ii)
+          ret[0][jj + ii] += basis_ll[ii] * factor_ll;
+      } // ll (quad points)
+    } // jj (intervals)
+    return ret;
+  } // void evaluate(...)
+
+  virtual void jacobian(const DomainType& u,
+                        DynamicDerivativeRangeType& result,
+                        const XT::Common::Parameter& /*param*/ = {}) const override final
+  {
+    const auto alpha = get_alpha(u, *get_isotropic_alpha(u), true)->first;
+    jacobian_with_alpha(alpha, result);
+  }
+
+  virtual void jacobian_with_alpha(const VectorType& alpha, DynamicDerivativeRangeType& result) const
+  {
+    VectorType H_diag, J_diag;
+    FieldVector<RangeFieldType, dimRange - 1> H_subdiag, J_subdiag;
+    calculate_hessian(alpha, M_, H_diag, H_subdiag);
+    calculate_J(M_, J_diag, J_subdiag);
+    calculate_J_Hinv(result[0], J_diag, J_subdiag, H_diag, H_subdiag);
+  }
+
+
+  // ============================================================================================
+  // ============ Evaluations of ansatz distribution, moments, hessian etc. =====================
+  // ============================================================================================
+
+
+  std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u) const
+  {
+    return get_alpha(u, *get_isotropic_alpha(u), true);
+  }
+
+  // returns (alpha, (actual_u, r)), where r is the regularization parameter and actual_u the regularized u
+  std::unique_ptr<AlphaReturnType>
+  get_alpha(const DomainType& u, const VectorType& alpha_in, const bool regularize) const
+  {
+    auto ret = std::make_unique<AlphaReturnType>();
+
+    constexpr bool rescale = (entropy == EntropyType::MaxwellBoltzmann);
+
+    // rescale u such that the density <psi> is 1 if rescale is true
+    RangeFieldType density = basis_functions_.density(u);
+    if (!(density > 0.) || std::isinf(density))
+      DUNE_THROW(Dune::MathError, "Negative, inf or NaN density!");
+    static const auto alpha_one = basis_functions_.alpha_one();
+    VectorType phi = rescale ? u / density : u;
+    VectorType alpha_initial = alpha_in;
+    if (rescale)
+      alpha_initial -= alpha_one * std::log(density);
+    RangeFieldType tau_prime =
+        rescale
+            ? std::min(tau_ / ((1 + std::sqrt(dimRange) * phi.two_norm()) * density + std::sqrt(dimRange) * tau_), tau_)
+            : tau_;
+    // The hessian H is always symmetric and tridiagonal, so we only need to store the diagonal and subdiagonal
+    // elements
+    VectorType H_diag;
+    FieldVector<RangeFieldType, dimRange - 1> H_subdiag;
+
+    // calculate moment vector for isotropic distribution
+    VectorType u_iso = basis_functions_.u_iso();
+    VectorType v;
+    VectorType alpha_k = alpha_initial;
+
+    const auto& r_sequence = regularize ? r_sequence_ : std::vector<RangeFieldType>{0.};
+    const auto r_max = r_sequence.back();
+    for (const auto& rr : r_sequence_) {
+      // regularize u
+      v = phi;
+      if (rr > 0) {
+        alpha_k = *get_isotropic_alpha(v);
+        VectorType r_times_u_iso(u_iso);
+        r_times_u_iso *= rr;
+        v *= 1 - rr;
+        v += r_times_u_iso;
+      }
+
+      // calculate f_0
+      RangeFieldType f_k = calculate_f(alpha_k, v);
+
+      int backtracking_failed = 0;
+      VectorType g_k, d_k, minus_g_k, u_alpha_prime;
+      for (size_t kk = 0; kk < k_max_; ++kk) {
+        // exit inner for loop to increase r if too many iterations are used
+        if (kk > k_0_ && rr < r_max)
+          break;
+        // calculate gradient g
+        calculate_gradient(alpha_k, v, g_k);
+        // calculate Hessian H
+        calculate_hessian(alpha_k, M_, H_diag, H_subdiag, entropy == EntropyType::MaxwellBoltzmann);
+        // calculate descent direction d_k;
+        minus_g_k = g_k;
+        minus_g_k *= -1;
+        try {
+          d_k = minus_g_k;
+          XT::LA::solve_sym_tridiag_posdef(H_diag, H_subdiag, d_k);
+        } catch (const Dune::MathError&) {
+          if (rr < r_max)
+            break;
+          else
+            DUNE_THROW(Dune::MathError, "Failure to converge!");
+        }
+
+        const auto& alpha_tilde = alpha_k;
+        const auto u_alpha_tilde = g_k + v;
+        auto density_tilde = basis_functions_.density(u_alpha_tilde);
+        if (!(density_tilde > 0.) || std::isinf(density_tilde))
+          break;
+        auto alpha_prime = alpha_tilde;
+        if (rescale) {
+          alpha_prime -= alpha_one * std::log(density_tilde);
+          calculate_u(alpha_prime, u_alpha_prime);
+        } else {
+          u_alpha_prime = u_alpha_tilde;
+        }
+        auto u_eps_diff = v - u_alpha_prime * (1 - epsilon_gamma_);
+        auto& eta_ast_prime_vals = working_storage();
+        // if rescale is true, working storage already contains the eta_ast_prime evaluations due to the call to
+        // calculate_u above
+        if (!rescale)
+          evaluate_eta_ast_prime(alpha_prime, eta_ast_prime_vals);
+        // checking realizability is cheap so we do not need the second stopping criterion
+        if (g_k.two_norm() < tau_prime && is_realizable(u_eps_diff)
+            && (entropy == EntropyType::MaxwellBoltzmann || all_positive(eta_ast_prime_vals))) {
+          ret->first = rescale ? alpha_prime + alpha_one * std::log(density) : alpha_prime;
+          ret->second = std::make_pair(rescale ? v * density : v, rr);
+          return ret;
+        } else {
+          RangeFieldType zeta_k = 1;
+          // backtracking line search
+          while (backtracking_failed >= 2 || zeta_k > epsilon_ * alpha_k.two_norm() / d_k.two_norm()) {
+            // calculate alpha_new = alpha_k + zeta_k d_k
+            auto alpha_new = d_k;
+            alpha_new *= zeta_k;
+            alpha_new += alpha_k;
+            // calculate f(alpha_new)
+            RangeFieldType f_new = calculate_f(alpha_new, v);
+            if (backtracking_failed >= 2 || XT::Common::FloatCmp::le(f_new, f_k + xi_ * zeta_k * (g_k * d_k))) {
+              alpha_k = alpha_new;
+              f_k = f_new;
+              backtracking_failed = 0.;
+              break;
+            }
+            zeta_k = chi_ * zeta_k;
+          } // backtracking linesearch while
+          // if (zeta_k <= epsilon_ * alpha_k.two_norm() / d_k.two_norm() * 100.)
+          if (zeta_k <= epsilon_ * alpha_k.two_norm() / d_k.two_norm())
+            ++backtracking_failed;
+        } // else (stopping conditions)
+      } // k loop (Newton iterations)
+    } // rr loop (Regularization parameter)
+    DUNE_THROW(MathError, "Failed to converge");
+    return ret;
+  } // ... get_alpha(...)
+
+  // returns density rho = < eta_ast_prime(alpha * b(v)) >
+  RangeFieldType get_rho(const DomainType& alpha) const
+  {
+    auto& eta_ast_prime_vals = working_storage();
+    evaluate_eta_ast_prime(alpha, eta_ast_prime_vals);
+    RangeFieldType ret(0.);
+    for (size_t jj = 0; jj < num_intervals; ++jj)
+      ret += std::inner_product(
+          quad_weights_[jj].begin(), quad_weights_[jj].end(), eta_ast_prime_vals[jj].begin(), RangeFieldType(0.));
+    return ret;
+  }
+
+  // returns < eta_ast(alpha * b(v)) >
+  RangeFieldType get_eta_ast_integrated(const DomainType& alpha) const
+  {
+    auto& eta_ast_vals = working_storage();
+    evaluate_eta_ast(alpha, eta_ast_vals);
+    RangeFieldType ret(0.);
+    for (size_t jj = 0; jj < num_intervals; ++jj)
+      ret += std::inner_product(
+          quad_weights_[jj].begin(), quad_weights_[jj].end(), eta_ast_vals[jj].begin(), RangeFieldType(0.));
+    return ret;
+  }
+
+  DomainType get_u(const DomainType& alpha) const
+  {
+    DomainType ret;
+    calculate_u(alpha, ret);
+    return ret;
+  }
+
+  DomainType get_u(const QuadratureWeightsType& eta_ast_prime_vals) const
+  {
+    DomainType ret;
+    calculate_u(eta_ast_prime_vals, ret);
+    return ret;
+  }
+
+  void calculate_u(const DomainType& alpha, DomainType& u) const
+  {
+    auto& eta_ast_prime_vals = working_storage();
+    evaluate_eta_ast_prime(alpha, eta_ast_prime_vals);
+    calculate_u(eta_ast_prime_vals, u);
+  } // void calculate_u(...)
+
+  void calculate_u(const QuadratureWeightsType& eta_ast_prime_vals, DomainType& u) const
+  {
+    std::fill(u.begin(), u.end(), 0.);
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        auto factor_ll = eta_ast_prime_vals[jj][ll] * quad_weights_[jj][ll];
+        for (size_t ii = 0; ii < 2; ++ii)
+          u[jj + ii] += M_[jj][ll][ii] * factor_ll;
+      } // ll (quad points)
+    } // jj (intervals)
+  }
+
+  RangeFieldType calculate_f(const VectorType& alpha, const VectorType& v) const
+  {
+    return get_eta_ast_integrated(alpha) - alpha * v;
+  } // void calculate_u(...)
+
+  void calculate_gradient(const VectorType& alpha, const VectorType& v, VectorType& g_k) const
+  {
+    calculate_u(alpha, g_k);
+    g_k -= v;
+  }
+
+  void calculate_hessian(const QuadratureWeightsType& eta_ast_twoprime_vals,
+                         const BasisValuesMatrixType& M,
+                         VectorType& H_diag,
+                         FieldVector<RangeFieldType, dimRange - 1>& H_subdiag) const
+  {
+    std::fill(H_diag.begin(), H_diag.end(), 0.);
+    std::fill(H_subdiag.begin(), H_subdiag.end(), 0.);
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto& basis_ll = M[jj][ll];
+        const auto factor = eta_ast_twoprime_vals[jj][ll] * quad_weights_[jj][ll];
+        for (size_t ii = 0; ii < 2; ++ii)
+          H_diag[jj + ii] += std::pow(basis_ll[ii], 2) * factor;
+        H_subdiag[jj] += basis_ll[0] * basis_ll[1] * factor;
+      } // ll (quad points)
+    } // jj (intervals)
+  } // void calculate_hessian(...)
+
+  void calculate_hessian(const DomainType& alpha,
+                         const BasisValuesMatrixType& M,
+                         VectorType& H_diag,
+                         FieldVector<RangeFieldType, dimRange - 1>& H_subdiag,
+                         const bool use_working_storage = false) const
+  {
+    auto& eta_ast_twoprime_vals = working_storage();
+    if (!use_working_storage)
+      evaluate_eta_ast_twoprime(alpha, eta_ast_twoprime_vals);
+    calculate_hessian(eta_ast_twoprime_vals, M, H_diag, H_subdiag);
+  } // void calculate_hessian(...)
+
+  // J = df/dalpha is the derivative of the flux with respect to alpha.
+  // As F = (f_1, f_2, f_3) is matrix-valued
+  // (div f = \sum_{i=1}^d \partial_{x_i} f_i  = \sum_{i=1}^d \partial_{x_i} < v_i m \hat{psi}(alpha) > is
+  // vector-valued),
+  // the derivative is the vector of matrices (df_1/dalpha, df_2/dalpha, ...)
+  // this function returns the dd-th matrix df_dd/dalpha of J
+  // assumes work_vecs already contains the needed eta_ast_twoprime(alpha * b) values
+  void calculate_J(const BasisValuesMatrixType& M,
+                   VectorType& J_diag,
+                   FieldVector<RangeFieldType, dimRange - 1>& J_subdiag) const
+  {
+    std::fill(J_diag.begin(), J_diag.end(), 0.);
+    std::fill(J_subdiag.begin(), J_subdiag.end(), 0.);
+    const auto& eta_ast_twoprime_vals = working_storage();
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto& basis_ll = M[jj][ll];
+        const auto factor = eta_ast_twoprime_vals[jj][ll] * quad_points_[jj][ll] * quad_weights_[jj][ll];
+        for (size_t ii = 0; ii < 2; ++ii)
+          J_diag[jj + ii] += std::pow(basis_ll[ii], 2) * factor;
+        J_subdiag[jj] += basis_ll[0] * basis_ll[1] * factor;
+      } // ll (quad points)
+    } // jj (intervals)
+  } // void calculate_J(...)
+
+  void
+  apply_inverse_hessian(const QuadratureWeightsType& density_evaluations, const DomainType& u, DomainType& Hinv_u) const
+  {
+    thread_local VectorType H_diag;
+    thread_local FieldVector<RangeFieldType, dimRange - 1> H_subdiag;
+    calculate_hessian(density_evaluations, M_, H_diag, H_subdiag);
+    // factorize H = LDL^T, where L is unit lower bidiagonal and D is diagonal
+    // H_diag is overwritten by the diagonal elements of D
+    // H_subdiag is overwritten by the subdiagonal elements of L
+    XT::LA::tridiagonal_ldlt(H_diag, H_subdiag);
+    // Solve H ret = u
+    Hinv_u = u;
+    XT::LA::solve_tridiagonal_ldlt_factorized(H_diag, H_subdiag, Hinv_u);
+  }
+
+  // calculates ret = J H^{-1}. Both J and H are symmetric tridiagonal, H is positive definite.
+  static void calculate_J_Hinv(DynamicRowDerivativeRangeType& ret,
+                               const VectorType& J_diag,
+                               const FieldVector<RangeFieldType, dimRange - 1>& J_subdiag,
+                               VectorType& H_diag,
+                               FieldVector<RangeFieldType, dimRange - 1>& H_subdiag)
+  {
+    // factorize H = LDL^T, where L is unit lower bidiagonal and D is diagonal
+    // H_diag is overwritten by the diagonal elements of D
+    // H_subdiag is overwritten by the subdiagonal elements of L
+    XT::LA::tridiagonal_ldlt(H_diag, H_subdiag);
+
+    // copy J to dense matrix
+    ret.set_all_entries(0.);
+    for (size_t ii = 0; ii < dimRange - 1; ++ii) {
+      ret.set_entry(ii, ii, J_diag[ii]);
+      ret.set_entry(ii + 1, ii, J_subdiag[ii]);
+      ret.set_entry(ii, ii + 1, J_subdiag[ii]);
+    }
+    ret.set_entry(dimRange - 1, dimRange - 1, J_diag[dimRange - 1]);
+
+    // Solve ret H = J which is equivalent to (as H and J are symmetric) to H ret^T = J;
+    XT::LA::solve_tridiagonal_ldlt_factorized(H_diag, H_subdiag, ret);
+    // transpose ret
+    RangeFieldType* ret_ptr = &(ret[0][0]);
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      for (size_t jj = 0; jj < ii; ++jj)
+        std::swap(ret_ptr[jj * dimRange + ii], ret_ptr[ii * dimRange + jj]);
+  } // void calculate_J_Hinv(...)
+
+
+  // ============================================================================================
+  // ============================= Entropy evaluations ==========================================
+  // ============================================================================================
+
+
+  // evaluates \eta_{\ast}(\alpha^T b(v_i)) for all quadrature points v_i
+  void evaluate_eta_ast(const VectorType& alpha, QuadratureWeightsType& ret) const
+  {
+    calculate_scalar_products(alpha, ret);
+    apply_exponential(ret);
+    evaluate_eta_ast(ret);
+  }
+
+  // evaluates \eta_{\ast}(\alpha^T b(v_i)) for all quadrature points v_i, assumes that ret already contains
+  // exp(alpha^T b(v_i))
+  void evaluate_eta_ast(QuadratureWeightsType& ret) const
+  {
+    if (entropy == EntropyType::BoseEinstein)
+      for (size_t jj = 0; jj < num_intervals; ++jj)
+        for (size_t ll = 0; ll < ret[jj].size(); ++ll)
+          ret[jj][ll] = -std::log(1 - ret[jj][ll]);
+  }
+
+  // evaluates \eta_{\ast}^{\prime}(\alpha^T b(v_i)) for all quadrature points v_i
+  void evaluate_eta_ast_prime(const VectorType& alpha, QuadratureWeightsType& ret) const
+  {
+    calculate_scalar_products(alpha, ret);
+    apply_exponential(ret);
+    evaluate_eta_ast_prime(ret);
+  }
+
+  // evaluates \eta_{\ast}^{\prime}(\alpha^T b(v_i)) for all quadrature points v_i, assumes that ret already contains
+  // exp(alpha^T b(v_i))
+  void evaluate_eta_ast_prime(QuadratureWeightsType& ret) const
+  {
+    if (entropy == EntropyType::BoseEinstein)
+      for (size_t jj = 0; jj < num_intervals; ++jj)
+        for (size_t ll = 0; ll < ret[jj].size(); ++ll)
+          ret[jj][ll] /= (1 - ret[jj][ll]);
+  }
+
+  // evaluates \eta_{\ast}^{\prime\prime}(\alpha^T b(v_i)) for all quadrature points v_i
+  void evaluate_eta_ast_twoprime(const VectorType& alpha, QuadratureWeightsType& ret) const
+  {
+    calculate_scalar_products(alpha, ret);
+    apply_exponential(ret);
+    evaluate_eta_ast_twoprime(ret);
+  }
+
+  // evaluates \eta_{\ast}^{\prime\prime}(\alpha^T b(v_i)) for all quadrature points v_i, assumes that ret already
+  // contains exp(alpha^T b(v_i))
+  void evaluate_eta_ast_twoprime(QuadratureWeightsType& ret) const
+  {
+    if (entropy == EntropyType::BoseEinstein)
+      for (size_t jj = 0; jj < num_intervals; ++jj)
+        for (size_t ll = 0; ll < ret[jj].size(); ++ll)
+          ret[jj][ll] /= std::pow(1 - ret[jj][ll], 2);
+  }
+
+  // stores evaluations of exp(alpha^T b(v_i)) for all quadrature points v_i
+  void store_exp_evaluations(QuadratureWeightsType& exp_evaluations, const DomainType& alpha) const
+  {
+    this->calculate_scalar_products(XT::LA::convert_to<VectorType>(alpha), exp_evaluations);
+    this->apply_exponential(exp_evaluations);
+  }
+
+  void store_eta_ast_prime_vals(const QuadratureWeightsType& exp_evaluations, QuadratureWeightsType& eta_ast_prime_vals)
+  {
+    eta_ast_prime_vals = exp_evaluations;
+    evaluate_eta_ast_prime(eta_ast_prime_vals);
+  }
+
+  void store_eta_ast_twoprime_vals(const QuadratureWeightsType& exp_evaluations,
+                                   QuadratureWeightsType& eta_ast_twoprime_vals)
+  {
+    eta_ast_twoprime_vals = exp_evaluations;
+    evaluate_eta_ast_twoprime(eta_ast_twoprime_vals);
+  }
+
+  // stores evaluations of a given boundary distribution psi(v) at all quadrature points v_i
+  void store_boundary_distribution_evaluations(
+      QuadratureWeightsType& boundary_distribution_evaluations,
+      const std::function<RangeFieldType(const FluxDomainType&)>& boundary_distribution) const
+  {
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      boundary_distribution_evaluations[jj].resize(quad_points_[jj].size());
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll)
+        boundary_distribution_evaluations[jj][ll] = boundary_distribution(quad_points_[jj][ll]);
+    }
+  }
+
+
+  // ============================================================================================
+  // =============================== Kinetic fluxes =============================================
+  // ============================================================================================
+
+
+  // calculate \sum_{i=1}^d < v_i m \psi > n_i, where n is the unit outer normal,
+  // m is the basis function vector, \psi is the ansatz corresponding to u
+  // and x, v, t are the space, velocity and time variable, respectively
+  // As we are using cartesian grids, n_i == 0 in all but one dimension, so only evaluate for i == dd
+  DomainType
+  evaluate_kinetic_flux(const DomainType& u_i, const DomainType& u_j, const FluxDomainType& n_ij, const size_t dd) const
+  {
+    const auto alpha_i = get_alpha(u_i, *get_isotropic_alpha(u_i), true)->first;
+    const auto alpha_j = get_alpha(u_j, *get_isotropic_alpha(u_j), true)->first;
+    return evaluate_kinetic_flux_with_alphas(alpha_i, alpha_j, n_ij, dd);
+  } // DomainType evaluate_kinetic_flux(...)
+
+  DomainType evaluate_kinetic_flux_with_alphas(const VectorType& alpha_i,
+                                               const VectorType& alpha_j,
+                                               const FluxDomainType& n_ij,
+                                               const size_t dd) const
+  {
+    assert(dd == 0);
+    thread_local FieldVector<QuadratureWeightsType, 2> eta_ast_prime_vals;
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      eta_ast_prime_vals[0][jj].resize(quad_points_[jj].size());
+      eta_ast_prime_vals[1][jj].resize(quad_points_[jj].size());
+    }
+    evaluate_eta_ast_prime(alpha_i, eta_ast_prime_vals[0]);
+    evaluate_eta_ast_prime(alpha_j, eta_ast_prime_vals[1]);
+    // calculate \sum_{i=1}^d < \omega_i m G_\alpha(u) > n_i
+    DomainType ret(0);
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto position = quad_points_[jj][ll];
+        RangeFieldType factor =
+            position * n_ij[dd] > 0. ? eta_ast_prime_vals[0][jj][ll] : eta_ast_prime_vals[1][jj][ll];
+        factor *= quad_weights_[jj][ll] * position;
+        for (size_t ii = 0; ii < 2; ++ii)
+          ret[jj + ii] += M_[jj][ll][ii] * factor;
+      } // ll (quad points)
+    } // jj (faces)
+    ret *= n_ij[dd];
+    return ret;
+  } // DomainType evaluate_kinetic_flux(...)
+
+  // Calculates left and right kinetic flux with reconstructed densities. Ansatz distribution values contains
+  // evaluations of the ansatz distribution at each quadrature point for a stencil of three entities. The distributions
+  // are reconstructed pointwise for each quadrature point and the resulting (part of) the kinetic flux is <
+  // psi_reconstr * b * v>_{+/-}.
+  template <SlopeLimiterType slope_type, class FluxesMapType>
+  void calculate_reconstructed_fluxes(const FieldVector<const QuadratureWeightsType*, 3>& ansatz_distribution_values,
+                                      FluxesMapType& flux_values,
+                                      const size_t dd) const
+  {
+    assert(dd == 0);
+    // get flux storage
+    BasisDomainType coord(0.5);
+    coord[dd] = 0;
+    auto& left_flux_value = flux_values[coord];
+    coord[dd] = 1;
+    auto& right_flux_value = flux_values[coord];
+    right_flux_value = left_flux_value = DomainType(0.);
+    thread_local XT::Common::FieldVector<std::vector<RangeFieldType>, 2> reconstructed_values(
+        std::vector<RangeFieldType>(quad_points_[0].size()));
+    const auto slope_func =
+        (slope_type == SlopeLimiterType::minmod) ? XT::Common::minmod<RangeFieldType> : superbee<RangeFieldType>;
+    auto& vals_left = reconstructed_values[0];
+    auto& vals_right = reconstructed_values[1];
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      // reconstruct densities
+      if (slope_type == SlopeLimiterType::no_slope) {
+        for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll)
+          vals_left[ll] = vals_right[ll] = (*ansatz_distribution_values[1])[jj][ll];
+      } else {
+        for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+          const auto slope =
+              slope_func((*ansatz_distribution_values[1])[jj][ll] - (*ansatz_distribution_values[0])[jj][ll],
+                         (*ansatz_distribution_values[2])[jj][ll] - (*ansatz_distribution_values[1])[jj][ll]);
+          vals_left[ll] = (*ansatz_distribution_values[1])[jj][ll] - 0.5 * slope;
+          vals_right[ll] = (*ansatz_distribution_values[1])[jj][ll] + 0.5 * slope;
+        } // ll (quad points)
+      }
+      // calculate fluxes
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto& position = quad_points_[jj][ll];
+        RangeFieldType factor = position > 0. ? vals_right[ll] : vals_left[ll];
+        factor *= quad_weights_[jj][ll] * position;
+        auto& val = position > 0. ? right_flux_value : left_flux_value;
+        const auto& basis_ll = M_[jj][ll];
+        for (size_t ii = 0; ii < 2; ++ii)
+          val[jj + ii] += basis_ll[ii] * factor;
+      } // ll (quad points)
+    } // jj
+  } // void calculate_reconstructed_fluxes(...)
+
+
+  // ============================================================================================
+  // ================================== Helper functions ========================================
+  // ============================================================================================
+
+
+  const MomentBasis& basis_functions() const
+  {
+    return basis_functions_;
+  }
+
+  void calculate_scalar_products(const VectorType& alpha, QuadratureWeightsType& scalar_products) const
+  {
+    LocalVectorType local_alpha;
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      scalar_products[jj].resize(quad_weights_[jj].size());
+      for (size_t ii = 0; ii < 2; ++ii)
+        local_alpha[ii] = alpha[jj + ii];
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll)
+        scalar_products[jj][ll] = local_alpha * M_[jj][ll];
+    } // jj
+  }
+
+  void apply_exponential(QuadratureWeightsType& values) const
+  {
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      assert(values[jj].size() < std::numeric_limits<int>::max());
+      XT::Common::Mkl::exp(static_cast<int>(values[jj].size()), values[jj].data(), values[jj].data());
+    }
+  }
+
+  std::unique_ptr<VectorType> get_isotropic_alpha(const RangeFieldType density) const
+  {
+    return std::make_unique<VectorType>(basis_functions_.alpha_iso(density));
+  }
+
+  std::unique_ptr<VectorType> get_isotropic_alpha(const DomainType& u) const
+  {
+    return get_isotropic_alpha(basis_functions_.density(u));
+  }
+
+  static bool is_realizable(const DomainType& u)
+  {
+    for (const auto& u_i : u)
+      if (!(u_i > 0.) || std::isinf(u_i))
+        return false;
+    return true;
+  }
+
+  QuadratureWeightsType& working_storage() const
+  {
+    thread_local QuadratureWeightsType work_vec;
+    for (size_t jj = 0; jj < num_intervals; ++jj)
+      work_vec[jj].resize(quad_points_[jj].size());
+    return work_vec;
+  }
+
+  bool all_positive(const QuadratureWeightsType& vals) const
+  {
+    for (size_t jj = 0; jj < num_intervals; ++jj)
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll) {
+        const auto val = vals[jj][ll];
+        if (val < 0. || std::isinf(val) || std::isnan(val))
+          return false;
+      }
+    return true;
+  }
+
+  const MomentBasis& basis_functions_;
+  QuadraturePointsType quad_points_;
+  QuadratureWeightsType quad_weights_;
+  const std::vector<RangeFieldType>& grid_points_;
+  BasisValuesMatrixType M_;
+  const RangeFieldType tau_;
+  const RangeFieldType epsilon_gamma_;
+  const RangeFieldType chi_;
+  const RangeFieldType xi_;
+  const std::vector<RangeFieldType> r_sequence_;
+  const size_t k_0_;
+  const size_t k_max_;
+  const RangeFieldType epsilon_;
+};
+#  endif // ENTROPY_FLUX_1D_HATFUNCTIONS_USE_ANALYTICAL_INTEGRALS
+#endif // ENTROPY_FLUX_USE_1D_HATFUNCTIONS_SPECIALIZATION
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_MOMENTMODELS_ENTROPYFLUX_IMPLEMENTATIONS_HH
diff --git a/dune/gdt/test/momentmodels/entropyflux_kineticcoords.hh b/dune/gdt/test/momentmodels/entropyflux_kineticcoords.hh
new file mode 100644
index 0000000000000000000000000000000000000000..a948f7f7441a008b23034e43b8b487ce895eb651
--- /dev/null
+++ b/dune/gdt/test/momentmodels/entropyflux_kineticcoords.hh
@@ -0,0 +1,276 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner (2019)
+
+#ifndef DUNE_GDT_LOCAL_FLUXES_ENTROPYBASED_KINETICCOORDS_HH
+#define DUNE_GDT_LOCAL_FLUXES_ENTROPYBASED_KINETICCOORDS_HH
+
+#include <list>
+#include <memory>
+
+#include <dune/xt/common/float_cmp.hh>
+#include <dune/xt/common/vector_less.hh>
+
+#include <dune/xt/functions/interfaces/flux-function.hh>
+
+#include <dune/gdt/test/momentmodels/basisfunctions.hh>
+#include <dune/gdt/test/momentmodels/entropyflux_implementations.hh>
+#include <dune/gdt/test/momentmodels/entropyflux.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+template <class GridViewImp, class MomentBasisImp, SlopeLimiterType slope>
+class EntropyBasedFluxEntropyCoordsFunction
+  : public XT::Functions::FluxFunctionInterface<XT::Grid::extract_entity_t<GridViewImp>,
+                                                MomentBasisImp::dimRange,
+                                                MomentBasisImp::dimFlux,
+                                                MomentBasisImp::dimRange,
+                                                typename MomentBasisImp::R>
+{
+  using BaseType = typename XT::Functions::FluxFunctionInterface<XT::Grid::extract_entity_t<GridViewImp>,
+                                                                 MomentBasisImp::dimRange,
+                                                                 MomentBasisImp::dimFlux,
+                                                                 MomentBasisImp::dimRange,
+                                                                 typename MomentBasisImp::R>;
+  using ThisType = EntropyBasedFluxEntropyCoordsFunction;
+
+public:
+  using GridViewType = GridViewImp;
+  using MomentBasis = MomentBasisImp;
+  using IndexSetType = typename GridViewType::IndexSet;
+  static const size_t dimFlux = MomentBasis::dimFlux;
+  static const size_t basis_dimRange = MomentBasis::dimRange;
+  using typename BaseType::DomainType;
+  using typename BaseType::E;
+  using typename BaseType::LocalFunctionType;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::StateType;
+  using ImplementationType = EntropyBasedFluxImplementation<MomentBasis>;
+  using AlphaReturnType = typename ImplementationType::AlphaReturnType;
+  using VectorType = typename ImplementationType::VectorType;
+  using I = XT::Grid::extract_intersection_t<GridViewType>;
+  using QuadratureWeightsType = typename ImplementationType::QuadratureWeightsType;
+  using BoundaryQuadratureWeightsType =
+      std::vector<XT::Common::FieldVector<XT::Common::FieldVector<QuadratureWeightsType, 2>, dimFlux>>;
+  static const EntropyType entropy = MomentBasis::entropy;
+
+  explicit EntropyBasedFluxEntropyCoordsFunction(
+      const MomentBasis& basis_functions,
+      const bool disable_realizability_check = false,
+      const RangeFieldType tau = 1e-9,
+      const RangeFieldType epsilon_gamma = 0.01,
+      const RangeFieldType chi = 0.5,
+      const RangeFieldType xi = 1e-3,
+      const std::vector<RangeFieldType> r_sequence = {0, 1e-8, 1e-6, 1e-4, 1e-3, 1e-2, 5e-2, 0.1, 0.5, 1},
+      const size_t k_0 = 500,
+      const size_t k_max = 1000,
+      const RangeFieldType epsilon = std::pow(2, -52))
+    : implementation_(std::make_shared<ImplementationType>(
+          basis_functions, tau, disable_realizability_check, epsilon_gamma, chi, xi, r_sequence, k_0, k_max, epsilon))
+  {}
+
+  explicit EntropyBasedFluxEntropyCoordsFunction(EntropyBasedFluxFunction<GridViewType, MomentBasis>& other)
+    : implementation_(other.implementation_)
+  {}
+
+  static const constexpr bool available = true;
+
+  class Localfunction : public LocalFunctionType
+  {
+    using BaseType = LocalFunctionType;
+
+  public:
+    using typename BaseType::DynamicJacobianRangeType;
+    using typename BaseType::E;
+    using typename BaseType::RangeReturnType;
+
+    Localfunction(const ImplementationType& implementation)
+      : implementation_(implementation)
+    {}
+
+    int order(const XT::Common::Parameter&) const override final
+    {
+      return 1.;
+    }
+
+    virtual RangeReturnType evaluate(const DomainType& /*point_in_reference_element*/,
+                                     const StateType& alpha,
+                                     const XT::Common::Parameter& /*param*/ = {}) const override final
+    {
+      return implementation_.evaluate_with_alpha(alpha);
+    }
+
+    virtual void jacobian(const DomainType& /*point_in_reference_element*/,
+                          const StateType& alpha,
+                          DynamicJacobianRangeType& result,
+                          const XT::Common::Parameter& /*param*/ = {}) const override final
+    {
+      implementation_.jacobian_with_alpha(alpha, result);
+    } // ... jacobian(...)
+
+  private:
+    const ImplementationType& implementation_;
+  }; // class Localfunction
+
+  bool x_dependent() const override final
+  {
+    return false;
+  }
+
+  std::unique_ptr<LocalFunctionType> local_function() const override final
+  {
+    return std::make_unique<Localfunction>(*implementation_);
+  }
+
+  virtual std::unique_ptr<Localfunction> derived_local_function() const
+  {
+    return std::make_unique<Localfunction>(*implementation_);
+  }
+
+  StateType evaluate_kinetic_flux(const E& /*inside_entity*/,
+                                  const E& /*outside_entity*/,
+                                  const StateType& flux_i,
+                                  const StateType& flux_j,
+                                  const DomainType& n_ij,
+                                  const size_t dd) const
+  {
+    return (flux_i + flux_j) * n_ij[dd];
+  } // StateType evaluate_kinetic_flux(...)
+
+  const MomentBasis& basis_functions() const
+  {
+    return implementation_->basis_functions();
+  }
+
+  StateType get_u(const StateType& alpha) const
+  {
+    return implementation_->get_u(alpha);
+  }
+
+  StateType get_u(const size_t entity_index) const
+  {
+    return implementation_->get_u((*eta_ast_prime_evaluations_)[entity_index]);
+  }
+
+  StateType get_alpha(const StateType& u) const
+  {
+    const auto alpha = implementation_->get_alpha(u)->first;
+    StateType ret;
+    std::copy(alpha.begin(), alpha.end(), ret.begin());
+    return ret;
+  }
+
+  template <class FluxesMapType>
+  void calculate_reconstructed_fluxes(const FieldVector<size_t, 3>& entity_indices,
+                                      const FieldVector<bool, 3>& boundary_direction,
+                                      FluxesMapType& precomputed_fluxes,
+                                      const size_t dd) const
+  {
+    FieldVector<const QuadratureWeightsType*, 3> densities_stencil;
+    for (size_t ii = 0; ii < 3; ++ii)
+      if (entity_indices[ii] == size_t(-1))
+        densities_stencil[ii] = &boundary_distribution_evaluations_[entity_indices[1]][dd][boundary_direction[ii]];
+      else
+        densities_stencil[ii] = &(*eta_ast_prime_evaluations_)[entity_indices[ii]];
+    implementation_->template calculate_reconstructed_fluxes<slope, FluxesMapType>(
+        densities_stencil, precomputed_fluxes, dd);
+  }
+
+  void apply_inverse_hessian(const size_t entity_index, const StateType& u, StateType& Hinv_u) const
+  {
+    implementation_->apply_inverse_hessian((*eta_ast_twoprime_evaluations_)[entity_index], u, Hinv_u);
+  }
+
+  void store_evaluations(const size_t entity_index, const StateType& alpha)
+  {
+    implementation_->store_exp_evaluations(exp_evaluations_[entity_index], alpha);
+    if (entropy != EntropyType::MaxwellBoltzmann) {
+      implementation_->store_eta_ast_prime_vals(exp_evaluations_[entity_index], eta_ast_prime_storage_[entity_index]);
+      implementation_->store_eta_ast_twoprime_vals(exp_evaluations_[entity_index],
+                                                   eta_ast_twoprime_storage_[entity_index]);
+    }
+  }
+
+  void set_eta_ast_pointers()
+  {
+    if (entropy == EntropyType::MaxwellBoltzmann) {
+      eta_ast_prime_evaluations_ = &exp_evaluations_;
+      eta_ast_twoprime_evaluations_ = &exp_evaluations_;
+    } else {
+      eta_ast_prime_evaluations_ = &eta_ast_prime_storage_;
+      eta_ast_twoprime_evaluations_ = &eta_ast_twoprime_storage_;
+    }
+  }
+
+  void store_boundary_evaluations(const std::function<RangeFieldType(const DomainType&)>& boundary_distribution,
+                                  const size_t entity_index,
+                                  const size_t intersection_index)
+  {
+    implementation_->store_boundary_distribution_evaluations(
+        boundary_distribution_evaluations_[entity_index][intersection_index / 2][intersection_index % 2],
+        boundary_distribution);
+  }
+
+  std::vector<QuadratureWeightsType>& exp_evaluations()
+  {
+    return exp_evaluations_;
+  }
+
+  const std::vector<QuadratureWeightsType>& exp_evaluations() const
+  {
+    return exp_evaluations_;
+  }
+
+  std::vector<QuadratureWeightsType>& eta_ast_prime_evaluations()
+  {
+    return *eta_ast_prime_evaluations_;
+  }
+
+  const std::vector<QuadratureWeightsType>& eta_ast_prime_evaluations() const
+  {
+    return *eta_ast_prime_evaluations_;
+  }
+
+  std::vector<QuadratureWeightsType>& eta_ast_twoprime_evaluations()
+  {
+    return *eta_ast_twoprime_evaluations_;
+  }
+
+  const std::vector<QuadratureWeightsType>& eta_ast_twoprime_evaluations() const
+  {
+    return *eta_ast_twoprime_evaluations_;
+  }
+
+  BoundaryQuadratureWeightsType& boundary_distribution_evaluations()
+  {
+    return boundary_distribution_evaluations_;
+  }
+
+  const BoundaryQuadratureWeightsType& boundary_distribution_evaluations() const
+  {
+    return boundary_distribution_evaluations_;
+  }
+
+
+private:
+  std::shared_ptr<ImplementationType> implementation_;
+  std::vector<QuadratureWeightsType> exp_evaluations_;
+  std::vector<QuadratureWeightsType> eta_ast_prime_storage_;
+  std::vector<QuadratureWeightsType> eta_ast_twoprime_storage_;
+  std::vector<QuadratureWeightsType>* eta_ast_prime_evaluations_;
+  std::vector<QuadratureWeightsType>* eta_ast_twoprime_evaluations_;
+  BoundaryQuadratureWeightsType boundary_distribution_evaluations_;
+};
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_LOCAL_FLUXES_ENTROPYBASED_KINETICCOORDS_HH
diff --git a/dune/gdt/test/momentmodels/entropysolver.hh b/dune/gdt/test/momentmodels/entropysolver.hh
new file mode 100644
index 0000000000000000000000000000000000000000..6540b7edc7ab81db8c8175503ea4be0a465cd407
--- /dev/null
+++ b/dune/gdt/test/momentmodels/entropysolver.hh
@@ -0,0 +1,190 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner (2018)
+
+#ifndef DUNE_GDT_MOMENTMODELS_ENTROPYSOLVER_HH
+#define DUNE_GDT_MOMENTMODELS_ENTROPYSOLVER_HH
+
+#include <string>
+
+#include <dune/xt/grid/functors/interfaces.hh>
+#include <dune/xt/common/parameter.hh>
+
+#include <dune/gdt/discretefunction/default.hh>
+#include <dune/gdt/test/momentmodels/entropyflux.hh>
+#include <dune/gdt/operators/interfaces.hh>
+#include <dune/gdt/type_traits.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+template <class SpaceType, class VectorType, class MomentBasis>
+class LocalEntropySolver : public XT::Grid::ElementFunctor<typename SpaceType::GridViewType>
+{
+  using GridViewType = typename SpaceType::GridViewType;
+  using BaseType = XT::Grid::ElementFunctor<GridViewType>;
+  using EntityType = typename GridViewType::template Codim<0>::Entity;
+  using IndexSetType = typename GridViewType::IndexSet;
+  using EntropyFluxType = EntropyBasedFluxFunction<GridViewType, MomentBasis>;
+  using RangeFieldType = typename EntropyFluxType::RangeFieldType;
+  using LocalVectorType = typename EntropyFluxType::VectorType;
+  static const size_t dimFlux = EntropyFluxType::dimFlux;
+  static const size_t dimRange = EntropyFluxType::basis_dimRange;
+  using DiscreteFunctionType = DiscreteFunction<VectorType, GridViewType, dimRange, 1, RangeFieldType>;
+  using ConstDiscreteFunctionType = ConstDiscreteFunction<VectorType, GridViewType, dimRange, 1, RangeFieldType>;
+
+public:
+  explicit LocalEntropySolver(const SpaceType& space,
+                              const VectorType& source_dofs,
+                              VectorType& range_dofs,
+                              const EntropyFluxType& analytical_flux,
+                              const RangeFieldType min_acceptable_density,
+                              const XT::Common::Parameter& param,
+                              const std::string filename = "")
+    : space_(space)
+    , source_(space_, source_dofs, "source")
+    , local_source_(source_.local_discrete_function())
+    , range_(space_, range_dofs, "range")
+    , local_range_(range_.local_discrete_function())
+    , analytical_flux_(analytical_flux)
+    , local_flux_(analytical_flux_.derived_local_function())
+    , min_acceptable_density_(min_acceptable_density)
+    , param_(param)
+    , filename_(filename.empty() ? "" : (filename + "_regularization.txt"))
+    , index_set_(space_.grid_view().indexSet())
+  {}
+
+  explicit LocalEntropySolver(LocalEntropySolver& other)
+    : BaseType(other)
+    , space_(other.space_)
+    , source_(space_, other.source_.dofs().vector(), "source")
+    , local_source_(source_.local_discrete_function())
+    , range_(space_, other.range_.dofs().vector(), "range")
+    , local_range_(range_.local_discrete_function())
+    , analytical_flux_(other.analytical_flux_)
+    , local_flux_(analytical_flux_.derived_local_function())
+    , min_acceptable_density_(other.min_acceptable_density_)
+    , param_(other.param_)
+    , filename_(other.filename_)
+    , index_set_(space_.grid_view().indexSet())
+  {}
+
+  XT::Grid::ElementFunctor<GridViewType>* copy() override final
+  {
+    return new LocalEntropySolver(*this);
+  }
+
+  void apply_local(const EntityType& entity) override final
+  {
+    local_source_->bind(entity);
+    local_range_->bind(entity);
+    XT::Common::FieldVector<RangeFieldType, dimRange> u;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      u[ii] = local_source_->dofs().get_entry(ii);
+    const auto& basis_functions = analytical_flux_.basis_functions();
+    basis_functions.ensure_min_density(u, min_acceptable_density_);
+    local_flux_->bind(entity);
+    const auto alpha_ret = local_flux_->get_alpha(u, true);
+    const auto regularization_params = alpha_ret->second;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      local_range_->dofs().set_entry(ii, regularization_params.first[ii]);
+    const auto s = regularization_params.second;
+    if (s > 0.) {
+      if (!filename_.empty()) {
+        static std::mutex outfile_lock;
+        outfile_lock.lock();
+        std::ofstream outfile(filename_, std::ios_base::app);
+        outfile << param_.get("t")[0];
+        for (size_t ii = 0; ii < dimFlux; ++ii)
+          outfile << " " << entity.geometry().center()[ii];
+        outfile << " " << s << " ";
+        outfile << XT::Common::to_string(u, 15) << std::endl;
+        outfile_lock.unlock();
+      }
+    }
+  } // void apply_local(...)
+
+private:
+  const SpaceType& space_;
+  const ConstDiscreteFunctionType source_;
+  std::unique_ptr<typename ConstDiscreteFunctionType::ConstLocalDiscreteFunctionType> local_source_;
+  DiscreteFunctionType range_;
+  std::unique_ptr<typename DiscreteFunctionType::LocalDiscreteFunctionType> local_range_;
+  const EntropyFluxType& analytical_flux_;
+  std::unique_ptr<typename EntropyFluxType::Localfunction> local_flux_;
+  const RangeFieldType min_acceptable_density_;
+  const XT::Common::Parameter& param_;
+  const std::string filename_;
+  const typename SpaceType::GridViewType::IndexSet& index_set_;
+}; // class LocalEntropySolver<...>
+
+
+template <class MomentBasisImp,
+          class SpaceImp,
+          class MatrixType = typename XT::LA::Container<typename MomentBasisImp::RangeFieldType>::MatrixType>
+class EntropySolver : public OperatorInterface<MatrixType, typename SpaceImp::GridViewType, MomentBasisImp::dimRange, 1>
+{
+  using BaseType = OperatorInterface<MatrixType, typename SpaceImp::GridViewType, MomentBasisImp::dimRange, 1>;
+
+public:
+  using typename BaseType::VectorType;
+  using MomentBasis = MomentBasisImp;
+  using SpaceType = SpaceImp;
+  using SourceSpaceType = SpaceImp;
+  using RangeSpaceType = SpaceImp;
+  using EntropyFluxType = EntropyBasedFluxFunction<typename SpaceType::GridViewType, MomentBasis>;
+  using RangeFieldType = typename MomentBasis::RangeFieldType;
+  using LocalVectorType = typename EntropyFluxType::VectorType;
+
+  EntropySolver(const EntropyFluxType& analytical_flux,
+                const SpaceType& space,
+                const RangeFieldType min_acceptable_density,
+                const std::string filename = "")
+    : analytical_flux_(analytical_flux)
+    , space_(space)
+    , min_acceptable_density_(min_acceptable_density)
+    , filename_(filename)
+  {}
+
+  bool linear() const override final
+  {
+    return false;
+  }
+
+  const SourceSpaceType& source_space() const override final
+  {
+    return space_;
+  }
+
+  const RangeSpaceType& range_space() const override final
+  {
+    return space_;
+  }
+
+  void apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& param) const override final
+  {
+    LocalEntropySolver<SpaceType, VectorType, MomentBasis> local_entropy_solver(
+        space_, source, range, analytical_flux_, min_acceptable_density_, param, filename_);
+    auto walker = XT::Grid::Walker<typename SpaceType::GridViewType>(space_.grid_view());
+    walker.append(local_entropy_solver);
+    walker.walk(true);
+  } // void apply(...)
+
+private:
+  const EntropyFluxType& analytical_flux_;
+  const SpaceType& space_;
+  const RangeFieldType min_acceptable_density_;
+  const std::string filename_;
+}; // class EntropySolver<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_MOMENTMODELS_ENTROPYSOLVER_HH
diff --git a/dune/gdt/test/momentmodels/hessianinverter.hh b/dune/gdt/test/momentmodels/hessianinverter.hh
new file mode 100644
index 0000000000000000000000000000000000000000..bbe655e48213bd20ffbb9d8552212852fc5c5ec3
--- /dev/null
+++ b/dune/gdt/test/momentmodels/hessianinverter.hh
@@ -0,0 +1,174 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner (2018)
+
+#ifndef DUNE_GDT_MOMENTMODELS_HESSIANINVERTER_HH
+#define DUNE_GDT_MOMENTMODELS_HESSIANINVERTER_HH
+
+#include <string>
+
+#include <dune/xt/grid/functors/interfaces.hh>
+#include <dune/xt/common/parameter.hh>
+
+#include <dune/gdt/discretefunction/default.hh>
+#include <dune/gdt/test/momentmodels/entropyflux_kineticcoords.hh>
+#include <dune/gdt/operators/interfaces.hh>
+#include <dune/gdt/type_traits.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+template <class SpaceType, class VectorType, class MomentBasis, SlopeLimiterType slope>
+class LocalEntropicHessianInverter : public XT::Grid::ElementFunctor<typename SpaceType::GridViewType>
+{
+  using GridViewType = typename SpaceType::GridViewType;
+  using BaseType = XT::Grid::ElementFunctor<GridViewType>;
+  using EntityType = typename GridViewType::template Codim<0>::Entity;
+  using IndexSetType = typename GridViewType::IndexSet;
+  using EntropyFluxType = EntropyBasedFluxEntropyCoordsFunction<GridViewType, MomentBasis, slope>;
+  using RangeFieldType = typename EntropyFluxType::RangeFieldType;
+  static const size_t dimFlux = EntropyFluxType::dimFlux;
+  static const size_t dimRange = EntropyFluxType::basis_dimRange;
+  using DiscreteFunctionType = DiscreteFunction<VectorType, GridViewType, dimRange, 1, RangeFieldType>;
+  using ConstDiscreteFunctionType = ConstDiscreteFunction<VectorType, GridViewType, dimRange, 1, RangeFieldType>;
+
+public:
+  explicit LocalEntropicHessianInverter(const SpaceType& space,
+                                        const VectorType& alpha_dofs,
+                                        const VectorType& u_update_dofs,
+                                        VectorType& alpha_range_dofs,
+                                        const EntropyFluxType& analytical_flux,
+                                        const XT::Common::Parameter& param)
+    : space_(space)
+    , alpha_(space_, alpha_dofs, "alpha")
+    , u_update_(space_, u_update_dofs, "u_update")
+    , local_alpha_(alpha_.local_discrete_function())
+    , local_u_update_(u_update_.local_discrete_function())
+    , range_(space_, alpha_range_dofs, "range")
+    , local_range_(range_.local_discrete_function())
+    , analytical_flux_(analytical_flux)
+    , param_(param)
+  {}
+
+  explicit LocalEntropicHessianInverter(LocalEntropicHessianInverter& other)
+    : BaseType(other)
+    , space_(other.space_)
+    , alpha_(space_, other.alpha_.dofs().vector(), "source")
+    , u_update_(space_, other.u_update_.dofs().vector(), "source")
+    , local_alpha_(alpha_.local_discrete_function())
+    , local_u_update_(u_update_.local_discrete_function())
+    , range_(space_, other.range_.dofs().vector(), "range")
+    , local_range_(range_.local_discrete_function())
+    , analytical_flux_(other.analytical_flux_)
+    , param_(other.param_)
+  {}
+
+  XT::Grid::ElementFunctor<GridViewType>* copy() override final
+  {
+    return new LocalEntropicHessianInverter(*this);
+  }
+
+  void apply_local(const EntityType& entity) override final
+  {
+    local_u_update_->bind(entity);
+    local_range_->bind(entity);
+    XT::Common::FieldVector<RangeFieldType, dimRange> u, Hinv_u;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      u[ii] = local_u_update_->dofs().get_entry(ii);
+    analytical_flux_.apply_inverse_hessian(space_.grid_view().indexSet().index(entity), u, Hinv_u);
+    for (auto&& entry : Hinv_u)
+      if (std::isnan(entry) || std::isinf(entry)) {
+        //        std::cout << "x: " << entity.geometry().center() << "u: " << u << ", alpha: " << alpha << ", Hinv_u: "
+        //        << Hinv_u << std::endl;
+        DUNE_THROW(Dune::MathError, "Hessian");
+      }
+
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      local_range_->dofs().set_entry(ii, Hinv_u[ii]);
+  } // void apply_local(...)
+
+private:
+  const SpaceType& space_;
+  const ConstDiscreteFunctionType alpha_;
+  const ConstDiscreteFunctionType u_update_;
+  std::unique_ptr<typename ConstDiscreteFunctionType::ConstLocalDiscreteFunctionType> local_alpha_;
+  std::unique_ptr<typename ConstDiscreteFunctionType::ConstLocalDiscreteFunctionType> local_u_update_;
+  DiscreteFunctionType range_;
+  std::unique_ptr<typename DiscreteFunctionType::LocalDiscreteFunctionType> local_range_;
+  const EntropyFluxType& analytical_flux_;
+  const XT::Common::Parameter& param_;
+}; // class LocalEntropicHessianInverter<...>
+
+template <class MomentBasisImp,
+          class SpaceImp,
+          SlopeLimiterType slope,
+          class MatrixType = typename XT::LA::Container<typename MomentBasisImp::RangeFieldType>::MatrixType>
+class EntropicHessianInverter
+  : public OperatorInterface<MatrixType, typename SpaceImp::GridViewType, MomentBasisImp::dimRange, 1>
+{
+  using BaseType = OperatorInterface<MatrixType, typename SpaceImp::GridViewType, MomentBasisImp::dimRange, 1>;
+
+public:
+  using typename BaseType::VectorType;
+  using MomentBasis = MomentBasisImp;
+  using SpaceType = SpaceImp;
+  using SourceSpaceType = SpaceImp;
+  using RangeSpaceType = SpaceImp;
+  using EntropyFluxType = EntropyBasedFluxEntropyCoordsFunction<typename SpaceType::GridViewType, MomentBasis, slope>;
+  using RangeFieldType = typename MomentBasis::RangeFieldType;
+
+  EntropicHessianInverter(const EntropyFluxType& analytical_flux, const SpaceType& space)
+    : analytical_flux_(analytical_flux)
+    , space_(space)
+  {}
+
+  bool linear() const override final
+  {
+    return false;
+  }
+
+  const SourceSpaceType& source_space() const override final
+  {
+    return space_;
+  }
+
+  const RangeSpaceType& range_space() const override final
+  {
+    return space_;
+  }
+
+  void apply(const VectorType& /*source*/,
+             VectorType& /*range*/,
+             const XT::Common::Parameter& /*param*/) const override final
+  {
+    DUNE_THROW(Dune::NotImplemented, "Use apply_inverse_hessian!");
+  } // void apply(...)
+
+  void apply_inverse_hessian(const VectorType& alpha,
+                             const VectorType& u_update,
+                             VectorType& alpha_update,
+                             const XT::Common::Parameter& param) const
+  {
+    LocalEntropicHessianInverter<SpaceType, VectorType, MomentBasis, slope> local_hessian_inverter(
+        space_, alpha, u_update, alpha_update, analytical_flux_, param);
+    auto walker = XT::Grid::Walker<typename SpaceType::GridViewType>(space_.grid_view());
+    walker.append(local_hessian_inverter);
+    walker.walk(true);
+  } // void apply(...)
+
+private:
+  const EntropyFluxType& analytical_flux_;
+  const SpaceType& space_;
+}; // class EntropicHessianInverter<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_MOMENTMODELS_HESSIANINVERTER_HH
diff --git a/dune/gdt/test/momentmodels/hyperbolic__momentmodels__entropic_coords_mn.cc b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__entropic_coords_mn.cc
new file mode 100644
index 0000000000000000000000000000000000000000..de05f5fd8cd5b786b94a646e17bca7894df20dd9
--- /dev/null
+++ b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__entropic_coords_mn.cc
@@ -0,0 +1,51 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
+// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+// Authors:
+//   Tobias Leibner  (2016)
+
+// This one has to come first (includes the config.h)!
+#include <dune/xt/common/test/main.hxx>
+
+#include <dune/gdt/test/momentmodels/kinetictransport/testcases.hh>
+#include <dune/gdt/test/momentmodels/entropic-coords-mn-discretization.hh>
+
+using Yasp1 = Dune::YaspGrid<1, Dune::EquidistantOffsetCoordinates<double, 1>>;
+using Yasp2 = Dune::YaspGrid<2, Dune::EquidistantOffsetCoordinates<double, 2>>;
+using Yasp3 = Dune::YaspGrid<3, Dune::EquidistantOffsetCoordinates<double, 3>>;
+
+using YaspGridTestCasesAll = testing::Types<
+    // The kinetic scheme does not work for Legendre Mn in the SourceBeam test
+    // Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, false, true>,
+    // Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 20>, true, true>
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, false, true>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, true, true>,
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, false, true>,
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, true, true>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, false, true>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, true, true>,
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, false, true>,
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, true, true>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, false, true>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, true, true>
+#if !DXT_DISABLE_LARGE_TESTS
+    ,
+    Dune::GDT::
+        PointSourceMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, false, true>,
+    Dune::GDT::
+        PointSourceMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, true, true>,
+    // Dune::GDT::ShadowMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 1, 1, 3>, true, true>,
+    Dune::GDT::CheckerboardMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, true, true>,
+    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, false, true>,
+    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, true, true>,
+    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::PartialMomentBasis<double, 3, double, 0, 1, 3>, false, true>,
+    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::PartialMomentBasis<double, 3, double, 0, 1, 3>, true, true>
+#endif // !DXT_DISABLE_LARGE_TESTS
+    >;
+
+TYPED_TEST_CASE(HyperbolicEntropicCoordsMnTest, YaspGridTestCasesAll);
+TYPED_TEST(HyperbolicEntropicCoordsMnTest, check)
+{
+  this->run();
+}
diff --git a/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_1dhatfunctions_analytic.cc b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_1dhatfunctions_analytic.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a5f3b0866d9dfac012fae39b97d13ba8e5604619
--- /dev/null
+++ b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_1dhatfunctions_analytic.cc
@@ -0,0 +1,30 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
+// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+// Authors:
+//   Tobias Leibner  (2016)
+
+#define ENTROPY_FLUX_1D_HATFUNCTIONS_USE_ANALYTICAL_INTEGRALS 1
+
+// This one has to come first (includes the config.h)!
+#include <dune/xt/common/test/main.hxx>
+
+#include <dune/gdt/test/momentmodels/kinetictransport/testcases.hh>
+#include <dune/gdt/test/momentmodels/mn-discretization.hh>
+
+using Yasp1 = Dune::YaspGrid<1, Dune::EquidistantOffsetCoordinates<double, 1>>;
+using Yasp2 = Dune::YaspGrid<2, Dune::EquidistantOffsetCoordinates<double, 2>>;
+using Yasp3 = Dune::YaspGrid<3, Dune::EquidistantOffsetCoordinates<double, 3>>;
+
+using YaspGridTestCases1dHatAnalytic = testing::Types<
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, false>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, false>,
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, true>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, true>>;
+
+TYPED_TEST_CASE(HyperbolicMnTest, YaspGridTestCases1dHatAnalytic);
+TYPED_TEST(HyperbolicMnTest, check)
+{
+  this->run(1e-3);
+}
diff --git a/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_boseeinstein.cc b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_boseeinstein.cc
new file mode 100644
index 0000000000000000000000000000000000000000..97e19ecb525e933d2cbc646857843feabb295bf6
--- /dev/null
+++ b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_boseeinstein.cc
@@ -0,0 +1,54 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
+// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+// Authors:
+//   Tobias Leibner  (2016)
+
+// This one has to come first (includes the config.h)!
+#include <dune/xt/common/test/main.hxx>
+
+#include <dune/gdt/test/momentmodels/kinetictransport/testcases.hh>
+#include <dune/gdt/test/momentmodels/mn-discretization.hh>
+
+using Yasp1 = Dune::YaspGrid<1, Dune::EquidistantOffsetCoordinates<double, 1>>;
+using Yasp2 = Dune::YaspGrid<2, Dune::EquidistantOffsetCoordinates<double, 2>>;
+using Yasp3 = Dune::YaspGrid<3, Dune::EquidistantOffsetCoordinates<double, 3>>;
+
+constexpr Dune::GDT::EntropyType entropy = Dune::GDT::EntropyType::BoseEinstein;
+
+using YaspGridTestCasesAll = testing::Types<
+#if HAVE_CLP
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7, 1, entropy>, false>,
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7, 1, entropy>, true>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7, 1, entropy>, false>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7, 1, entropy>, true>,
+#endif
+    Dune::GDT::
+        SourceBeamMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1, 1, entropy>, false>,
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1, 1, entropy>, true>,
+    Dune::GDT::
+        SourceBeamMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1, entropy>, false>,
+    Dune::GDT::
+        SourceBeamMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1, entropy>, true>,
+    Dune::GDT::
+        PointSourceMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3, entropy>, false>,
+    Dune::GDT::
+        PointSourceMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3, entropy>, true>
+#if HAVE_CLP
+    ,
+    Dune::GDT::PointSourceMnTestCase<Yasp3,
+                                     Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3, false, entropy>,
+                                     true>
+#endif
+#if HAVE_QHULL
+    ,
+    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::PartialMomentBasis<double, 3, double, 0, 1, 3, 1, entropy>, true>
+#endif
+    >;
+
+TYPED_TEST_CASE(HyperbolicMnTest, YaspGridTestCasesAll);
+TYPED_TEST(HyperbolicMnTest, check)
+{
+  this->run();
+}
diff --git a/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_nochangeofbasis.cc b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_nochangeofbasis.cc
new file mode 100644
index 0000000000000000000000000000000000000000..07c2ef64a55f041faaf6144c3d5a26a870d452b4
--- /dev/null
+++ b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_nochangeofbasis.cc
@@ -0,0 +1,39 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
+// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+// Authors:
+//   Tobias Leibner  (2016)
+
+#define ENTROPY_FLUX_UNSPECIALIZED_USE_ADAPTIVE_CHANGE_OF_BASIS 0
+
+// This one has to come first (includes the config.h)!
+#include <dune/xt/common/test/main.hxx>
+
+#include <dune/gdt/test/momentmodels/kinetictransport/testcases.hh>
+#include <dune/gdt/test/momentmodels/mn-discretization.hh>
+
+using Yasp1 = Dune::YaspGrid<1, Dune::EquidistantOffsetCoordinates<double, 1>>;
+using Yasp3 = Dune::YaspGrid<3, Dune::EquidistantOffsetCoordinates<double, 3>>;
+
+using YaspGridTestCasesNoBasisChange = testing::Types<
+#if HAVE_CLP
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, false>,
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, true>,
+#endif
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, false>,
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, true>
+#if !DXT_DISABLE_LARGE_TESTS
+    ,
+#  if HAVE_CLP
+    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, false>,
+#  endif
+    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, false>
+#endif
+    >;
+
+TYPED_TEST_CASE(HyperbolicMnTest, YaspGridTestCasesNoBasisChange);
+TYPED_TEST(HyperbolicMnTest, check)
+{
+  this->run();
+}
diff --git a/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_ord1.cc b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_ord1.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a7e727e77c5c0d9a3e4ec98225aaa0813860870a
--- /dev/null
+++ b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_ord1.cc
@@ -0,0 +1,48 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
+// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+// Authors:
+//   Tobias Leibner  (2016)
+
+// This one has to come first (includes the config.h)!
+#include <dune/xt/common/test/main.hxx>
+
+#define USE_LP_POSITIVITY_LIMITER 1
+
+#include <dune/gdt/test/momentmodels/kinetictransport/testcases.hh>
+#include <dune/gdt/test/momentmodels/mn-discretization.hh>
+
+using Yasp1 = Dune::YaspGrid<1, Dune::EquidistantOffsetCoordinates<double, 1>>;
+using Yasp2 = Dune::YaspGrid<2, Dune::EquidistantOffsetCoordinates<double, 2>>;
+using Yasp3 = Dune::YaspGrid<3, Dune::EquidistantOffsetCoordinates<double, 3>>;
+
+using YaspGridTestCasesOrd1 = testing::Types<
+#if HAVE_CLP
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, false>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, false>,
+#endif
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, false>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, false>,
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, false>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, false>
+#if !DXT_DISABLE_LARGE_TESTS
+    ,
+#  if HAVE_CLP
+    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, false>,
+    Dune::GDT::CheckerboardMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, false>,
+    Dune::GDT::ShadowMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, false>,
+#  endif
+    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, false>
+#  if HAVE_QHULL
+    ,
+    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::PartialMomentBasis<double, 3, double, 0, 1, 3>, false>
+#  endif
+#endif
+    >;
+
+TYPED_TEST_CASE(HyperbolicMnTest, YaspGridTestCasesOrd1);
+TYPED_TEST(HyperbolicMnTest, check)
+{
+  this->run();
+}
diff --git a/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_ord2.cc b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_ord2.cc
new file mode 100644
index 0000000000000000000000000000000000000000..70250ddeb73672d5c53b3b92953f28b130d0d60a
--- /dev/null
+++ b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__mn_ord2.cc
@@ -0,0 +1,50 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
+// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+// Authors:
+//   Tobias Leibner  (2016)
+
+// This one has to come first (includes the config.h)!
+#include <dune/xt/common/test/main.hxx>
+
+#define USE_LP_POSITIVITY_LIMITER 1
+
+#include <dune/gdt/test/momentmodels/kinetictransport/testcases.hh>
+#include <dune/gdt/test/momentmodels/mn-discretization.hh>
+
+using Yasp1 = Dune::YaspGrid<1, Dune::EquidistantOffsetCoordinates<double, 1>>;
+using Yasp2 = Dune::YaspGrid<2, Dune::EquidistantOffsetCoordinates<double, 2>>;
+using Yasp3 = Dune::YaspGrid<3, Dune::EquidistantOffsetCoordinates<double, 3>>;
+
+using YaspGridTestCasesOrd2 = testing::Types<
+#if HAVE_CLP
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, true>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, true>,
+#endif
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, true>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, true>,
+    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, true>,
+    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, true>
+#if !DXT_DISABLE_LARGE_TESTS
+#  if HAVE_CLP
+    ,
+    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, true>
+#  endif
+// Our shifted qr eigensolver fails for this problem, needs better shifting strategy
+#  if HAVE_MKL || HAVE_LAPACKE || HAVE_EIGEN
+    ,
+    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, true>
+#  endif
+#  if HAVE_QHULL
+    ,
+    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::PartialMomentBasis<double, 3, double, 0, 1, 3>, true>
+#  endif
+#endif
+    >;
+
+TYPED_TEST_CASE(HyperbolicMnTest, YaspGridTestCasesOrd2);
+TYPED_TEST(HyperbolicMnTest, check)
+{
+  this->run();
+}
diff --git a/dune/gdt/test/momentmodels/hyperbolic__momentmodels__pn.cc b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__pn.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b90d55bddc6b6c88c8f0f2c7aa8df1f7fcc4f5fb
--- /dev/null
+++ b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__pn.cc
@@ -0,0 +1,31 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
+// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+// Authors:
+//   Tobias Leibner  (2016)
+
+// This one has to come first (includes the config.h)!
+#include <dune/xt/common/test/main.hxx>
+
+#define USE_FULL_LINEAR_RECONSTRUCTION_OPERATOR 1
+#include <dune/gdt/test/momentmodels/hyperbolic_momentmodels_pn_base.hh>
+#include <dune/gdt/test/momentmodels/pn-discretization.hh>
+
+TYPED_TEST_CASE(HyperbolicPnTest, YaspGridTestCasesWithoutReconstruction);
+TYPED_TEST(HyperbolicPnTest, check)
+{
+  this->run();
+}
+
+template <class TestCaseType>
+struct HyperbolicPnTestWithReconstruction : public HyperbolicPnTest<TestCaseType>
+{};
+
+TYPED_TEST_CASE(HyperbolicPnTestWithReconstruction, YaspGridTestCasesWithReconstruction);
+TYPED_TEST(HyperbolicPnTestWithReconstruction, check_with_full_linear_reconstruction)
+{
+  // evaluation of reconstructed function is not thread-safe at the moment
+  DXTC_CONFIG["threading.max_count"] = "1";
+  this->run();
+}
diff --git a/dune/gdt/test/momentmodels/hyperbolic__momentmodels__pn__pointwise_reconstruction.cc b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__pn__pointwise_reconstruction.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0ffe18e93681efc9de772569e8b249135071bbd3
--- /dev/null
+++ b/dune/gdt/test/momentmodels/hyperbolic__momentmodels__pn__pointwise_reconstruction.cc
@@ -0,0 +1,19 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
+// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+// Authors:
+//   Tobias Leibner  (2016)
+
+// This one has to come first (includes the config.h)!
+#include <dune/xt/common/test/main.hxx>
+
+#define USE_FULL_LINEAR_RECONSTRUCTION_OPERATOR 0
+#include <dune/gdt/test/momentmodels/hyperbolic_momentmodels_pn_base.hh>
+#include <dune/gdt/test/momentmodels/pn-discretization.hh>
+
+TYPED_TEST_CASE(HyperbolicPnTest, YaspGridTestCasesWithReconstruction);
+TYPED_TEST(HyperbolicPnTest, check_with_pointwise_linear_reconstruction)
+{
+  this->run();
+}
diff --git a/dune/gdt/test/momentmodels/hyperbolic_momentmodels_pn_base.hh b/dune/gdt/test/momentmodels/hyperbolic_momentmodels_pn_base.hh
new file mode 100644
index 0000000000000000000000000000000000000000..84ba22d199fa4353fbb22ebc89629aa1681d7786
--- /dev/null
+++ b/dune/gdt/test/momentmodels/hyperbolic_momentmodels_pn_base.hh
@@ -0,0 +1,53 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
+// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+// Authors:
+//   Tobias Leibner  (2016)
+
+#ifndef DUNE_GDT_TEST_HYPERBOLIC_MOMENTMODELS_PN_HH
+#define DUNE_GDT_TEST_HYPERBOLIC_MOMENTMODELS_PN_HH
+
+#include <dune/xt/common/test/gtest/gtest.h>
+
+#include <dune/gdt/test/momentmodels/kinetictransport/testcases.hh>
+
+using Yasp1 = Dune::YaspGrid<1, Dune::EquidistantOffsetCoordinates<double, 1>>;
+using Yasp2 = Dune::YaspGrid<2, Dune::EquidistantOffsetCoordinates<double, 2>>;
+using Yasp3 = Dune::YaspGrid<3, Dune::EquidistantOffsetCoordinates<double, 3>>;
+
+using YaspGridTestCasesWithoutReconstruction = testing::Types<
+    Dune::GDT::SourceBeamPnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, false>,
+    Dune::GDT::PlaneSourcePnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, false>,
+    Dune::GDT::SourceBeamPnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, false>,
+    Dune::GDT::PlaneSourcePnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, false>,
+    Dune::GDT::SourceBeamPnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, false>,
+    Dune::GDT::PlaneSourcePnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, false>,
+    Dune::GDT::PointSourcePnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, false>,
+    Dune::GDT::CheckerboardPnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, false>,
+    Dune::GDT::ShadowPnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, false>
+#if !DXT_DISABLE_LARGE_TESTS
+    ,
+    Dune::GDT::PointSourcePnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, false>,
+    Dune::GDT::PointSourcePnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 1, 1, 3>, false>,
+    Dune::GDT::PointSourcePnTestCase<Yasp3, Dune::GDT::PartialMomentBasis<double, 3, double, 0, 1, 3>, false>,
+    Dune::GDT::PointSourcePnTestCase<Yasp3, Dune::GDT::PartialMomentBasis<double, 3, double, 1, 1, 3>, false>
+#endif
+    >;
+
+using YaspGridTestCasesWithReconstruction = testing::Types<
+    Dune::GDT::SourceBeamPnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, true>,
+    Dune::GDT::PlaneSourcePnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, true>,
+    Dune::GDT::SourceBeamPnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, true>,
+    Dune::GDT::PlaneSourcePnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, true>,
+    Dune::GDT::SourceBeamPnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, true>,
+    Dune::GDT::PlaneSourcePnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, true>
+#if !DXT_DISABLE_LARGE_TESTS
+    ,
+    Dune::GDT::PointSourcePnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, true>,
+    Dune::GDT::PointSourcePnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, true>,
+    Dune::GDT::PointSourcePnTestCase<Yasp3, Dune::GDT::PartialMomentBasis<double, 3, double, 0, 1, 3>, true>
+#endif
+    >;
+
+#endif // DUNE_GDT_TEST_HYPERBOLIC_MOMENTMODELS_PN_HH
diff --git a/dune/gdt/test/momentmodels/kineticequation.hh b/dune/gdt/test/momentmodels/kineticequation.hh
new file mode 100644
index 0000000000000000000000000000000000000000..d9ff3ff2fc27498b802a63f41c530e531dc8cf5c
--- /dev/null
+++ b/dune/gdt/test/momentmodels/kineticequation.hh
@@ -0,0 +1,106 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2017 - 2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_KINETICEQUATION_HH
+#define DUNE_GDT_HYPERBOLIC_PROBLEMS_KINETICEQUATION_HH
+
+#include <dune/xt/grid/gridprovider.hh>
+
+#include <dune/xt/functions/generic/flux-function.hh>
+#include <dune/xt/functions/generic/function.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+template <class E, class MomentBasisImp>
+class KineticEquationInterface
+{
+  using ThisType = KineticEquationInterface;
+
+public:
+  using MomentBasis = MomentBasisImp;
+  using DomainFieldType = typename MomentBasis::DomainFieldType;
+  using RangeFieldType = typename MomentBasis::RangeFieldType;
+  static const size_t dimDomain = MomentBasis::dimDomain;
+  static const size_t dimRange = MomentBasis::dimRange;
+  static const size_t dimRangeCols = MomentBasis::dimRangeCols;
+  static const size_t dimFlux = MomentBasis::dimFlux;
+  using FluxType = XT::Functions::FluxFunctionInterface<E, dimRange, dimFlux, dimRange, RangeFieldType>;
+  using GenericFluxFunctionType = XT::Functions::GenericFluxFunction<E, dimRange, dimFlux, dimRange, RangeFieldType>;
+  using InitialValueType = XT::Functions::FunctionInterface<dimFlux, dimRange, 1, RangeFieldType>;
+  using GenericFunctionType = XT::Functions::GenericFunction<dimFlux, dimRange, 1, RangeFieldType>;
+  using ScalarFunctionType = XT::Functions::FunctionInterface<dimFlux, 1, 1, RangeFieldType>;
+  using BoundaryValueType = InitialValueType;
+  using MatrixType = typename Dune::DynamicMatrix<RangeFieldType>;
+  using DomainType = typename InitialValueType::DomainType;
+  using BasisDomainType = typename MomentBasis::DomainType;
+  using StateType = typename FluxType::StateType;
+  using RangeReturnType = typename InitialValueType::RangeReturnType;
+  using DynamicRangeType = Dune::DynamicVector<RangeFieldType>;
+
+  KineticEquationInterface(const MomentBasis& basis_functions)
+    : basis_functions_(basis_functions)
+  {}
+
+  virtual ~KineticEquationInterface() {}
+
+  static XT::Common::Configuration default_grid_cfg()
+  {
+    XT::Common::Configuration grid_config;
+    grid_config["type"] = XT::Grid::cube_gridprovider_default_config()["type"];
+    grid_config["lower_left"] = "[0.0]";
+    grid_config["upper_right"] = "[1.0]";
+    grid_config["num_elements"] = "[100]";
+    grid_config["overlap_size"] = "[1]";
+    return grid_config;
+  }
+
+  static XT::Common::Configuration default_boundary_cfg()
+  {
+    XT::Common::Configuration boundary_config;
+    boundary_config["type"] = "dirichlet";
+    return boundary_config;
+  }
+
+  virtual std::unique_ptr<FluxType> flux() const = 0;
+
+  virtual std::unique_ptr<InitialValueType> initial_values() const = 0;
+
+  virtual std::unique_ptr<BoundaryValueType> boundary_values() const = 0;
+
+  virtual RangeFieldType CFL() const = 0;
+
+  virtual RangeFieldType t_end() const = 0;
+
+  virtual std::unique_ptr<ScalarFunctionType> sigma_a() const = 0;
+
+  virtual std::unique_ptr<ScalarFunctionType> sigma_s() const = 0;
+
+  virtual std::unique_ptr<ScalarFunctionType> Q() const = 0;
+
+  virtual XT::Common::Configuration grid_config() const = 0;
+
+  virtual XT::Common::Configuration boundary_config() const = 0;
+
+  static std::string static_id()
+  {
+    return "kineticequationinterface";
+  }
+
+protected:
+  const MomentBasis& basis_functions_;
+}; // class KineticEquationInterface<E, ...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_KINETICEQUATION_HH
diff --git a/dune/gdt/test/momentmodels/kinetictransport.hh b/dune/gdt/test/momentmodels/kinetictransport.hh
new file mode 100644
index 0000000000000000000000000000000000000000..e6569ae9b6e27ac3806f34f13a7fd26b372bc0f6
--- /dev/null
+++ b/dune/gdt/test/momentmodels/kinetictransport.hh
@@ -0,0 +1,21 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2017 - 2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_KINETICTRANSPORT_HH
+#define DUNE_GDT_HYPERBOLIC_PROBLEMS_KINETICTRANSPORT_HH
+
+#include "kinetictransport/base.hh"
+#include "kinetictransport/checkerboard.hh"
+#include "kinetictransport/planesource.hh"
+#include "kinetictransport/pointsource.hh"
+#include "kinetictransport/shadow.hh"
+#include "kinetictransport/sourcebeam.hh"
+
+#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_KINETICTRANSPORT_HH
diff --git a/dune/gdt/test/momentmodels/kinetictransport/base.hh b/dune/gdt/test/momentmodels/kinetictransport/base.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c9705f94278d971d8877fdb332d2d2d35afe3073
--- /dev/null
+++ b/dune/gdt/test/momentmodels/kinetictransport/base.hh
@@ -0,0 +1,251 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_KINETICTRANSPORTEQUATION_BASE_HH
+#define DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_KINETICTRANSPORTEQUATION_BASE_HH
+
+#include <dune/grid/common/partitionset.hh>
+
+#include <dune/xt/functions/constant.hh>
+
+#include <dune/xt/la/solver.hh>
+
+#include <dune/gdt/test/momentmodels/basisfunctions.hh>
+#include <dune/gdt/test/momentmodels/entropyflux.hh>
+#include <dune/gdt/test/momentmodels/kineticequation.hh>
+
+#include "../kineticequation.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+template <class E, class MomentBasisImp>
+class KineticTransportEquationBase : public KineticEquationInterface<E, MomentBasisImp>
+{
+  using ThisType = KineticTransportEquationBase;
+  using BaseType = KineticEquationInterface<E, MomentBasisImp>;
+
+public:
+  using BaseType::dimDomain;
+  using BaseType::dimFlux;
+  using BaseType::dimRange;
+  using BaseType::dimRangeCols;
+  using typename BaseType::BasisDomainType;
+  using typename BaseType::DomainFieldType;
+  using typename BaseType::DomainType;
+  using typename BaseType::GenericFluxFunctionType;
+  using typename BaseType::GenericFunctionType;
+  using typename BaseType::MatrixType;
+  using typename BaseType::MomentBasis;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::RangeReturnType;
+  using typename BaseType::StateType;
+
+  using typename BaseType::BoundaryValueType;
+  using typename BaseType::FluxType;
+  using typename BaseType::InitialValueType;
+  using FluxRangeType = typename FluxType::LocalFunctionType::RangeReturnType;
+  using DynamicFluxRangeType = typename FluxType::LocalFunctionType::DynamicRangeType;
+  using FluxJacobianRangeType = typename FluxType::LocalFunctionType::JacobianRangeReturnType;
+  using DynamicFluxJacobianRangeType = typename FluxType::LocalFunctionType::DynamicJacobianRangeType;
+  using GenericScalarFunctionType = XT::Functions::GenericFunction<dimFlux, 1, 1, RangeFieldType>;
+  using ConstantScalarFunctionType = XT::Functions::ConstantFunction<dimFlux, 1, 1, RangeFieldType>;
+  using BoundaryDistributionType =
+      std::function<std::function<RangeFieldType(const BasisDomainType&)>(const DomainType&)>;
+
+  using BaseType::default_boundary_cfg;
+  using BaseType::default_grid_cfg;
+
+  KineticTransportEquationBase(const MomentBasis& basis_functions,
+                               const XT::Common::Configuration& grid_cfg = default_grid_cfg(),
+                               const XT::Common::Configuration& boundary_cfg = default_boundary_cfg(),
+                               const RangeFieldType psi_vac = 5e-9)
+    : BaseType(basis_functions)
+    , grid_cfg_(grid_cfg)
+    , boundary_cfg_(boundary_cfg)
+    , psi_vac_(psi_vac)
+  {}
+
+  template <class VectorType>
+  void solve(const MatrixType& mat,
+             VectorType& x,
+             const VectorType& rhs,
+             const MomentBasisInterface<DomainFieldType,
+                                        MomentBasis::dimDomain,
+                                        RangeFieldType,
+                                        dimRange,
+                                        dimRangeCols,
+                                        dimFlux,
+                                        MomentBasis::entropy>&) const
+  {
+    // copy to CommonDenseMatrix as the FieldMatrix copies itself on the stack during solve which may case a
+    // stackoverflow for large matrices
+    XT::LA::CommonDenseMatrix<RangeFieldType> xt_la_mat(mat);
+    XT::LA::CommonDenseVector<RangeFieldType> xt_la_rhs(rhs, 0);
+    XT::LA::CommonDenseVector<RangeFieldType> xt_la_x(rhs.size());
+    XT::LA::solve(xt_la_mat, xt_la_rhs, xt_la_x);
+    for (size_t ii = 0; ii < xt_la_x.size(); ++ii)
+      x[ii] = xt_la_x[ii];
+  }
+
+  template <class VectorType, size_t refinements>
+  void solve(const MatrixType& mat,
+             VectorType& x,
+             const VectorType& rhs,
+             const PartialMomentBasis<DomainFieldType, 3, RangeFieldType, refinements, dimRangeCols, 3, 1>&) const
+  {
+    const size_t num_blocks = dimRange / 4;
+    const size_t block_size = 4;
+    XT::Common::FieldMatrix<RangeFieldType, block_size, block_size> local_mat;
+    XT::Common::FieldVector<RangeFieldType, block_size> local_x, local_rhs;
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      const size_t offset = jj * block_size;
+      // copy to local matrix and vector
+      for (size_t rr = 0; rr < block_size; ++rr) {
+        local_rhs[rr] = rhs[offset + rr];
+        for (size_t cc = 0; cc < block_size; ++cc)
+          local_mat[rr][cc] = mat[offset + rr][offset + cc];
+      } // rr
+      local_mat.solve(local_x, local_rhs);
+      for (size_t rr = 0; rr < block_size; ++rr)
+        x[offset + rr] = local_x[rr];
+    } // jj
+  }
+
+  // flux matrix A = B M^{-1} with B_{ij} = <v h_i h_j>
+  std::unique_ptr<FluxType> flux() const override
+  {
+    // calculate B row-wise by solving M^{T} A^T = B^T column-wise
+    auto A = basis_functions_.flux_matrix();
+    const auto M_T = basis_functions_.mass_matrix(); // mass matrix is symmetric
+    // solve
+    DynamicVector<RangeFieldType> tmp_row(M_T.N(), 0.);
+    for (size_t dd = 0; dd < dimFlux; ++dd) {
+      for (size_t ii = 0; ii < M_T.N(); ++ii) {
+        solve(M_T, tmp_row, A[dd][ii], basis_functions_);
+        A[dd][ii] = tmp_row;
+      }
+    }
+    auto order_func = [](const XT::Common::Parameter&) -> int { return 1; };
+    DynamicVector<XT::LA::CommonDenseMatrix<RangeFieldType>> A_la(dimFlux);
+    for (size_t dd = 0; dd < dimFlux; ++dd)
+      A_la[dd] = A[dd];
+    auto eval_func =
+        [A_la](const DomainType&, const StateType& u, DynamicFluxRangeType& ret, const XT::Common::Parameter&) {
+          for (size_t dd = 0; dd < dimFlux; ++dd) {
+            auto row_view = ret[dd];
+            A_la[dd].mv(u, row_view);
+          }
+        };
+    auto jacobian_func =
+        [A_la](const DomainType&, const StateType&, DynamicFluxJacobianRangeType& ret, const XT::Common::Parameter&) {
+          for (size_t dd = 0; dd < dimFlux; ++dd)
+            ret[dd] = A_la[dd];
+        };
+    return std::make_unique<GenericFluxFunctionType>(order_func,
+                                                     GenericFluxFunctionType::default_post_bind_function(),
+                                                     eval_func,
+                                                     XT::Common::ParameterType{},
+                                                     "flux",
+                                                     jacobian_func);
+  }
+
+  // Initial value of the kinetic equation is a constant vacuum concentration psi_vac.
+  // Thus, the initial value of the n-th moment is basis_integrated * psi_vac.
+  std::unique_ptr<InitialValueType> initial_values() const override
+  {
+    RangeReturnType value = basis_functions_.integrated() * psi_vac_;
+    return std::make_unique<GenericFunctionType>(
+        [](const XT::Common::Parameter&) { return 0; },
+        [value](const DomainType&, const XT::Common::Parameter&) { return value; });
+  } // ... initial_values()
+
+  // Use a constant vacuum concentration basis_integrated * psi_vac as default boundary value
+  std::unique_ptr<BoundaryValueType> boundary_values() const override
+  {
+    RangeReturnType value = basis_functions_.integrated() * psi_vac_;
+    return std::make_unique<GenericFunctionType>(
+        [](const XT::Common::Parameter&) { return 0; },
+        [=](const DomainType&, const XT::Common::Parameter&) { return value; });
+  } // ... boundary_values()
+
+  virtual BoundaryDistributionType boundary_distribution() const
+  {
+    return [this](const DomainType&) { return [this](const BasisDomainType&) { return this->psi_vac_; }; };
+  }
+
+  RangeReturnType
+  kinetic_boundary_flux_from_quadrature(const DomainType& x, const RangeFieldType& n, const size_t dd) const
+  {
+    RangeReturnType ret(0.);
+    const auto boundary_density = boundary_distribution()(x);
+    const auto& quadratures = basis_functions_.quadratures();
+    for (size_t jj = 0; jj < quadratures.size(); ++jj) {
+      for (size_t ll = 0; ll < quadratures[jj].size(); ++ll) {
+        const auto v = quadratures[jj][ll].position();
+        const auto b = basis_functions_.evaluate(v, jj);
+        if (v[dd] * n < 0) {
+          const RangeFieldType psi = boundary_density(v);
+          ret += b * psi * v[dd] * quadratures[jj][ll].weight();
+        }
+      } // ll
+    } // jj
+    return ret;
+  }
+
+  virtual RangeReturnType kinetic_boundary_flux(const DomainType& x, const RangeFieldType& n, const size_t dd) const
+  {
+    return kinetic_boundary_flux_from_quadrature(x, n, dd);
+  }
+
+  RangeFieldType CFL() const override
+  {
+    return 0.49;
+  }
+
+  RangeFieldType t_end() const override
+  {
+    return 1.;
+  }
+
+  XT::Common::Configuration grid_config() const override
+  {
+    return grid_cfg_;
+  }
+
+  XT::Common::Configuration boundary_config() const override
+  {
+    return boundary_cfg_;
+  }
+
+  static std::string static_id()
+  {
+    return "kinetictransportequation";
+  }
+
+  virtual const RangeFieldType psi_vac() const
+  {
+    return psi_vac_;
+  }
+
+protected:
+  using BaseType::basis_functions_;
+  const XT::Common::Configuration grid_cfg_;
+  const XT::Common::Configuration boundary_cfg_;
+  const RangeFieldType psi_vac_;
+  XT::Common::ParameterType parameter_type_;
+}; // class KineticTransportEquation<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_KINETICTRANSPORTEQUATION_BASE_HH
diff --git a/dune/gdt/test/momentmodels/kinetictransport/checkerboard.hh b/dune/gdt/test/momentmodels/kinetictransport/checkerboard.hh
new file mode 100644
index 0000000000000000000000000000000000000000..3d8bbf3a7ea7b31c809ece44d4393bdaeace339c
--- /dev/null
+++ b/dune/gdt/test/momentmodels/kinetictransport/checkerboard.hh
@@ -0,0 +1,188 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 - 2017)
+//   Rene Milk       (2016 - 2018)
+//   Tobias Leibner  (2016 - 2017)
+
+#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_KINETICTRANSPORT_CHECKERBOARD_HH
+#define DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_KINETICTRANSPORT_CHECKERBOARD_HH
+
+#include "base.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+/**
+ * In 2D, this is the Testcase for the Boltzmann equation in two dimensions,
+ * see Section 4.2 of Brunner, Holloway, "Two-dimensional time dependent Riemann solvers for neutron transport", Journal
+ * of Computational Physics, Volume 210, Issue 1, 2005
+ * http://dx.doi.org/10.1016/j.jcp.2005.04.011
+ * The 3D version is a straightforward generalization of the setup to 3 dimensions.
+ * */
+template <class E, class MomentBasisImp>
+class CheckerboardPn : public KineticTransportEquationBase<E, MomentBasisImp>
+{
+  using BaseType = KineticTransportEquationBase<E, MomentBasisImp>;
+
+public:
+  using BaseType::dimDomain;
+  using typename BaseType::BoundaryValueType;
+  using typename BaseType::DomainType;
+  using typename BaseType::GenericScalarFunctionType;
+  using typename BaseType::MomentBasis;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::ScalarFunctionType;
+
+  using BaseType::default_boundary_cfg;
+
+  CheckerboardPn(const MomentBasis& basis_functions,
+                 const XT::Common::Configuration& grid_cfg = default_grid_cfg(),
+                 const XT::Common::Configuration& boundary_cfg = default_boundary_cfg())
+    : BaseType(basis_functions, grid_cfg, boundary_cfg, 1e-8 / (4 * M_PI))
+  {}
+
+  static std::string static_id()
+  {
+    size_t domainDim = dimDomain; // avoid linker error for dimDomain
+    return "checkerboard" + XT::Common::to_string(domainDim) + "d_pn";
+  }
+
+  static XT::Common::Configuration default_grid_cfg()
+  {
+    XT::Common::Configuration grid_config;
+    grid_config["type"] = XT::Grid::cube_gridprovider_default_config()["type"];
+    grid_config["lower_left"] = "[0.0 0.0 0.0]";
+    grid_config["upper_right"] = "[7.0 7.0 7.0]";
+    grid_config["num_elements"] = "[14 14 14]";
+    grid_config["overlap_size"] = "[1 1 1]";
+    return grid_config;
+  }
+
+  RangeFieldType t_end() const override
+  {
+    return 3.2;
+  }
+
+  // Q = 0 except in the center where Q = 1. sigma_s = sigma_t = 1 in scattering regions, sigma_s = 0, sigma_t
+  // = 10 in absorbing regions. Center is also a scattering region.
+  std::unique_ptr<ScalarFunctionType> sigma_a() const override
+  {
+    return create_parameter_function(10., 0., 0.);
+  }
+
+  std::unique_ptr<ScalarFunctionType> sigma_s() const override
+  {
+    return create_parameter_function(0., 1., 1.);
+  }
+
+  std::unique_ptr<ScalarFunctionType> Q() const override
+  {
+    return create_parameter_function(0., 0., 1. / std::sqrt(4. * M_PI));
+  }
+
+  std::unique_ptr<ScalarFunctionType> create_parameter_function(const RangeFieldType value_on_absorbing_parts,
+                                                                const RangeFieldType value_on_scattering_parts,
+                                                                const RangeFieldType value_on_center) const
+  {
+    return std::make_unique<GenericScalarFunctionType>([](const XT::Common::Parameter&) { return 0; },
+                                                       [=](const DomainType& x, const XT::Common::Parameter&) {
+                                                         if (is_center(x))
+                                                           return value_on_center;
+                                                         else if (is_absorbing(x))
+                                                           return value_on_absorbing_parts;
+                                                         else
+                                                           return value_on_scattering_parts;
+                                                       });
+  }
+
+protected:
+  static bool is_absorbing(const FieldVector<RangeFieldType, 2>& x)
+  {
+    size_t row = XT::Common::numeric_cast<size_t>(std::floor(x[1]));
+    if (row == 7)
+      row = 6;
+    size_t col = XT::Common::numeric_cast<size_t>(std::floor(x[0]));
+    if (col == 7)
+      col = 6;
+    assert(row < 7 && col < 7);
+    return (row == 1 && col % 2 == 1) || ((row == 2 || row == 4) && (col == 2 || col == 4))
+           || ((row == 3 || row == 5) && (col == 1 || col == 5));
+  }
+
+  static bool is_absorbing(const FieldVector<RangeFieldType, 3>& x)
+  {
+    size_t plane = XT::Common::numeric_cast<size_t>(std::floor(x[2]));
+    size_t col = XT::Common::numeric_cast<size_t>(std::floor(x[0]));
+    size_t row = XT::Common::numeric_cast<size_t>(std::floor(x[1]));
+    assert(plane <= 7 && row <= 7 && col <= 7);
+    if (plane == 0 || plane >= 6)
+      return false;
+    if (row == 0 || row >= 6)
+      return false;
+    if (col == 0 || col >= 6)
+      return false;
+    return (plane + row + col) % 2 == 1 && !is_center(x) && !(plane == 3 && row == 5 && col == 3);
+  }
+
+  static bool is_center(const FieldVector<RangeFieldType, 2>& x)
+  {
+    size_t row = XT::Common::numeric_cast<size_t>(std::floor(x[1]));
+    size_t col = XT::Common::numeric_cast<size_t>(std::floor(x[0]));
+    return row == 3 && col == 3;
+  }
+
+  static bool is_center(const FieldVector<RangeFieldType, 3>& x)
+  {
+    size_t plane = XT::Common::numeric_cast<size_t>(std::floor(x[2]));
+    size_t row = XT::Common::numeric_cast<size_t>(std::floor(x[1]));
+    size_t col = XT::Common::numeric_cast<size_t>(std::floor(x[0]));
+    return plane == 3 && row == 3 && col == 3;
+  }
+}; // class CheckerboardPn<...>
+
+template <class GV, class MomentBasis>
+class CheckerboardMn : public CheckerboardPn<XT::Grid::extract_entity_t<GV>, MomentBasis>
+{
+  using BaseType = CheckerboardPn<XT::Grid::extract_entity_t<GV>, MomentBasis>;
+
+public:
+  using typename BaseType::FluxType;
+  using ActualFluxType = EntropyBasedFluxFunction<GV, MomentBasis>;
+
+  using BaseType::default_boundary_cfg;
+  using BaseType::default_grid_cfg;
+
+  CheckerboardMn(const MomentBasis& basis_functions,
+                 const GV& grid_view,
+                 const XT::Common::Configuration& grid_cfg = default_grid_cfg(),
+                 const XT::Common::Configuration& boundary_cfg = default_boundary_cfg())
+    : BaseType(basis_functions, grid_cfg, boundary_cfg)
+    , grid_view_(grid_view)
+  {}
+
+  static std::string static_id()
+  {
+    return "checkerboardmn";
+  }
+
+  std::unique_ptr<FluxType> flux() const override
+  {
+    return std::make_unique<ActualFluxType>(grid_view_, basis_functions_);
+  }
+
+protected:
+  using BaseType::basis_functions_;
+  const GV& grid_view_;
+}; // class CheckerboardMn<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_KINETICTRANSPORT_CHECKERBOARD_HH
diff --git a/dune/gdt/test/momentmodels/kinetictransport/planesource.hh b/dune/gdt/test/momentmodels/kinetictransport/planesource.hh
new file mode 100644
index 0000000000000000000000000000000000000000..01906d8e43f53de2a98da736734bd35cc72f4377
--- /dev/null
+++ b/dune/gdt/test/momentmodels/kinetictransport/planesource.hh
@@ -0,0 +1,153 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_PLANESOURCE_HH
+#define DUNE_GDT_HYPERBOLIC_PROBLEMS_PLANESOURCE_HH
+
+#include "base.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+template <class E, class MomentBasisImp>
+class PlaneSourcePn : public KineticTransportEquationBase<E, MomentBasisImp>
+{
+  using BaseType = KineticTransportEquationBase<E, MomentBasisImp>;
+
+public:
+  using BaseType::default_boundary_cfg;
+  using BaseType::dimDomain;
+  using BaseType::dimRange;
+  using typename BaseType::ConstantScalarFunctionType;
+  using typename BaseType::DomainType;
+  using typename BaseType::GenericFunctionType;
+  using typename BaseType::InitialValueType;
+  using typename BaseType::MomentBasis;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::RangeReturnType;
+  using typename BaseType::ScalarFunctionType;
+
+  PlaneSourcePn(const MomentBasis& basis_functions,
+                const XT::Common::Configuration& grid_cfg = default_grid_cfg(),
+                const XT::Common::Configuration& boundary_cfg = default_boundary_cfg())
+    : BaseType(basis_functions, grid_cfg, boundary_cfg)
+  {}
+
+  static std::string static_id()
+  {
+    return "planesourcepn";
+  }
+
+  static XT::Common::Configuration default_grid_cfg()
+  {
+    XT::Common::Configuration grid_config;
+    grid_config["type"] = XT::Grid::cube_gridprovider_default_config()["type"];
+    grid_config["lower_left"] = "[-1.2]";
+    grid_config["upper_right"] = "[1.2]";
+    grid_config["num_elements"] = "[240]";
+    grid_config["overlap_size"] = "[1]";
+    return grid_config;
+  }
+
+  // Initial value of the kinetic equation is psi_vac + delta(x).
+  // Thus the initial value for the n-th moment is base_integrated_n * (psi_vac + delta(x))
+  std::unique_ptr<InitialValueType> initial_values() const override
+  {
+    const DomainType lower_left = XT::Common::from_string<DomainType>(grid_cfg_["lower_left"]);
+    const DomainType upper_right = XT::Common::from_string<DomainType>(grid_cfg_["upper_right"]);
+    const size_t num_elements = XT::Common::from_string<std::vector<size_t>>(grid_cfg_["num_elements"])[0];
+    const RangeFieldType len_domain = upper_right[0] - lower_left[0];
+    const RangeFieldType vol_entity = len_domain / num_elements;
+    RangeReturnType basis_integrated = basis_functions_.integrated();
+    const RangeFieldType domain_center = lower_left[0] + len_domain / 2;
+
+    // approximate delta function by constant value of 1/(2*vol_entity) on cells on both side of 0.
+    const auto eval_func = [=](const DomainType& x, const XT::Common::Parameter&) {
+      auto ret = basis_integrated;
+      if (XT::Common::FloatCmp::ge(x[0], domain_center - vol_entity)
+          && XT::Common::FloatCmp::le(x[0], domain_center + vol_entity))
+        ret *= psi_vac_ + 1. / (2. * vol_entity);
+      else
+        ret *= psi_vac_;
+      return ret;
+    };
+    return std::make_unique<GenericFunctionType>(0, eval_func);
+  } // ... initial_values()
+
+  RangeFieldType t_end() const override
+  {
+    return 1.0;
+  }
+
+  std::unique_ptr<ScalarFunctionType> sigma_a() const override
+  {
+    return std::make_unique<ConstantScalarFunctionType>(0.);
+  }
+
+  std::unique_ptr<ScalarFunctionType> sigma_s() const override
+  {
+    return std::make_unique<ConstantScalarFunctionType>(1.);
+  }
+
+  std::unique_ptr<ScalarFunctionType> Q() const override
+  {
+    return std::make_unique<ConstantScalarFunctionType>(0.);
+  }
+
+protected:
+  using BaseType::basis_functions_;
+  using BaseType::grid_cfg_;
+  using BaseType::psi_vac_;
+}; // class PlaneSourcePn<...>
+
+
+template <class GV, class MomentBasis>
+class PlaneSourceMn : public PlaneSourcePn<XT::Grid::extract_entity_t<GV>, MomentBasis>
+{
+  using BaseType = PlaneSourcePn<XT::Grid::extract_entity_t<GV>, MomentBasis>;
+  using ThisType = PlaneSourceMn;
+
+public:
+  using typename BaseType::FluxType;
+  using typename BaseType::RangeReturnType;
+  using ActualFluxType = EntropyBasedFluxFunction<GV, MomentBasis>;
+
+  using BaseType::default_boundary_cfg;
+  using BaseType::default_grid_cfg;
+
+  PlaneSourceMn(const MomentBasis& basis_functions,
+                const GV& grid_view,
+                const XT::Common::Configuration& grid_cfg = default_grid_cfg(),
+                const XT::Common::Configuration& boundary_cfg = default_boundary_cfg())
+    : BaseType(basis_functions, grid_cfg, boundary_cfg)
+    , grid_view_(grid_view)
+  {}
+
+  static std::string static_id()
+  {
+    return "planesourcemn";
+  }
+
+  std::unique_ptr<FluxType> flux() const override
+  {
+    return std::make_unique<ActualFluxType>(grid_view_, basis_functions_);
+  }
+
+protected:
+  using BaseType::basis_functions_;
+  const GV& grid_view_;
+}; // class PlaneSourceMn<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_PLANESOURCE_HH
diff --git a/dune/gdt/test/momentmodels/kinetictransport/pointsource.hh b/dune/gdt/test/momentmodels/kinetictransport/pointsource.hh
new file mode 100644
index 0000000000000000000000000000000000000000..132bf1f7600c1b909124a82f206ca6b7204437e8
--- /dev/null
+++ b/dune/gdt/test/momentmodels/kinetictransport/pointsource.hh
@@ -0,0 +1,142 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 (2017)
+//   Rene Milk       (2017 - 2018)
+//   Tobias Leibner  (2017)
+
+#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_POINTSOURCE_HH
+#define DUNE_GDT_HYPERBOLIC_PROBLEMS_POINTSOURCE_HH
+
+#include "base.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+template <class E, class MomentBasisImp>
+class PointSourcePn : public KineticTransportEquationBase<E, MomentBasisImp>
+{
+  using BaseType = KineticTransportEquationBase<E, MomentBasisImp>;
+
+public:
+  using BaseType::dimDomain;
+  using typename BaseType::ConstantScalarFunctionType;
+  using typename BaseType::DomainType;
+  using typename BaseType::GenericFunctionType;
+  using typename BaseType::InitialValueType;
+  using typename BaseType::MomentBasis;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::RangeReturnType;
+  using typename BaseType::ScalarFunctionType;
+
+  using BaseType::default_boundary_cfg;
+
+  PointSourcePn(const MomentBasis& basis_functions,
+                const XT::Common::Configuration& grid_cfg = default_grid_cfg(),
+                const XT::Common::Configuration& boundary_cfg = default_boundary_cfg())
+    : BaseType(basis_functions, grid_cfg, boundary_cfg, 1e-8 / (4 * M_PI))
+  {}
+
+  static std::string static_id()
+  {
+    return "pointsourcepn";
+  }
+
+  static XT::Common::Configuration default_grid_cfg()
+  {
+    XT::Common::Configuration grid_config;
+    grid_config["type"] = XT::Grid::cube_gridprovider_default_config()["type"];
+    grid_config["lower_left"] = "[-1 -1 -1]";
+    grid_config["upper_right"] = "[1 1 1]";
+    grid_config["num_elements"] = "[10 10 10]";
+    grid_config["overlap_size"] = "[1 1 1]";
+    return grid_config;
+  }
+
+  // Initial value of the kinetic equation is psi_vac + 1/(4 pi^4 sigma^3) * exp(-||x||^2/(pi*sigma^2)).
+  std::unique_ptr<InitialValueType> initial_values() const override
+  {
+    RangeReturnType basis_integrated = basis_functions_.integrated();
+    const auto psi_vac = psi_vac_;
+    const auto eval_func = [basis_integrated, psi_vac](const DomainType& x, const XT::Common::Parameter&) {
+      static const auto sigma = 0.03;
+      static const auto first_factor = 1. / (4 * M_PI * std::pow(M_PI * sigma, 3));
+      static const auto second_factor = 1. / (M_PI * std::pow(sigma, 2));
+      return basis_integrated * std::max(first_factor * std::exp(-x.two_norm2() * second_factor), psi_vac);
+    };
+    return std::make_unique<GenericFunctionType>(21, eval_func);
+  } // ... initial_values()
+
+  RangeFieldType t_end() const override
+  {
+    return 0.75;
+  }
+
+  // sigma_a = 0, sigma_s = 1, Q = 0
+  std::unique_ptr<ScalarFunctionType> sigma_a() const override
+  {
+    return std::make_unique<ConstantScalarFunctionType>(0.);
+  }
+
+  std::unique_ptr<ScalarFunctionType> sigma_s() const override
+  {
+    return std::make_unique<ConstantScalarFunctionType>(1.);
+  }
+
+  std::unique_ptr<ScalarFunctionType> Q() const override
+  {
+    return std::make_unique<ConstantScalarFunctionType>(0.);
+  }
+
+protected:
+  using BaseType::basis_functions_;
+  using BaseType::grid_cfg_;
+  using BaseType::psi_vac_;
+}; // class PointSourcePn<...>
+
+template <class GV, class MomentBasis>
+class PointSourceMn : public PointSourcePn<XT::Grid::extract_entity_t<GV>, MomentBasis>
+{
+  using BaseType = PointSourcePn<XT::Grid::extract_entity_t<GV>, MomentBasis>;
+  using ThisType = PointSourceMn;
+
+public:
+  using typename BaseType::FluxType;
+  using ActualFluxType = EntropyBasedFluxFunction<GV, MomentBasis>;
+
+  using BaseType::default_boundary_cfg;
+  using BaseType::default_grid_cfg;
+
+  PointSourceMn(const MomentBasis& basis_functions,
+                const GV& grid_view,
+                const XT::Common::Configuration& grid_cfg = default_grid_cfg(),
+                const XT::Common::Configuration& boundary_cfg = default_boundary_cfg())
+    : BaseType(basis_functions, grid_cfg, boundary_cfg)
+    , grid_view_(grid_view)
+  {}
+
+  static std::string static_id()
+  {
+    return "pointsourcemn";
+  }
+
+  std::unique_ptr<FluxType> flux() const override final
+  {
+    return std::make_unique<ActualFluxType>(grid_view_, basis_functions_);
+  }
+
+protected:
+  using BaseType::basis_functions_;
+  const GV& grid_view_;
+}; // class PointSourceMn<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_POINTSOURCE_HH
diff --git a/dune/gdt/test/momentmodels/kinetictransport/shadow.hh b/dune/gdt/test/momentmodels/kinetictransport/shadow.hh
new file mode 100644
index 0000000000000000000000000000000000000000..a1d0301a08f2faf32b9489d8004562a6eace9bfe
--- /dev/null
+++ b/dune/gdt/test/momentmodels/kinetictransport/shadow.hh
@@ -0,0 +1,150 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
+// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+// Authors:
+//   Felix Schindler (2016)
+//   Tobias Leibner  (2016)
+
+#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_SHADOW_HH
+#define DUNE_GDT_HYPERBOLIC_PROBLEMS_SHADOW_HH
+
+#include "base.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+template <class E, class MomentBasisImp>
+class ShadowPn : public KineticTransportEquationBase<E, MomentBasisImp>
+{
+  using BaseType = KineticTransportEquationBase<E, MomentBasisImp>;
+
+public:
+  using typename BaseType::BoundaryValueType;
+  using typename BaseType::ConstantScalarFunctionType;
+  using typename BaseType::DomainType;
+  using typename BaseType::GenericFunctionType;
+  using typename BaseType::GenericScalarFunctionType;
+  using typename BaseType::MomentBasis;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::ScalarFunctionType;
+
+  using BaseType::default_boundary_cfg;
+
+  ShadowPn(const MomentBasis& basis_functions,
+           const XT::Common::Configuration& grid_cfg = default_grid_cfg(),
+           const XT::Common::Configuration& boundary_cfg = default_boundary_cfg())
+    : BaseType(basis_functions, grid_cfg, boundary_cfg, 1e-8 / (4 * M_PI))
+  {}
+
+  static std::string static_id()
+  {
+    return "Shadowpn";
+  }
+
+  static XT::Common::Configuration default_grid_cfg()
+  {
+    XT::Common::Configuration grid_config;
+    grid_config["type"] = XT::Grid::cube_gridprovider_default_config()["type"];
+    grid_config["lower_left"] = "[0 0 0]";
+    grid_config["upper_right"] = "[12 4 3]";
+    grid_config["num_elements"] = "[4 4 4]";
+    grid_config["overlap_size"] = "[1 1 1]";
+    return grid_config;
+  }
+
+  RangeFieldType t_end() const override
+  {
+    return 20;
+  }
+
+  // sigma_a = 50 on [2,3]x[1,3]x[0,2], sigma_s = 0, Q = 0
+  std::unique_ptr<ScalarFunctionType> sigma_a() const override
+  {
+    return std::make_unique<GenericScalarFunctionType>(0, [=](const DomainType& x, const XT::Common::Parameter&) {
+      return (x[0] < 2 || x[0] > 3 || x[1] < 1 || x[1] > 3 || x[2] < 0 || x[2] > 2) ? 0. : 50.;
+    });
+  }
+
+  std::unique_ptr<ScalarFunctionType> sigma_s() const override
+  {
+    return std::make_unique<ConstantScalarFunctionType>(0.);
+  }
+
+  std::unique_ptr<ScalarFunctionType> Q() const override
+  {
+    return std::make_unique<ConstantScalarFunctionType>(0.);
+  }
+
+#define USE_DIRAC_BOUNDARY 0
+  // Boundary value of kinetic equation is either \dirac(v - (1, 0, 0)) or an isotropic beam with density 2,
+  // i.e. 2/(4 pi), at x = 0 and psi_vac else
+  std::unique_ptr<BoundaryValueType> boundary_values() const override final
+  {
+    auto basis_integrated = basis_functions_.integrated();
+    const auto psi_vac = psi_vac_;
+    return std::make_unique<GenericFunctionType>(
+        0, [basis_integrated, psi_vac](const DomainType& x, const XT::Common::Parameter&) {
+          auto ret = basis_integrated;
+          ret *= psi_vac;
+          // left boundary
+          if (XT::Common::FloatCmp::eq(x[0], 0.)) {
+#if USE_DIRAC_BOUNDARY
+            auto dirac_integrated = basis_functions_.integrate_dirac_at(DomainType{1, 0, 0});
+            ret += dirac_integrated;
+#else // USE_DIRAC_BOUNDARY
+                                                    ret += basis_integrated * 2. / (4 * M_PI);
+#endif // USE_DIRAC_BOUNDARY
+          }
+          return ret;
+        });
+  } // ... boundary_values()
+
+protected:
+  using BaseType::basis_functions_;
+  using BaseType::grid_cfg_;
+  using BaseType::psi_vac_;
+}; // class ShadowPn<...>
+
+template <class GV, class MomentBasis>
+class ShadowMn : public ShadowPn<XT::Grid::extract_entity_t<GV>, MomentBasis>
+{
+  using BaseType = ShadowPn<XT::Grid::extract_entity_t<GV>, MomentBasis>;
+  using ThisType = ShadowMn;
+
+public:
+  using typename BaseType::FluxType;
+  using ActualFluxType = EntropyBasedFluxFunction<GV, MomentBasis>;
+
+  using BaseType::default_boundary_cfg;
+  using BaseType::default_grid_cfg;
+
+  ShadowMn(const MomentBasis& basis_functions,
+           const GV& grid_view,
+           const XT::Common::Configuration& grid_cfg = default_grid_cfg(),
+           const XT::Common::Configuration& boundary_cfg = default_boundary_cfg())
+    : BaseType(basis_functions, grid_cfg, boundary_cfg)
+    , grid_view_(grid_view)
+  {}
+
+  static std::string static_id()
+  {
+    return "Shadowmn";
+  }
+
+  std::unique_ptr<FluxType> flux() const override
+  {
+    return std::make_unique<ActualFluxType>(grid_view_, basis_functions_);
+  }
+
+protected:
+  using BaseType::basis_functions_;
+  const GV& grid_view_;
+}; // class ShadowMn<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_SHADOW_HH
diff --git a/dune/gdt/test/momentmodels/kinetictransport/sourcebeam.hh b/dune/gdt/test/momentmodels/kinetictransport/sourcebeam.hh
new file mode 100644
index 0000000000000000000000000000000000000000..184277d80b73614dd4d51d233de4eaa14abc2ee4
--- /dev/null
+++ b/dune/gdt/test/momentmodels/kinetictransport/sourcebeam.hh
@@ -0,0 +1,407 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 - 2017)
+//   Rene Milk       (2016 - 2018)
+//   Tobias Leibner  (2016 - 2017)
+
+#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_SOURCEBEAM_HH
+#define DUNE_GDT_HYPERBOLIC_PROBLEMS_SOURCEBEAM_HH
+
+#include <cmath>
+#include <memory>
+#include <vector>
+#include <string>
+
+#include <dune/xt/common/string.hh>
+#include <dune/xt/common/math.hh>
+
+#include <dune/xt/la/eigen-solver.hh>
+
+#include "base.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+template <class E, class MomentBasisImp>
+class SourceBeamPn : public KineticTransportEquationBase<E, MomentBasisImp>
+{
+  using BaseType = KineticTransportEquationBase<E, MomentBasisImp>;
+  using ThisType = SourceBeamPn;
+
+public:
+  using BaseType::dimDomain;
+  using BaseType::dimRange;
+  using typename BaseType::BoundaryDistributionType;
+  using typename BaseType::BoundaryValueType;
+  using typename BaseType::DomainFieldType;
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicRangeType;
+  using typename BaseType::FluxType;
+  using typename BaseType::GenericFunctionType;
+  using typename BaseType::GenericScalarFunctionType;
+  using typename BaseType::MomentBasis;
+  using typename BaseType::RangeFieldType;
+  using typename BaseType::RangeReturnType;
+  using typename BaseType::ScalarFunctionType;
+
+  using BaseType::default_boundary_cfg;
+
+  SourceBeamPn(const MomentBasis& basis_functions,
+               const XT::Common::Configuration& grid_cfg = default_grid_cfg(),
+               const XT::Common::Configuration& boundary_cfg = default_boundary_cfg(),
+               const bool is_mn_model = false)
+    : BaseType(basis_functions, grid_cfg, boundary_cfg)
+    , is_mn_model_(is_mn_model)
+  {}
+
+  static std::string static_id()
+  {
+    return "sourcebeampn";
+  }
+
+  static XT::Common::Configuration default_grid_cfg()
+  {
+    XT::Common::Configuration grid_config;
+    grid_config["type"] = XT::Grid::cube_gridprovider_default_config()["type"];
+    grid_config["lower_left"] = "[0.0]";
+    grid_config["upper_right"] = "[3.0]";
+    grid_config["num_elements"] = "[300]";
+    grid_config["overlap_size"] = "[2]";
+    return grid_config;
+  }
+
+  // Boundary value of kinetic equation is \frac{g}{<g>} at x = 0 and
+  // \psi_{vac} = 0.5*10^(-8) at x = 3, with g(v) = exp(-10^5(v-1)^2), so n-th component of boundary value has to be
+  // \frac{<base_n(v)*g(v)>}{<g>} at x = 0 and \psi_{vac}*base_integrated_n
+  // at x = 3.
+  std::unique_ptr<BoundaryValueType> boundary_values() const override final
+  {
+    return std::make_unique<GenericFunctionType>(1, [&](const DomainType& x, const XT::Common::Parameter&) {
+      if (x[0] < 1.5) {
+        static auto ret = helper<MomentBasis>::get_left_boundary_values(basis_functions_, psi_vac_, is_mn_model_);
+        return ret;
+      } else {
+        auto ret = basis_functions_.integrated();
+        ret *= psi_vac_;
+        return ret;
+      }
+    });
+  } // ... boundary_values()
+
+  BoundaryDistributionType boundary_distribution() const override final
+  {
+    return [this](const DomainType& x) -> std::function<RangeFieldType(const DomainType&)> {
+      if (x[0] > 1.5)
+        return [this](const DomainType& /*v*/) { return this->psi_vac_; };
+      else
+        return [this](const DomainType& v) {
+          const RangeFieldType val = std::exp(-1e5 * std::pow(v[0] - 1., 2));
+          return (val > this->psi_vac_) ? val : this->psi_vac_;
+        };
+    };
+  }
+
+  virtual RangeReturnType
+  kinetic_boundary_flux(const DomainType& x, const RangeFieldType& n, const size_t dd) const override final
+  {
+    return helper<MomentBasis>::get_kinetic_boundary_flux(basis_functions_, psi_vac_, is_mn_model_, x, n, dd, *this);
+  }
+
+  RangeReturnType left_boundary_value() const
+  {
+    return helper<MomentBasis>::get_left_boundary_values(basis_functions_, psi_vac_, is_mn_model_);
+  }
+
+  RangeFieldType t_end() const override
+  {
+    return 2.5;
+  }
+
+  // sigma_a = 1 if x <= 2, 0 else
+  std::unique_ptr<ScalarFunctionType> sigma_a() const override
+  {
+    return std::make_unique<GenericScalarFunctionType>(
+        0, [](const DomainType& x, const XT::Common::Parameter&) { return x[0] > 2 ? 0. : 1.; });
+  }
+
+  // sigma_s = 0 if x <= 1, 2 if 1 < x <= 2, 10 else
+  std::unique_ptr<ScalarFunctionType> sigma_s() const override
+  {
+    return std::make_unique<GenericScalarFunctionType>(0, [](const DomainType& x, const XT::Common::Parameter&) {
+      if (x[0] > 2)
+        return 10.;
+      else if (x[0] > 1)
+        return 2.;
+      else
+        return 0.;
+    });
+  }
+
+  // Q = 0.5 if 1 <= x <= 1.5, 0 else
+  std::unique_ptr<ScalarFunctionType> Q() const override
+  {
+    return std::make_unique<GenericScalarFunctionType>(
+        0, [](const DomainType& x, const XT::Common::Parameter&) { return x[0] < 1 || x[0] > 1.5 ? 0. : 0.5; });
+  }
+
+protected:
+  struct helper_base
+  {
+    // returns the numerator g of the left boundary value (see create_boundary_values)
+    static RangeFieldType g(const RangeFieldType v)
+    {
+      return std::exp(-1e5 * (v - 1) * (v - 1));
+    }
+
+    static RangeFieldType dg(const RangeFieldType v)
+    {
+      return -2e5 * (v - 1) * g(v);
+    }
+
+    // returns the denominator <g> of the left boundary value (see create_boundary_values)
+    static RangeFieldType denominator()
+    {
+      static RangeFieldType ret = 1 / 200. * std::sqrt(M_PI / 10) * std::erf(200 * std::sqrt(10));
+      return ret;
+    }
+
+    // calculates integral from v_l to v_u of numerator g
+    static RangeFieldType integral_1(RangeFieldType v_l, RangeFieldType v_u)
+    {
+      return 1 / 200. * std::sqrt(M_PI / 10)
+             * (std::erf(100 * std::sqrt(10) * (v_u - 1)) - std::erf(100 * std::sqrt(10) * (v_l - 1)));
+    }
+
+    // calculates integral from v_l to v_u of v*g
+    static RangeFieldType integral_2(RangeFieldType v_l, RangeFieldType v_u)
+    {
+      return integral_1(v_l, v_u) - 1. / 2e5 * (g(v_u) - g(v_l));
+    }
+
+    // calculates integral from v_l to v_u of v^2*g
+    static RangeFieldType integral_3(RangeFieldType v_l, RangeFieldType v_u)
+    {
+      return 0.25e-10 * (dg(v_u) - dg(v_l)) + 2. * integral_2(v_l, v_u) + (0.5e-5 - 1.) * integral_1(v_l, v_u);
+    }
+  };
+
+
+  template <class B, class anything = void>
+  struct helper : public helper_base
+  {
+    using helper_base::denominator;
+    using helper_base::g;
+
+    static DynamicRangeType get_left_boundary_values(const MomentBasisImp& basis_functions,
+                                                     const RangeFieldType& psi_vac,
+                                                     const bool is_mn_model)
+    {
+      DynamicRangeType ret(dimRange, 0);
+      // For the MN-Models, we have to use the quadrature also used in the optimization problem to guarantee
+      // realizability of the boundary_values.
+      // For the PN-Models, we do not have these issues and just use a very fine quadrature (which is not a performance
+      // problem as the integration is only done once).
+      const auto& quadratures =
+          is_mn_model ? basis_functions.quadratures() : MomentBasisImp::gauss_lobatto_quadratures(100, 31);
+      for (size_t ii = 0; ii < quadratures.size(); ++ii) {
+        const auto& quadrature = quadratures[ii];
+        for (const auto& quad_point : quadrature) {
+          const auto& v = quad_point.position()[0];
+          auto summand = basis_functions.evaluate(v, ii);
+          summand *= g(v) * quad_point.weight();
+          ret += summand;
+        }
+      }
+      ret /= denominator();
+      // add small vacuum concentration to move away from realizable boundary
+      ret += basis_functions.integrated() * psi_vac;
+      return ret;
+    }
+
+    static DynamicRangeType get_kinetic_boundary_flux(const MomentBasisImp& /*basis_functions*/,
+                                                      const RangeFieldType& /*psi_vac*/,
+                                                      const bool /*is_mn_model*/,
+                                                      const DomainType& x,
+                                                      const RangeFieldType& n,
+                                                      const size_t dd,
+                                                      const ThisType& problem)
+    {
+      return problem.kinetic_boundary_flux_from_quadrature(x, n, dd);
+    }
+  };
+
+  template <class anything, EntropyType entropy>
+  struct helper<HatFunctionMomentBasis<DomainFieldType, dimDomain, RangeFieldType, dimRange, 1, 1, entropy>, anything>
+    : public helper_base
+  {
+    using helper_base::denominator;
+    using helper_base::g;
+    using helper_base::integral_1;
+    using helper_base::integral_2;
+    using helper_base::integral_3;
+
+    static DynamicRangeType get_left_boundary_values(const MomentBasisImp& basis_functions,
+                                                     const RangeFieldType psi_vac,
+                                                     const bool /*is_mn_model*/)
+    {
+      DynamicRangeType ret(dimRange, 0);
+      for (size_t nn = 0; nn < dimRange; ++nn) {
+        const auto& partitioning = basis_functions.partitioning();
+        const auto vn = partitioning[nn];
+        if (nn < dimRange - 1) {
+          const auto vnp = partitioning[nn + 1];
+          ret[nn] += 1. / ((vn - vnp) * denominator()) * (integral_2(vn, vnp) - vnp * integral_1(vn, vnp));
+        }
+        if (nn > 0) {
+          const auto vnm = partitioning[nn - 1];
+          ret[nn] += 1. / ((vn - vnm) * denominator()) * (integral_2(vnm, vn) - vnm * integral_1(vnm, vn));
+        }
+      }
+      // add small vacuum concentration to move away from realizable boundary
+      ret += basis_functions.integrated() * psi_vac;
+      return ret;
+    } // ... get_left_boundary_values(...)
+
+    static DynamicRangeType get_kinetic_boundary_flux(const MomentBasisImp& basis_functions,
+                                                      const RangeFieldType& /*psi_vac*/,
+                                                      const bool is_mn_model,
+                                                      const DomainType& x,
+                                                      const RangeFieldType& n,
+                                                      const size_t dd,
+                                                      const ThisType& problem)
+    {
+      if (!is_mn_model)
+        DUNE_THROW(Dune::NotImplemented, "Only implemented for mn");
+      if (x < 1.5) {
+        DynamicRangeType ret(dimRange, 0.);
+        const auto& partitioning = basis_functions.partitioning();
+        for (size_t nn = 0; nn < dimRange; ++nn) {
+          const auto vn = partitioning[nn];
+          if (nn < dimRange - 1) {
+            const auto vnp = partitioning[nn + 1];
+            if (vnp > 0.) {
+              const auto left_limit = vn > 0. ? vn : 0.;
+              ret[nn] +=
+                  1. / ((vn - vnp) * denominator()) * (integral_3(left_limit, vnp) - vnp * integral_2(left_limit, vnp));
+            } // if (vnp > 0.)
+          } // if (nn < dimRange -1)
+          if (vn > 0.) {
+            if (nn > 0) {
+              const auto vnm = partitioning[nn - 1];
+              const auto left_limit = vnm > 0. ? vnm : 0.;
+              ret[nn] +=
+                  1. / ((vn - vnm) * denominator()) * (integral_2(left_limit, vn) - vnm * integral_1(left_limit, vn));
+            } // if (nn > 0)
+          } // if (vn > 0.)
+        } // nn
+        return ret;
+      } else {
+        return problem.kinetic_boundary_flux_from_quadrature(x, n, dd);
+      }
+    } // ... get_kinetic_boundary_flux(...)
+  };
+
+  template <class anything, EntropyType entropy>
+  struct helper<PartialMomentBasis<DomainFieldType, dimDomain, RangeFieldType, dimRange, 1, 1, 1, entropy>, anything>
+    : public helper_base
+  {
+    using helper_base::denominator;
+    using helper_base::integral_1;
+    using helper_base::integral_2;
+    using helper_base::integral_3;
+
+    static DynamicRangeType get_left_boundary_values(const MomentBasisImp& basis_functions,
+                                                     const RangeFieldType psi_vac,
+                                                     const bool /*is_mn_model*/)
+    {
+      const auto& partitioning = basis_functions.partitioning();
+      DynamicRangeType ret(dimRange, 0);
+      for (size_t ii = 0; ii < dimRange / 2; ++ii) {
+        ret[2 * ii] = integral_1(partitioning[ii], partitioning[ii + 1]) / denominator();
+        ret[2 * ii + 1] = integral_2(partitioning[ii], partitioning[ii + 1]) / denominator();
+      }
+      // add small vacuum concentration to move away from realizable boundary
+      ret += basis_functions.integrated() * psi_vac;
+      return ret;
+    }
+
+    static DynamicRangeType get_kinetic_boundary_flux(const MomentBasisImp& basis_functions,
+                                                      const RangeFieldType& /*psi_vac*/,
+                                                      const bool is_mn_model,
+                                                      const DomainType& x,
+                                                      const RangeFieldType& n,
+                                                      const size_t dd,
+                                                      const ThisType& problem)
+    {
+      if (!is_mn_model)
+        DUNE_THROW(Dune::NotImplemented, "Only implemented for mn");
+      if (x < 1.5) {
+        const auto& partitioning = basis_functions.partitioning();
+        DynamicRangeType ret(dimRange, 0.);
+        for (size_t ii = 0; ii < dimRange / 2; ++ii) {
+          if (partitioning[ii + 1] > 0.) {
+            const auto left_limit = partitioning[ii] > 0. ? partitioning[ii] : 0.;
+            ret[2 * ii] = integral_2(left_limit, partitioning[ii + 1]) / denominator();
+            ret[2 * ii + 1] = integral_3(left_limit, partitioning[ii + 1]) / denominator();
+          }
+        } // ii
+        return ret;
+      } else {
+        return problem.kinetic_boundary_flux_from_quadrature(x, n, dd);
+      }
+    } // ... get_kinetic_boundary_flux(...)
+  };
+
+  using BaseType::basis_functions_;
+  using BaseType::psi_vac_;
+  const bool is_mn_model_;
+}; // class SourceBeamPn<...>
+
+template <class GV, class MomentBasis>
+class SourceBeamMn : public SourceBeamPn<XT::Grid::extract_entity_t<GV>, MomentBasis>
+{
+  using BaseType = SourceBeamPn<XT::Grid::extract_entity_t<GV>, MomentBasis>;
+  using ThisType = SourceBeamMn;
+
+public:
+  using typename BaseType::FluxType;
+  using typename BaseType::RangeReturnType;
+  using ActualFluxType = EntropyBasedFluxFunction<GV, MomentBasis>;
+
+  using BaseType::default_boundary_cfg;
+  using BaseType::default_grid_cfg;
+
+  SourceBeamMn(const MomentBasis& basis_functions,
+               const GV& grid_view,
+               const XT::Common::Configuration& grid_cfg = default_grid_cfg(),
+               const XT::Common::Configuration& boundary_cfg = default_boundary_cfg())
+    : BaseType(basis_functions, grid_cfg, boundary_cfg, true)
+    , grid_view_(grid_view)
+  {}
+
+  static std::string static_id()
+  {
+    return "sourcebeammn";
+  }
+
+  std::unique_ptr<FluxType> flux() const override
+  {
+    return std::make_unique<ActualFluxType>(grid_view_, basis_functions_);
+  }
+
+protected:
+  using BaseType::basis_functions_;
+  const GV& grid_view_;
+}; // class SourceBeamMn<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_SOURCEBEAM_HH
diff --git a/dune/gdt/test/momentmodels/kinetictransport/testcases.hh b/dune/gdt/test/momentmodels/kinetictransport/testcases.hh
new file mode 100644
index 0000000000000000000000000000000000000000..f16229a02abd021ac0eda054f83b60b15e16b604
--- /dev/null
+++ b/dune/gdt/test/momentmodels/kinetictransport/testcases.hh
@@ -0,0 +1,882 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_KINETICTRANSPORT_TESTCASES_HH
+#define DUNE_GDT_HYPERBOLIC_PROBLEMS_KINETICTRANSPORT_TESTCASES_HH
+
+#include <dune/grid/yaspgrid.hh>
+
+#include <dune/gdt/test/momentmodels/basisfunctions.hh>
+#include <dune/gdt/spaces/l2/finite-volume.hh>
+#include <dune/gdt/spaces/l2/discontinuous-lagrange.hh>
+#include <dune/gdt/tools/timestepper/interface.hh>
+#include <dune/gdt/operators/reconstruction/slopes.hh>
+
+#include "checkerboard.hh"
+#include "planesource.hh"
+#include "pointsource.hh"
+#include "shadow.hh"
+#include "sourcebeam.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+// choose Quadrature suitable for MomentBasisImp
+template <class MomentBasisImp>
+struct QuadratureChooser;
+
+template <size_t order, EntropyType entropy>
+struct QuadratureChooser<LegendreMomentBasis<double, double, order, 1, entropy>>
+{
+  static constexpr size_t quad_order = 54;
+  static constexpr size_t quad_refinements = 1;
+};
+
+template <size_t dimRange, EntropyType entropy>
+struct QuadratureChooser<HatFunctionMomentBasis<double, 1, double, dimRange, 1, 1, entropy>>
+{
+  static constexpr size_t quad_order = 15;
+  static constexpr size_t quad_refinements = 0;
+};
+
+template <size_t dimRange, EntropyType entropy>
+struct QuadratureChooser<PartialMomentBasis<double, 1, double, dimRange, 1, 1, 1, entropy>>
+{
+  static constexpr size_t quad_order = 15;
+  static constexpr size_t quad_refinements = 0;
+};
+
+template <size_t order, EntropyType entropy>
+struct QuadratureChooser<RealSphericalHarmonicsMomentBasis<double, double, order, 3, false, entropy>>
+{
+  static constexpr size_t quad_order = 2 * order + 8;
+  static constexpr size_t quad_refinements = 0;
+};
+
+template <size_t refinements, EntropyType entropy>
+struct QuadratureChooser<HatFunctionMomentBasis<double, 3, double, refinements, 1, 3, entropy>>
+{
+  static constexpr size_t quad_order = refinements == 0 ? 18 /*fekete rule number 7*/ : 9 /*fekete rule number 3*/;
+  static constexpr size_t quad_refinements = 0;
+};
+
+template <size_t refinements, EntropyType entropy>
+struct QuadratureChooser<PartialMomentBasis<double, 3, double, refinements, 1, 3, 1, entropy>>
+{
+  static constexpr size_t quad_order = refinements == 0 ? 18 /*fekete rule number 7*/ : 9 /*fekete rule number 3*/;
+  static constexpr size_t quad_refinements = 0;
+};
+
+
+// choose RealizabilityLimiter suitable for MomentBasisImp
+template <class GV, class MomentBasisImp, class AnalyticalFluxType, class DiscreteFunctionType>
+struct RealizabilityLimiterChooser;
+
+#if HAVE_CLP
+template <class GV, size_t order, class AnalyticalFluxType, class DiscreteFunctionType, EntropyType entropy>
+struct RealizabilityLimiterChooser<GV,
+                                   LegendreMomentBasis<double, double, order, 1, entropy>,
+                                   AnalyticalFluxType,
+                                   DiscreteFunctionType>
+{
+  using MomentBasis = LegendreMomentBasis<double, double, order, 1, entropy>;
+  using EntropyFluxType = EntropyBasedFluxFunction<GV, MomentBasis>;
+
+  template <class EigenVectorWrapperType>
+  static std::unique_ptr<LpConvexhullRealizabilityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>>
+  make_slope(const EntropyFluxType& entropy_flux, const MomentBasis& basis_functions, const double epsilon)
+  {
+    using SlopeType = LpConvexhullRealizabilityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>;
+    return std::make_unique<SlopeType>(entropy_flux, basis_functions, epsilon);
+  }
+};
+#endif
+
+#ifndef USE_LP_POSITIVITY_LIMITER
+#  define USE_LP_POSITIVITY_LIMITER 0
+#endif // USE_LP_POSITIVITY_LIMITER
+template <class GV, size_t dimRange, class AnalyticalFluxType, class DiscreteFunctionType, EntropyType entropy>
+struct RealizabilityLimiterChooser<GV,
+                                   HatFunctionMomentBasis<double, 1, double, dimRange, 1, 1, entropy>,
+                                   AnalyticalFluxType,
+                                   DiscreteFunctionType>
+{
+  using MomentBasis = HatFunctionMomentBasis<double, 1, double, dimRange, 1, 1, entropy>;
+  using EntropyFluxType = EntropyBasedFluxFunction<GV, MomentBasis>;
+
+#if HAVE_CLP && USE_LP_POSITIVITY_LIMITER
+  template <class EigenVectorWrapperType>
+  static std::unique_ptr<LpPositivityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>>
+  make_slope(const EntropyFluxType& entropy_flux, const MomentBasis& /*basis_functions*/, const double epsilon)
+  {
+    using SlopeType = LpPositivityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>;
+    return std::make_unique<SlopeType>(entropy_flux, epsilon);
+  }
+#else // HAVE_CLP
+  template <class EigenVectorWrapperType>
+  static std::unique_ptr<PositivityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>>
+  make_slope(const EntropyFluxType& entropy_flux, const MomentBasis& /*basis_functions*/, const double epsilon)
+  {
+    using SlopeType = PositivityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>;
+    return std::make_unique<SlopeType>(entropy_flux, epsilon);
+  }
+#endif // HAVE_CLP
+};
+
+template <class GV, size_t dimRange, class AnalyticalFluxType, class DiscreteFunctionType, EntropyType entropy>
+struct RealizabilityLimiterChooser<GV,
+                                   PartialMomentBasis<double, 1, double, dimRange, 1, 1, 1, entropy>,
+                                   AnalyticalFluxType,
+                                   DiscreteFunctionType>
+{
+  using MomentBasis = PartialMomentBasis<double, 1, double, dimRange, 1, 1, 1, entropy>;
+  using EntropyFluxType = EntropyBasedFluxFunction<GV, MomentBasis>;
+
+  template <class EigenVectorWrapperType>
+  static std::unique_ptr<Dg1dRealizabilityLimitedSlope<GV, double, dimRange, EigenVectorWrapperType, entropy>>
+  make_slope(const EntropyFluxType& entropy_flux, const MomentBasis& basis_functions, const double epsilon)
+  {
+    using SlopeType = Dg1dRealizabilityLimitedSlope<GV, double, dimRange, EigenVectorWrapperType, entropy>;
+    return std::make_unique<SlopeType>(entropy_flux, basis_functions, epsilon);
+  }
+};
+
+#if HAVE_CLP
+template <class GV, size_t order, class AnalyticalFluxType, class DiscreteFunctionType, EntropyType entropy>
+struct RealizabilityLimiterChooser<GV,
+                                   RealSphericalHarmonicsMomentBasis<double, double, order, 3, false, entropy>,
+                                   AnalyticalFluxType,
+                                   DiscreteFunctionType>
+{
+  using MomentBasis = RealSphericalHarmonicsMomentBasis<double, double, order, 3, false, entropy>;
+  using EntropyFluxType = EntropyBasedFluxFunction<GV, MomentBasis>;
+
+  template <class EigenVectorWrapperType>
+  static std::unique_ptr<LpConvexhullRealizabilityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>>
+  make_slope(const EntropyFluxType& entropy_flux, const MomentBasis& basis_functions, const double epsilon)
+  {
+    using SlopeType = LpConvexhullRealizabilityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>;
+    return std::make_unique<SlopeType>(entropy_flux, basis_functions, epsilon);
+  }
+};
+#endif
+
+template <class GV, size_t refinements, class AnalyticalFluxType, class DiscreteFunctionType, EntropyType entropy>
+struct RealizabilityLimiterChooser<GV,
+                                   HatFunctionMomentBasis<double, 3, double, refinements, 1, 3, entropy>,
+                                   AnalyticalFluxType,
+                                   DiscreteFunctionType>
+{
+  using MomentBasis = HatFunctionMomentBasis<double, 3, double, refinements, 1, 3, entropy>;
+  using EntropyFluxType = EntropyBasedFluxFunction<GV, MomentBasis>;
+
+#if HAVE_CLP && USE_LP_POSITIVITY_LIMITER
+  template <class EigenVectorWrapperType>
+  static std::unique_ptr<LpPositivityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>>
+  make_slope(const EntropyFluxType& entropy_flux, const MomentBasis& /*basis_functions*/, const double epsilon)
+  {
+    using SlopeType = LpPositivityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>;
+    return std::make_unique<SlopeType>(entropy_flux, epsilon);
+  }
+#else // HAVE_CLP
+  template <class EigenVectorWrapperType>
+  static std::unique_ptr<PositivityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>>
+  make_slope(const EntropyFluxType& entropy_flux, const MomentBasis& /*basis_functions*/, const double epsilon)
+  {
+    using SlopeType = PositivityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>;
+    return std::make_unique<SlopeType>(entropy_flux, epsilon);
+  }
+#endif // HAVE_CLP
+};
+
+#if HAVE_QHULL
+template <class GV, size_t refinements, class AnalyticalFluxType, class DiscreteFunctionType, EntropyType entropy>
+struct RealizabilityLimiterChooser<GV,
+                                   PartialMomentBasis<double, 3, double, refinements, 1, 3, 1, entropy>,
+                                   AnalyticalFluxType,
+                                   DiscreteFunctionType>
+{
+  using MomentBasis = PartialMomentBasis<double, 3, double, refinements, 1, 3, 1, entropy>;
+  using EntropyFluxType = EntropyBasedFluxFunction<GV, MomentBasis>;
+
+  template <class EigenVectorWrapperType>
+  static std::unique_ptr<DgConvexHullRealizabilityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>>
+  make_slope(const EntropyFluxType& entropy_flux, const MomentBasis& basis_functions, const double epsilon)
+  {
+    using SlopeType = DgConvexHullRealizabilityLimitedSlope<GV, MomentBasis, EigenVectorWrapperType>;
+    return std::make_unique<SlopeType>(entropy_flux, basis_functions, epsilon);
+  }
+};
+#endif // HAVE_QHULL
+
+// SourceBeam Pn
+template <class MomentBasisImp, bool reconstruct>
+struct SourceBeamPnExpectedResults;
+
+template <bool reconstruct>
+struct SourceBeamPnExpectedResults<LegendreMomentBasis<double, double, 7>, reconstruct>
+{
+  static constexpr double l1norm = reconstruct ? 0.33066818456325309 : 0.33107004463414219;
+  static constexpr double l2norm = reconstruct ? 0.46157514055648202 : 0.44609169128864046;
+  static constexpr double linfnorm = reconstruct ? 1.1553979882432905 : 1.0882801946666183;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct SourceBeamPnExpectedResults<HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, reconstruct>
+{
+  static constexpr double l1norm = reconstruct ? 0.33146057542497681 : 0.33146794280840425;
+  static constexpr double l2norm = reconstruct ? 0.46411980559363358 : 0.44913032300780292;
+  static constexpr double linfnorm = reconstruct ? 0.98904667015384473 : 0.98709215129457029;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct SourceBeamPnExpectedResults<PartialMomentBasis<double, 1, double, 8, 1, 1>, reconstruct>
+{
+  static constexpr double l1norm = reconstruct ? 0.33140398337610927 : 0.33140398337603194;
+  static constexpr double l2norm = reconstruct ? 0.47294828933204164 : 0.45667075585121392;
+  static constexpr double linfnorm = reconstruct ? 1.0490804598503625 : 0.99004736850989217;
+  static constexpr double tol = 1e-9;
+};
+
+template <class GridImp, class MomentBasisImp, bool reconstruct>
+struct SourceBeamPnTestCase
+{
+  using MomentBasis = MomentBasisImp;
+  static constexpr size_t dimDomain = MomentBasis::dimDomain;
+  static constexpr size_t dimRange = MomentBasis::dimRange;
+  using DomainFieldType = typename MomentBasis::DomainFieldType;
+  using RangeFieldType = typename MomentBasis::RangeFieldType;
+  using GridType = GridImp;
+  using GridViewType = typename GridType::LeafGridView;
+  using E = XT::Grid::extract_entity_t<GridViewType>;
+  using SpaceType = FiniteVolumeSpace<GridViewType, dimRange, 1, RangeFieldType>;
+  using AdvectionSourceSpaceType =
+      std::conditional_t<reconstruct, DiscontinuousLagrangeSpace<GridViewType, dimRange, RangeFieldType>, SpaceType>;
+  using VectorType = typename Dune::XT::LA::Container<RangeFieldType, Dune::XT::LA::default_backend>::VectorType;
+  using DiscreteFunctionType = DiscreteFunction<VectorType, GridViewType, dimRange, 1, RangeFieldType>;
+  using ProblemType = SourceBeamPn<E, MomentBasis>;
+  static constexpr RangeFieldType t_end = 0.25;
+  static constexpr bool reconstruction = reconstruct;
+  using ExpectedResultsType = SourceBeamPnExpectedResults<MomentBasisImp, reconstruction>;
+};
+
+
+// SourceBeam Mn
+template <class MomentBasisImp, bool reconstruct, bool kinetic_scheme = false>
+struct SourceBeamMnExpectedResults
+{
+  static constexpr double l1norm = 0.;
+  static constexpr double l2norm = 0.;
+  static constexpr double linfnorm = 0.;
+  static constexpr double tol = 1e-15;
+};
+
+template <bool reconstruct, bool kinetic_scheme>
+struct SourceBeamMnExpectedResults<LegendreMomentBasis<double, double, 7>, reconstruct, kinetic_scheme>
+{
+  static constexpr double l1norm = reconstruct ? 0.28535354296013105 : 0.28535354295945792;
+  static constexpr double l2norm = reconstruct ? 0.37115145999473981 : 0.36265752973701221;
+  static constexpr double linfnorm = reconstruct ? 0.78506610334488358 : 0.78315544039143314;
+  static constexpr double tol = 1e-5;
+};
+
+template <bool reconstruct, bool kinetic_scheme>
+struct SourceBeamMnExpectedResults<LegendreMomentBasis<double, double, 7, 1, EntropyType::BoseEinstein>,
+                                   reconstruct,
+                                   kinetic_scheme>
+{
+  static constexpr double l1norm = reconstruct ? 0.28535354297901544 : 0.28535354297288812;
+  static constexpr double l2norm = reconstruct ? 0.37115153411073604 : 0.362657577171562;
+  static constexpr double linfnorm = reconstruct ? 0.78506610330723181 : 0.78315544052307973;
+  static constexpr double tol = 1e-5;
+};
+
+template <bool reconstruct>
+struct SourceBeamMnExpectedResults<HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, reconstruct, false>
+{
+  static constexpr double l1norm = reconstruct ? 0.33140398330545301 : 0.33140398330533227;
+  static constexpr double l2norm = reconstruct ? 0.45584140597017353 : 0.44485191601010715;
+  static constexpr double linfnorm = reconstruct ? 0.99172197084890834 : 0.98930925210045084;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct SourceBeamMnExpectedResults<HatFunctionMomentBasis<double, 1, double, 8, 1, 1, EntropyType::BoseEinstein>,
+                                   reconstruct,
+                                   false>
+{
+  static constexpr double l1norm = reconstruct ? 0.33140398337940113 : 0.33140398338096477;
+  static constexpr double l2norm = reconstruct ? 0.45580284843165519 : 0.44483205570831974;
+  static constexpr double linfnorm = reconstruct ? 0.99172119511603896 : 0.98930804287194951;
+  static constexpr double tol = 1e-9;
+};
+
+
+template <bool reconstruct>
+struct SourceBeamMnExpectedResults<HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, reconstruct, true>
+{
+  static constexpr double l1norm = reconstruct ? 371.54588397717055 : 367.97988291905477;
+  static constexpr double l2norm = reconstruct ? 236.4476851910448 : 235.54814675091959;
+  static constexpr double linfnorm = reconstruct ? 210.63369526083264 : 208.81107020771216;
+  static constexpr double tol = 1e-5;
+};
+
+template <bool reconstruct>
+struct SourceBeamMnExpectedResults<PartialMomentBasis<double, 1, double, 8, 1, 1>, reconstruct, false>
+{
+  static constexpr double l1norm = reconstruct ? 0.33140398337368543 : 0.3314039833756291;
+  static constexpr double l2norm = reconstruct ? 0.45583354074069732 : 0.44484887610818585;
+  static constexpr double linfnorm = reconstruct ? 0.99172184304625632 : 0.98930905293056492;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct SourceBeamMnExpectedResults<PartialMomentBasis<double, 1, double, 8, 1, 1, 1, EntropyType::BoseEinstein>,
+                                   reconstruct,
+                                   false>
+{
+  static constexpr double l1norm = reconstruct ? 0.33140398337969496 : 0.33140398335992233;
+  static constexpr double l2norm = reconstruct ? 0.45580154156528901 : 0.44483189012485808;
+  static constexpr double linfnorm = reconstruct ? 0.99172111701075782 : 0.98930792103242149;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct SourceBeamMnExpectedResults<PartialMomentBasis<double, 1, double, 8, 1, 1>, reconstruct, true>
+{
+  static constexpr double l1norm = reconstruct ? 254.20216502516391 : 270.74268779687191;
+  static constexpr double l2norm = reconstruct ? 187.86036790841933 : 202.76054800096165;
+  static constexpr double linfnorm = reconstruct ? 265.10790627160509 : 260.82089045524185;
+  static constexpr double tol = 1e-5;
+};
+
+template <class GridImp, class MomentBasisImp, bool reconstruct, bool kinetic_scheme = false>
+struct SourceBeamMnTestCase : public SourceBeamPnTestCase<GridImp, MomentBasisImp, reconstruct>
+{
+  using BaseType = SourceBeamPnTestCase<GridImp, MomentBasisImp, reconstruct>;
+  using typename BaseType::DiscreteFunctionType;
+  using typename BaseType::GridViewType;
+  using ProblemType = SourceBeamMn<GridViewType, MomentBasisImp>;
+  using ExpectedResultsType = SourceBeamMnExpectedResults<MomentBasisImp, reconstruct, kinetic_scheme>;
+  using QuadratureChooserType = QuadratureChooser<MomentBasisImp>;
+  static constexpr size_t quad_order = QuadratureChooserType::quad_order;
+  static constexpr size_t quad_refinements = QuadratureChooserType::quad_refinements;
+  using RealizabilityLimiterChooserType =
+      RealizabilityLimiterChooser<GridViewType, MomentBasisImp, typename ProblemType::FluxType, DiscreteFunctionType>;
+};
+
+// PlaneSource Pn
+template <class MomentBasisImp, bool reconstruct>
+struct PlaneSourcePnExpectedResults;
+
+template <bool reconstruct>
+struct PlaneSourcePnExpectedResults<LegendreMomentBasis<double, double, 7>, reconstruct>
+{
+  static constexpr double l1norm = reconstruct ? 2.0000000240000007 : 2.0000000240000029;
+  static constexpr double l2norm = reconstruct ? 2.9616518419466558 : 2.7792352623482848;
+  static constexpr double linfnorm = reconstruct ? 7.5355813391308644 : 5.9472849007944166;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct PlaneSourcePnExpectedResults<HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, reconstruct>
+{
+  static constexpr double l1norm = 2.0000000240000149;
+  static constexpr double l2norm = reconstruct ? 2.8915349919892397 : 2.7676677008555917;
+  static constexpr double linfnorm = reconstruct ? 6.9950740716997668 : 5.8904604670932663;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct PlaneSourcePnExpectedResults<PartialMomentBasis<double, 1, double, 8, 1, 1>, reconstruct>
+{
+  static constexpr double l1norm = reconstruct ? 2.0000000239999896 : 2.0000000239999918;
+  static constexpr double l2norm = reconstruct ? 2.8799152602279068 : 2.771228836660768;
+  static constexpr double linfnorm = reconstruct ? 6.9320887958307775 : 6.0090382693364512;
+  static constexpr double tol = 1e-9;
+};
+
+template <class GridImp, class MomentBasisImp, bool reconstruct>
+struct PlaneSourcePnTestCase : SourceBeamPnTestCase<GridImp, MomentBasisImp, reconstruct>
+{
+  using BaseType = SourceBeamPnTestCase<GridImp, MomentBasisImp, reconstruct>;
+  using RangeFieldType = typename BaseType::RangeFieldType;
+  using typename BaseType::E;
+  using ProblemType = PlaneSourcePn<E, MomentBasisImp>;
+  static constexpr RangeFieldType t_end = 0.25;
+  static constexpr bool reconstruction = reconstruct;
+  using ExpectedResultsType = PlaneSourcePnExpectedResults<MomentBasisImp, reconstruction>;
+};
+
+
+// PlaneSource Mn
+template <class MomentBasisImp, bool reconstruct, bool kinetic_scheme = false>
+struct PlaneSourceMnExpectedResults
+{
+  static constexpr double l1norm = 0.;
+  static constexpr double l2norm = 0.;
+  static constexpr double linfnorm = 0.;
+  static constexpr double tol = 0.;
+};
+
+template <bool reconstruct>
+struct PlaneSourceMnExpectedResults<LegendreMomentBasis<double, double, 7>, reconstruct, false>
+{
+  static constexpr double l1norm = reconstruct ? 2.0000000240000007 : 2.0000000240000029;
+  static constexpr double l2norm = reconstruct ? 2.785411193059216 : 2.746101358507282;
+  static constexpr double linfnorm = reconstruct ? 4.9069101475812698 : 5.327698357914608;
+  static constexpr double tol = 1e-7;
+};
+
+template <bool reconstruct>
+struct PlaneSourceMnExpectedResults<LegendreMomentBasis<double, double, 7, 1, EntropyType::BoseEinstein>,
+                                    reconstruct,
+                                    false>
+{
+  static constexpr double l1norm = reconstruct ? 2.000000024000002 : 2.0000000239999993;
+  static constexpr double l2norm = reconstruct ? 2.8065243992927944 : 2.7602055903929905;
+  static constexpr double linfnorm = reconstruct ? 6.4715719275169796 : 6.5649315387146858;
+  // Results are not really stable here, even very small numerical errors (e.g. due to the parallel quadrature in the
+  // Legendre integrated() initializer) can lead to quite large errors in the result, so we use a high tolerance here.
+  static constexpr double tol = 1e-2;
+};
+
+template <bool reconstruct>
+struct PlaneSourceMnExpectedResults<LegendreMomentBasis<double, double, 7>, reconstruct, true>
+{
+  static constexpr double l1norm = reconstruct ? 33.830651291425575 : 31.119878976551046;
+  static constexpr double l2norm = reconstruct ? 24.726893737746675 : 23.385570207485049;
+  static constexpr double linfnorm = reconstruct ? 19.113827924512311 : 19.113827924512311;
+  static constexpr double tol = 1e-5;
+};
+
+template <bool reconstruct>
+struct PlaneSourceMnExpectedResults<HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, reconstruct, false>
+{
+  static constexpr double l1norm = 2.0000000239315696;
+  static constexpr double l2norm = reconstruct ? 2.7966600752714887 : 2.7457411547488615;
+  static constexpr double linfnorm = reconstruct ? 5.2425259627991894 : 4.9923971272638816;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct PlaneSourceMnExpectedResults<HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, reconstruct, true>
+{
+  static constexpr double l1norm = reconstruct ? 268.42768559247429 : 246.7429359648828;
+  static constexpr double l2norm = reconstruct ? 197.1506094198385 : 186.09403264481648;
+  static constexpr double linfnorm = reconstruct ? 152.91062339609854 : 152.91062339609854;
+  static constexpr double tol = 1e-5;
+};
+
+template <bool reconstruct>
+struct PlaneSourceMnExpectedResults<PartialMomentBasis<double, 1, double, 8, 1, 1>, reconstruct, false>
+{
+  static constexpr double l1norm = reconstruct ? 2.0000000239999913 : 2.0000000239999904;
+  static constexpr double l2norm = reconstruct ? 2.8215879031834015 : 2.7633864171098814;
+  static constexpr double linfnorm = reconstruct ? 6.0674052799351612 : 6.2607864745531092;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct PlaneSourceMnExpectedResults<PartialMomentBasis<double, 1, double, 8, 1, 1>, reconstruct, true>
+{
+  static constexpr double l1norm = reconstruct ? 144.19157186249112 : 135.86834797834712;
+  static constexpr double l2norm = reconstruct ? 104.28938402311856 : 100.2359224660796;
+  static constexpr double linfnorm = reconstruct ? 100.43185554232102 : 97.933985765677008;
+  static constexpr double tol = 1e-5;
+};
+
+
+template <class GridImp, class MomentBasisImp, bool reconstruct, bool kinetic_scheme = false>
+struct PlaneSourceMnTestCase : SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct>
+{
+  using BaseType = SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct>;
+  using typename BaseType::DiscreteFunctionType;
+  using RangeFieldType = typename BaseType::RangeFieldType;
+  using typename BaseType::GridViewType;
+  using ProblemType = PlaneSourceMn<GridViewType, MomentBasisImp>;
+  static constexpr RangeFieldType t_end = 0.25;
+  static constexpr bool reconstruction = reconstruct;
+  using ExpectedResultsType = PlaneSourceMnExpectedResults<MomentBasisImp, reconstruction, kinetic_scheme>;
+  using QuadratureChooserType = QuadratureChooser<MomentBasisImp>;
+  static constexpr size_t quad_order = QuadratureChooserType::quad_order;
+  static constexpr size_t quad_refinements = QuadratureChooserType::quad_refinements;
+  using RealizabilityLimiterChooserType =
+      RealizabilityLimiterChooser<GridViewType, MomentBasisImp, typename ProblemType::FluxType, DiscreteFunctionType>;
+};
+
+
+// PointSourcePn
+template <class MomentBasisImp, bool reconstruct>
+struct PointSourcePnExpectedResults
+{
+  static constexpr double l1norm = 0.;
+  static constexpr double l2norm = 0.;
+  static constexpr double linfnorm = 0.;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct PointSourcePnExpectedResults<RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, reconstruct>
+{
+  static constexpr double l1norm = reconstruct ? 1.0000013830443908 : 1.000001383044226;
+  static constexpr double l2norm = reconstruct ? 2.6933361115324854 : 2.6827446884685;
+  static constexpr double linfnorm = reconstruct ? 10.361584898132795 : 10.368534349621724;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct PointSourcePnExpectedResults<HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, reconstruct>
+{
+  static constexpr double l1norm = reconstruct ? 1.000000489200628 : 1.0000004892004557;
+  static constexpr double l2norm = reconstruct ? 2.7000542373965715 : 2.6889777333363365;
+  static constexpr double linfnorm = reconstruct ? 10.393925182562946 : 10.395628177780834;
+  // The matrices in this test case all have eigenvalues [+-0.808311035811965, 0, 0, 0, 0].
+  // Thus, the eigenvectors are not unique, and the eigensolvers are extremely sensitive
+  // to numerical errors. A difference of 1e-16 in the jacobians entries suffices to
+  // result in completely different eigenvectors. In all cases, the eigenvectors are
+  // valid eigenvectors to the correct eigenvalues. However, this difference in the
+  // eigendecomposition leads to differences in the results with linear reconstruction
+  // that are larger than would be expected by pure numerical errors.
+  static constexpr double tol = reconstruct ? 1e-5 : 1e-9;
+};
+
+template <bool reconstruct>
+struct PointSourcePnExpectedResults<HatFunctionMomentBasis<double, 3, double, 1, 1, 3>, reconstruct>
+{
+  // Results with reconstruction not available yet
+  static constexpr double l1norm = 0.9999999937547992;
+  static constexpr double l2norm = 2.6881086659719111;
+  static constexpr double linfnorm = 10.393501289579167;
+  // see above
+  static constexpr double tol = reconstruct ? 1e-5 : 1e-9;
+};
+
+template <bool reconstruct>
+struct PointSourcePnExpectedResults<PartialMomentBasis<double, 3, double, 0, 1, 3>, reconstruct>
+{
+  static constexpr double l1norm = reconstruct ? 1.000000489200628 : 1.0000004892004604;
+  static constexpr double l2norm = reconstruct ? 2.6985809847834017 : 2.6881899717088591;
+  static constexpr double linfnorm = reconstruct ? 10.391256326798887 : 10.394092510258828;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct PointSourcePnExpectedResults<PartialMomentBasis<double, 3, double, 1, 1, 3>, reconstruct>
+{
+  static_assert(!reconstruct, "Results with reconstruction not available yet!");
+  static constexpr double l1norm = 0.99999999375479631;
+  static constexpr double l2norm = 2.6881891561264872;
+  static constexpr double linfnorm = 10.394089431581479;
+  static constexpr double tol = 1e-9;
+};
+
+
+template <class GridImp, class MomentBasisImp, bool reconstruct>
+struct PointSourcePnTestCase : SourceBeamPnTestCase<GridImp, MomentBasisImp, reconstruct>
+{
+  using BaseType = SourceBeamPnTestCase<GridImp, MomentBasisImp, reconstruct>;
+  using RangeFieldType = typename BaseType::RangeFieldType;
+  using typename BaseType::E;
+  using ProblemType = PointSourcePn<E, MomentBasisImp>;
+  static constexpr RangeFieldType t_end = 0.1;
+  static constexpr bool reconstruction = reconstruct;
+  using ExpectedResultsType = PointSourcePnExpectedResults<MomentBasisImp, reconstruction>;
+};
+
+// CheckerboardPn
+template <class MomentBasisImp, bool reconstruct>
+struct CheckerboardPnExpectedResults
+{
+  static constexpr double l1norm = 0.;
+  static constexpr double l2norm = 0.;
+  static constexpr double linfnorm = 0;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct CheckerboardPnExpectedResults<RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, reconstruct>
+{
+  static constexpr double l1norm = 0.35405006483527851;
+  static constexpr double l2norm = 0.32921416691428851;
+  static constexpr double linfnorm = 0.32895256210981677;
+  static constexpr double tol = 1e-9;
+};
+
+template <class GridImp, class MomentBasisImp, bool reconstruct>
+struct CheckerboardPnTestCase : SourceBeamPnTestCase<GridImp, MomentBasisImp, reconstruct>
+{
+  using BaseType = SourceBeamPnTestCase<GridImp, MomentBasisImp, reconstruct>;
+  using RangeFieldType = typename BaseType::RangeFieldType;
+  using typename BaseType::E;
+  using ProblemType = CheckerboardPn<E, MomentBasisImp>;
+  static constexpr RangeFieldType t_end = 0.1;
+  static constexpr bool reconstruction = reconstruct;
+  using ExpectedResultsType = CheckerboardPnExpectedResults<MomentBasisImp, reconstruction>;
+};
+
+// ShadowPn
+template <class MomentBasisImp, bool reconstruct>
+struct ShadowPnExpectedResults
+{
+  static constexpr double l1norm = 0.;
+  static constexpr double l2norm = 0.;
+  static constexpr double linfnorm = 0;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct ShadowPnExpectedResults<RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, reconstruct>
+{
+  static constexpr double l1norm = 0.59263334787808175;
+  static constexpr double l2norm = 0.097679818213367978;
+  static constexpr double linfnorm = 0.016484487060897713;
+  static constexpr double tol = 1e-9;
+};
+
+template <class GridImp, class MomentBasisImp, bool reconstruct>
+struct ShadowPnTestCase : SourceBeamPnTestCase<GridImp, MomentBasisImp, reconstruct>
+{
+  using BaseType = SourceBeamPnTestCase<GridImp, MomentBasisImp, reconstruct>;
+  using RangeFieldType = typename BaseType::RangeFieldType;
+  using typename BaseType::E;
+  using ProblemType = ShadowPn<E, MomentBasisImp>;
+  static constexpr RangeFieldType t_end = 0.1;
+  static constexpr bool reconstruction = reconstruct;
+  using ExpectedResultsType = ShadowPnExpectedResults<MomentBasisImp, reconstruction>;
+};
+
+
+// PointSourceMn
+template <class MomentBasisImp, bool reconstruct, bool kinetic_scheme = false>
+struct PointSourceMnExpectedResults
+{
+  static constexpr double l1norm = 0.;
+  static constexpr double l2norm = 0.;
+  static constexpr double linfnorm = 0.;
+  static constexpr double tol = 0.;
+};
+
+template <bool reconstruct>
+struct PointSourceMnExpectedResults<RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, reconstruct, false>
+{
+  static constexpr double l1norm = reconstruct ? 1.0000013830443908 : 1.0000013830442143;
+  static constexpr double l2norm = reconstruct ? 2.6901467570598112 : 2.684314243798307;
+  static constexpr double linfnorm = reconstruct ? 10.371048798431969 : 10.377307670780343;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct PointSourceMnExpectedResults<
+    RealSphericalHarmonicsMomentBasis<double, double, 2, 3, false, EntropyType::BoseEinstein>,
+    reconstruct,
+    false>
+{
+  static constexpr double l1norm = reconstruct ? 1.0000013830443903 : 0.;
+  static constexpr double l2norm = reconstruct ? 2.6909504479323516 : 0.;
+  static constexpr double linfnorm = reconstruct ? 10.375951173911345 : 0.;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct PointSourceMnExpectedResults<RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, reconstruct, true>
+{
+  static constexpr double l1norm = reconstruct ? 1674.9008041695579 : 1585.7044225325101;
+  static constexpr double l2norm = reconstruct ? 619.41343145125302 : 589.93299566257235;
+  static constexpr double linfnorm = reconstruct ? 264.16080528868997 : 266.5453598444696;
+  static constexpr double tol = 1e-5;
+};
+
+template <bool reconstruct>
+struct PointSourceMnExpectedResults<HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, reconstruct, false>
+{
+  static constexpr double l1norm = reconstruct ? 1.0000000829624791 : 1.0000000829622864;
+  static constexpr double l2norm = reconstruct ? 2.694751941188763 : 2.6892684619955305;
+  static constexpr double linfnorm = reconstruct ? 10.379060444346454 : 10.395305896397684;
+  // The matrices in this test case all have eigenvalues [+-0.808311035811965, 0, 0, 0, 0].
+  // Thus, the eigenvectors are not unique, and the eigensolvers are extremely sensitive
+  // to numerical errors. A difference of 1e-16 in the jacobians entries suffices to
+  // result in completely different eigenvectors. In all cases, the eigenvectors are
+  // valid eigenvectors to the correct eigenvalues. However, this difference in the
+  // eigendecomposition leads to differences in the results with linear reconstruction
+  // that are larger than would be expected by pure numerical errors.
+  static constexpr double tol = reconstruct ? 1e-5 : 1e-9;
+};
+
+template <bool reconstruct>
+struct PointSourceMnExpectedResults<HatFunctionMomentBasis<double, 3, double, 0, 1, 3, EntropyType::BoseEinstein>,
+                                    reconstruct,
+                                    false>
+{
+  static constexpr double l1norm = reconstruct ? 1.0000000829624884 : 1.0000000829622837;
+  static constexpr double l2norm = reconstruct ? 2.694161596061091 : 2.6895958084783342;
+  static constexpr double linfnorm = reconstruct ? 10.377805677445533 : 10.396217979398697;
+  static constexpr double tol = reconstruct ? 1e-5 : 1e-9;
+};
+
+template <bool reconstruct>
+struct PointSourceMnExpectedResults<HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, reconstruct, true>
+{
+  static constexpr double l1norm = reconstruct ? 818.73622981959204 : 781.1965079003387;
+  static constexpr double l2norm = reconstruct ? 301.48148670872598 : 289.21738528985526;
+  static constexpr double linfnorm = reconstruct ? 125.7102296804655 : 125.71014355518233;
+  static constexpr double tol = 1e-5;
+};
+
+template <bool reconstruct>
+struct PointSourceMnExpectedResults<PartialMomentBasis<double, 3, double, 0, 1, 3, 1>, reconstruct, false>
+{
+  static constexpr double l1norm = reconstruct ? 1.0000000829624787 : 1.0000000829623072;
+  static constexpr double l2norm = reconstruct ? 2.6983516853120966 : 2.6881937835020211;
+  static constexpr double linfnorm = reconstruct ? 10.391142640527102 : 10.394108065213185;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct PointSourceMnExpectedResults<PartialMomentBasis<double, 3, double, 0, 1, 3, 1, EntropyType::BoseEinstein>,
+                                    reconstruct,
+                                    false>
+{
+  static constexpr double l1norm = reconstruct ? 1.0000000829624796 : 0.;
+  static constexpr double l2norm = reconstruct ? 2.6983603000374528 : 0.;
+  static constexpr double linfnorm = reconstruct ? 10.391283146511036 : 0.;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct PointSourceMnExpectedResults<PartialMomentBasis<double, 3, double, 0, 1, 3, 1>, reconstruct, true>
+{
+  static constexpr double l1norm = reconstruct ? 1167.5985275432627 : 1126.5174600848904;
+  static constexpr double l2norm = reconstruct ? 428.15220455351266 : 415.19451310103005;
+  static constexpr double linfnorm = reconstruct ? 188.26590907577059 : 191.48910050886076;
+  static constexpr double tol = 1e-5;
+};
+
+template <class GridImp, class MomentBasisImp, bool reconstruct, bool kinetic_scheme = false>
+struct PointSourceMnTestCase : SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct, kinetic_scheme>
+{
+  using BaseType = SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct, kinetic_scheme>;
+  using typename BaseType::GridViewType;
+  using ProblemType = PointSourceMn<GridViewType, MomentBasisImp>;
+  using typename BaseType::RangeFieldType;
+  static constexpr RangeFieldType t_end = 0.1;
+  static constexpr bool reconstruction = reconstruct;
+  using ExpectedResultsType = PointSourceMnExpectedResults<MomentBasisImp, reconstruction, kinetic_scheme>;
+  using QuadratureChooserType = QuadratureChooser<MomentBasisImp>;
+  static constexpr size_t quad_order = QuadratureChooserType::quad_order;
+  static constexpr size_t quad_refinements = QuadratureChooserType::quad_refinements;
+  using RealizabilityLimiterChooserType = RealizabilityLimiterChooser<GridViewType,
+                                                                      MomentBasisImp,
+                                                                      typename ProblemType::FluxType,
+                                                                      typename BaseType::DiscreteFunctionType>;
+};
+
+
+// CheckerboardMn
+template <class MomentBasisImp, bool reconstruct, bool kinetic_scheme = false>
+struct CheckerboardMnExpectedResults;
+
+template <bool reconstruct>
+struct CheckerboardMnExpectedResults<RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, reconstruct, false>
+{
+  static constexpr double l1norm = reconstruct ? 0. : 0.35404509573284748;
+  static constexpr double l2norm = reconstruct ? 0. : 0.32922954029850499;
+  static constexpr double linfnorm = reconstruct ? 0. : 0.32896894056609421;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct CheckerboardMnExpectedResults<RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, reconstruct, true>
+{
+  static constexpr double l1norm = reconstruct ? 0. : 0.;
+  static constexpr double l2norm = reconstruct ? 0. : 0.;
+  static constexpr double linfnorm = reconstruct ? 0. : 0.;
+  static constexpr double tol = 1e-5;
+};
+
+template <bool reconstruct>
+struct CheckerboardMnExpectedResults<HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, reconstruct, false>
+{
+  static constexpr double l1norm = reconstruct ? 0. : 0.;
+  static constexpr double l2norm = reconstruct ? 0. : 0.;
+  static constexpr double linfnorm = reconstruct ? 0. : 0.;
+  static constexpr double tol = 1e-9;
+};
+
+template <bool reconstruct>
+struct CheckerboardMnExpectedResults<HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, reconstruct, true>
+{
+  static constexpr double l1norm = reconstruct ? 42799.981949017187 : 0.;
+  static constexpr double l2norm = reconstruct ? 2318.887531040597 : 0.;
+  static constexpr double linfnorm = reconstruct ? 131.24293776745421 : 0.;
+  static constexpr double tol = 1e-5;
+};
+
+template <class GridImp, class MomentBasisImp, bool reconstruct, bool kinetic_scheme = false>
+struct CheckerboardMnTestCase : SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct, kinetic_scheme>
+{
+  using BaseType = SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct, kinetic_scheme>;
+  using typename BaseType::GridViewType;
+  using ProblemType = CheckerboardMn<GridViewType, MomentBasisImp>;
+  using typename BaseType::RangeFieldType;
+  static constexpr RangeFieldType t_end = 0.1;
+  static constexpr bool reconstruction = reconstruct;
+  using ExpectedResultsType = CheckerboardMnExpectedResults<MomentBasisImp, reconstruction, kinetic_scheme>;
+  using QuadratureChooserType = QuadratureChooser<MomentBasisImp>;
+  static constexpr size_t quad_order = QuadratureChooserType::quad_order;
+  static constexpr size_t quad_refinements = QuadratureChooserType::quad_refinements;
+  using RealizabilityLimiterChooserType = RealizabilityLimiterChooser<GridViewType,
+                                                                      MomentBasisImp,
+                                                                      typename ProblemType::FluxType,
+                                                                      typename BaseType::DiscreteFunctionType>;
+};
+
+
+// ShadowMn
+template <class MomentBasisImp, bool reconstruct>
+struct ShadowMnExpectedResults;
+
+template <bool reconstruct>
+struct ShadowMnExpectedResults<RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, reconstruct>
+{
+  static constexpr double l1norm = reconstruct ? 0. : 0.59248402251960053;
+  static constexpr double l2norm = reconstruct ? 0. : 0.097644561106262767;
+  static constexpr double linfnorm = reconstruct ? 0. : 0.016480889201743513;
+  static constexpr double tol = 1e-9;
+};
+
+
+template <class GridImp, class MomentBasisImp, bool reconstruct>
+struct ShadowMnTestCase : SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct>
+{
+  using BaseType = SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct>;
+  using typename BaseType::GridViewType;
+  using ProblemType = ShadowMn<GridViewType, MomentBasisImp>;
+  using typename BaseType::RangeFieldType;
+  static constexpr RangeFieldType t_end = 0.1;
+  static constexpr bool reconstruction = reconstruct;
+  using ExpectedResultsType = ShadowMnExpectedResults<MomentBasisImp, reconstruction>;
+  using QuadratureChooserType = QuadratureChooser<MomentBasisImp>;
+  static constexpr size_t quad_order = QuadratureChooserType::quad_order;
+  static constexpr size_t quad_refinements = QuadratureChooserType::quad_refinements;
+  using RealizabilityLimiterChooserType = RealizabilityLimiterChooser<GridViewType,
+                                                                      MomentBasisImp,
+                                                                      typename ProblemType::FluxType,
+                                                                      typename BaseType::DiscreteFunctionType>;
+};
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_KINETICTRANSPORT_TESTCASES_HH
diff --git a/dune/gdt/test/momentmodels/mn-discretization.hh b/dune/gdt/test/momentmodels/mn-discretization.hh
new file mode 100644
index 0000000000000000000000000000000000000000..2458bd8f449ec51076802e604de7fa36333859ed
--- /dev/null
+++ b/dune/gdt/test/momentmodels/mn-discretization.hh
@@ -0,0 +1,265 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
+// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+// Authors:
+//   Tobias Leibner  (2016)
+
+#ifndef DUNE_GDT_TEST_HYPERBOLIC_MN_DISCRETIZATION_HH
+#define DUNE_GDT_TEST_HYPERBOLIC_MN_DISCRETIZATION_HH
+
+#include <chrono>
+
+#include <dune/xt/common/parallel/threadmanager.hh>
+#include <dune/xt/common/string.hh>
+#include <dune/xt/common/test/gtest/gtest.h>
+
+#include <dune/xt/grid/information.hh>
+#include <dune/xt/grid/gridprovider.hh>
+
+#include <dune/xt/la/container.hh>
+
+#include <dune/gdt/discretefunction/default.hh>
+#include <dune/gdt/operators/advection-fv-entropybased.hh>
+#include <dune/gdt/operators/advection-fv.hh>
+#include <dune/gdt/interpolations/default.hh>
+#include <dune/gdt/test/momentmodels/entropysolver.hh>
+#include <dune/gdt/local/numerical-fluxes/kinetic.hh>
+#include <dune/gdt/local/operators/advection-fv.hh>
+#include <dune/gdt/spaces/l2/finite-volume.hh>
+#include <dune/gdt/tools/timestepper/fractional-step.hh>
+#include <dune/gdt/tools/timestepper/explicit-rungekutta.hh>
+#include <dune/gdt/tools/timestepper/matrix-exponential-kinetic-isotropic.hh>
+
+#include <dune/gdt/test/momentmodels/kineticequation.hh>
+
+#include "pn-discretization.hh"
+
+template <class TestCaseType>
+struct HyperbolicMnDiscretization
+{
+  // returns: (l1norm, l2norm, linfnorm, MPI rank)
+  static std::pair<Dune::FieldVector<double, 3>, int> run(size_t num_save_steps = 1,
+                                                          size_t num_output_steps = 0,
+                                                          size_t quad_order = size_t(-1),
+                                                          size_t quad_refinements = size_t(-1),
+                                                          std::string grid_size = "",
+                                                          size_t overlap_size = 2,
+                                                          double t_end = 0.,
+                                                          std::string filename = "",
+                                                          bool disable_thread_cache = false)
+  {
+    using namespace Dune;
+    using namespace Dune::GDT;
+
+    //******************* get typedefs and constants from ProblemType **********************//
+    using MomentBasis = typename TestCaseType::MomentBasis;
+    using DiscreteFunctionType = typename TestCaseType::DiscreteFunctionType;
+    using GridType = typename TestCaseType::GridType;
+    using SpaceType = typename TestCaseType::SpaceType;
+    using AdvectionSourceSpaceType = typename TestCaseType::AdvectionSourceSpaceType;
+    using GV = typename TestCaseType::GridViewType;
+    using I = XT::Grid::extract_intersection_t<GV>;
+    using ProblemType = typename TestCaseType::ProblemType;
+    using RangeFieldType = typename MomentBasis::RangeFieldType;
+    using BoundaryValueType = typename ProblemType::BoundaryValueType;
+    static constexpr size_t dimDomain = MomentBasis::dimDomain;
+    static constexpr size_t dimRange = MomentBasis::dimRange;
+    using MatrixType = typename XT::LA::Container<RangeFieldType>::MatrixType;
+    using VectorType = typename XT::LA::Container<RangeFieldType>::VectorType;
+
+    //******************* create grid and FV space ***************************************
+    auto grid_config = ProblemType::default_grid_cfg();
+    if (!grid_size.empty())
+      grid_config["num_elements"] = grid_size;
+    grid_config["overlap_size"] = XT::Common::to_string(overlap_size);
+    const auto grid_ptr =
+        Dune::XT::Grid::CubeGridProviderFactory<GridType>::create(grid_config, MPIHelper::getCommunicator()).grid_ptr();
+    assert(grid_ptr->comm().size() == 1 || grid_ptr->overlapSize(0) > 0);
+    const GV grid_view(grid_ptr->leafGridView());
+    const SpaceType fv_space(grid_view);
+    const AdvectionSourceSpaceType advection_source_space(grid_view);
+
+    //******************* create EquationType object ***************************************
+    std::shared_ptr<const MomentBasis> basis_functions = std::make_shared<const MomentBasis>(
+        quad_order == size_t(-1) ? MomentBasis::default_quad_order() : quad_order,
+        quad_refinements == size_t(-1) ? MomentBasis::default_quad_refinements() : quad_refinements);
+    const std::unique_ptr<ProblemType> problem_ptr =
+        XT::Common::make_unique<ProblemType>(*basis_functions, grid_view, grid_config);
+    const auto& problem = *problem_ptr;
+    const auto initial_values = problem.initial_values();
+    const auto boundary_values = problem.boundary_values();
+    using AnalyticalFluxType = typename ProblemType::FluxType;
+    using EntropyFluxType = typename ProblemType::ActualFluxType;
+    auto analytical_flux = problem.flux();
+    // for Legendre polynomials and real spherical harmonics, the results are sensitive to the initial guess in the
+    // Newton algorithm. If the thread cache is enabled, the guess is different dependent on how many threads we are
+    // using, so for the tests we disable this cache to get reproducible results.
+    if (disable_thread_cache)
+      dynamic_cast<EntropyFluxType*>(analytical_flux.get())->disable_thread_cache();
+    const RangeFieldType CFL = problem.CFL();
+
+    // ***************** project initial values to discrete function *********************
+    // create a discrete function for the solution
+    DiscreteFunctionType u(fv_space, "solution");
+    // project initial values
+    default_interpolation(*initial_values, u, grid_view);
+
+    // ******************** choose flux and rhs operator and timestepper ******************************************
+
+    using AdvectionOperatorType = AdvectionFvOperator<MatrixType, GV, dimRange>;
+    using EigenvectorWrapperType = typename EigenvectorWrapperChooser<MomentBasis, AnalyticalFluxType>::type;
+    using EntropySolverType = EntropySolver<MomentBasis, SpaceType>;
+    //    using ReconstructionOperatorType =
+    //        LinearReconstructionOperator<AnalyticalFluxType, BoundaryValueType, GV, MatrixType,
+    //        EigenvectorWrapperType>;
+    using ReconstructionOperatorType = PointwiseLinearReconstructionOperator<AnalyticalFluxType,
+                                                                             BoundaryValueType,
+                                                                             GV,
+                                                                             VectorType,
+                                                                             EigenvectorWrapperType>;
+    using ReconstructionAdvectionOperatorType =
+        AdvectionWithPointwiseReconstructionOperator<AdvectionOperatorType, ReconstructionOperatorType>;
+    using FvOperatorType = EntropyBasedMomentFvOperator<
+        std::conditional_t<TestCaseType::reconstruction, ReconstructionAdvectionOperatorType, AdvectionOperatorType>,
+        EntropySolverType>;
+    using OperatorTimeStepperType =
+        ExplicitRungeKuttaTimeStepper<FvOperatorType,
+                                      DiscreteFunctionType,
+                                      TimeStepperMethods::explicit_rungekutta_second_order_ssp>;
+    using RhsTimeStepperType = KineticIsotropicTimeStepper<DiscreteFunctionType, MomentBasis>;
+    using TimeStepperType = StrangSplittingTimeStepper<RhsTimeStepperType, OperatorTimeStepperType>;
+
+    // *************** Calculate dx and initial dt **************************************
+    Dune::XT::Grid::Dimensions<GV> dimensions(grid_view);
+    RangeFieldType dx = dimensions.entity_width.max();
+    if (dimDomain == 2)
+      dx /= std::sqrt(2);
+    if (dimDomain == 3)
+      dx /= std::sqrt(3);
+    RangeFieldType dt = CFL * dx;
+
+    // *********************** create operators and timesteppers ************************************
+    NumericalKineticFlux<GV, MomentBasis> numerical_flux(*analytical_flux, *basis_functions);
+    AdvectionOperatorType advection_operator(grid_view, numerical_flux, advection_source_space, fv_space);
+
+    // boundary treatment
+    using BoundaryOperator =
+        LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator<I, VectorType, GV, dimRange>;
+    using LambdaType = typename BoundaryOperator::LambdaType;
+    using StateType = typename BoundaryOperator::StateType;
+    LambdaType boundary_lambda =
+        [&boundary_values](const I& intersection,
+                           const FieldVector<RangeFieldType, dimDomain - 1>& xx_in_reference_intersection_coordinates,
+                           const AnalyticalFluxType& /*flux*/,
+                           const StateType& /*u*/,
+                           const XT::Common::Parameter& /*param*/) {
+          return boundary_values->evaluate(intersection.geometry().global(xx_in_reference_intersection_coordinates));
+        };
+    XT::Grid::ApplyOn::NonPeriodicBoundaryIntersections<GV> filter;
+    advection_operator.append(boundary_lambda, {}, filter);
+
+    constexpr double epsilon = 1e-11;
+    auto slope = TestCaseType::RealizabilityLimiterChooserType::template make_slope<EigenvectorWrapperType>(
+        *dynamic_cast<EntropyFluxType*>(analytical_flux.get()), *basis_functions, epsilon);
+    ReconstructionOperatorType reconstruction_operator(*analytical_flux, *boundary_values, fv_space, *slope, false);
+    ReconstructionAdvectionOperatorType reconstruction_advection_operator(advection_operator, reconstruction_operator);
+
+    if (XT::Common::is_zero(t_end))
+      t_end = problem.t_end();
+
+    if (!filename.empty())
+      filename += "_";
+    filename += ProblemType::static_id();
+    filename += "_grid_" + grid_config["num_elements"];
+    filename += "_tend_" + XT::Common::to_string(t_end);
+    filename += "_quad_" + XT::Common::to_string(quad_order);
+    filename += MomentBasis::entropy == EntropyType::MaxwellBoltzmann ? "_MaxwellBoltzmann_" : "_BoseEinstein_";
+    filename += TestCaseType::reconstruction ? "_ord2" : "_ord1";
+    filename += "_" + basis_functions->mn_name();
+
+    EntropySolverType entropy_solver(*(dynamic_cast<EntropyFluxType*>(analytical_flux.get())),
+                                     fv_space,
+                                     problem.psi_vac() * basis_functions->unit_ball_volume() / 10,
+                                     filename);
+    FvOperatorType fv_operator(
+        FvOperatorChooser<TestCaseType::reconstruction>::choose(advection_operator, reconstruction_advection_operator),
+        entropy_solver);
+
+    // ******************************** do the time steps ***********************************************************
+    const auto sigma_a = problem.sigma_a();
+    const auto sigma_s = problem.sigma_s();
+    const auto Q = problem.Q();
+    OperatorTimeStepperType timestepper_op(fv_operator, u, -1.0);
+    RhsTimeStepperType timestepper_rhs(*basis_functions, u, *sigma_a, *sigma_s, *Q);
+    TimeStepperType timestepper(timestepper_rhs, timestepper_op);
+
+    auto begin_time = std::chrono::steady_clock::now();
+    timestepper.solve(t_end,
+                      dt,
+                      num_save_steps,
+                      num_output_steps,
+                      false,
+                      true,
+                      true,
+                      false,
+                      filename,
+                      *basis_functions->visualizer(),
+                      basis_functions->stringifier());
+    auto end_time = std::chrono::steady_clock::now();
+    std::chrono::duration<double> time_diff = end_time - begin_time;
+    if (grid_view.comm().rank() == 0)
+      std::cout << "Solving took: " << XT::Common::to_string(time_diff.count(), 15) << " s" << std::endl;
+
+    auto ret = std::make_pair(FieldVector<double, 3>(0.), int(0));
+    double& l1norm = ret.first[0];
+    double& l2norm = ret.first[1];
+    double& linfnorm = ret.first[2];
+    ret.second = grid_view.comm().rank();
+    const auto& current_sol = timestepper.current_solution();
+    const auto local_sol = current_sol.local_function();
+    for (const auto& entity : elements(grid_view, Dune::Partitions::interior)) {
+      local_sol->bind(entity);
+      const auto val = local_sol->evaluate(entity.geometry().local(entity.geometry().center()));
+      RangeFieldType psi = basis_functions->density(val);
+      l1norm += std::abs(psi) * entity.geometry().volume();
+      l2norm += std::pow(psi, 2) * entity.geometry().volume();
+      linfnorm = std::max(std::abs(psi), linfnorm);
+    }
+    l1norm = grid_view.comm().sum(l1norm);
+    l2norm = grid_view.comm().sum(l2norm);
+    linfnorm = grid_view.comm().max(linfnorm);
+    l2norm = std::sqrt(l2norm);
+    return ret;
+  }
+};
+
+template <class TestCaseType>
+struct HyperbolicMnTest
+  : public HyperbolicMnDiscretization<TestCaseType>
+  , public ::testing::Test
+{
+  void run(const double tol = TestCaseType::ExpectedResultsType::tol)
+  {
+    auto norms = HyperbolicMnDiscretization<TestCaseType>::run(
+                     1,
+                     0,
+                     TestCaseType::quad_order,
+                     TestCaseType::quad_refinements,
+                     "",
+                     2,
+                     TestCaseType::t_end,
+                     "test",
+                     Dune::GDT::is_full_moment_basis<typename TestCaseType::MomentBasis>::value)
+                     .first;
+    const double l1norm = norms[0];
+    const double l2norm = norms[1];
+    const double linfnorm = norms[2];
+    using ResultsType = typename TestCaseType::ExpectedResultsType;
+    EXPECT_NEAR(ResultsType::l1norm, l1norm, ResultsType::l1norm * tol);
+    EXPECT_NEAR(ResultsType::l2norm, l2norm, ResultsType::l2norm * tol);
+    EXPECT_NEAR(ResultsType::linfnorm, linfnorm, ResultsType::linfnorm * tol);
+  }
+};
+
+#endif // DUNE_GDT_TEST_HYPERBOLIC_MN_DISCRETIZATION_HH
diff --git a/dune/gdt/test/momentmodels/pn-discretization.hh b/dune/gdt/test/momentmodels/pn-discretization.hh
new file mode 100644
index 0000000000000000000000000000000000000000..f446d49e371c054aa440a8a2c41f020747765a09
--- /dev/null
+++ b/dune/gdt/test/momentmodels/pn-discretization.hh
@@ -0,0 +1,390 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
+// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
+// Authors:
+//   Tobias Leibner  (2016)
+
+#ifndef DUNE_GDT_TEST_HYPERBOLIC_PN_DISCRETIZATION_HH
+#define DUNE_GDT_TEST_HYPERBOLIC_PN_DISCRETIZATION_HH
+
+#include <dune/common/exceptions.hh>
+
+#include <dune/xt/common/parallel/threadmanager.hh>
+#include <dune/xt/common/string.hh>
+#include <dune/xt/common/test/gtest/gtest.h>
+
+#include <dune/xt/grid/information.hh>
+#include <dune/xt/grid/gridprovider.hh>
+
+#include <dune/xt/la/container.hh>
+
+#include <dune/xt/functions/checkerboard.hh>
+
+#include <dune/gdt/discretefunction/default.hh>
+#include <dune/gdt/operators/advection-fv.hh>
+#include <dune/gdt/operators/advection-with-reconstruction.hh>
+#include <dune/gdt/operators/reconstruction/linear.hh>
+#include <dune/gdt/interpolations/default.hh>
+#include <dune/gdt/local/numerical-fluxes/kinetic.hh>
+#include <dune/gdt/local/numerical-fluxes/lax-friedrichs.hh>
+#include <dune/gdt/local/operators/advection-fv.hh>
+#include <dune/gdt/tools/timestepper/fractional-step.hh>
+#include <dune/gdt/tools/timestepper/explicit-rungekutta.hh>
+#include <dune/gdt/tools/timestepper/matrix-exponential-kinetic-isotropic.hh>
+
+#include <dune/gdt/test/momentmodels/kineticequation.hh>
+
+void parse_momentmodel_arguments(int argc,
+                                 char** argv,
+                                 size_t& num_threads,
+                                 size_t& threading_partition_factor,
+                                 size_t& num_save_steps,
+                                 size_t& num_output_steps,
+                                 size_t& quad_order,
+                                 size_t& quad_refinements,
+                                 std::string& grid_size,
+                                 size_t& overlap_size,
+                                 double& t_end,
+                                 std::string& filename)
+{
+  using namespace Dune;
+  using namespace Dune::GDT;
+  MPIHelper::instance(argc, argv);
+
+  // default values
+  num_threads = 1;
+  threading_partition_factor = 1;
+  num_save_steps = 10;
+  num_output_steps = num_save_steps;
+  quad_order = -1;
+  quad_refinements = -1;
+  grid_size = "";
+  overlap_size = 2;
+  t_end = 0.;
+  filename = "";
+
+  for (int i = 1; i < argc; ++i) {
+    if (std::string(argv[i]) == "--num_threads") {
+      if (i + 1 < argc)
+        num_threads = XT::Common::from_string<size_t>(argv[++i]);
+      else
+        DUNE_THROW(Dune::IOError, "--num_threads option requires one argument.");
+    } else if (std::string(argv[i]) == "--threading_partition_factor") {
+      if (i + 1 < argc)
+        threading_partition_factor = XT::Common::from_string<size_t>(argv[++i]);
+      else
+        DUNE_THROW(Dune::IOError, "--threading_partition_factor option requires one argument.");
+    } else if (std::string(argv[i]) == "--filename") {
+      if (i + 1 < argc)
+        filename = std::string(argv[++i]);
+      else
+        DUNE_THROW(Dune::IOError, "--filename option requires one argument.");
+    } else if (std::string(argv[i]) == "--quad_order") {
+      if (i + 1 < argc)
+        quad_order = XT::Common::from_string<size_t>(argv[++i]);
+      else
+        DUNE_THROW(Dune::IOError, "--quad_order option requires one argument.");
+    } else if (std::string(argv[i]) == "--quad_refinements") {
+      if (i + 1 < argc)
+        quad_refinements = XT::Common::from_string<size_t>(argv[++i]);
+      else
+        DUNE_THROW(Dune::IOError, "--quad_refinements option requires one argument.");
+    } else if (std::string(argv[i]) == "--num_save_steps") {
+      if (i + 1 < argc)
+        num_save_steps = XT::Common::from_string<size_t>(argv[++i]);
+      else
+        DUNE_THROW(Dune::IOError, "--num_save_steps option requires one argument.");
+    } else if (std::string(argv[i]) == "--num_output_steps") {
+      if (i + 1 < argc)
+        num_output_steps = XT::Common::from_string<size_t>(argv[++i]);
+      else
+        DUNE_THROW(Dune::IOError, "--num_output_steps option requires one argument.");
+    } else if (std::string(argv[i]) == "--grid_size") {
+      if (i + 1 < argc)
+        grid_size = argv[++i];
+      else
+        DUNE_THROW(Dune::IOError, "--grid_size option requires one argument.");
+    } else if (std::string(argv[i]) == "--overlap_size") {
+      if (i + 1 < argc)
+        overlap_size = XT::Common::from_string<size_t>(argv[++i]);
+      else
+        DUNE_THROW(Dune::IOError, "--overlap_size option requires one argument.");
+    } else if (std::string(argv[i]) == "--t_end") {
+      if (i + 1 < argc)
+        t_end = XT::Common::from_string<double>(argv[++i]);
+      else
+        DUNE_THROW(Dune::IOError, "--t_end option requires one argument.");
+    } else {
+      DUNE_THROW(Dune::IOError, "Unknown option " + std::string(argv[i]));
+    }
+  }
+
+  DXTC_CONFIG.set("threading.partition_factor", threading_partition_factor, true);
+  XT::Common::threadManager().set_max_threads(num_threads);
+}
+
+
+template <class MomentBasis, class AnalyticalFluxType>
+struct EigenvectorWrapperChooser
+{
+  using type = Dune::GDT::internal::EigenvectorWrapper<AnalyticalFluxType>;
+};
+
+template <class AnalyticalFluxType,
+          class DomainFieldType,
+          size_t dimDomain,
+          class RangeFieldType,
+          size_t dimRange_or_refinements,
+          Dune::GDT::EntropyType entropy>
+struct EigenvectorWrapperChooser<Dune::GDT::PartialMomentBasis<DomainFieldType,
+                                                               dimDomain,
+                                                               RangeFieldType,
+                                                               dimRange_or_refinements,
+                                                               1,
+                                                               dimDomain,
+                                                               1,
+                                                               entropy>,
+                                 AnalyticalFluxType>
+{
+  using type = Dune::GDT::internal::BlockedEigenvectorWrapper<AnalyticalFluxType>;
+};
+
+template <bool reconstruction>
+struct FvOperatorChooser
+{
+  template <class AdvectionOperatorType, class ReconstructionOperatorType>
+  static ReconstructionOperatorType& choose(AdvectionOperatorType& /*advection_operator*/,
+                                            ReconstructionOperatorType& reconstruction_operator)
+  {
+    return reconstruction_operator;
+  }
+};
+
+template <>
+struct FvOperatorChooser<false>
+{
+  template <class AdvectionOperatorType, class ReconstructionOperatorType>
+  static AdvectionOperatorType& choose(AdvectionOperatorType& advection_operator,
+                                       ReconstructionOperatorType& /*reconstruction_operator*/)
+  {
+    return advection_operator;
+  }
+};
+
+#ifndef USE_FULL_LINEAR_RECONSTRUCTION_OPERATOR
+#  define USE_FULL_LINEAR_RECONSTRUCTION_OPERATOR 0
+#endif
+
+template <class TestCaseType>
+struct HyperbolicPnDiscretization
+{
+
+  Dune::FieldVector<double, 3> run(size_t num_save_steps = 1,
+                                   size_t num_output_steps = 0,
+                                   size_t quad_order = size_t(-1),
+                                   size_t quad_refinements = size_t(-1),
+                                   std::string grid_size = "",
+                                   size_t overlap_size = 2,
+                                   double t_end = 0.,
+                                   std::string filename = "")
+  {
+    using namespace Dune;
+    using namespace Dune::GDT;
+
+    //******************* get typedefs and constants from ProblemType **********************//
+    using MomentBasis = typename TestCaseType::MomentBasis;
+    using DiscreteFunctionType = typename TestCaseType::DiscreteFunctionType;
+    using GridType = typename TestCaseType::GridType;
+    using SpaceType = typename TestCaseType::SpaceType;
+    using AdvectionSourceSpaceType = typename TestCaseType::AdvectionSourceSpaceType;
+    using GV = typename TestCaseType::GridViewType;
+    using I = XT::Grid::extract_intersection_t<GV>;
+    using E = XT::Grid::extract_entity_t<GV>;
+    using ProblemType = typename TestCaseType::ProblemType;
+    using RangeFieldType = typename ProblemType::RangeFieldType;
+    using BoundaryValueType = typename ProblemType::BoundaryValueType;
+    static constexpr size_t dimDomain = MomentBasis::dimDomain;
+    static constexpr size_t dimRange = MomentBasis::dimRange;
+    using MatrixType = typename XT::LA::Container<RangeFieldType>::MatrixType;
+    using VectorType = typename XT::LA::Container<RangeFieldType>::VectorType;
+
+    //******************* create grid and FV space ***************************************
+    auto grid_config = ProblemType::default_grid_cfg();
+    if (!grid_size.empty())
+      grid_config["num_elements"] = grid_size;
+    grid_config["overlap_size"] = XT::Common::to_string(overlap_size);
+    const auto grid_ptr =
+        Dune::XT::Grid::CubeGridProviderFactory<GridType>::create(grid_config, MPIHelper::getCommunicator()).grid_ptr();
+    assert(grid_ptr->comm().size() == 1 || grid_ptr->overlapSize(0) > 0);
+    const GV grid_view(grid_ptr->leafGridView());
+    const SpaceType fv_space(grid_view);
+    const AdvectionSourceSpaceType advection_source_space(grid_view);
+
+    //******************* create EquationType object ***************************************
+    std::shared_ptr<const MomentBasis> basis_functions = std::make_shared<const MomentBasis>(
+        quad_order == size_t(-1) ? MomentBasis::default_quad_order() : quad_order,
+        quad_refinements == size_t(-1) ? MomentBasis::default_quad_refinements() : quad_refinements);
+    const std::unique_ptr<ProblemType> problem_ptr =
+        XT::Common::make_unique<ProblemType>(*basis_functions, grid_config);
+    const auto& problem = *problem_ptr;
+    const auto initial_values = problem.initial_values();
+    const auto boundary_values = problem.boundary_values();
+    const RangeFieldType CFL = problem.CFL();
+
+    // ***************** project initial values to discrete function *********************
+    // create a discrete function for the solution
+    const size_t vec_size = fv_space.mapper().size();
+    // we do very few whole-container operations with this vec, so using that many mutexes improves performance as it
+    // avoids locking
+    const size_t num_mutexes = XT::Common::threadManager().max_threads() * 100;
+    typename DiscreteFunctionType::VectorType vec(vec_size, 0., num_mutexes);
+    DiscreteFunctionType u(fv_space, vec, "solution");
+    // project initial values
+    default_interpolation(*initial_values, u, grid_view);
+
+    // ************************* create analytical flux object ***************************************
+    using AnalyticalFluxType = typename ProblemType::FluxType;
+    const auto analytical_flux = problem.flux();
+
+    // ******************** choose flux and rhs operator and timestepper ******************************************
+    using AdvectionOperatorType = AdvectionFvOperator<MatrixType, GV, dimRange>;
+    using EigenvectorWrapperType = typename EigenvectorWrapperChooser<MomentBasis, AnalyticalFluxType>::type;
+    using ReconstructionOperatorType =
+#if USE_FULL_LINEAR_RECONSTRUCTION_OPERATOR
+        LinearReconstructionOperator<AnalyticalFluxType, BoundaryValueType, GV, MatrixType, EigenvectorWrapperType>;
+#else
+        PointwiseLinearReconstructionOperator<AnalyticalFluxType,
+                                              BoundaryValueType,
+                                              GV,
+                                              VectorType,
+                                              EigenvectorWrapperType>;
+#endif
+
+    using ReconstructionFvOperatorType =
+#if USE_FULL_LINEAR_RECONSTRUCTION_OPERATOR
+        AdvectionWithReconstructionOperator<AdvectionOperatorType, ReconstructionOperatorType>;
+#else
+        AdvectionWithPointwiseReconstructionOperator<AdvectionOperatorType, ReconstructionOperatorType>;
+#endif
+    using FvOperatorType =
+        std::conditional_t<TestCaseType::reconstruction, ReconstructionFvOperatorType, AdvectionOperatorType>;
+    using OperatorTimeStepperType =
+        ExplicitRungeKuttaTimeStepper<FvOperatorType,
+                                      DiscreteFunctionType,
+                                      TimeStepperMethods::explicit_rungekutta_second_order_ssp>;
+    using RhsTimeStepperType = KineticIsotropicTimeStepper<DiscreteFunctionType, MomentBasis>;
+    using TimeStepperType = StrangSplittingTimeStepper<RhsTimeStepperType, OperatorTimeStepperType>;
+
+    // *************** choose t_end and initial dt **************************************
+    // calculate dx and choose initial dt
+    Dune::XT::Grid::Dimensions<GV> dimensions(grid_view);
+    RangeFieldType dx = dimensions.entity_width.max();
+    if (dimDomain == 2)
+      dx /= std::sqrt(2);
+    if (dimDomain == 3)
+      dx /= std::sqrt(3);
+    RangeFieldType dt = CFL * dx;
+
+    // *********************** create operators and timesteppers ************************************
+    NumericalKineticFlux<GV, MomentBasis> numerical_flux(*analytical_flux, *basis_functions);
+    //    NumericalLaxFriedrichsFlux<I, dimDomain, dimRange, RangeFieldType> numerical_flux(*analytical_flux, 1.);
+    AdvectionOperatorType advection_operator(grid_view, numerical_flux, advection_source_space, fv_space);
+    // boundary treatment
+    using BoundaryOperator =
+        LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator<I, VectorType, GV, dimRange>;
+    using LambdaType = typename BoundaryOperator::LambdaType;
+    using StateType = typename BoundaryOperator::StateType;
+    LambdaType boundary_lambda =
+        [&boundary_values](const I& intersection,
+                           const FieldVector<RangeFieldType, dimDomain - 1>& xx_in_reference_intersection_coordinates,
+                           const AnalyticalFluxType& /*flux*/,
+                           const StateType& /*u*/,
+                           const XT::Common::Parameter& /*param*/) {
+          return boundary_values->evaluate(intersection.geometry().global(xx_in_reference_intersection_coordinates));
+        };
+    XT::Grid::ApplyOn::NonPeriodicBoundaryIntersections<GV> filter;
+    advection_operator.append(boundary_lambda, {}, filter);
+
+    MinmodSlope<E, EigenvectorWrapperType> slope;
+    ReconstructionOperatorType reconstruction_operator(*analytical_flux, *boundary_values, fv_space, slope, true);
+
+    ReconstructionFvOperatorType reconstruction_fv_operator(advection_operator, reconstruction_operator);
+    FvOperatorType& fv_operator =
+        FvOperatorChooser<TestCaseType::reconstruction>::choose(advection_operator, reconstruction_fv_operator);
+
+    if (!filename.empty())
+      filename += "_";
+    filename += ProblemType::static_id();
+    filename += "_grid_" + grid_config["num_elements"];
+    filename += "_tend_" + XT::Common::to_string(t_end);
+    filename += TestCaseType::reconstruction ? "_ord2" : "_ord1";
+    filename += "_" + basis_functions->pn_name();
+
+    // ******************************** do the time steps ***********************************************************
+    const auto sigma_a = problem.sigma_a();
+    const auto sigma_s = problem.sigma_s();
+    const auto Q = problem.Q();
+    OperatorTimeStepperType timestepper_op(fv_operator, u, -1.0);
+    RhsTimeStepperType timestepper_rhs(*basis_functions, u, *sigma_a, *sigma_s, *Q);
+    TimeStepperType timestepper(timestepper_rhs, timestepper_op);
+
+    auto begin_time = std::chrono::steady_clock::now();
+    timestepper.solve(t_end,
+                      dt,
+                      num_save_steps,
+                      num_output_steps,
+                      false,
+                      true,
+                      true,
+                      false,
+                      filename,
+                      *basis_functions->visualizer(),
+                      basis_functions->stringifier());
+    auto end_time = std::chrono::steady_clock::now();
+    std::chrono::duration<double> time_diff = end_time - begin_time;
+    if (grid_view.comm().rank() == 0)
+      std::cout << "Solving took: " << XT::Common::to_string(time_diff.count(), 15) << " s" << std::endl;
+
+    FieldVector<double, 3> ret(0);
+    double& l1norm = ret[0];
+    double& l2norm = ret[1];
+    double& linfnorm = ret[2];
+    const auto& current_sol = timestepper.current_solution();
+    const auto local_sol = current_sol.local_function();
+    for (const auto& entity : elements(grid_view, Dune::Partitions::interior)) {
+      local_sol->bind(entity);
+      const auto val = local_sol->evaluate(entity.geometry().local(entity.geometry().center()));
+      RangeFieldType psi = basis_functions->density(val);
+      l1norm += std::abs(psi) * entity.geometry().volume();
+      l2norm += std::pow(psi, 2) * entity.geometry().volume();
+      linfnorm = std::max(std::abs(psi), linfnorm);
+    }
+    l1norm = grid_view.comm().sum(l1norm);
+    l2norm = grid_view.comm().sum(l2norm);
+    linfnorm = grid_view.comm().max(linfnorm);
+    l2norm = std::sqrt(l2norm);
+    return ret;
+  }
+};
+
+template <class TestCaseType>
+struct HyperbolicPnTest
+  : public HyperbolicPnDiscretization<TestCaseType>
+  , public ::testing::Test
+{
+  void run()
+  {
+    auto norms =
+        HyperbolicPnDiscretization<TestCaseType>::run(1, 0, size_t(-1), size_t(-1), "", 2, TestCaseType::t_end, "test");
+    const double l1norm = norms[0];
+    const double l2norm = norms[1];
+    const double linfnorm = norms[2];
+    using ResultsType = typename TestCaseType::ExpectedResultsType;
+    EXPECT_NEAR(ResultsType::l1norm, l1norm, ResultsType::l1norm * ResultsType::tol);
+    EXPECT_NEAR(ResultsType::l2norm, l2norm, ResultsType::l2norm * ResultsType::tol);
+    EXPECT_NEAR(ResultsType::linfnorm, linfnorm, ResultsType::linfnorm * ResultsType::tol);
+  }
+};
+
+#endif // DUNE_GDT_TEST_HYPERBOLIC_PN_DISCRETIZATION_HH
diff --git a/dune/gdt/test/momentmodels/triangulation.hh b/dune/gdt/test/momentmodels/triangulation.hh
new file mode 100644
index 0000000000000000000000000000000000000000..d70891f9cda028b8aaea981a138a19a57a015497
--- /dev/null
+++ b/dune/gdt/test/momentmodels/triangulation.hh
@@ -0,0 +1,477 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 (2017)
+//   Rene Milk       (2018)
+//   Tobias Leibner  (2017)
+
+#ifndef DUNE_GDT_MOMENTMODELS_TRIANGULATION_HH
+#define DUNE_GDT_MOMENTMODELS_TRIANGULATION_HH
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <mutex>
+
+#include <dune/common/typetraits.hh>
+
+#include <dune/geometry/quadraturerules.hh>
+
+#include <dune/xt/common/fvector.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+template <class FieldType, size_t dimDomain>
+class Vertex
+{
+public:
+  using DomainType = XT::Common::FieldVector<FieldType, dimDomain>;
+
+  Vertex() = default;
+
+  Vertex(const DomainType& pos, const size_t index)
+    : position_(pos)
+    , index_(index)
+  {}
+
+  const DomainType& position() const
+  {
+    return position_;
+  }
+
+  DomainType& position()
+  {
+    return position_;
+  }
+
+  size_t index() const
+  {
+    return index_;
+  }
+
+  void set_child_with(const std::shared_ptr<Vertex>& parent, std::shared_ptr<Vertex>& child)
+  {
+    children_.insert({parent->index(), child});
+  }
+
+  bool has_child_with(const std::shared_ptr<Vertex>& parent)
+  {
+    return children_.count(parent->index());
+  }
+
+  const std::shared_ptr<Vertex>& child(const std::shared_ptr<Vertex>& parent)
+  {
+    return children_[parent->index()];
+  }
+
+private:
+  DomainType position_;
+  size_t index_;
+  std::map<size_t, std::shared_ptr<Vertex>> children_;
+}; // class Vertex
+
+template <class RangeFieldImp = double>
+class SphericalTriangle
+{
+  using ThisType = SphericalTriangle;
+
+public:
+  using RangeFieldType = RangeFieldImp;
+  using VertexType = Vertex<RangeFieldType, 3>;
+  using DomainType = typename VertexType::DomainType;
+  using TriangulationVerticesVectorType = typename std::vector<std::shared_ptr<VertexType>>;
+  using VertexVectorType = typename XT::Common::FieldVector<std::shared_ptr<VertexType>, 3>;
+  using SubtrianglesVectorType = typename XT::Common::FieldVector<std::shared_ptr<ThisType>, 4>;
+  using QuadraturePointType = QuadraturePoint<RangeFieldType, 3>;
+  using QuadratureRuleType = QuadratureRule<RangeFieldType, 3>;
+  using FieldVectorType = XT::Common::FieldVector<RangeFieldType, 3>;
+
+  SphericalTriangle() = default;
+  SphericalTriangle(ThisType&& other) = default;
+  ThisType& operator=(ThisType&& other) = default;
+
+  SphericalTriangle(TriangulationVerticesVectorType& triangulation_vertices,
+                    const VertexVectorType& vertices,
+                    std::shared_ptr<size_t> current_vertex_index,
+                    std::shared_ptr<std::mutex> triangulation_vertices_mutex)
+    : triangulation_vertices_(triangulation_vertices)
+    , vertices_(vertices)
+    , current_vertex_index_(current_vertex_index)
+    , triangulation_vertices_mutex_(triangulation_vertices_mutex)
+  {}
+
+  SphericalTriangle(TriangulationVerticesVectorType& triangulation_vertices,
+                    const std::shared_ptr<VertexType> vertex_1,
+                    const std::shared_ptr<VertexType> vertex_2,
+                    const std::shared_ptr<VertexType> vertex_3,
+                    std::shared_ptr<size_t> current_vertex_index,
+                    std::shared_ptr<std::mutex> triangulation_vertices_mutex)
+    : triangulation_vertices_(triangulation_vertices)
+    , vertices_{vertex_1, vertex_2, vertex_3}
+    , current_vertex_index_(current_vertex_index)
+    , triangulation_vertices_mutex_(triangulation_vertices_mutex)
+  {}
+
+  const SubtrianglesVectorType& subtriangles() const
+  {
+    return subtriangles_;
+  }
+
+  SubtrianglesVectorType& subtriangles()
+  {
+    return subtriangles_;
+  }
+
+  const VertexVectorType& vertices()
+  {
+    return vertices_;
+  }
+
+  DomainType center() const
+  {
+    DomainType center = (vertices_[0]->position() + vertices_[1]->position() + vertices_[2]->position()) / 3.;
+    center /= center.two_norm();
+    return center;
+  }
+
+  QuadratureRuleType quadrature_rule(const QuadratureRule<RangeFieldType, 2>& reference_quadrature_rule) const
+  {
+    QuadratureRuleType quadrature_rule;
+    quadrature_rule.reserve(reference_quadrature_rule.size());
+    const FieldVectorType vertices_1_minus_0 = vertices_[1]->position() - vertices_[0]->position();
+    const FieldVectorType vertices_2_minus_0 = vertices_[2]->position() - vertices_[0]->position();
+    FieldVectorType ff, partial_s_gg, partial_t_gg;
+    for (const auto& quad_point : reference_quadrature_rule) {
+      const auto& ref_pos = quad_point.position();
+      const auto& ref_weight = quad_point.weight();
+      // map point to spherical triangle
+      ff = vertices_[0]->position() + ref_pos[0] * vertices_1_minus_0 + ref_pos[1] * vertices_2_minus_0;
+      const RangeFieldType norm_ff = ff.two_norm();
+      const RangeFieldType norm_ff_3 = std::pow(norm_ff, 3);
+      partial_s_gg = vertices_2_minus_0 / norm_ff - ff * ((ff * vertices_2_minus_0) / norm_ff_3);
+      partial_t_gg = vertices_1_minus_0 / norm_ff - ff * ((ff * vertices_1_minus_0) / norm_ff_3);
+      const RangeFieldType weight = XT::Common::cross_product(partial_s_gg, partial_t_gg).two_norm() * ref_weight;
+      quadrature_rule.emplace_back(ff / norm_ff, weight);
+    }
+    return quadrature_rule;
+  }
+
+  void initialize_subtriangles()
+  {
+    if (!(subtriangles_[0])) {
+      std::lock_guard<std::mutex> vertices_lock(*triangulation_vertices_mutex_);
+      VertexVectorType midpoints;
+      for (size_t ii = 0; ii < 3; ++ii) {
+        auto& vertex1 = vertices_[ii];
+        auto& vertex2 = vertices_[(1 + ii) % 3];
+        DomainType midpoint_position = vertex1->position() + vertex2->position();
+        midpoint_position /= midpoint_position.two_norm();
+        if (vertex1->has_child_with(vertex2)) {
+          midpoints[ii] = vertex1->child(vertex2);
+        } else {
+          triangulation_vertices_.emplace_back(
+              std::make_shared<VertexType>(midpoint_position, (*current_vertex_index_)++));
+          vertex1->set_child_with(vertex2, triangulation_vertices_.back());
+          vertex2->set_child_with(vertex1, triangulation_vertices_.back());
+          midpoints[ii] = triangulation_vertices_.back();
+        }
+      } // ii
+      subtriangles_[0] = std::make_shared<ThisType>(triangulation_vertices_,
+                                                    vertices_[0],
+                                                    midpoints[0],
+                                                    midpoints[2],
+                                                    current_vertex_index_,
+                                                    triangulation_vertices_mutex_);
+      subtriangles_[1] = std::make_shared<ThisType>(triangulation_vertices_,
+                                                    vertices_[1],
+                                                    midpoints[1],
+                                                    midpoints[0],
+                                                    current_vertex_index_,
+                                                    triangulation_vertices_mutex_);
+      subtriangles_[2] = std::make_shared<ThisType>(triangulation_vertices_,
+                                                    vertices_[2],
+                                                    midpoints[2],
+                                                    midpoints[1],
+                                                    current_vertex_index_,
+                                                    triangulation_vertices_mutex_);
+      subtriangles_[3] = std::make_shared<ThisType>(triangulation_vertices_,
+                                                    midpoints[0],
+                                                    midpoints[1],
+                                                    midpoints[2],
+                                                    current_vertex_index_,
+                                                    triangulation_vertices_mutex_);
+    }
+  } // initialize_subtriangles()
+
+private:
+  TriangulationVerticesVectorType& triangulation_vertices_;
+  const VertexVectorType vertices_;
+  SubtrianglesVectorType subtriangles_;
+  std::shared_ptr<size_t> current_vertex_index_;
+  std::shared_ptr<std::mutex> triangulation_vertices_mutex_;
+}; // class SphericalTriangle<...>
+
+template <class RangeFieldImp = double>
+class SphericalTriangulation
+{
+public:
+  using TriangleType = SphericalTriangle<RangeFieldImp>;
+  using TriangleVectorType = std::vector<std::shared_ptr<TriangleType>>;
+  using VertexVectorType = typename TriangleType::TriangulationVerticesVectorType;
+  using VertexType = typename TriangleType::VertexType;
+  using DomainType = typename VertexType::DomainType;
+
+  static QuadratureRule<RangeFieldImp, 2> barycentre_rule()
+  {
+    QuadratureRule<RangeFieldImp, 2> ret;
+    ret.push_back(QuadraturePoint<RangeFieldImp, 2>({1. / 3., 1. / 3.}, 0.5));
+    return ret;
+  }
+
+  SphericalTriangulation() {}
+
+  SphericalTriangulation(size_t num_refinements,
+                         const std::vector<DomainType>& initial_points =
+                             {{1., 0., 0.}, {-1., 0., 0.}, {0., 1., 0.}, {0., -1., 0.}, {0., 0., 1.}, {0., 0., -1.}})
+    : current_vertex_index_(std::make_shared<size_t>(0))
+    , all_vertices_mutex_(std::make_shared<std::mutex>())
+  {
+    calculate_faces(initial_points);
+    refine(num_refinements);
+  }
+
+  // Do not allow copying as the triangles hold references to the vectors in this class.
+  SphericalTriangulation(const SphericalTriangulation& other) = delete;
+  SphericalTriangulation(SphericalTriangulation&& other) = delete;
+  SphericalTriangulation& operator=(const SphericalTriangulation& other) = delete;
+  SphericalTriangulation& operator=(SphericalTriangulation&& other) = delete;
+
+  const TriangleVectorType& faces() const
+  {
+    return faces_;
+  }
+
+  TriangleVectorType& faces()
+  {
+    return faces_;
+  }
+
+  const VertexVectorType& vertices() const
+  {
+    return vertices_;
+  }
+
+  VertexVectorType& vertices()
+  {
+    return vertices_;
+  }
+
+  // get indices of all faces that contain point
+  std::vector<size_t> get_face_indices(const DomainType& v) const
+  {
+    std::vector<size_t> face_indices;
+    assert(XT::Common::FloatCmp::eq(v * v, 1.));
+    FieldMatrix<RangeFieldImp, 3, 3> vertices_matrix;
+    FieldMatrix<RangeFieldImp, 3, 3> determinant_matrix;
+    for (size_t kk = 0; kk < faces().size(); ++kk) {
+      const auto& face = faces_[kk];
+      bool v_in_this_face = false;
+      bool second_check = true;
+      const auto& vertices = face->vertices();
+      for (size_t ii = 0; ii < 3; ++ii) {
+        // if v is not on the same octant of the sphere as the vertices, return false
+        // assumes the triangulation is fine enough that vertices[ii]*vertices[jj] >= 0 for all triangles
+        const auto scalar_prod = v * vertices[ii]->position();
+        if (XT::Common::FloatCmp::lt(scalar_prod, 0.)) {
+          second_check = false;
+          break;
+        } else if (XT::Common::FloatCmp::eq(scalar_prod, 1.)) {
+          v_in_this_face = true;
+          second_check = false;
+          break;
+        }
+        vertices_matrix[ii] = vertices[ii]->position();
+      } // ii
+
+      if (second_check) {
+        // Vertices are ordered counterclockwise, so if the point is inside the spherical triangle,
+        // the coordinate system formed by two adjacent vertices and v is always right-handed, i.e.
+        // the triple product is positive.
+        // The triple products that need to be positive are the determinants of the matrices (v1, v2, v), (v2, v3, v),
+        // (v3, v1, v), where vi is the ith vertex. Swapping two columns changes the sign of det, the matrices used
+        // below all have an even number of column swaps.
+        // The determinant is 0 iff v is on the same plane as the two vertices. Then, to be on the edge of the
+        // current face, v has be in between the two vertices, i.e.  v = x * v1 + y * v2 with x, y >= 0. This is
+        // equivalent to v * v1 >= (v*v2)v2 * v1 && v * v2 >= (v*v1)v1 * v2 (the projection of v to v1 has to be
+        // greater than the projection of its v2-projection to v1).
+        v_in_this_face = true;
+        for (size_t ii = 0; ii < 3; ++ii) {
+          determinant_matrix = vertices_matrix;
+          determinant_matrix[ii] = v;
+          auto det = determinant_matrix.determinant();
+          if (XT::Common::FloatCmp::eq(det, 0.)) {
+            const auto& v1 = vertices_matrix[(ii + 1) % 3];
+            const auto& v2 = vertices_matrix[(ii + 2) % 3];
+            v_in_this_face = v * v1 > (v * v2) * (v2 * v1) && v * v2 > (v * v1) * (v1 * v2) ? true : false;
+            break;
+          } else if (det < 0.) {
+            v_in_this_face = false;
+            break;
+          }
+        } // ii
+      } // if (second_check)
+      if (v_in_this_face)
+        face_indices.push_back(kk);
+    } // faces
+    assert(face_indices.size());
+    return face_indices;
+  }
+
+  std::vector<QuadratureRule<RangeFieldImp, 3>>
+  quadrature_rules(size_t refinements = 0,
+                   const QuadratureRule<RangeFieldImp, 2>& reference_quadrature_rule = barycentre_rule()) const
+  {
+    std::vector<TriangleVectorType> quadrature_faces = get_subtriangles(refinements);
+    std::vector<QuadratureRule<RangeFieldImp, 3>> ret(faces_.size());
+    for (size_t jj = 0; jj < faces_.size(); ++jj) {
+      for (size_t ii = 0; ii < quadrature_faces[jj].size(); ++ii) {
+        const auto quad_rule = quadrature_faces[jj][ii]->quadrature_rule(reference_quadrature_rule);
+        ret[jj].insert(ret[jj].end(), quad_rule.begin(), quad_rule.end());
+      }
+    }
+    return ret;
+  }
+
+  // This returns a vector, which contains for each face kk a FieldVector<size_t, 3> of the
+  // indices of the faces that correspond to face kk when it is reflected in direction ii,
+  // i.e. reflected_face_indices()[kk][0] is the index of the face which is the result of
+  // reflecting face kk through the y-z Plane.
+  const std::vector<XT::Common::FieldVector<size_t, 3>>& reflected_face_indices() const
+  {
+    return reflected_face_indices_;
+  }
+
+private:
+  void refine(size_t times = 1)
+  {
+    set_faces_to_subtriangles(times);
+    vertices_ = all_vertices_;
+    calculate_reflected_faces();
+  } // void refine(...)
+
+  std::vector<TriangleVectorType> get_subtriangles(const size_t refinements = 1) const
+  {
+    std::vector<TriangleVectorType> ret(faces_.size());
+    for (size_t jj = 0; jj < faces_.size(); ++jj) {
+      TriangleVectorType subtriangles(1, faces_[jj]);
+      size_t refs = refinements;
+      while (refs-- > 0)
+        get_subtriangles(subtriangles);
+      ret[jj] = subtriangles;
+    } // jj
+    return ret;
+  } // ... get_subtriangles(...)
+
+  void get_subtriangles(TriangleVectorType& subtriangles) const
+  {
+    const size_t old_size = subtriangles.size();
+    subtriangles.resize(4. * old_size);
+    for (size_t ii = 0; ii < old_size; ++ii) {
+      subtriangles[ii]->initialize_subtriangles();
+      const auto& local_subtriangles = subtriangles[ii]->subtriangles();
+      for (size_t jj = 0; jj < 4; ++jj)
+        subtriangles[(3 - jj) * old_size + ii] = local_subtriangles[jj];
+    }
+  }
+
+  void set_faces_to_subtriangles(size_t refinements = 1)
+  {
+    while (refinements-- > 0)
+      get_subtriangles(faces_);
+  }
+
+  void calculate_faces(const std::vector<DomainType>& points0)
+  {
+    for (const auto& point : points0)
+      all_vertices_.emplace_back(std::make_shared<VertexType>(point, (*current_vertex_index_)++));
+    vertices_ = all_vertices_;
+    const auto all_vertices = all_vertices_;
+    auto vertices0 = all_vertices_;
+    while (vertices0.size() > 0) {
+      const auto v0 = vertices0.back();
+      vertices0.pop_back();
+      auto vertices1 = vertices0;
+      while (vertices1.size() > 0) {
+        const auto v1 = vertices1.back();
+        vertices1.pop_back();
+        for (const auto& v2 : vertices1) {
+          // calculate plane equation defined by three points
+          const DomainType v0v1 = v1->position() - v0->position();
+          const DomainType v0v2 = v2->position() - v0->position();
+          const DomainType normal = XT::Common::cross_product(v0v1, v0v2);
+          if (XT::Common::FloatCmp::ne(normal.two_norm2(), 0.)) {
+            bool is_face = true;
+            double max_value = std::numeric_limits<double>::lowest();
+            double min_value = std::numeric_limits<double>::max();
+            for (const auto& v3 : all_vertices) {
+              const auto v0v3 = v3->position() - v0->position();
+              const auto value = normal * v0v3;
+              max_value = std::max(max_value, value);
+              min_value = std::min(min_value, value);
+              if (XT::Common::FloatCmp::lt(min_value * max_value, 0.)) {
+                is_face = false;
+                break;
+              }
+            } // p3
+            if (is_face) {
+              // if max_value is <= 0, all values are less or equal zero,
+              // i.e the normal points outwards and thus p0, p1, p2 are oriented counterclockwise, which is what we want
+              if (XT::Common::FloatCmp::le(max_value, 0.))
+                faces_.emplace_back(std::make_shared<TriangleType>(
+                    all_vertices_, v0, v1, v2, current_vertex_index_, all_vertices_mutex_));
+              else
+                faces_.emplace_back(std::make_shared<TriangleType>(
+                    all_vertices_, v0, v2, v1, current_vertex_index_, all_vertices_mutex_));
+            } // if (is_face)
+          } // check if points define a plane
+        } // p2
+      } // p1
+    } // p0
+    calculate_reflected_faces();
+  } // void calculate_faces(...)
+
+  void calculate_reflected_faces()
+  {
+    reflected_face_indices_.resize(faces_.size());
+    for (size_t kk = 0; kk < faces_.size(); ++kk) {
+      const auto midpoint_rule = faces_[kk]->quadrature_rule(barycentre_rule());
+      assert(midpoint_rule.size() == 1);
+      for (size_t ii = 0; ii < 3; ++ii) {
+        auto midpoint_reflected_in_dir_ii = midpoint_rule[0].position();
+        midpoint_reflected_in_dir_ii[ii] = -midpoint_reflected_in_dir_ii[ii];
+        auto reflected_index = get_face_indices(midpoint_reflected_in_dir_ii);
+        assert(reflected_index.size() == 1);
+        reflected_face_indices_[kk][ii] = reflected_index[0];
+      }
+    }
+  }
+
+  TriangleVectorType faces_;
+  VertexVectorType vertices_;
+  VertexVectorType all_vertices_;
+  std::shared_ptr<size_t> current_vertex_index_;
+  std::vector<XT::Common::FieldVector<size_t, 3>> reflected_face_indices_;
+  std::shared_ptr<std::mutex> all_vertices_mutex_;
+}; // class SphericalTriangulation<...>
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_MOMENTMODELS_TRIANGULATION_HH
diff --git a/dune/gdt/test/operators/oswald-interpolation.hh b/dune/gdt/test/operators/oswald-interpolation.hh
index 19ba54d87673b9162148bb057387636f231312d1..b8845f82625022a890c7af15a701caa9585075c9 100644
--- a/dune/gdt/test/operators/oswald-interpolation.hh
+++ b/dune/gdt/test/operators/oswald-interpolation.hh
@@ -33,7 +33,7 @@
 #include <dune/gdt/local/bilinear-forms/integrals.hh>
 #include <dune/gdt/local/functionals/integrals.hh>
 #include <dune/gdt/local/integrands/abs.hh>
-#include <dune/gdt/local/integrands/elliptic.hh>
+#include <dune/gdt/local/integrands/laplace.hh>
 #include <dune/gdt/local/integrands/product.hh>
 #include <dune/gdt/operators/localizable-operator.hh>
 #include <dune/gdt/operators/oswald-interpolation.hh>
@@ -253,7 +253,7 @@ struct OswaldInterpolationOperatorOnLeafViewTest : public ::testing::Test
                     auto local_difference = difference.local_function();
                     local_difference->bind(element);
                     auto local_indicator = h1_element_indicators.local_discrete_function(element);
-                    local_indicator->dofs()[0] += LocalElementIntegralBilinearForm<E>(LocalEllipticIntegrand<E>(1.))
+                    local_indicator->dofs()[0] += LocalElementIntegralBilinearForm<E>(LocalLaplaceIntegrand<E>(1.))
                                                       .apply2(*local_difference, *local_difference)[0][0];
                   },
                   []() {});
@@ -376,6 +376,8 @@ template <class G>
 struct OswaldInterpolationOperatorOnCubicLeafViewTest : public OswaldInterpolationOperatorOnLeafViewTest<G>
 {
   using BaseType = OswaldInterpolationOperatorOnLeafViewTest<G>;
+  using BaseType::d;
+  using typename BaseType::D;
   using typename BaseType::E;
   using typename BaseType::GV;
   using typename BaseType::M;
@@ -385,8 +387,6 @@ struct OswaldInterpolationOperatorOnCubicLeafViewTest : public OswaldInterpolati
 
   std::shared_ptr<XT::Grid::GridProvider<G>> make_grid() override final
   {
-    using D = typename G::ctype;
-    static const constexpr size_t d = G::dimension;
     FieldVector<D, d> lower_left(0.);
     auto upper_right = XT::Common::from_string<FieldVector<double, d>>("[3 1 1 1]");
     std::array<unsigned int, d> num_elements;
diff --git a/dune/gdt/test/spaces/base.hh b/dune/gdt/test/spaces/base.hh
index fbbefbba18cac2212a184179987882ec33c2107f..30ad635280e53d1614499679a9459637b39a4aff 100644
--- a/dune/gdt/test/spaces/base.hh
+++ b/dune/gdt/test/spaces/base.hh
@@ -49,6 +49,7 @@ struct SpaceTestBase : public ::testing::Test
   using R = typename SpaceType::R;
   static const constexpr size_t r = SpaceType::r;
   static const constexpr size_t rC = SpaceType::rC;
+  static constexpr double default_tolerance = p > 2 ? (d > 3 ? 1e-7 : (d == 3 ? 1e-10 : 1e-13)) : 1e-15;
 
   using GlobalBasisType = typename SpaceType::GlobalBasisType;
   using MapperType = typename SpaceType::MapperType;
@@ -98,7 +99,7 @@ struct SpaceTestBase : public ::testing::Test
                 space->finite_elements().get(geometry_type, p).lagrange_points().size());
   }
 
-  void basis_is_lagrange_basis(const double& tolerance = 1e-15)
+  void basis_is_lagrange_basis(const double tolerance = default_tolerance)
   {
     ASSERT_NE(grid_view, nullptr);
     ASSERT_NE(space, nullptr);
@@ -226,7 +227,7 @@ struct SpaceTestBase : public ::testing::Test
       EXPECT_EQ(1, map_to_global.count(global_index));
   } // ... mapper_of_discontinuous_space_maps_correctly(...)
 
-  void local_interpolation_seems_to_be_correct()
+  void local_interpolation_seems_to_be_correct(const double tolerance = default_tolerance)
   {
     ASSERT_NE(grid_view, nullptr);
     ASSERT_NE(space, nullptr);
@@ -240,7 +241,7 @@ struct SpaceTestBase : public ::testing::Test
             [&](const auto& x) { return shape_functions.evaluate(x)[ii]; }, shape_functions.order());
         ASSERT_GE(dofs.size(), shape_functions.size());
         for (size_t jj = 0; jj < shape_functions.size(); ++jj)
-          EXPECT_TRUE(XT::Common::FloatCmp::eq(ii == jj ? 1. : 0., dofs[jj]))
+          EXPECT_TRUE(XT::Common::FloatCmp::eq(ii == jj ? 1. : 0., dofs[jj], tolerance, tolerance))
               << "\nii == jj ? 1. : 0. = " << (ii == jj ? 1. : 0.) << "\ndofs[jj] = " << dofs[jj];
       }
     }
diff --git a/dune/gdt/test/spaces/h1_continuous_lagrange.hh b/dune/gdt/test/spaces/h1_continuous_lagrange.hh
index ba11ed9169842d3fa4d62f2776fd01849705c6fb..e2fa806d0b764cae11c371aa19e42bc508d57872 100644
--- a/dune/gdt/test/spaces/h1_continuous_lagrange.hh
+++ b/dune/gdt/test/spaces/h1_continuous_lagrange.hh
@@ -70,7 +70,7 @@ struct ContinuousLagrangeSpaceTest
     std::set<size_t> global_DoF_indices;
     for (const auto& entry : global_lagrange_point_to_global_indices_map) {
       const auto global_DoF_indices_per_point = entry.second;
-      EXPECT_EQ(global_DoF_indices_per_point.size(), 1);
+      EXPECT_EQ(1, global_DoF_indices_per_point.size());
       global_DoF_indices.insert(*(global_DoF_indices_per_point.begin()));
     }
     EXPECT_EQ(global_lagrange_point_to_global_indices_map.size(), global_DoF_indices.size());
diff --git a/dune/gdt/test/spaces/spaces_h1_continuous_lagrange__cubic_grids.cc b/dune/gdt/test/spaces/spaces_h1_continuous_lagrange__cubic_grids.cc
index b75e6053b6a99b99d54c21ef80895107c2456447..9e523211dbb1ff9301918d798ce69ce785b40c13 100644
--- a/dune/gdt/test/spaces/spaces_h1_continuous_lagrange__cubic_grids.cc
+++ b/dune/gdt/test/spaces/spaces_h1_continuous_lagrange__cubic_grids.cc
@@ -60,3 +60,48 @@ TYPED_TEST(Order2CubicContinuousLagrangeSpace, local_interpolation_seems_to_be_c
 {
   this->local_interpolation_seems_to_be_correct();
 }
+
+
+template <class G>
+using Order3CubicContinuousLagrangeSpace = ContinuousLagrangeSpaceOnCubicLeafViewTest<G, 1, double, 3>;
+TYPED_TEST_CASE(Order3CubicContinuousLagrangeSpace, CubicGrids);
+TYPED_TEST(Order3CubicContinuousLagrangeSpace, gives_correct_identification)
+{
+  this->gives_correct_identification();
+}
+TYPED_TEST(Order3CubicContinuousLagrangeSpace, basis_exists_on_each_element_with_correct_size)
+{
+  this->basis_of_lagrange_space_exists_on_each_element_with_correct_size();
+}
+TYPED_TEST(Order3CubicContinuousLagrangeSpace, basis_exists_on_each_element_with_correct_order)
+{
+  this->basis_of_lagrange_space_exists_on_each_element_with_correct_order();
+}
+TYPED_TEST(Order3CubicContinuousLagrangeSpace, mapper_reports_correct_num_DoFs_on_each_element)
+{
+  this->mapper_reports_correct_num_DoFs_of_lagrange_space_on_each_element();
+}
+TYPED_TEST(Order3CubicContinuousLagrangeSpace, mapper_reports_correct_max_num_DoFs)
+{
+  this->mapper_reports_correct_max_num_DoFs();
+}
+TYPED_TEST(Order3CubicContinuousLagrangeSpace, mapper_maps_correctly)
+{
+  this->mapper_maps_correctly();
+}
+TYPED_TEST(Order3CubicContinuousLagrangeSpace, lagrange_points_exist_on_each_element_with_correct_size)
+{
+  this->lagrange_points_exist_on_each_element_with_correct_size();
+}
+TYPED_TEST(Order3CubicContinuousLagrangeSpace, basis_is_lagrange_basis)
+{
+  this->basis_is_lagrange_basis();
+}
+TYPED_TEST(Order3CubicContinuousLagrangeSpace, basis_jacobians_seem_to_be_correct)
+{
+  this->template basis_jacobians_of_lagrange_space_seem_to_be_correct<>();
+}
+TYPED_TEST(Order3CubicContinuousLagrangeSpace, local_interpolation_seems_to_be_correct)
+{
+  this->local_interpolation_seems_to_be_correct();
+}
diff --git a/dune/gdt/test/spaces/spaces_hdiv_raviart_thomas.cc b/dune/gdt/test/spaces/spaces_hdiv_raviart_thomas.cc
index 8036a88d95ef9e8b62a10a9bacaf16f36fb4c4a3..64fbca0d078829ebfb099a2aa3cc72f7c4e54c3f 100644
--- a/dune/gdt/test/spaces/spaces_hdiv_raviart_thomas.cc
+++ b/dune/gdt/test/spaces/spaces_hdiv_raviart_thomas.cc
@@ -398,14 +398,15 @@ struct RtSpaceOnCubicLeafView : public RtSpace<typename Dune::XT::Grid::GridProv
 {
   using GridProviderType = Dune::XT::Grid::GridProvider<G>;
   using LeafGridViewType = typename GridProviderType::LeafGridViewType;
+  using BaseType = RtSpace<typename Dune::XT::Grid::GridProvider<G>::LeafGridViewType, p>;
+  using BaseType::d;
+  using typename BaseType::D;
 
   std::shared_ptr<GridProviderType> grid_provider;
   std::shared_ptr<LeafGridViewType> leaf_view;
 
   RtSpaceOnCubicLeafView()
   {
-    using D = typename G::ctype;
-    static const constexpr size_t d = G::dimension;
     Dune::FieldVector<D, d> lower_left(-1.5); //  (i) negative coordinates and not the same as the reference element
     Dune::FieldVector<D, d> upper_right(-1.);
     std::array<unsigned int, d> num_elements; // (ii) at least 3 elements to have fully inner ones
@@ -495,14 +496,15 @@ struct RtSpaceOnMixedLeafView : public RtSpace<typename Dune::XT::Grid::GridProv
 {
   using GridProviderType = Dune::XT::Grid::GridProvider<G>;
   using LeafGridViewType = typename GridProviderType::LeafGridViewType;
+  using BaseType = RtSpace<typename Dune::XT::Grid::GridProvider<G>::LeafGridViewType, p>;
+  using BaseType::d;
+  using typename BaseType::D;
 
   std::shared_ptr<GridProviderType> grid_provider;
   std::shared_ptr<LeafGridViewType> leaf_view;
 
   RtSpaceOnMixedLeafView()
   {
-    using D = typename G::ctype;
-    static const constexpr size_t d = G::dimension;
     switch (d) {
       case 1: {
         // cannot use ASSERT_... in a ctor
diff --git a/dune/gdt/test/spaces/spaces_l2_finite_volume.cc b/dune/gdt/test/spaces/spaces_l2_finite_volume.cc
index 1f3a6cfeb1b12593e0922f5d0aa09ac2d92e9ad3..8caf7032fd254bb10174132933657881a2a66c87 100644
--- a/dune/gdt/test/spaces/spaces_l2_finite_volume.cc
+++ b/dune/gdt/test/spaces/spaces_l2_finite_volume.cc
@@ -254,14 +254,15 @@ struct FiniteVolumeSpaceOnCubicLeafView
 {
   using GridProviderType = Dune::XT::Grid::GridProvider<G>;
   using LeafGridViewType = typename GridProviderType::LeafGridViewType;
+  using BaseType = FiniteVolumeSpace<typename Dune::XT::Grid::GridProvider<G>::LeafGridViewType, r>;
+  using BaseType::d;
+  using typename BaseType::D;
 
   std::shared_ptr<GridProviderType> grid_provider;
   std::shared_ptr<LeafGridViewType> leaf_view;
 
   FiniteVolumeSpaceOnCubicLeafView()
   {
-    using D = typename G::ctype;
-    static const constexpr size_t d = G::dimension;
     Dune::FieldVector<D, d> lower_left(-1.5); //  (i) negative coordinates and not the same as the reference element
     Dune::FieldVector<D, d> upper_right(-1.);
     std::array<unsigned int, d> num_elements; // (ii) at least 3 elements to have fully inner ones
@@ -345,14 +346,15 @@ struct FiniteVolumeSpaceOnPrismLeafView
 {
   using GridProviderType = Dune::XT::Grid::GridProvider<G>;
   using LeafGridViewType = typename GridProviderType::LeafGridViewType;
+  using BaseType = FiniteVolumeSpace<typename Dune::XT::Grid::GridProvider<G>::LeafGridViewType, r>;
+  using BaseType::d;
+  using typename BaseType::D;
 
   std::shared_ptr<GridProviderType> grid_provider;
   std::shared_ptr<LeafGridViewType> leaf_view;
 
   FiniteVolumeSpaceOnPrismLeafView()
   {
-    using D = typename G::ctype;
-    static const constexpr size_t d = G::dimension;
     if (d == 3) {
       Dune::GridFactory<G> factory;
       for (auto&& vertex : {Dune::XT::Common::FieldVector<D, d>({-1., -1.5, -1.5}),
@@ -435,14 +437,15 @@ struct FiniteVolumeSpaceOnPyramidLeafView
 {
   using GridProviderType = Dune::XT::Grid::GridProvider<G>;
   using LeafGridViewType = typename GridProviderType::LeafGridViewType;
+  using BaseType = FiniteVolumeSpace<typename Dune::XT::Grid::GridProvider<G>::LeafGridViewType, r>;
+  using BaseType::d;
+  using typename BaseType::D;
 
   std::shared_ptr<GridProviderType> grid_provider;
   std::shared_ptr<LeafGridViewType> leaf_view;
 
   FiniteVolumeSpaceOnPyramidLeafView()
   {
-    using D = typename G::ctype;
-    static const constexpr size_t d = G::dimension;
     if (d == 3) {
       Dune::GridFactory<G> factory;
       for (auto&& vertex : {Dune::XT::Common::FieldVector<D, d>({0, 0, 0}),
@@ -524,14 +527,15 @@ struct FiniteVolumeSpaceOnMixedLeafView
 {
   using GridProviderType = Dune::XT::Grid::GridProvider<G>;
   using LeafGridViewType = typename GridProviderType::LeafGridViewType;
+  using BaseType = FiniteVolumeSpace<typename Dune::XT::Grid::GridProvider<G>::LeafGridViewType, r>;
+  using BaseType::d;
+  using typename BaseType::D;
 
   std::shared_ptr<GridProviderType> grid_provider;
   std::shared_ptr<LeafGridViewType> leaf_view;
 
   FiniteVolumeSpaceOnMixedLeafView()
   {
-    using D = typename G::ctype;
-    static const constexpr size_t d = G::dimension;
     switch (d) {
       case 1: {
         // cannot use ASSERT_... in a ctor
diff --git a/dune/gdt/test/stationary-eocstudies/base.hh b/dune/gdt/test/stationary-eocstudies/base.hh
index 86295d83fb9f443adfb8ea9b693ee1c848043db7..dbb19d99e4039f5d8ede975dc1f24d82fa69c19b 100644
--- a/dune/gdt/test/stationary-eocstudies/base.hh
+++ b/dune/gdt/test/stationary-eocstudies/base.hh
@@ -36,7 +36,7 @@
 #include <dune/gdt/local/bilinear-forms/integrals.hh>
 #include <dune/gdt/local/functionals/integrals.hh>
 #include <dune/gdt/local/integrands/abs.hh>
-#include <dune/gdt/local/integrands/elliptic.hh>
+#include <dune/gdt/local/integrands/laplace.hh>
 #include <dune/gdt/local/integrands/identity.hh>
 #include <dune/gdt/local/integrands/product.hh>
 #include <dune/gdt/operators/constant.hh>
@@ -101,33 +101,33 @@ public:
     , reference_solution_on_reference_grid_(nullptr)
   {}
 
-  virtual size_t num_refinements() const override
+  size_t num_refinements() const override
   {
     return num_refinements_;
   }
 
-  virtual std::vector<std::string> targets() const override
+  std::vector<std::string> targets() const override
   {
     return {"h"};
   }
 
-  virtual std::vector<std::string> norms() const override
+  std::vector<std::string> norms() const override
   {
     // We currently support the following norms: L_1, L_2, H_1_semi
     return {"L_2", "H_1_semi"};
   }
 
-  virtual std::vector<std::pair<std::string, std::string>> estimates() const override
+  std::vector<std::pair<std::string, std::string>> estimates() const override
   {
     return {};
   }
 
-  virtual std::vector<std::string> quantities() const override
+  std::vector<std::string> quantities() const override
   {
     return {"time to solution (s)"};
   }
 
-  virtual std::string discretization_info_title() const override
+  std::string discretization_info_title() const override
   {
     return " |grid| |   #DoFs";
   }
@@ -138,7 +138,7 @@ protected:
   virtual std::unique_ptr<S> make_space(const GP& current_grid) = 0;
 
 public:
-  virtual std::string discretization_info(const size_t refinement_level) override
+  std::string discretization_info(const size_t refinement_level) override
   {
     if (current_refinement_ != refinement_level) {
       // clear the current state
@@ -266,7 +266,7 @@ public:
           spatial_norm = [&](const DF& func) {
             auto localizable_product = make_localizable_bilinear_form(reference_space.grid_view(), func, func);
             localizable_product.append(LocalElementIntegralBilinearForm<E, m>(
-                LocalEllipticIntegrand<E, m>(), DXTC_TEST_CONFIG_GET("setup.over_integrate", 3)));
+                LocalLaplaceIntegrand<E, m>(), DXTC_TEST_CONFIG_GET("setup.over_integrate", 3)));
             localizable_product.assemble(DXTC_TEST_CONFIG_GET("setup.use_tbb", true));
             return std::sqrt(localizable_product.result());
           };
diff --git a/dune/gdt/test/stationary-eocstudies/diffusion-ipdg.hh b/dune/gdt/test/stationary-eocstudies/diffusion-ipdg.hh
index 3625a2f6ee3c7b5fbca25283658c5465f925139f..6ac3c8bf29639fbe2c55ba0dd4cb6a273402d5f4 100644
--- a/dune/gdt/test/stationary-eocstudies/diffusion-ipdg.hh
+++ b/dune/gdt/test/stationary-eocstudies/diffusion-ipdg.hh
@@ -27,8 +27,9 @@
 #include <dune/gdt/functionals/vector-based.hh>
 #include <dune/gdt/local/functionals/integrals.hh>
 #include <dune/gdt/local/bilinear-forms/integrals.hh>
-#include <dune/gdt/local/integrands/elliptic-ipdg.hh>
-#include <dune/gdt/local/integrands/elliptic.hh>
+#include <dune/gdt/local/integrands/laplace.hh>
+#include <dune/gdt/local/integrands/laplace-ipdg.hh>
+#include <dune/gdt/local/integrands/ipdg.hh>
 #include <dune/gdt/local/integrands/product.hh>
 #include <dune/gdt/local/integrands/conversion.hh>
 #include <dune/gdt/norms.hh>
@@ -81,6 +82,7 @@ public:
   StationaryDiffusionIpdgEocStudy()
     : BaseType()
     , space_type_("")
+    , one_(1.)
   {}
 
 protected:
@@ -89,13 +91,11 @@ protected:
 
   virtual const XT::Grid::BoundaryInfo<I>& boundary_info() const = 0;
 
-  virtual const FF& diffusion_factor() const = 0;
-
-  virtual const FT& diffusion_tensor() const = 0;
+  virtual const FT& diffusion() const = 0;
 
   virtual const FF& force() const = 0;
 
-  virtual std::vector<std::string> norms() const override
+  std::vector<std::string> norms() const override
   {
     auto nrms = BaseType::norms();
     nrms.push_back("eta_NC");
@@ -125,15 +125,15 @@ protected:
     if (DXTC_TEST_CONFIG_GET("setup.visualize", false)) {
       const std::string prefix = XT::Common::Test::get_unique_test_name() + "_problem_";
       const std::string postfix = "_ref_" + XT::Common::to_string(refinement_level);
-      self.diffusion_factor().visualize(current_space.grid_view(), prefix + "diffusion_factor" + postfix);
-      self.diffusion_tensor().visualize(current_space.grid_view(), prefix + "diffusion_tensor" + postfix);
+      //      self.diffusion_factor().visualize(current_space.grid_view(), prefix + "diffusion_factor" + postfix);
+      self.diffusion().visualize(current_space.grid_view(), prefix + "diffusion" + postfix);
       self.force().visualize(current_space.grid_view(), prefix + "force" + postfix);
       //      self.dirichlet().visualize(current_space.grid_view(), prefix + "dirichlet" + postfix);
       //      self.neumann().visualize(current_space.grid_view(), prefix + "neumann" + postfix);
     }
     Timer timer;
     const auto solution = make_discrete_function(current_space, self.solve(current_space));
-    const auto diffusion = diffusion_factor() * diffusion_tensor();
+    const auto& one = one_.template as_grid_function<E>();
     // only set time if this did not happen in solve()
     if (self.current_data_["quantity"].count("time to solution (s)") == 0)
       self.current_data_["quantity"]["time to solution (s)"] = timer.elapsed();
@@ -146,21 +146,21 @@ protected:
             current_space.grid_view(), current_space, current_space, boundary_info());
         oswald_interpolation_operator.assemble(/*parallel=*/true);
         const auto h1_interpolation = oswald_interpolation_operator.apply(solution);
-        self.current_data_["norm"][norm_id] = elliptic_norm(
-            current_space.grid_view(), diffusion_factor(), diffusion_tensor(), solution - h1_interpolation);
+        self.current_data_["norm"][norm_id] =
+            elliptic_norm(current_space.grid_view(), diffusion(), solution - h1_interpolation);
       } else if (norm_id == "eta_R") {
         norm_it = remaining_norms.erase(norm_it); // ... or here ...
         // compute estimate
         auto rt_space = make_raviart_thomas_space(current_space.grid_view(), current_space.max_polorder() - 1);
         auto reconstruction_op = make_ipdg_flux_reconstruction_operator<M, ipdg_method>(
-            current_space.grid_view(), current_space, rt_space, diffusion_factor(), diffusion_tensor());
+            current_space.grid_view(), current_space, rt_space, one, diffusion());
         auto flux_reconstruction = reconstruction_op.apply(solution);
         double eta_R_2 = 0.;
         std::mutex eta_R_2_mutex;
         auto walker = XT::Grid::make_walker(current_space.grid_view());
         walker.append([]() {},
                       [&](const auto& element) {
-                        auto local_df = diffusion.local_function();
+                        auto local_df = this->diffusion().local_function();
                         local_df->bind(element);
                         auto local_force = this->force().local_function();
                         local_force->bind(element);
@@ -201,40 +201,36 @@ protected:
         // compute estimate
         auto rt_space = make_raviart_thomas_space(current_space.grid_view(), current_space.max_polorder() - 1);
         auto reconstruction_op = make_ipdg_flux_reconstruction_operator<M, ipdg_method>(
-            current_space.grid_view(), current_space, rt_space, diffusion_factor(), diffusion_tensor());
+            current_space.grid_view(), current_space, rt_space, one, diffusion());
         auto flux_reconstruction = reconstruction_op.apply(solution);
         double eta_DF_2 = 0.;
         std::mutex eta_DF_2_mutex;
         auto walker = XT::Grid::make_walker(current_space.grid_view());
-        walker.append([]() {},
-                      [&](const auto& element) {
-                        auto local_df = this->diffusion_factor().local_function();
-                        local_df->bind(element);
-                        auto local_dt = this->diffusion_tensor().local_function();
-                        local_dt->bind(element);
-                        auto local_solution = solution.local_function();
-                        local_solution->bind(element);
-                        auto local_reconstruction = flux_reconstruction.local_function();
-                        local_reconstruction->bind(element);
-                        auto result = XT::Grid::element_integral(
-                            element,
-                            [&](const auto& xx) {
-                              const auto df = local_df->evaluate(xx);
-                              const auto dt = local_dt->evaluate(xx);
-                              const auto diff = dt * df;
-                              const auto diff_inv = XT::LA::invert_matrix(diff);
-                              const auto solution_grad = local_solution->jacobian(xx)[0];
-                              const auto flux_rec = local_reconstruction->evaluate(xx);
-                              auto difference = diff * solution_grad + flux_rec;
-                              return (diff_inv * difference) * difference;
-                            },
-                            std::max(local_df->order() + local_dt->order() + std::max(local_solution->order() - 1, 0),
-                                     local_reconstruction->order())
-                                + /*over_integrate=*/3);
-                        std::lock_guard<std::mutex> lock(eta_DF_2_mutex);
-                        eta_DF_2 += result;
-                      },
-                      []() {});
+        walker.append(
+            []() {},
+            [&](const auto& element) {
+              auto local_df = this->diffusion().local_function();
+              local_df->bind(element);
+              auto local_solution = solution.local_function();
+              local_solution->bind(element);
+              auto local_reconstruction = flux_reconstruction.local_function();
+              local_reconstruction->bind(element);
+              auto result = XT::Grid::element_integral(
+                  element,
+                  [&](const auto& xx) {
+                    const auto diff = local_df->evaluate(xx);
+                    const auto diff_inv = XT::LA::invert_matrix(diff);
+                    const auto solution_grad = local_solution->jacobian(xx)[0];
+                    const auto flux_rec = local_reconstruction->evaluate(xx);
+                    auto difference = diff * solution_grad + flux_rec;
+                    return (diff_inv * difference) * difference;
+                  },
+                  std::max(local_df->order() + std::max(local_solution->order() - 1, 0), local_reconstruction->order())
+                      + /*over_integrate=*/3);
+              std::lock_guard<std::mutex> lock(eta_DF_2_mutex);
+              eta_DF_2 += result;
+            },
+            []() {});
         walker.walk(/*parallel=*/true);
         self.current_data_["norm"][norm_id] = std::sqrt(eta_DF_2);
       } else
@@ -244,7 +240,7 @@ protected:
     return BaseType::compute(refinement_level, remaining_norms, remaining_estimates, remaining_quantities);
   } // ... compute(...)
 
-  virtual std::unique_ptr<S> make_space(const GP& current_grid) override
+  std::unique_ptr<S> make_space(const GP& current_grid) override
   {
     if (space_type_ == "fv")
       return std::make_unique<FiniteVolumeSpace<GV>>(current_grid.leaf_view());
@@ -260,23 +256,35 @@ protected:
     }
   } // ... make_space(...)
 
-  virtual std::unique_ptr<O> make_residual_operator(const S& space) override
+  std::unique_ptr<O> make_residual_operator(const S& space) override
   {
+    const auto& one = one_.template as_grid_function<E>();
     // define lhs operator (has to be a pointer to allow the residual operator to manage the memory in the end)
     auto lhs_op = std::make_unique<MatrixOperator<M, GV>>(make_matrix_operator<M>(
         space,
         (space_type_.size() >= 2 && space_type_.substr(0, 2) == "cg") ? Stencil::element
                                                                       : Stencil::element_and_intersection));
-    lhs_op->append(LocalElementIntegralBilinearForm<E>(
-        LocalEllipticIntegrand<E>(this->diffusion_factor(), this->diffusion_tensor())));
-    lhs_op->append(LocalIntersectionIntegralBilinearForm<I>(LocalEllipticIpdgIntegrands::Inner<I, double, ipdg_method>(
-                       this->diffusion_factor(), this->diffusion_tensor())),
+    // - volume term
+    lhs_op->append(LocalElementIntegralBilinearForm<E>(LocalLaplaceIntegrand<E>(this->diffusion())));
+    // - inner faces
+    lhs_op->append(LocalIntersectionIntegralBilinearForm<I>(
+                       LocalLaplaceIPDGIntegrands::InnerCoupling<I>(1., this->diffusion(), this->diffusion())),
                    {},
                    XT::Grid::ApplyOn::InnerIntersectionsOnce<GV>());
+    lhs_op->append(
+        LocalIntersectionIntegralBilinearForm<I>(LocalIPDGIntegrands::InnerPenalty<I>(
+            8, this->diffusion(), [](const auto& intersection) { return intersection.geometry().volume(); })),
+        {},
+        XT::Grid::ApplyOn::InnerIntersectionsOnce<GV>());
+    // - Dirichlet faces
     lhs_op->append(
         LocalIntersectionIntegralBilinearForm<I>(
-            LocalEllipticIpdgIntegrands::DirichletBoundaryLhs<I, double, ipdg_method>(this->diffusion_factor(),
-                                                                                      this->diffusion_tensor())),
+            LocalLaplaceIPDGIntegrands::DirichletCoupling<I>(1., this->diffusion())),
+        {},
+        XT::Grid::ApplyOn::CustomBoundaryIntersections<GV>(this->boundary_info(), new XT::Grid::DirichletBoundary()));
+    lhs_op->append(
+        LocalIntersectionIntegralBilinearForm<I>(LocalIPDGIntegrands::BoundaryPenalty<I>(
+            14, this->diffusion(), [](const auto& intersection) { return intersection.geometry().volume(); })),
         {},
         XT::Grid::ApplyOn::CustomBoundaryIntersections<GV>(this->boundary_info(), new XT::Grid::DirichletBoundary()));
     // define rhs functional
@@ -297,6 +305,7 @@ protected:
   } // ... make_residual_operator(...)
 
   std::string space_type_;
+  XT::Functions::ConstantFunction<d> one_;
 }; // class StationaryDiffusionIpdgEocStudy
 
 
diff --git a/dune/gdt/test/stationary-heat-equation/ESV2007.hh b/dune/gdt/test/stationary-heat-equation/ESV2007.hh
index f3872f39ddf9f18fb3d25a974fd57c0f4e0fe1ac..390c8451f26ea47c824d7442acde6ad62bb81c88 100644
--- a/dune/gdt/test/stationary-heat-equation/ESV2007.hh
+++ b/dune/gdt/test/stationary-heat-equation/ESV2007.hh
@@ -48,8 +48,7 @@ struct ESV2007DiffusionProblem
   // We can only reproduce the results from ESV2007 by using a quadrature of order 3, which we obtain with a p1 DG space
   // and a force of order 2.
   ESV2007DiffusionProblem(int force_order = 2)
-    : diffusion_factor(1)
-    , diffusion_tensor(XT::LA::eye_matrix<XT::Common::FieldMatrix<double, d, d>>(d, d))
+    : diffusion(XT::LA::eye_matrix<XT::Common::FieldMatrix<double, d, d>>(d, d))
     , dirichlet(0)
     , neumann(0)
     , force(force_order)
@@ -75,8 +74,7 @@ struct ESV2007DiffusionProblem
       EXPECT_TRUE(false) << "Please add a specialization for '" << XT::Common::Typename<G>::value << "'!";
   } // ... make_initial_grid(...)
 
-  const XT::Functions::ConstantFunction<d> diffusion_factor;
-  const XT::Functions::ConstantFunction<d, d, d> diffusion_tensor;
+  const XT::Functions::ConstantFunction<d, d, d> diffusion;
   const XT::Functions::ConstantFunction<d> dirichlet;
   const XT::Functions::ConstantFunction<d> neumann;
   const XT::Functions::ESV2007::Testcase1Force<d, 1> force;
@@ -144,14 +142,9 @@ protected:
     return problem.boundary_info;
   }
 
-  const FF& diffusion_factor() const override final
+  const FT& diffusion() const override final
   {
-    return problem.diffusion_factor.template as_grid_function<E>();
-  }
-
-  const FT& diffusion_tensor() const override final
-  {
-    return problem.diffusion_tensor.template as_grid_function<E>();
+    return problem.diffusion.template as_grid_function<E>();
   }
 
   const FF& force() const override final
diff --git a/dune/gdt/test/stationary-heat-equation/stationary_heat_equation__ESV2007__table_1.cc b/dune/gdt/test/stationary-heat-equation/stationary_heat_equation__ESV2007__table_1.cc
index a228d8e0412e5e1a03cea7efe1ad1a67d39c5cf6..440bbc40afed167af65e7601e85efdafefea933f 100644
--- a/dune/gdt/test/stationary-heat-equation/stationary_heat_equation__ESV2007__table_1.cc
+++ b/dune/gdt/test/stationary-heat-equation/stationary_heat_equation__ESV2007__table_1.cc
@@ -22,7 +22,9 @@ using namespace Dune;
 using namespace Dune::GDT::Test;
 
 
-using ESV2007Table1Test = ESV2007DiffusionTest<ALU_2D_SIMPLEX_CONFORMING>;
+#if SIMPLEXGRID_2D_AVAILABLE
+
+using ESV2007Table1Test = ESV2007DiffusionTest<SIMPLEXGRID_2D>;
 TEST_F(ESV2007Table1Test, columns_1_to_5)
 {
   this->space_type_ = "dg_p1";
@@ -30,3 +32,15 @@ TEST_F(ESV2007Table1Test, columns_1_to_5)
   const auto expected_results = DXTC_TEST_CONFIG_SUB("results");
   XT::Test::check_eoc_study_for_success(expected_results, actual_results);
 }
+
+#endif // SIMPLEXGRID_2D_AVAILABLE
+
+
+using NearlyESV2007Table1ButWithCubicGridTest = ESV2007DiffusionTest<CUBEGRID_2D>;
+TEST_F(NearlyESV2007Table1ButWithCubicGridTest, columns_1_to_5)
+{
+  this->space_type_ = "dg_p1";
+  const auto actual_results = this->run();
+  const auto expected_results = DXTC_TEST_CONFIG_SUB("results");
+  XT::Test::check_eoc_study_for_success(expected_results, actual_results);
+}
diff --git a/dune/gdt/test/stationary-heat-equation/stationary_heat_equation__ESV2007__table_1.mini b/dune/gdt/test/stationary-heat-equation/stationary_heat_equation__ESV2007__table_1.mini
index 6f6bd6a3990e670c59147dda8ff618854b077fe5..74a38f7bb7f3c4ab755f4aedd8ab8c136e866b42 100644
--- a/dune/gdt/test/stationary-heat-equation/stationary_heat_equation__ESV2007__table_1.mini
+++ b/dune/gdt/test/stationary-heat-equation/stationary_heat_equation__ESV2007__table_1.mini
@@ -19,3 +19,18 @@ norm.eta_R    = [7.23e-02 1.82e-02 4.54e-03] # 1.14e-03]
 # norm.eta_DF = [3.38e-01 1.69e-01 8.39e-02 4.18e-02]
 norm.eta_DF  = [3.55e-01 1.76e-01 8.73e-02] # 4.35e-02]
 
+
+[NearlyESV2007Table1ButWithCubicGridTest.columns_1_to_5.setup]
+force_order = 4
+over_integrate = 0
+num_refinements = 2
+num_additional_refinements_for_reference = 0
+reference_solution_order = 4
+use_tbb = false
+
+[NearlyESV2007Table1ButWithCubicGridTest.columns_1_to_5.results]
+target.h      = [3.54e-01 1.77e-01 8.84e-02]
+norm.H_1_semi = [2.52e-01 1.26e-01 6.30e-02]
+norm.eta_NC   = [1.58e-02 4.40e-03 1.15e-03]
+norm.eta_R    = [8.85e-02 2.22e-02 5.56e-03]
+norm.eta_DF  = [3.51e-01 1.77e-01 8.90e-02]
diff --git a/dune/gdt/test/stokes/stokes-taylorhood.hh b/dune/gdt/test/stokes/stokes-taylorhood.hh
index dc471cb0902bc4a7606e16fb8cd6ab2342b29b69..d6bb86047e689ef79345586332e1769e030c03a4 100644
--- a/dune/gdt/test/stokes/stokes-taylorhood.hh
+++ b/dune/gdt/test/stokes/stokes-taylorhood.hh
@@ -31,7 +31,7 @@
 #include <dune/gdt/local/bilinear-forms/integrals.hh>
 #include <dune/gdt/local/integrands/conversion.hh>
 #include <dune/gdt/local/integrands/div.hh>
-#include <dune/gdt/local/integrands/elliptic.hh>
+#include <dune/gdt/local/integrands/laplace.hh>
 #include <dune/gdt/local/integrands/product.hh>
 #include <dune/gdt/local/functionals/integrals.hh>
 #include <dune/gdt/norms.hh>
@@ -60,16 +60,15 @@ struct StokesDirichletProblem
   using ScalarGridFunction = XT::Functions::GridFunctionInterface<E, 1, 1>;
   using VectorGridFunction = XT::Functions::GridFunctionInterface<E, d, 1>;
   using DomainType = typename ScalarGridFunction::LocalFunctionType::DomainType;
+  using DiffusionTensor = XT::Functions::GridFunctionInterface<E, d, d, RangeField>;
 
-  StokesDirichletProblem(std::shared_ptr<const ScalarGridFunction> diffusion_factor_in = default_diffusion_factor(),
+  StokesDirichletProblem(std::shared_ptr<const DiffusionTensor> diffusion_in = default_diffusion,
                          std::shared_ptr<const VectorGridFunction> rhs_f_in = default_rhs_f(),
                          std::shared_ptr<const VectorGridFunction> rhs_g_in = default_rhs_g(),
                          std::shared_ptr<const VectorGridFunction> dirichlet_in = default_dirichlet_values(),
                          std::shared_ptr<const VectorGridFunction> reference_sol_u = nullptr,
                          std::shared_ptr<const ScalarGridFunction> reference_sol_p = nullptr)
-    : diffusion_factor_(diffusion_factor_in)
-    , diffusion_tensor_(std::make_shared<const XT::Functions::ConstantGridFunction<E, d, d>>(
-          XT::LA::eye_matrix<FieldMatrix<double, d, d>>(d, d)))
+    : diffusion_(diffusion_in)
     , rhs_f_(rhs_f_in)
     , rhs_g_(rhs_g_in)
     , dirichlet_(dirichlet_in)
@@ -80,9 +79,10 @@ struct StokesDirichletProblem
     , grid_view_(grid_.leaf_view())
   {}
 
-  static std::shared_ptr<const ScalarGridFunction> default_diffusion_factor()
+  static std::shared_ptr<const DiffusionTensor> default_diffusion()
   {
-    return std::make_shared<const XT::Functions::ConstantGridFunction<E, 1, 1>>(1., "default diffusion factor");
+    return std::make_shared<const XT::Functions::ConstantGridFunction<E, d, d>>(
+        XT::LA::eye_matrix<FieldMatrix<double, d, d>>(d, d), "isotropic_diffusion");
   }
 
   static std::shared_ptr<const VectorGridFunction> default_rhs_f()
@@ -105,14 +105,9 @@ struct StokesDirichletProblem
     return grid_view_;
   } // ... make_initial_grid(...)
 
-  const ScalarGridFunction& diffusion_factor()
+  const XT::Functions::GridFunctionInterface<E, d, d>& diffusion()
   {
-    return *diffusion_factor_;
-  } // ... make_initial_grid(...)
-
-  const XT::Functions::GridFunctionInterface<E, d, d>& diffusion_tensor()
-  {
-    return *diffusion_tensor_;
+    return *diffusion_;
   } // ... make_initial_grid(...)
 
   const VectorGridFunction& rhs_f()
@@ -147,8 +142,7 @@ struct StokesDirichletProblem
     return boundary_info_;
   } // ... make_initial_grid(...)
 
-  std::shared_ptr<const ScalarGridFunction> diffusion_factor_;
-  std::shared_ptr<const XT::Functions::GridFunctionInterface<E, d, d, RangeField>> diffusion_tensor_;
+  std::shared_ptr<const DiffusionTensor> diffusion_;
   std::shared_ptr<const VectorGridFunction> rhs_f_;
   std::shared_ptr<const VectorGridFunction> rhs_g_;
   std::shared_ptr<const VectorGridFunction> dirichlet_;
@@ -206,7 +200,7 @@ public:
     }
   }
 
-  void run()
+  void run(const int velocity_order, const double expected_error_u, const double expected_error_p)
   {
     const auto& grid_view = problem_.grid_view();
     // Setup spaces and matrices and vectors
@@ -215,8 +209,8 @@ public:
     // \int (div u) q = \int gg q
     // System is [A B; B^T C] [u; p] = [f; g]
     // Dimensions are: A: n x n, B: n x m, C: m x m, u: n, f: n, p: m, g: m
-    const VelocitySpace velocity_space(grid_view, 2);
-    const PressureSpace pressure_space(grid_view, 1);
+    const VelocitySpace velocity_space(grid_view, velocity_order);
+    const PressureSpace pressure_space(grid_view, velocity_order - 1);
     const size_t m = velocity_space.mapper().size();
     const size_t n = pressure_space.mapper().size();
     auto pattern_A = make_element_sparsity_pattern(velocity_space, velocity_space, grid_view);
@@ -227,9 +221,8 @@ public:
     MatrixOperator<Matrix, GV, d> A_operator(grid_view, velocity_space, velocity_space, A);
     MatrixOperator<Matrix, GV, 1, 1, d> B_operator(grid_view, pressure_space, velocity_space, B);
     // calculate A_{ij} as \int \nabla v_i \nabla v_j
-    A_operator.append(LocalElementIntegralBilinearForm<E, d>(
-        LocalEllipticIntegrand<E, d>(problem_.diffusion_factor(), problem_.diffusion_tensor())));
-    // calculate B_{ij} as \int \nabla p_i div(v_j)
+    A_operator.append(LocalElementIntegralBilinearForm<E, d>(LocalLaplaceIntegrand<E, d>(problem_.diffusion())));
+    // calculate B_{ij} as \int -\nabla p_i div(v_j)
     B_operator.append(LocalElementIntegralBilinearForm<E, d, 1, RangeField, RangeField, 1>(
         LocalElementAnsatzValueTestDivProductIntegrand<E>(-1.)));
     // calculate rhs f as \int ff v and the integrated pressure space basis \int q_i
@@ -283,7 +276,7 @@ public:
     C.set_entry(dof_index, dof_index, 1.);
 
     // now solve the system
-    XT::LA::SaddlePointSolver<double> solver(A, B, B, C);
+    XT::LA::SaddlePointSolver<Vector, Matrix> solver(A, B, B, C);
     Vector solution_u(m), solution_p(n);
     // solve both by direct solver and by schurcomplement (where the schur complement is inverted by CG and the inner
     // solves with A are using a direct method)
@@ -294,31 +287,39 @@ public:
       const auto actual_u_vector = solution_u + dirichlet_vector;
       // ensure int_\Omega p = 0
       auto p_integral = p_basis_integrated_vector * solution_p;
+      auto p_ref_integral = p_basis_integrated_vector * reference_solution_p_vector;
       auto p_correction = p_basis_integrated_vector;
       auto p_correction_func = make_discrete_function(pressure_space, p_correction);
-      auto vol_domain = 4.;
+      auto p_ref_correction = reference_solution_p_vector;
+      auto p_ref_correction_func = make_discrete_function(pressure_space, p_ref_correction);
+      const auto vol_domain = 4.;
       XT::Functions::ConstantGridFunction<E> const_p_integral_func(p_integral / vol_domain);
+      XT::Functions::ConstantGridFunction<E> const_p_ref_integral_func(p_ref_integral / vol_domain);
       default_interpolation(const_p_integral_func, p_correction_func);
+      default_interpolation(const_p_ref_integral_func, p_ref_correction_func);
       const auto actual_p_vector = solution_p - p_correction;
+      const auto actual_p_ref_vector = reference_solution_p_vector - p_ref_correction;
       // calculate difference to reference solution
       const auto u_diff_vector = actual_u_vector - reference_solution_u_vector;
-      const auto p_diff_vector = actual_p_vector - reference_solution_p_vector;
+      const auto p_diff_vector = actual_p_vector - actual_p_ref_vector;
 
       auto sol_u_func = make_discrete_function(velocity_space, actual_u_vector);
       auto sol_p_func = make_discrete_function(pressure_space, actual_p_vector);
       auto p_diff = make_discrete_function(pressure_space, p_diff_vector);
       auto u_diff = make_discrete_function(velocity_space, u_diff_vector);
-      bool visualize = false;
+      auto actual_p_ref = make_discrete_function(pressure_space, actual_p_ref_vector);
+      bool visualize = true;
+      std::string grid_name = XT::Common::Typename<G>::value();
       if (visualize) {
-        sol_u_func.visualize("solution_u_" + type);
-        sol_p_func.visualize("solution_p_" + type);
-        reference_solution_u.visualize("u_ref");
-        reference_solution_p.visualize("p_ref");
-        u_diff.visualize("u_error_" + type);
-        p_diff.visualize("p_error_" + type);
+        sol_u_func.visualize("solution_u_" + type + "_" + grid_name);
+        sol_p_func.visualize("solution_p_" + type + "_" + grid_name);
+        reference_solution_u.visualize("u_ref_" + grid_name);
+        actual_p_ref.visualize("p_ref_" + grid_name);
+        u_diff.visualize("u_error_" + type + "_" + grid_name);
+        p_diff.visualize("p_error_" + type + "_" + grid_name);
       }
-      DXTC_EXPECT_FLOAT_LE(l2_norm(problem_.grid_view(), u_diff), 2.29e-06);
-      DXTC_EXPECT_FLOAT_LE(l2_norm(problem_.grid_view(), p_diff), 2.22e-05);
+      DXTC_EXPECT_FLOAT_LE(l2_norm(problem_.grid_view(), u_diff), expected_error_u);
+      DXTC_EXPECT_FLOAT_LE(l2_norm(problem_.grid_view(), p_diff), expected_error_p);
     }
   } // run
 
@@ -338,7 +339,7 @@ class StokesTestcase1 : public StokesDirichletTest<G>
 
 public:
   StokesTestcase1()
-    : BaseType(ProblemType(ProblemType::default_diffusion_factor(),
+    : BaseType(ProblemType(ProblemType::default_diffusion(),
                            ProblemType::default_rhs_f(),
                            ProblemType::default_rhs_g(),
                            dirichlet(),
diff --git a/dune/gdt/test/stokes/stokes.cc b/dune/gdt/test/stokes/stokes.cc
new file mode 100644
index 0000000000000000000000000000000000000000..90066d4d2877b65a60bda3157f4a4395cedcb241
--- /dev/null
+++ b/dune/gdt/test/stokes/stokes.cc
@@ -0,0 +1,83 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Tobias Leibner (2019)
+
+#define DUNE_XT_COMMON_TEST_MAIN_ENABLE_TIMED_LOGGING 1
+#define DUNE_XT_COMMON_TEST_MAIN_ENABLE_INFO_LOGGING 1
+
+#define DUNE_XT_COMMON_TEST_MAIN_CATCH_EXCEPTIONS 1
+
+#include <dune/xt/common/test/main.hxx> // <- this one has to come first (includes the config.h)!
+
+#include <dune/xt/grid/grids.hh>
+
+#include <dune/gdt/test/stokes/stokes-taylorhood.hh>
+
+#if HAVE_DUNE_ISTL
+
+using namespace Dune;
+using namespace Dune::GDT::Test;
+
+using SimplexGrids2D = ::testing::Types<
+#  if HAVE_DUNE_ALUGRID
+    ALU_2D_SIMPLEX_CONFORMING,
+    ALU_2D_SIMPLEX_NONCONFORMING
+#  endif
+#  if HAVE_DUNE_UGGRID || HAVE_UG
+#    if HAVE_DUNE_ALUGRID
+    ,
+#    endif
+    UG_2D
+#  endif
+    >;
+
+using CubeGrids2D = ::testing::Types<YASP_2D_EQUIDISTANT_OFFSET
+#  if HAVE_DUNE_ALUGRID
+                                     ,
+                                     ALU_2D_CUBE
+#  endif
+                                     >;
+
+DUNE_XT_COMMON_TYPENAME(YASP_2D_EQUIDISTANT_OFFSET)
+#  if HAVE_DUNE_ALUGRID
+DUNE_XT_COMMON_TYPENAME(ALU_2D_SIMPLEX_CONFORMING)
+DUNE_XT_COMMON_TYPENAME(ALU_2D_SIMPLEX_NONCONFORMING)
+DUNE_XT_COMMON_TYPENAME(ALU_2D_CUBE)
+#  endif
+#  if HAVE_DUNE_UGGRID || HAVE_UG
+DUNE_XT_COMMON_TYPENAME(UG_2D)
+#  endif
+#  if HAVE_ALBERTA
+DUNE_XT_COMMON_TYPENAME(ALBERTA_2D)
+#  endif
+
+
+template <class G>
+using StokesTestSimplex = StokesTestcase1<G>;
+TYPED_TEST_CASE(StokesTestSimplex, SimplexGrids2D);
+
+TYPED_TEST(StokesTestSimplex, order2)
+{
+  this->run(2, 2e-5, 3e-3);
+}
+
+template <class G>
+using StokesTestCube = StokesTestcase1<G>;
+TYPED_TEST_CASE(StokesTestCube, CubeGrids2D);
+
+TYPED_TEST(StokesTestCube, order2)
+{
+  this->run(2, 3e-6, 3e-5);
+}
+
+TYPED_TEST(StokesTestCube, order3)
+{
+  this->run(3, 4e-7, 7e-6);
+}
+
+#endif // HAVE_DUNE_ISTL
diff --git a/dune/gdt/test/stokes/stokes_testcase1.mini b/dune/gdt/test/stokes/stokes_testcase1.mini
deleted file mode 100644
index cd684836bc81eb30fe0fc67b62124340bc24168e..0000000000000000000000000000000000000000
--- a/dune/gdt/test/stokes/stokes_testcase1.mini
+++ /dev/null
@@ -1,13 +0,0 @@
-__name = ESV2007Table1Test_column_1
-
-[ESV2007Table1Test.column_1.setup]
-force_order = 4
-over_integrate = 0
-num_refinements = 3
-num_additional_refinements_for_reference = 0
-reference_solution_order = 4
-use_tbb = false
-
-[ESV2007Table1Test.column_1.results]
-target.h      = [3.54e-01 1.77e-01 8.84e-02 4.42e-02]
-norm.H_1_semi = [3.28e-01 1.62e-01 8.04e-02 4.01e-02]
diff --git a/dune/gdt/tools/adaptation-helper.hh b/dune/gdt/tools/adaptation-helper.hh
index 318b1072d3048d79e36bcf6ab4843958ae7f50f3..c1ec256ca9da804894c05849d647a24326bbc8b2 100644
--- a/dune/gdt/tools/adaptation-helper.hh
+++ b/dune/gdt/tools/adaptation-helper.hh
@@ -35,7 +35,7 @@ namespace GDT {
 template <class V, class GV, size_t r = 1, size_t rC = 1, class RF = double>
 class AdaptationHelper
 {
-  using ThisType = AdaptationHelper<V, GV, r, rC, RF>;
+  using ThisType = AdaptationHelper;
 
 public:
   using DiscreteFunctionType = DiscreteFunction<V, GV, r, rC, RF>;
@@ -50,11 +50,11 @@ public:
 
   ThisType& append(SpaceType& space, DiscreteFunctionType& discrete_function)
   {
-    data_->emplace_back(space,
-                        discrete_function,
+    data_->emplace_back(XT::Common::StorageProvider<SpaceType>(space),
+                        XT::Common::StorageProvider<DiscreteFunctionType>(discrete_function),
                         PersistentContainer<G, std::pair<DynamicVector<RF>, DynamicVector<RF>>>(
                             grid_, 0, std::make_pair(DynamicVector<RF>(), DynamicVector<RF>())),
-                        discrete_function.local_discrete_function());
+                        std::move(discrete_function.local_discrete_function()));
     return *this;
   }
 
diff --git a/dune/gdt/tools/dirichlet-constraints.hh b/dune/gdt/tools/dirichlet-constraints.hh
index 11ff5c2945da836baa814bb345e77be727e1efe8..262d38975d8302dc68e73c66ccfc849f6838c0a6 100644
--- a/dune/gdt/tools/dirichlet-constraints.hh
+++ b/dune/gdt/tools/dirichlet-constraints.hh
@@ -51,7 +51,7 @@ class DirichletConstraints
                                               std::set<size_t>,
                                               internal::setUnion<size_t>>
 {
-  using ThisType = DirichletConstraints<IntersectionType, SpaceType>;
+  using ThisType = DirichletConstraints;
   using BaseType = XT::Grid::ElementFunctor<typename SpaceType::GridViewType>;
   using Propagator = XT::Common::ThreadResultPropagator<ThisType, std::set<size_t>, internal::setUnion<size_t>>;
   friend Propagator;
@@ -64,21 +64,22 @@ public:
   static const constexpr size_t r = SpaceType::r;
   static const constexpr size_t rC = SpaceType::rC;
   using R = typename SpaceType::R;
+  using SpaceInterfaceType = SpaceInterface<GridView, r, rC, R>;
 
   DirichletConstraints(const BoundaryInfoType& bnd_info, const SpaceType& space)
     : BaseType()
     , Propagator(this)
     , boundary_info_(bnd_info)
-    , space_(space)
-    , basis_(space_.basis().localize())
+    , space_(space.copy())
+    , basis_(space_->basis().localize())
   {}
 
   DirichletConstraints(const ThisType& other)
     : BaseType(other)
     , Propagator(this)
     , boundary_info_(other.boundary_info_)
-    , space_(other.space_)
-    , basis_(space_.basis().localize())
+    , space_(other.space_->copy())
+    , basis_(space_->basis().localize())
   {}
 
   void apply_local(const ElementType& element) override final
@@ -87,8 +88,8 @@ public:
     basis_->bind(element);
     const auto& reference_element = ReferenceElements<double, d>::general(element.geometry().type());
     const auto local_key_indices = basis_->finite_element().coefficients().local_key_indices();
-    const auto intersection_it_end = space_.grid_view().iend(element);
-    for (auto intersection_it = space_.grid_view().ibegin(element); intersection_it != intersection_it_end;
+    const auto intersection_it_end = space_->grid_view().iend(element);
+    for (auto intersection_it = space_->grid_view().ibegin(element); intersection_it != intersection_it_end;
          ++intersection_it) {
       const auto& intersection = *intersection_it;
       // actual dirichlet intersections + process boundaries for parallel runs
@@ -107,7 +108,7 @@ public:
       }
     }
     for (const auto& local_DoF : local_DoFs) {
-      dirichlet_DoFs_.insert(space_.mapper().global_index(element, local_DoF));
+      dirichlet_DoFs_.insert(space_->mapper().global_index(element, local_DoF));
     }
   } // ... apply_local(...)
 
@@ -207,8 +208,8 @@ public:
 
 private:
   const BoundaryInfoType& boundary_info_;
-  const SpaceType& space_;
-  mutable std::unique_ptr<typename SpaceType::GlobalBasisType::LocalizedType> basis_;
+  std::unique_ptr<const SpaceInterfaceType> space_;
+  mutable std::unique_ptr<typename SpaceInterfaceType::GlobalBasisType::LocalizedType> basis_;
   std::set<size_t> dirichlet_DoFs_;
 }; // class DirichletConstraints
 
diff --git a/dune/gdt/tools/discretevalued-grid-function.hh b/dune/gdt/tools/discretevalued-grid-function.hh
new file mode 100644
index 0000000000000000000000000000000000000000..614c2de20e2b1174b0f99a51931f6d9d17b83477
--- /dev/null
+++ b/dune/gdt/tools/discretevalued-grid-function.hh
@@ -0,0 +1,127 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_TOOLS_DISCRETEVALUED_GRID_FUNCTION_HH
+#define DUNE_GDT_TOOLS_DISCRETEVALUED_GRID_FUNCTION_HH
+
+#include <memory>
+
+#include <dune/xt/common/fvector.hh>
+#include <dune/xt/common/string.hh>
+#include <dune/xt/common/vector_less.hh>
+
+#include <dune/xt/functions/interfaces/grid-function.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+/**
+ * \brief Wrapper for the map of reconstructed values that fulfills the XT::Functions::LocalizableFunctionInterface
+ */
+template <class GV, size_t rangeDim, size_t rangeDimCols, class RangeField>
+class DiscreteValuedGridFunction
+  : public XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GV>, rangeDim, rangeDimCols, RangeField>
+{
+  using BaseType =
+      XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GV>, rangeDim, rangeDimCols, RangeField>;
+  using ThisType = DiscreteValuedGridFunction;
+
+public:
+  using BaseType::d;
+  using BaseType::r;
+  using BaseType::rC;
+
+  using IndexSetType = typename GV::IndexSet;
+  using E = XT::Grid::extract_entity_t<GV>;
+  using typename BaseType::LocalFunctionType;
+  using typename BaseType::R;
+
+private:
+  class DiscreteValuedLocalFunction : public LocalFunctionType
+  {
+    using BaseType = LocalFunctionType;
+
+  public:
+    using typename BaseType::DomainType;
+    using typename BaseType::E;
+    using typename BaseType::RangeReturnType;
+    using LocalFunctionValuesType = std::map<DomainType, RangeReturnType, XT::Common::FieldVectorLess>;
+
+    DiscreteValuedLocalFunction(std::vector<LocalFunctionValuesType>& values, const IndexSetType& index_set)
+      : values_(values)
+      , index_set_(index_set)
+    {}
+
+    int order(const XT::Common::Parameter& /*mu*/ = {}) const override
+    {
+      DUNE_THROW(Dune::InvalidStateException, "This function can't be integrated!");
+      return 2;
+    }
+
+    using BaseType::element;
+
+    void post_bind(const E& elem) override final
+    {
+      local_values_ = &(values_[index_set_.index(elem)]);
+    }
+
+    RangeReturnType evaluate(const DomainType& xx, const XT::Common::Parameter& /*param*/) const override
+    {
+      try {
+        return local_values_->at(xx);
+      } catch (const std::out_of_range& /*e*/) {
+        DUNE_THROW(Dune::RangeError,
+                   "There are no values for local coord "
+                       << XT::Common::to_string(xx) << " (global coord "
+                       << XT::Common::to_string(element().geometry().global(xx)) << ") on entity "
+                       << XT::Common::to_string(element().geometry().center()) << " in this function!");
+      }
+      return RangeReturnType{};
+    }
+
+  private:
+    std::vector<LocalFunctionValuesType>& values_;
+    const IndexSetType& index_set_;
+    LocalFunctionValuesType* local_values_;
+  };
+
+public:
+  using LocalFunctionValuesType = typename DiscreteValuedLocalFunction::LocalFunctionValuesType;
+
+  static const bool available = true;
+
+  DiscreteValuedGridFunction(const GV& grid_view, std::vector<LocalFunctionValuesType>& values)
+    : index_set_(grid_view.indexSet())
+    , values_(values)
+  {
+    assert(grid_view.size(0) >= 0);
+  }
+
+  std::unique_ptr<LocalFunctionType> local_function() const override final
+  {
+    return std::make_unique<DiscreteValuedLocalFunction>(values_, index_set_);
+  }
+
+  LocalFunctionValuesType& local_values(const E& elem)
+  {
+    return values_[index_set_.index(elem)];
+  }
+
+private:
+  const IndexSetType& index_set_;
+  std::vector<LocalFunctionValuesType>& values_;
+}; // class DiscreteValuedGridFunction
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_TOOLS_DISCRETEVALUED_GRID_FUNCTION_HH
diff --git a/dune/gdt/tools/grid-quality-estimates.hh b/dune/gdt/tools/grid-quality-estimates.hh
new file mode 100644
index 0000000000000000000000000000000000000000..baecd3fc444a02dbb270d4f9c45ee38fdce73d77
--- /dev/null
+++ b/dune/gdt/tools/grid-quality-estimates.hh
@@ -0,0 +1,134 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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)
+
+#ifndef DUNE_GDT_TOOLS_GRID_QUALITY_ESTIMATES_HH
+#define DUNE_GDT_TOOLS_GRID_QUALITY_ESTIMATES_HH
+
+#include <limits>
+#include <vector>
+
+#include <dune/grid/common/rangegenerators.hh>
+
+#include <dune/xt/common/exceptions.hh>
+#include <dune/xt/la/container/common.hh>
+#include <dune/xt/la/container/conversion.hh>
+#include <dune/xt/la/generalized-eigen-solver.hh>
+#include <dune/xt/grid/entity.hh>
+#include <dune/xt/grid/intersection.hh>
+#include <dune/xt/grid/type_traits.hh>
+
+#include <dune/gdt/local/bilinear-forms/integrals.hh>
+#include <dune/gdt/local/integrands/laplace.hh>
+#include <dune/gdt/local/integrands/product.hh>
+#include <dune/gdt/spaces/interface.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+template <class GV, size_t r>
+double estimate_inverse_inequality_constant(const SpaceInterface<GV, r>& space)
+{
+  DUNE_THROW_IF(!XT::Common::Lapacke::available(), XT::Common::Exceptions::dependency_missing, "lapacke");
+  using E = XT::Grid::extract_entity_t<GV>;
+  double result = std::numeric_limits<double>::min();
+  auto basis = space.basis().localize();
+  for (auto&& element : elements(space.grid_view())) {
+    basis->bind(element);
+    const double h = XT::Grid::diameter(element);
+    auto H1_product_matrix = XT::LA::convert_to<XT::LA::CommonDenseMatrix<double>>(
+        LocalElementIntegralBilinearForm<E, r>(LocalLaplaceIntegrand<E, r>()).apply2(*basis, *basis));
+    auto L2_product_matrix = XT::LA::convert_to<XT::LA::CommonDenseMatrix<double>>(
+        LocalElementIntegralBilinearForm<E, r>(LocalElementProductIntegrand<E, r>()).apply2(*basis, *basis));
+    auto evs =
+        XT::LA::make_generalized_eigen_solver(H1_product_matrix,
+                                              L2_product_matrix,
+                                              {{"type", XT::LA::generalized_eigen_solver_types(H1_product_matrix)[0]},
+                                               {"compute_eigenvectors", "false"},
+                                               {"assert_real_eigenvalues", "1e-15"}})
+            .real_eigenvalues();
+    double min_ev = std::numeric_limits<double>::max();
+    for (auto&& ev : evs)
+      if (std::abs(ev) > 1e-7) // TODO: find a better tolerance here!
+        min_ev = std::min(min_ev, ev);
+    // the smalles nonzero eigenvalue is (C_I / h)^2
+    result = std::max(result, h * std::sqrt(min_ev));
+  }
+  return result;
+} // ... estimate_inverse_inequality_constant(...)
+
+
+template <class GV, size_t r>
+double estimate_combined_inverse_trace_inequality_constant(const SpaceInterface<GV, r>& space)
+{
+  DUNE_THROW_IF(!XT::Common::Lapacke::available(), XT::Common::Exceptions::dependency_missing, "lapacke");
+  using E = XT::Grid::extract_entity_t<GV>;
+  using I = XT::Grid::extract_intersection_t<GV>;
+  double result = std::numeric_limits<double>::min();
+  auto basis = space.basis().localize();
+  for (auto&& element : elements(space.grid_view())) {
+    basis->bind(element);
+    const double h = XT::Grid::diameter(element);
+    XT::LA::CommonDenseMatrix<double> L2_face_product_matrix(basis->size(), basis->size(), 0.);
+    DynamicMatrix<double> tmp_L2_face_product_matrix(basis->size(), basis->size(), 0.);
+    for (auto&& intersection : intersections(space.grid_view(), element)) {
+      LocalIntersectionIntegralBilinearForm<I, r>(LocalIntersectionProductIntegrand<I, r>(1.))
+          .apply2(intersection, *basis, *basis, tmp_L2_face_product_matrix);
+      for (size_t ii = 0; ii < basis->size(); ++ii)
+        for (size_t jj = 0; jj < basis->size(); ++jj)
+          L2_face_product_matrix.add_to_entry(ii, jj, tmp_L2_face_product_matrix[ii][jj]);
+    }
+    auto L2_element_product_matrix = XT::LA::convert_to<XT::LA::CommonDenseMatrix<double>>(
+        LocalElementIntegralBilinearForm<E, r>(LocalElementProductIntegrand<E, r>(1.)).apply2(*basis, *basis));
+    auto evs = XT::LA::make_generalized_eigen_solver(
+                   L2_face_product_matrix,
+                   L2_element_product_matrix,
+                   {{"type", XT::LA::generalized_eigen_solver_types(L2_face_product_matrix)[0]},
+                    {"compute_eigenvectors", "false"},
+                    {"assert_real_eigenvalues", "1e-15"}})
+                   .real_eigenvalues();
+    double min_ev = std::numeric_limits<double>::max();
+    for (auto&& ev : evs)
+      if (std::abs(ev) > 1e-7) // TODO: find a better tolerance here!
+        min_ev = std::min(min_ev, ev);
+    // the smalles nonzero eigenvalue is (C_M (1 + C_I)) / h
+    result = std::max(result, h * min_ev);
+  }
+  return result;
+} // ... estimate_combined_inverse_trace_inequality_constant(...)
+
+
+template <class GV>
+double estimate_element_to_intersection_equivalence_constant(
+    const GridView<GV>& grid_view,
+    const std::function<double(const XT::Grid::extract_intersection_t<GridView<GV>>&)>& intersection_diameter =
+        [](const auto& intersection) {
+          if (GridView<GV>::dimension == 1) {
+            if (intersection.neighbor())
+              return 0.5 * (XT::Grid::diameter(intersection.inside()) + XT::Grid::diameter(intersection.outside()));
+            else
+              return XT::Grid::diameter(intersection.inside());
+          } else
+            return XT::Grid::diameter(intersection);
+        })
+{
+  auto result = std::numeric_limits<double>::min();
+  for (auto&& element : elements(grid_view)) {
+    const double h = XT::Grid::diameter(element);
+    for (auto&& intersection : intersections(grid_view, element))
+      result = std::max(result, intersection_diameter(intersection) / h);
+  }
+  return result;
+} // ... estimate_element_to_intersection_equivalence_constant(...)
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_TOOLS_GRID_QUALITY_ESTIMATES_HH
diff --git a/dune/gdt/tools/local-mass-matrix.hh b/dune/gdt/tools/local-mass-matrix.hh
index 3aa5f4ea1f19c16660a211bdcc17820230aa455f..67f404f94908bbca8ae2fc59a06e4786dacdb3e4 100644
--- a/dune/gdt/tools/local-mass-matrix.hh
+++ b/dune/gdt/tools/local-mass-matrix.hh
@@ -41,7 +41,7 @@ class LocalMassMatrixProvider
 {
   static_assert(XT::Grid::is_view<AGV>::value, "");
 
-  using ThisType = LocalMassMatrixProvider<GV, r, rC, F>;
+  using ThisType = LocalMassMatrixProvider;
   using BaseType = XT::Grid::ElementFunctor<GV>;
   using Propagator = XT::Common::ThreadResultPropagator<
       LocalMassMatrixProvider<GV, r, rC, F>,
@@ -60,20 +60,20 @@ public:
     : BaseType()
     , Propagator(this)
     , grid_view_(grid_view)
-    , space_(space)
+    , space_(space.copy())
     , element_mapper_(grid_view_)
     , instance_counter_(0)
-    , local_basis_(space_.basis().localize())
+    , local_basis_(space_->basis().localize())
   {}
 
   LocalMassMatrixProvider(const ThisType& other)
     : BaseType(other)
     , Propagator(this)
     , grid_view_(other.grid_view_)
-    , space_(other.space_)
+    , space_(other.space_->copy())
     , element_mapper_(grid_view_)
     , instance_counter_(other.instance_counter_ + 1)
-    , local_basis_(space_.basis().localize())
+    , local_basis_(space_->basis().localize())
   {}
 
   void apply_local(const ElementType& element) override
@@ -131,8 +131,8 @@ public:
   }
 
 private:
-  const AssemblyGridView& grid_view_;
-  const SpaceType& space_;
+  const AssemblyGridView grid_view_;
+  std::unique_ptr<const SpaceType> space_;
   const FiniteVolumeMapper<GV> element_mapper_;
   size_t instance_counter_;
   std::unique_ptr<typename SpaceType::GlobalBasisType::LocalizedType> local_basis_;
diff --git a/dune/gdt/tools/timestepper/adaptive-rungekutta.hh b/dune/gdt/tools/timestepper/adaptive-rungekutta.hh
new file mode 100644
index 0000000000000000000000000000000000000000..00a1d84ee60e42b1950bbab5ced0810401979178
--- /dev/null
+++ b/dune/gdt/tools/timestepper/adaptive-rungekutta.hh
@@ -0,0 +1,414 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 - 2017)
+//   Rene Milk       (2016 - 2018)
+//   Tobias Leibner  (2016 - 2017)
+
+#ifndef DUNE_GDT_TIMESTEPPER_ADAPTIVE_RUNGEKUTTA_HH
+#define DUNE_GDT_TIMESTEPPER_ADAPTIVE_RUNGEKUTTA_HH
+
+#include <utility>
+
+#include <dune/gdt/operators/interfaces.hh>
+
+#include <dune/xt/common/memory.hh>
+#include <dune/xt/common/string.hh>
+
+#include "interface.hh"
+
+
+namespace Dune {
+namespace GDT {
+
+
+namespace internal {
+
+
+// unspecialized
+template <class RangeFieldType, TimeStepperMethods method>
+struct AdaptiveButcherArrayProvider
+{
+  static_assert(AlwaysFalse<RangeFieldType>::value,
+                "You cannot use AdaptiveRungeKuttaTimeStepper with this value of TimeStepperMethods!");
+};
+
+// user-provided Butcher array
+template <class RangeFieldType>
+struct AdaptiveButcherArrayProvider<RangeFieldType, TimeStepperMethods::adaptive_rungekutta_other>
+{
+  static Dune::DynamicMatrix<RangeFieldType> A()
+  {
+    DUNE_THROW(Dune::NotImplemented,
+               "You have to provide a Butcher array in AdaptiveRungeKuttaTimeStepper's constructor for this method!");
+    return Dune::DynamicMatrix<RangeFieldType>();
+  }
+
+  static Dune::DynamicVector<RangeFieldType> b_1()
+  {
+    DUNE_THROW(Dune::NotImplemented,
+               "You have to provide a Butcher array in AdaptiveRungeKuttaTimeStepper's constructor for this method!");
+    return Dune::DynamicVector<RangeFieldType>();
+  }
+
+  static Dune::DynamicVector<RangeFieldType> b_2()
+  {
+    DUNE_THROW(Dune::NotImplemented,
+               "You have to provide a Butcher array in AdaptiveRungeKuttaTimeStepper's constructor for this method!");
+    return Dune::DynamicVector<RangeFieldType>();
+  }
+
+  static Dune::DynamicVector<RangeFieldType> c()
+  {
+    DUNE_THROW(Dune::NotImplemented,
+               "You have to provide a Butcher array in AdaptiveRungeKuttaTimeStepper's constructor for this method!");
+    return Dune::DynamicVector<RangeFieldType>();
+  }
+};
+
+// Bogacki-Shampine (adaptive RK23)
+template <class RangeFieldType>
+class AdaptiveButcherArrayProvider<RangeFieldType, TimeStepperMethods::bogacki_shampine>
+{
+public:
+  static Dune::DynamicMatrix<RangeFieldType> A()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicMatrix<RangeFieldType>>(
+        "[0 0 0 0; 0.5 0 0 0; 0 0.75 0 0; " + Dune::XT::Common::to_string(2.0 / 9.0, 15) + " "
+        + Dune::XT::Common::to_string(1.0 / 3.0, 15) + " " + Dune::XT::Common::to_string(4.0 / 9.0, 15) + " 0]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> b_1()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>(
+        "[" + Dune::XT::Common::to_string(2.0 / 9.0, 15) + " " + Dune::XT::Common::to_string(1.0 / 3.0, 15) + " "
+        + Dune::XT::Common::to_string(4.0 / 9.0, 15) + " 0]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> b_2()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>(
+        "[" + Dune::XT::Common::to_string(7.0 / 24.0, 15) + " " + Dune::XT::Common::to_string(1.0 / 4.0, 15) + " "
+        + Dune::XT::Common::to_string(1.0 / 3.0, 15) + " " + Dune::XT::Common::to_string(1.0 / 8.0, 15) + " 0]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> c()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>("[0.5 0.75 1 0]");
+  }
+};
+
+// Dormand-Prince (adaptive RK45)
+template <class RangeFieldType>
+class AdaptiveButcherArrayProvider<RangeFieldType, TimeStepperMethods::dormand_prince>
+{
+public:
+  static Dune::DynamicMatrix<RangeFieldType> A()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicMatrix<RangeFieldType>>(
+        std::string("[0 0 0 0 0 0 0;") + " 0.2 0 0 0 0 0 0;" + " 0.075 0.225 0 0 0 0 0;" + " "
+        + Dune::XT::Common::to_string(44.0 / 45.0, 15) + " " + Dune::XT::Common::to_string(-56.0 / 15.0, 15) + " "
+        + Dune::XT::Common::to_string(32.0 / 9.0, 15) + " 0 0 0 0;" + " "
+        + Dune::XT::Common::to_string(19372.0 / 6561.0, 15) + " " + Dune::XT::Common::to_string(-25360.0 / 2187.0, 15)
+        + " " + Dune::XT::Common::to_string(64448.0 / 6561.0, 15) + " "
+        + Dune::XT::Common::to_string(-212.0 / 729.0, 15) + " 0 0 0;" + " "
+        + Dune::XT::Common::to_string(9017.0 / 3168.0, 15) + " " + Dune::XT::Common::to_string(-355.0 / 33.0, 15) + " "
+        + Dune::XT::Common::to_string(46732.0 / 5247.0, 15) + " " + Dune::XT::Common::to_string(49.0 / 176.0, 15) + " "
+        + Dune::XT::Common::to_string(-5103.0 / 18656.0, 15) + " 0 0;" + " "
+        + Dune::XT::Common::to_string(35.0 / 384.0, 15) + " 0 " + Dune::XT::Common::to_string(500.0 / 1113.0, 15) + " "
+        + Dune::XT::Common::to_string(125.0 / 192.0, 15) + " " + Dune::XT::Common::to_string(-2187.0 / 6784.0, 15) + " "
+        + Dune::XT::Common::to_string(11.0 / 84.0, 15) + " 0]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> b_1()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>(
+        "[" + Dune::XT::Common::to_string(35.0 / 384.0, 15) + " 0 " + Dune::XT::Common::to_string(500.0 / 1113.0, 15)
+        + " " + Dune::XT::Common::to_string(125.0 / 192.0, 15) + " " + Dune::XT::Common::to_string(-2187.0 / 6784.0, 15)
+        + " " + Dune::XT::Common::to_string(11.0 / 84.0, 15) + " 0]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> b_2()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>(
+        "[" + Dune::XT::Common::to_string(5179.0 / 57600.0, 15) + " 0 "
+        + Dune::XT::Common::to_string(7571.0 / 16695.0, 15) + " " + Dune::XT::Common::to_string(393.0 / 640.0, 15) + " "
+        + Dune::XT::Common::to_string(-92097.0 / 339200.0, 15) + " " + Dune::XT::Common::to_string(187.0 / 2100.0, 15)
+        + " " + Dune::XT::Common::to_string(1.0 / 40.0, 15) + "]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> c()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>(
+        "[0 0.2 0.3 0.8 " + Dune::XT::Common::to_string(8.0 / 9.0, 15) + " 1 1]");
+  }
+}; // Dormand-Prince (RK45)
+
+
+} // namespace internal
+
+
+/** \brief Time stepper using adaptive Runge Kutta methods
+ *
+ * Timestepper using adaptive Runge Kutta methods to solve equations of the form u_t = r * L(u, t) where u is a
+ * discrete function, L an operator acting on u and \alpha a scalar factor (e.g. -1).
+ * The specific Runge Kutta method can be chosen as the third template argument. If your desired Runge Kutta method is
+ * not contained in AdaptiveRungeKuttaMethods, choose AdaptiveRungeKuttaMethods::other and
+ * supply a DynamicMatrix< RangeFieldType > A and vectors b_1, b_2 (DynamicVector< RangeFieldType >) and c
+ * (DynamicVector< RangeFieldType >) in the constructor. Here, A, b_1, b_2 and c form the butcher tableau (see
+ * https://en.wikipedia.org/wiki/List_of_Runge%E2%80%93Kutta_methods#Embedded_methods, A is composed of the coefficients
+ * a_{ij}, b_1 of b_j, b_2 of b_j^* and c of c_j). The default is the Dormand-Prince RK45 method.
+ * In each time step, the error is estimated using the difference between the two solutions obtained using either b_1 or
+ * b_2. If the estimated error is higher than a specified tolerance tol, the calculation is repeated with a smaller
+ * time step. The tolerance tol and the error estimate are also used to estimate the optimal time step length for the
+ * next time step via dt_new = dt_old*min(max(0.9*(tol/error)^(1/5), scale_factor_min), scale_factor_max_);
+ *
+ * Notation: For an s-stage method,
+ * \mathbf{u}^{n+1} = \mathbf{u}^n + dt \sum_{i=0}^{s-1} b_i \mathbf{k}_i
+ * \mathbf{k}_i = L(\mathbf{u}_i, t^n + dt c_i)
+ * \mathbf{u}_i = \mathbf{u}^n + dt \sum_{j=0}^{i-1} a_{ij} \mathbf{k}_j,
+ *
+ * \tparam OperatorImp Type of operator L
+ * \tparam DiscreteFunctionImp Type of initial values and solution at a fixed time
+ * \tparam method Adaptive Runge-Kutta method that is used (default is AdaptiveRungeKuttaMethods::dormand_prince)
+ */
+template <class OperatorImp, class DiscreteFunctionImp, TimeStepperMethods method = TimeStepperMethods::dormand_prince>
+class AdaptiveRungeKuttaTimeStepper : public TimeStepperInterface<DiscreteFunctionImp>
+{
+  typedef TimeStepperInterface<DiscreteFunctionImp> BaseType;
+  typedef typename internal::AdaptiveButcherArrayProvider<typename BaseType::RangeFieldType, method>
+      ButcherArrayProviderType;
+
+public:
+  typedef OperatorImp OperatorType;
+  typedef DiscreteFunctionImp DiscreteFunctionType;
+
+  typedef typename DiscreteFunctionType::DomainFieldType DomainFieldType;
+  typedef typename DiscreteFunctionType::RangeFieldType RangeFieldType;
+  typedef typename Dune::DynamicMatrix<RangeFieldType> MatrixType;
+  typedef typename Dune::DynamicVector<RangeFieldType> VectorType;
+  typedef typename std::vector<std::pair<RangeFieldType, DiscreteFunctionType>> SolutionType;
+
+  /**
+   * \brief Constructor for AdaptiveRungeKuttaTimeStepper time stepper
+   * \param op Operator L
+   * \param initial_values Discrete function containing initial values for u at time t_0.
+   * \param r Scalar factor (see above, default is 1)
+   * \param t_0 Initial time (default is 0)
+   * \param tol Error tolerance for the adaptive scheme (default is 1e-4)
+   * \param scale_factor_min Minimum allowed factor for time step scaling (default is 0.2).
+   * \param scale_factor_max Maximum allowed factor for time step scaling (default is 5).
+   * \param A Coefficient matrix (only provide if you use AdaptiveRungeKuttaMethods::other)
+   * \param b_1 First set of coefficients (only provide if you use AdaptiveRungeKuttaMethods::other)
+   * \param b_2 Second set of coefficients (only provide if you use AdaptiveRungeKuttaMethods::other)
+   * \param c Coefficients for time steps (only provide if you use AdaptiveRungeKuttaMethods::other)
+   */
+  AdaptiveRungeKuttaTimeStepper(const OperatorType& op,
+                                DiscreteFunctionType& initial_values,
+                                const RangeFieldType r = 1.0,
+                                const double t_0 = 0.0,
+                                const RangeFieldType tol = 1e-4,
+                                const RangeFieldType scale_factor_min = 0.2,
+                                const RangeFieldType scale_factor_max = 5,
+                                const MatrixType& A = ButcherArrayProviderType::A(),
+                                const VectorType& b_1 = ButcherArrayProviderType::b_1(),
+                                const VectorType& b_2 = ButcherArrayProviderType::b_2(),
+                                const VectorType& c = ButcherArrayProviderType::c())
+    : BaseType(t_0, initial_values)
+    , op_(op)
+    , r_(r)
+    , tol_(tol)
+    , scale_factor_min_(scale_factor_min)
+    , scale_factor_max_(scale_factor_max)
+    , u_tmp_(BaseType::current_solution())
+    , A_(A)
+    , b_1_(b_1)
+    , b_2_(b_2)
+    , c_(c)
+    , b_diff_(b_2_ - b_1_)
+    , num_stages_(A_.rows())
+  {
+    assert(Dune::XT::Common::FloatCmp::gt(tol_, 0.0));
+    assert(Dune::XT::Common::FloatCmp::le(scale_factor_min_, 1.0));
+    assert(Dune::XT::Common::FloatCmp::ge(scale_factor_max_, 1.0));
+    assert(A_.rows() == A_.cols() && "A has to be a square matrix");
+    assert(b_1_.size() == A_.rows());
+    assert(b_2_.size() == A_.rows());
+    assert(c_.size() == A_.rows());
+#ifndef NDEBUG
+    for (size_t ii = 0; ii < A_.rows(); ++ii) {
+      for (size_t jj = ii; jj < A_.cols(); ++jj) {
+        assert(Dune::XT::Common::FloatCmp::eq(A_[ii][jj], 0.0)
+               && "A has to be a lower triangular matrix with 0 on the main diagonal");
+      }
+    }
+#endif // NDEBUG
+    // store as many discrete functions as needed for intermediate stages
+    for (size_t ii = 0; ii < num_stages_; ++ii) {
+      stages_k_.emplace_back(current_solution());
+    }
+  } // constructor AdaptiveRungeKuttaTimeStepper
+
+  using BaseType::current_solution;
+  using BaseType::current_time;
+  using BaseType::solve;
+
+  virtual RangeFieldType solve(const RangeFieldType t_end,
+                               const RangeFieldType initial_dt,
+                               const size_t num_save_steps,
+                               const size_t num_output_steps,
+                               const bool save_solution,
+                               const bool visualize,
+                               const bool write_discrete,
+                               const bool write_exact,
+                               const std::string prefix,
+                               typename BaseType::DiscreteSolutionType& sol,
+                               const typename BaseType::VisualizerType& visualizer,
+                               const typename BaseType::StringifierType& stringifier,
+                               const typename BaseType::GridFunctionType& exact_solution) override final
+  {
+    const auto ret = BaseType::solve(t_end,
+                                     initial_dt,
+                                     num_save_steps,
+                                     num_output_steps,
+                                     save_solution,
+                                     visualize,
+                                     write_discrete,
+                                     write_exact,
+                                     prefix,
+                                     sol,
+                                     visualizer,
+                                     stringifier,
+                                     exact_solution);
+    // in a fractional step scheme, we cannot use last_stage_of_previous_step
+    last_stage_of_previous_step_ = nullptr;
+    return ret;
+  }
+
+  RangeFieldType step(const RangeFieldType dt, const RangeFieldType max_dt) override final
+  {
+    RangeFieldType actual_dt = std::min(dt, max_dt);
+    RangeFieldType mixed_error = std::numeric_limits<RangeFieldType>::max();
+    RangeFieldType time_step_scale_factor = 1.0;
+
+    auto& t = current_time();
+    auto& u_n = current_solution();
+
+    while (Dune::XT::Common::FloatCmp::gt(mixed_error, tol_)) {
+      bool skip_error_computation = false;
+      actual_dt *= time_step_scale_factor;
+      size_t first_stage_to_compute = 0;
+      if (last_stage_of_previous_step_) {
+        stages_k_[0].dofs().vector() = last_stage_of_previous_step_->dofs().vector();
+        first_stage_to_compute = 1;
+      }
+
+      for (size_t ii = first_stage_to_compute; ii < num_stages_; ++ii) {
+        std::fill(stages_k_[ii].dofs().vector().begin(), stages_k_[ii].dofs().vector().end(), RangeFieldType(0.));
+        u_tmp_.dofs().vector() = u_n.dofs().vector();
+        for (size_t jj = 0; jj < ii; ++jj)
+          u_tmp_.dofs().vector() += stages_k_[jj].dofs().vector() * (actual_dt * r_ * (A_[ii][jj]));
+        try {
+          op_.apply(u_tmp_.dofs().vector(), stages_k_[ii].dofs().vector(), t + actual_dt * c_[ii]);
+        } catch (const Dune::MathError& e) {
+          mixed_error = 1e10;
+          skip_error_computation = true;
+          time_step_scale_factor = 0.5;
+          break;
+#if HAVE_TBB
+        } catch (const tbb::captured_exception& e) {
+          mixed_error = 1e10;
+          skip_error_computation = true;
+          time_step_scale_factor = 0.5;
+          break;
+#endif
+        }
+      }
+
+      if (!skip_error_computation) {
+        // compute error vector
+        u_tmp_.dofs().vector() = stages_k_[0].dofs().vector() * b_diff_[0];
+        for (size_t ii = 1; ii < num_stages_; ++ii)
+          u_tmp_.dofs().vector() += stages_k_[ii].dofs().vector() * b_diff_[ii];
+        u_tmp_.dofs().vector() *= actual_dt * r_;
+
+        // calculate u at timestep n+1
+        for (size_t ii = 0; ii < num_stages_; ++ii)
+          u_n.dofs().vector() += stages_k_[ii].dofs().vector() * (actual_dt * r_ * b_1_[ii]);
+
+        // scale error, use absolute error if norm is less than 0.01 and relative error else
+        auto& diff_vector = u_tmp_.dofs().vector();
+        for (size_t ii = 0; ii < diff_vector.size(); ++ii) {
+          if (std::abs(u_n.dofs().vector()[ii]) > 0.01)
+            diff_vector[ii] /= std::abs(u_n.dofs().vector()[ii]);
+        }
+        mixed_error = diff_vector.sup_norm();
+        // scale dt to get the estimated optimal time step length, TODO: adapt formula
+        time_step_scale_factor =
+            std::min(std::max(0.9 * std::pow(tol_ / mixed_error, 1.0 / 5.0), scale_factor_min_), scale_factor_max_);
+
+        if (mixed_error > tol_) { // go back from u at timestep n+1 to timestep n
+          for (size_t ii = 0; ii < num_stages_; ++ii)
+            u_n.dofs().vector() += stages_k_[ii].dofs().vector() * (-1.0 * r_ * actual_dt * b_1_[ii]);
+        }
+      }
+    } // while (mixed_error > tol_)
+    if (!last_stage_of_previous_step_)
+      last_stage_of_previous_step_ = Dune::XT::Common::make_unique<DiscreteFunctionType>(u_n);
+    last_stage_of_previous_step_->dofs().vector() = stages_k_[num_stages_ - 1].dofs().vector();
+
+#if 0
+    const auto u_local_func = u_n.local_discrete_function();
+    for (auto&& element : Dune::elements(u_n.space().grid_view())) {
+      u_local_func->bind(element);
+      for (size_t ii = 0; ii < BaseType::dimRange; ++ii) {
+//        constexpr double min_val = -100.;
+        constexpr double max_val = 1000.;
+        const auto entry_ii = u_local_func->dofs().get_entry(ii);
+        if (std::abs(entry_ii) > max_val) {
+//          std::cout << "limited from " << u_local_func->dofs().get_entry(ii) << " to " << min_val << " in entity " << element.geometry().center() << std::endl;
+//          std::cout << "Entries are: ";
+//          for (size_t jj = 0; jj < BaseType::dimRange; ++jj) {
+//            std::cout << u_local_func->dofs().get_entry(jj) << " ";
+//          }
+//          std::cout << std::endl;
+          last_stage_of_previous_step_ = nullptr;
+//          u_local_func->dofs().set_entry(ii, min_val);
+          std::cout << "Replacing " << entry_ii << " by " << std::copysign(max_val, entry_ii) << std::endl;
+          u_local_func->dofs().set_entry(ii, std::copysign(max_val, entry_ii));
+        }
+      } // ii
+    } // elements
+#endif
+
+    t += actual_dt;
+
+    return actual_dt * time_step_scale_factor;
+  } // ... step(...)
+
+private:
+  const OperatorType& op_;
+  const RangeFieldType r_;
+  const RangeFieldType tol_;
+  const RangeFieldType scale_factor_min_;
+  const RangeFieldType scale_factor_max_;
+  DiscreteFunctionType u_tmp_;
+  const MatrixType A_;
+  const VectorType b_1_;
+  const VectorType b_2_;
+  const VectorType c_;
+  const VectorType b_diff_;
+  std::vector<DiscreteFunctionType> stages_k_;
+  const size_t num_stages_;
+  std::unique_ptr<DiscreteFunctionType> last_stage_of_previous_step_;
+}; // class AdaptiveRungeKuttaTimeStepper
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_TIMESTEPPER_ADAPTIVE_RUNGEKUTTA_HH
diff --git a/dune/gdt/tools/timestepper/enums.hh b/dune/gdt/tools/timestepper/enums.hh
new file mode 100644
index 0000000000000000000000000000000000000000..60dd1f7fc2768581eaad9eacfbc42718db2dd658
--- /dev/null
+++ b/dune/gdt/tools/timestepper/enums.hh
@@ -0,0 +1,45 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_TIMESTEPPER_ENUMS_HH
+#define DUNE_GDT_TIMESTEPPER_ENUMS_HH
+
+namespace Dune {
+namespace GDT {
+
+
+enum class TimeStepperMethods
+{
+  bogacki_shampine,
+  dormand_prince,
+  adaptive_rungekutta_other,
+  explicit_euler,
+  explicit_rungekutta_second_order_ssp,
+  explicit_rungekutta_third_order_ssp,
+  explicit_rungekutta_classic_fourth_order,
+  explicit_rungekutta_other,
+  implicit_euler,
+  implicit_midpoint,
+  trapezoidal_rule,
+  diagonally_implicit_other,
+  matrix_exponential
+};
+
+enum class TimeStepperSplittingMethods
+{
+  fractional_step,
+  strang
+};
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_TIMESTEPPER_ENUMS_HH
diff --git a/dune/gdt/tools/timestepper/explicit-rungekutta.hh b/dune/gdt/tools/timestepper/explicit-rungekutta.hh
new file mode 100644
index 0000000000000000000000000000000000000000..b1804fe385807f62b475c7beb7adb005d3f57421
--- /dev/null
+++ b/dune/gdt/tools/timestepper/explicit-rungekutta.hh
@@ -0,0 +1,337 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 - 2017)
+//   Rene Milk       (2016 - 2018)
+//   Tobias Leibner  (2016 - 2017)
+
+#ifndef DUNE_GDT_TIMESTEPPER_EXPLICIT_RUNGEKUTTA_HH
+#define DUNE_GDT_TIMESTEPPER_EXPLICIT_RUNGEKUTTA_HH
+
+#include <utility>
+
+#include "enums.hh"
+#include "interface.hh"
+
+
+namespace Dune {
+namespace GDT {
+
+
+namespace internal {
+
+
+// unspecialized
+template <class RangeFieldType, TimeStepperMethods method>
+struct ButcherArrayProvider
+{
+  static_assert(AlwaysFalse<RangeFieldType>::value,
+                "You cannot use ExplicitRungeKuttaTimeStepper with this value of TimeStepperMethods!");
+};
+
+// user-provided Butcher array
+template <class RangeFieldType>
+struct ButcherArrayProvider<RangeFieldType, TimeStepperMethods::explicit_rungekutta_other>
+{
+  static Dune::DynamicMatrix<RangeFieldType> A()
+  {
+    DUNE_THROW(Dune::NotImplemented,
+               "You have to provide a Butcher array in ExplicitRungeKuttaTimeStepper's constructor for this method!");
+    return Dune::DynamicMatrix<RangeFieldType>();
+  }
+
+  static Dune::DynamicVector<RangeFieldType> b()
+  {
+    DUNE_THROW(Dune::NotImplemented,
+               "You have to provide a Butcher array in ExplicitRungeKuttaTimeStepper's constructor for this method!");
+    return Dune::DynamicVector<RangeFieldType>();
+  }
+
+  static Dune::DynamicVector<RangeFieldType> c()
+  {
+    DUNE_THROW(Dune::NotImplemented,
+               "You have to provide a Butcher array in ExplicitRungeKuttaTimeStepper's constructor for this method!");
+    return Dune::DynamicVector<RangeFieldType>();
+  }
+};
+
+// Euler
+template <class RangeFieldType>
+struct ButcherArrayProvider<RangeFieldType, TimeStepperMethods::explicit_euler>
+{
+  static Dune::DynamicMatrix<RangeFieldType> A()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicMatrix<RangeFieldType>>("[0]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> b()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>("[1]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> c()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>("[0]");
+  }
+};
+
+// Second order SSP
+template <class RangeFieldType>
+struct ButcherArrayProvider<RangeFieldType, TimeStepperMethods::explicit_rungekutta_second_order_ssp>
+{
+  static Dune::DynamicMatrix<RangeFieldType> A()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicMatrix<RangeFieldType>>("[0 0; 1 0]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> b()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>("[0.5 0.5]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> c()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>("[0 1]");
+  }
+};
+
+// Third order SSP
+template <class RangeFieldType>
+struct ButcherArrayProvider<RangeFieldType, TimeStepperMethods::explicit_rungekutta_third_order_ssp>
+{
+  static Dune::DynamicMatrix<RangeFieldType> A()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicMatrix<RangeFieldType>>("[0 0 0; 1 0 0; 0.25 0.25 0]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> b()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>(
+        "[" + Dune::XT::Common::to_string(1.0 / 6.0, 15) + " " + Dune::XT::Common::to_string(1.0 / 6.0, 15) + " "
+        + Dune::XT::Common::to_string(2.0 / 3.0, 15) + "]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> c()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>("[0 1 0.5]");
+  }
+};
+
+// Classic fourth order RK
+template <class RangeFieldType>
+struct ButcherArrayProvider<RangeFieldType, TimeStepperMethods::explicit_rungekutta_classic_fourth_order>
+{
+  static Dune::DynamicMatrix<RangeFieldType> A()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicMatrix<RangeFieldType>>(
+        "[0 0 0 0; 0.5 0 0 0; 0 0.5 0 0; 0 0 1 0]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> b()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>(
+        "[" + Dune::XT::Common::to_string(1.0 / 6.0, 15) + " " + Dune::XT::Common::to_string(1.0 / 3.0, 15) + " "
+        + Dune::XT::Common::to_string(1.0 / 3.0, 15) + " " + Dune::XT::Common::to_string(1.0 / 6.0, 15) + "]");
+  }
+
+  static Dune::DynamicVector<RangeFieldType> c()
+  {
+    return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>("[0 0.5 0.5 1]");
+  }
+};
+
+
+} // namespace internal
+
+
+/** \brief Time stepper using Runge Kutta methods
+ *
+ * Timestepper using explicit Runge Kutta methods to solve equations of the form u_t = r * L(u, t) where u is a
+ * discrete function, L an operator acting on u and r a scalar factor (e.g. -1).
+ * The specific Runge Kutta method can be chosen as the third template argument. If your desired Runge Kutta method is
+ * not contained in ExplicitRungeKuttaMethods, choose ExplicitRungeKuttaMethods::other and supply a
+ * DynamicMatrix< RangeFieldType > A and vectors (DynamicVector< RangeFieldType >) b and c in the constructor. Here, A,
+ * b and c form the butcher tableau (see https://en.wikipedia.org/wiki/List_of_Runge%E2%80%93Kutta_methods, A is
+ * composed of the coefficients a_{ij}, b of b_j and c of c_j). The default is a forward euler method.
+ *
+ * Notation: For an s-stage method,
+ * \mathbf{u}^{n+1} = \mathbf{u}^n + dt \sum_{i=0}^{s-1} b_i \mathbf{k}_i
+ * \mathbf{k}_i = L(\mathbf{u}_i, t^n + dt c_i)
+ * \mathbf{u}_i = \mathbf{u}^n + dt \sum_{j=0}^{i-1} a_{ij} \mathbf{k}_j,
+ *
+ * \tparam OperatorImp Type of operator L
+ * \tparam DiscreteFunctionImp Type of initial values
+ */
+template <class OperatorImp, class DiscreteFunctionImp, TimeStepperMethods method = TimeStepperMethods::explicit_euler>
+class ExplicitRungeKuttaTimeStepper : public TimeStepperInterface<DiscreteFunctionImp>
+{
+  using BaseType = TimeStepperInterface<DiscreteFunctionImp>;
+  using ButcherArrayProviderType = typename internal::ButcherArrayProvider<typename BaseType::RangeFieldType, method>;
+
+public:
+  using typename BaseType::DataHandleType;
+  using typename BaseType::DiscreteFunctionType;
+  using typename BaseType::DiscreteSolutionType;
+  using typename BaseType::DomainFieldType;
+  using typename BaseType::RangeFieldType;
+
+  using OperatorType = OperatorImp;
+  using MatrixType = Dune::DynamicMatrix<RangeFieldType>;
+  using VectorType = Dune::DynamicVector<RangeFieldType>;
+
+  using BaseType::current_solution;
+  using BaseType::current_time;
+
+  /**
+   * \brief Constructor for RungeKutta time stepper
+   * \param op Operator L
+   * \param initial_values Discrete function containing initial values for u at time t_0.
+   * \param r Scalar factor (see above, default is 1)
+   * \param t_0 Initial time (default is 0)
+   * \param A Coefficient matrix (only provide if you use ExplicitRungeKuttaMethods::other)
+   * \param b Coefficient vector (only provide if you use ExplicitRungeKuttaMethods::other)
+   * \param c Coefficients for time steps (only provide if you use ExplicitRungeKuttaMethods::other)
+   */
+  ExplicitRungeKuttaTimeStepper(const OperatorType& op,
+                                DiscreteFunctionType& initial_values,
+                                const RangeFieldType r = 1.0,
+                                const double t_0 = 0.0,
+                                const MatrixType& A = ButcherArrayProviderType::A(),
+                                const VectorType& b = ButcherArrayProviderType::b(),
+                                const VectorType& c = ButcherArrayProviderType::c())
+    : BaseType(t_0, initial_values)
+    , op_(op)
+    , r_(r)
+    , u_i_(BaseType::current_solution())
+    , A_(A)
+    , b_(b)
+    , c_(c)
+    , num_stages_(A_.rows())
+  {
+    assert(A_.rows() == A_.cols() && "A has to be a square matrix");
+    assert(b_.size() == A_.rows());
+    assert(c_.size() == A_.rows());
+#ifndef NDEBUG
+    for (size_t ii = 0; ii < A_.rows(); ++ii) {
+      for (size_t jj = ii; jj < A_.cols(); ++jj) {
+        assert(Dune::XT::Common::FloatCmp::eq(A_[ii][jj], 0.0)
+               && "A has to be a lower triangular matrix with 0 on the main diagonal");
+      }
+    }
+#endif // NDEBUG
+    // store as many discrete functions as needed for the stages k
+    for (size_t ii = 0; ii < num_stages_; ++ii) {
+      stages_k_.emplace_back(current_solution());
+    }
+  } // constructor
+
+  /**
+   * \brief Constructor ignoring the tol argument for compatibility with AdaptiveRungeKuttaTimeStepper
+   */
+  ExplicitRungeKuttaTimeStepper(const OperatorType& op,
+                                const DiscreteFunctionType& initial_values,
+                                const RangeFieldType r,
+                                const double t_0,
+                                const RangeFieldType /*tol*/)
+    : ExplicitRungeKuttaTimeStepper(op, initial_values, r, t_0)
+  {}
+
+  RangeFieldType step(const RangeFieldType dt, const RangeFieldType max_dt) override final
+  {
+    const RangeFieldType actual_dt = std::min(dt, max_dt);
+    auto& t = current_time();
+    auto& u_n = current_solution();
+    // calculate stages
+    for (size_t ii = 0; ii < num_stages_; ++ii) {
+      u_i_.dofs().vector() = u_n.dofs().vector();
+      for (size_t jj = 0; jj < ii; ++jj)
+        u_i_.dofs().vector() += stages_k_[jj].dofs().vector() * (actual_dt * r_ * (A_[ii][jj]));
+      // TODO: provide actual_dt to op_. This leads to spurious oscillations in the Lax-Friedrichs flux
+      // because actual_dt/dx may become very small.
+      op_.apply(u_i_.dofs().vector(),
+                stages_k_[ii].dofs().vector(),
+                XT::Common::Parameter({{"t", {t + actual_dt * c_[ii]}}, {"dt", {dt}}}));
+      DataHandleType stages_k_ii_handle(stages_k_[ii]);
+      stages_k_[ii].space().grid_view().template communicate<DataHandleType>(
+          stages_k_ii_handle, Dune::InteriorBorder_All_Interface, Dune::ForwardCommunication);
+    }
+
+    // calculate value of u at next time step
+    for (size_t ii = 0; ii < num_stages_; ++ii)
+      u_n.dofs().vector() += stages_k_[ii].dofs().vector() * (r_ * actual_dt * b_[ii]);
+
+    // augment time
+    t += actual_dt;
+
+    return dt;
+  } // ... step(...)
+
+  const std::pair<bool, RangeFieldType>
+  find_suitable_dt(const RangeFieldType initial_dt,
+                   const RangeFieldType dt_refinement_factor = 2,
+                   const RangeFieldType treshold = 0.9 * std::numeric_limits<RangeFieldType>::max(),
+                   const size_t max_steps_per_dt = 20,
+                   const size_t max_refinements = 20)
+  {
+    auto& t = current_time();
+    auto& u_n = current_solution();
+    assert(treshold > 0);
+    // save current state
+    DiscreteFunctionType initial_u_n = u_n;
+    RangeFieldType initial_t = t;
+    // start with initial dt
+    RangeFieldType current_dt = initial_dt;
+    size_t num_refinements = 0;
+    while (num_refinements < max_refinements) {
+      std::cout << "Trying time step length dt = " << current_dt << "... " << std::flush;
+      bool unlikely_value_occured = false;
+      size_t num_steps = 0;
+      // do max_steps_per_dt time steps...
+      while (!unlikely_value_occured) {
+        step(current_dt);
+        ++num_steps;
+        // ... unless there is a value above threshold
+        for (size_t kk = 0; kk < u_n.dofs().vector().size(); ++kk) {
+          if (std::abs(u_n.dofs().vector()[kk]) > treshold) {
+            unlikely_value_occured = true;
+            std::cout << "failed" << std::endl;
+            break;
+          }
+        }
+        // if we are able to do max_steps_per_dt time steps with this dt, we accept this dt
+        if (num_steps == max_steps_per_dt) {
+          std::cout << "looks fine" << std::endl;
+          u_n.dofs().vector() = initial_u_n.dofs().vector();
+          t = initial_t;
+          return std::make_pair(bool(true), current_dt);
+        }
+      }
+      // if there was a value above threshold start over with smaller dt
+      u_n.dofs().vector() = initial_u_n.dofs().vector();
+      t = initial_t;
+      current_dt /= dt_refinement_factor;
+      ++num_refinements;
+    }
+    return std::make_pair(bool(false), current_dt);
+  }
+
+private:
+  const OperatorType& op_;
+  const RangeFieldType r_;
+  DiscreteFunctionType u_i_;
+  const MatrixType A_;
+  const VectorType b_;
+  const VectorType c_;
+  std::vector<DiscreteFunctionType> stages_k_;
+  const size_t num_stages_;
+};
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_TIMESTEPPER_EXPLICIT_RUNGEKUTTA_HH
diff --git a/dune/gdt/tools/timestepper/fractional-step.hh b/dune/gdt/tools/timestepper/fractional-step.hh
new file mode 100644
index 0000000000000000000000000000000000000000..902ef00effda3b764c8fbaa1cf4224123c3407fa
--- /dev/null
+++ b/dune/gdt/tools/timestepper/fractional-step.hh
@@ -0,0 +1,136 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 - 2017)
+//   Rene Milk       (2016 - 2018)
+//   Tobias Leibner  (2016 - 2017)
+
+#ifndef DUNE_GDT_TIMESTEPPER_FRACTIONAL_STEP_HH
+#define DUNE_GDT_TIMESTEPPER_FRACTIONAL_STEP_HH
+
+#include <utility>
+
+#include <dune/gdt/operators/interfaces.hh>
+
+#include <dune/xt/common/memory.hh>
+#include <dune/xt/common/string.hh>
+#include <dune/xt/la/container.hh>
+
+#include "interface.hh"
+
+
+namespace Dune {
+namespace GDT {
+
+
+/**
+ * Takes two time steppers and performs a simple fractional step scheme (see e.g. LeVeque, "Finite Volume Methods for
+ * Hyperbolic Problems", 2002, Section 17.1). In each time step t, the first stepper is applied up to time t+dt and the
+ * result is taken as input for the second time stepper, which is also applied up to time t+dt. Initial values and time
+ * are taken from the first time stepper.
+ */
+template <class FirstStepperImp, class SecondStepperImp>
+class FractionalTimeStepper : public TimeStepperInterface<typename FirstStepperImp::DiscreteFunctionType>
+{
+  using BaseType = TimeStepperInterface<typename FirstStepperImp::DiscreteFunctionType>;
+
+public:
+  using typename BaseType::DiscreteFunctionType;
+  using typename BaseType::DomainFieldType;
+  using typename BaseType::RangeFieldType;
+
+  using FirstStepperType = FirstStepperImp;
+  using SecondStepperType = SecondStepperImp;
+
+  using BaseType::current_solution;
+  using BaseType::current_time;
+  using BaseType::solution;
+
+  FractionalTimeStepper(FirstStepperType& first_stepper, SecondStepperType& second_stepper)
+    : BaseType(first_stepper.current_time(), first_stepper.current_solution())
+    , first_stepper_(first_stepper)
+    , second_stepper_(second_stepper)
+  {
+    first_stepper_.set_current_solution_pointer(current_solution());
+    first_stepper_.set_solution_pointer(solution());
+    second_stepper_.set_current_solution_pointer(current_solution());
+    second_stepper_.set_solution_pointer(solution());
+  } // constructor
+
+  RangeFieldType step(const RangeFieldType dt, const RangeFieldType max_dt) override final
+  {
+    auto& t = current_time();
+    const RangeFieldType actual_dt = std::min(dt, max_dt);
+    const auto dt_1 = first_stepper_.solve(t + actual_dt, dt, -1, 0, false);
+    const auto dt_2 = second_stepper_.solve(t + actual_dt, dt_1, -1, 0, false);
+    t += actual_dt;
+    return dt_2;
+  } // ... step(...)
+
+private:
+  FirstStepperType& first_stepper_;
+  SecondStepperType& second_stepper_;
+}; // class FractionalTimeStepper
+
+/**
+ * Takes two time steppers and performs a strang splitting fractional step scheme (see e.g. LeVeque, "Finite Volume
+ * Methods for
+ * Hyperbolic Problems", 2002, Section 17.1). In each time step t, the first stepper is applied up to time t+dt/2, then
+ * the
+ * second time stepper is applied up to time t+dt, then the first stepper is applied again up to time t+dt. Initial
+ * values and time
+ * are taken from the first time stepper.
+ */
+template <class FirstStepperImp, class SecondStepperImp>
+class StrangSplittingTimeStepper : public TimeStepperInterface<typename FirstStepperImp::DiscreteFunctionType>
+{
+  using BaseType = TimeStepperInterface<typename FirstStepperImp::DiscreteFunctionType>;
+
+public:
+  using typename BaseType::DiscreteFunctionType;
+  using typename BaseType::DomainFieldType;
+  using typename BaseType::RangeFieldType;
+
+  using FirstStepperType = FirstStepperImp;
+  using SecondStepperType = SecondStepperImp;
+
+  using BaseType::current_solution;
+  using BaseType::current_time;
+  using BaseType::solution;
+
+  StrangSplittingTimeStepper(FirstStepperType& first_stepper, SecondStepperType& second_stepper)
+    : BaseType(first_stepper.current_time(), first_stepper.current_solution())
+    , first_stepper_(first_stepper)
+    , second_stepper_(second_stepper)
+  {
+    first_stepper_.set_current_solution_pointer(current_solution());
+    first_stepper_.set_solution_pointer(solution());
+    second_stepper_.set_current_solution_pointer(current_solution());
+    second_stepper_.set_solution_pointer(solution());
+  } // constructor
+
+  RangeFieldType step(const RangeFieldType dt, const RangeFieldType max_dt) override final
+  {
+    auto& t = current_time();
+    const RangeFieldType actual_dt = std::min(dt, max_dt);
+    first_stepper_.solve(t + actual_dt / 2, actual_dt / 2, static_cast<size_t>(-1), 0, false);
+    second_stepper_.solve(t + actual_dt, actual_dt, static_cast<size_t>(-1), 0, false);
+    first_stepper_.solve(t + actual_dt, actual_dt / 2, static_cast<size_t>(-1), 0, false);
+    t += actual_dt;
+    return dt;
+  } // ... step(...)
+
+private:
+  FirstStepperType& first_stepper_;
+  SecondStepperType& second_stepper_;
+}; // class StrangSplittingTimeStepper
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_TIMESTEPPER_FRACTIONAL_STEP_HH
diff --git a/dune/gdt/tools/timestepper/interface.hh b/dune/gdt/tools/timestepper/interface.hh
new file mode 100644
index 0000000000000000000000000000000000000000..08b88370ecc5167f793962a1a11fde80c25e25db
--- /dev/null
+++ b/dune/gdt/tools/timestepper/interface.hh
@@ -0,0 +1,470 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 - 2017)
+//   Rene Milk       (2016 - 2018)
+//   Tobias Leibner  (2016 - 2017)
+
+#ifndef DUNE_GDT_TIMESTEPPER_INTERFACE_HH
+#define DUNE_GDT_TIMESTEPPER_INTERFACE_HH
+
+#include <cstdio>
+#include <utility>
+
+#include <dune/xt/common/memory.hh>
+#include <dune/xt/common/string.hh>
+#include <dune/xt/common/tuple.hh>
+
+#include <dune/xt/la/container.hh>
+
+#include <dune/xt/functions/interfaces/function.hh>
+#include <dune/xt/functions/interfaces/grid-function.hh>
+#include <dune/xt/functions/generic/grid-function.hh>
+
+#include <dune/gdt/operators/interfaces.hh>
+#include <dune/gdt/discretefunction/default.hh>
+
+#include "enums.hh"
+
+namespace Dune {
+namespace GDT {
+namespace internal {
+
+
+struct FloatCmpLt
+{
+  template <class A, class B>
+  bool operator()(const A& a, const B& b) const
+  {
+    return Dune::XT::Common::FloatCmp::lt(a, b);
+  }
+};
+
+
+} // namespace internal
+
+
+template <class DiscreteFunctionImp>
+class TimeStepperInterface
+  : Dune::XT::Common::StorageProvider<DiscreteFunctionImp>
+  , Dune::XT::Common::StorageProvider<
+        std::map<typename DiscreteFunctionImp::RangeFieldType, DiscreteFunctionImp, typename internal::FloatCmpLt>>
+{
+public:
+  using DiscreteFunctionType = DiscreteFunctionImp;
+  using GridViewType = typename DiscreteFunctionType::SpaceType::GridViewType;
+  using EntityType = typename GridViewType::template Codim<0>::Entity;
+  using DomainFieldType = typename DiscreteFunctionType::DomainFieldType;
+  using RangeFieldType = typename DiscreteFunctionType::RangeFieldType;
+  static const size_t dimDomain = DiscreteFunctionType::d;
+  static const size_t dimRange = DiscreteFunctionType::r;
+  static const size_t dimRangeCols = DiscreteFunctionType::rC;
+  using DomainType = typename DiscreteFunctionType::LocalFunctionType::DomainType;
+  using RangeType = typename DiscreteFunctionType::LocalFunctionType::RangeType;
+  using DiscreteSolutionType = typename std::map<RangeFieldType, DiscreteFunctionType, typename internal::FloatCmpLt>;
+  using GridFunctionType = XT::Functions::GridFunctionInterface<EntityType, dimRange, dimRangeCols, RangeFieldType>;
+  using DataHandleType = DiscreteFunctionDataHandle<DiscreteFunctionType>;
+  using VisualizerType = XT::Functions::VisualizerInterface<dimRange, dimRangeCols, RangeFieldType>;
+  using StringifierType = std::function<std::string(const RangeType&)>;
+  using ThisType = TimeStepperInterface;
+
+private:
+  using CurrentSolutionStorageProviderType = typename Dune::XT::Common::StorageProvider<DiscreteFunctionImp>;
+  using SolutionStorageProviderType = typename Dune::XT::Common::StorageProvider<
+      std::map<RangeFieldType, DiscreteFunctionImp, typename internal::FloatCmpLt>>;
+
+protected:
+  TimeStepperInterface(const RangeFieldType t_0, DiscreteFunctionType& initial_values)
+    : CurrentSolutionStorageProviderType(initial_values)
+    , SolutionStorageProviderType(new DiscreteSolutionType())
+    , t_(t_0)
+    , u_n_(&CurrentSolutionStorageProviderType::access())
+    , solution_(&SolutionStorageProviderType::access())
+  {}
+
+public:
+  TimeStepperInterface(const ThisType& other) = delete;
+  TimeStepperInterface(ThisType&& source) = delete;
+  ThisType& operator=(const ThisType& other) = delete;
+  ThisType& operator=(ThisType&& source) = delete;
+
+  virtual ~TimeStepperInterface() = default;
+
+  /**
+   * \brief Perform one time step and return the estimated optimal time step length for the next time step.
+   * \param dt Time step length.
+   * \param max_dt Maximal allowed time step length in this time step.
+   * \return Estimated optimal time step length for the next time step.
+   * \note For adaptive methods or if max_dt < dt, dt is just the initial time step length, the actual time step taken
+   * may be shorter. To get the actual time step length you need to compare current_time() before and after calling
+   * step().
+   * \note This method has to increase the current time by the actual time step length taken, otherwise
+   * solve will never finish execution (if current_time is not increased) or give wrong results (if current time is
+   * increased by a wrong dt).
+   */
+  virtual RangeFieldType step(const RangeFieldType dt, const RangeFieldType max_dt) = 0;
+
+  const RangeFieldType& current_time() const
+  {
+    return t_;
+  }
+
+  RangeFieldType& current_time()
+  {
+    return t_;
+  }
+
+  const DiscreteFunctionType& current_solution() const
+  {
+    return *u_n_;
+  }
+
+  DiscreteFunctionType& current_solution()
+  {
+    return *u_n_;
+  }
+
+  void set_current_solution_pointer(DiscreteFunctionType& discrete_function)
+  {
+    u_n_ = &discrete_function;
+  }
+
+  const DiscreteSolutionType& solution() const
+  {
+    return *solution_;
+  }
+
+  DiscreteSolutionType& solution()
+  {
+    return *solution_;
+  }
+
+  void set_solution_pointer(DiscreteSolutionType& solution_ref)
+  {
+    solution_ = &solution_ref;
+  }
+
+  static const GridFunctionType& dummy_solution()
+  {
+    static auto dummy_sol = XT::Functions::GenericGridFunction<EntityType, dimRange, dimRangeCols, RangeFieldType>(0);
+    return dummy_sol;
+  }
+
+  /**
+   * \brief solve Applies time stepping scheme up to time t_end
+   * \param t_end Final time.
+   * \param initial_dt Initial time step length. Non-adaptive schemes will use this dt for all time steps. Adaptive
+   * schemes will use initial_dt as a first guess for the time step length.
+   * \param num_save_steps Number of time points that will be stored/visualized/written Default is size_t(-1), which
+   * will store all time steps.
+   * \param num_output_steps Number of time points where current time and current time step length will be written to
+   * std::cout. Default is size_t(-1), which will output all time steps. Set to 0 to suppress output.
+   * \param save_solution If true, the discrete solution at num_save_steps equidistant time points will be stored in
+   * solution.
+   * \param visualize If true, solution will be written to .vtp/.vtu files at num_save_steps equidistant time points
+   * \param write_discrete If true, discrete solution will be written to .txt files at num_save_steps equidistant time
+   * points
+   * \param write_exact If true, exact solution will be written to .txt files at num_save_steps equidistant time points
+   * \param prefix Filename prefix of .vtp/.vtu/.txt files that are written
+   * \param sol The discrete solution
+   * \param visualizer Function object that determines how the discrete solution is visualized.
+   * \param stringifier Function object that determines how RangeType is converted to string before writing to .txt
+   * files.
+   * \param exact_solution Exact solution function.
+   * \return estimated optimal time step length for next step
+   * \note If num_save_steps is specified (i.e. if it is not size_t(-1), the solution will be stored/visualized at
+   * exactly num_save_steps + 1 equidistant time points (including the initial time and t_end), even if the time step
+   * length has to be reduced to hit these time points.
+   */
+  virtual RangeFieldType solve(const RangeFieldType t_end,
+                               const RangeFieldType initial_dt,
+                               const size_t num_save_steps,
+                               const size_t num_output_steps,
+                               const bool save_solution,
+                               const bool visualize,
+                               const bool write_discrete,
+                               const bool write_exact,
+                               const std::string prefix,
+                               DiscreteSolutionType& sol,
+                               const VisualizerType& visualizer,
+                               const StringifierType& stringifier,
+                               const GridFunctionType& exact_solution)
+  {
+    RangeFieldType dt = initial_dt;
+    RangeFieldType t = current_time();
+    assert(Dune::XT::Common::FloatCmp::ge(t_end, t));
+    size_t time_step_counter = 0;
+
+    const RangeFieldType save_interval = (t_end - t) / num_save_steps;
+    const RangeFieldType output_interval =
+        (num_output_steps == 0 ? std::numeric_limits<RangeFieldType>::max() : (t_end - t) / num_output_steps);
+    RangeFieldType next_save_time = t + save_interval > t_end ? t_end : t + save_interval;
+    RangeFieldType next_output_time = t + output_interval > t_end ? t_end : t + output_interval;
+    size_t save_step_counter = 1;
+
+    // save/visualize initial solution
+    if (save_solution)
+      sol.insert(std::make_pair(t, current_solution()));
+    write_files(visualize,
+                write_discrete,
+                write_exact,
+                current_solution(),
+                exact_solution,
+                prefix,
+                0,
+                t,
+                stringifier,
+                visualizer);
+
+    while (Dune::XT::Common::FloatCmp::lt(t, t_end)) {
+      RangeFieldType max_dt = dt;
+      // match saving times and t_end exactly
+      if (Dune::XT::Common::FloatCmp::gt(t + dt, t_end))
+        max_dt = t_end - t;
+      if (Dune::XT::Common::FloatCmp::gt(t + dt, next_save_time) && num_save_steps != size_t(-1))
+        max_dt = std::min(next_save_time - t, max_dt);
+
+      // do a timestep
+      dt = step(dt, max_dt);
+      t = current_time();
+
+      // augment time step counter
+      ++time_step_counter;
+
+      // check if data should be written in this timestep (and write)
+      if (Dune::XT::Common::FloatCmp::ge(t, next_save_time) || num_save_steps == size_t(-1)) {
+        if (save_solution)
+          sol.insert(sol.end(), std::make_pair(t, current_solution()));
+        write_files(visualize,
+                    write_discrete,
+                    write_exact,
+                    current_solution(),
+                    exact_solution,
+                    prefix,
+                    save_step_counter,
+                    t,
+                    stringifier,
+                    visualizer);
+        next_save_time += save_interval;
+        ++save_step_counter;
+      }
+      if (num_output_steps && (Dune::XT::Common::FloatCmp::ge(t, next_output_time) || num_output_steps == size_t(-1))) {
+        if (current_solution().space().grid_view().comm().rank() == 0)
+          std::cout << "time step " << time_step_counter << " done, time =" << t << ", current dt= " << dt << std::endl;
+        next_output_time += output_interval;
+      }
+    } // while (t < t_end)
+
+    return dt;
+  } // ... solve(...)
+
+  // default solve, use internal solution
+  virtual RangeFieldType solve(const RangeFieldType t_end,
+                               const RangeFieldType initial_dt,
+                               const size_t num_save_steps = size_t(-1),
+                               const size_t num_output_steps = size_t(-1),
+                               const bool save_solution = false,
+                               const bool visualize = false,
+                               const bool write_discrete = false,
+                               const bool write_exact = false,
+                               const std::string prefix = "solution",
+                               const VisualizerType& visualizer = default_visualizer(),
+                               const StringifierType& stringifier = vector_stringifier(),
+                               const GridFunctionType& exact_solution = dummy_solution())
+  {
+    return solve(t_end,
+                 initial_dt,
+                 num_save_steps,
+                 num_output_steps,
+                 save_solution,
+                 visualize,
+                 write_discrete,
+                 write_exact,
+                 prefix,
+                 *solution_,
+                 visualizer,
+                 stringifier,
+                 exact_solution);
+  }
+
+  // solve and store in sol, no (file) output
+  virtual RangeFieldType solve(const RangeFieldType t_end,
+                               const RangeFieldType initial_dt,
+                               const size_t num_save_steps,
+                               DiscreteSolutionType& sol)
+  {
+    return solve(t_end,
+                 initial_dt,
+                 num_save_steps,
+                 0,
+                 true,
+                 false,
+                 false,
+                 false,
+                 "",
+                 sol,
+                 default_visualizer(),
+                 vector_stringifier(),
+                 dummy_solution());
+  }
+
+  /**
+   * \brief Find discrete solution for time point that is closest to t.
+   *
+   * The timestepper only stores the solution at discrete time points. This function returns the discrete solution
+   * for
+   * the stored time point that is closest to the query time t.
+   *
+   * \param t Time
+   * \return std::pair with pair.second the discrete solution at time pair.first
+   */
+  virtual const typename DiscreteSolutionType::value_type& solution_closest_to_time(const RangeFieldType t) const
+  {
+    if (solution().empty())
+      DUNE_THROW(Dune::InvalidStateException, "Solution is empty!");
+    return solution().upper_bound(t)->first - t > t - solution().lower_bound(t)->first ? *solution().lower_bound(t)
+                                                                                       : *solution().upper_bound(t);
+  }
+
+  virtual const DiscreteFunctionType& solution_at_time(const RangeFieldType t) const
+  {
+    const auto it = solution().find(t);
+    if (it == solution().end())
+      DUNE_THROW(Dune::InvalidStateException,
+                 "There is no solution for time " + Dune::XT::Common::to_string(t) + " stored in this object!");
+    return it->second;
+  }
+
+  virtual void visualize_solution(const std::string prefix = "",
+                                  const VisualizerType& visualizer = default_visualizer()) const
+  {
+    size_t counter = 0;
+    for (const auto& pair : solution()) {
+      pair.second.visualize(pair.second.space().grid_view(),
+                            prefix + "_" + Dune::XT::Common::to_string(counter),
+                            false,
+                            VTK::appendedraw,
+                            {},
+                            visualizer);
+      ++counter;
+    }
+  }
+
+  static const VisualizerType& default_visualizer()
+  {
+    static auto default_vis =
+        std::make_unique<XT::Functions::DefaultVisualizer<dimRange, dimRangeCols, RangeFieldType>>();
+    return *default_vis;
+  }
+
+  static StringifierType vector_stringifier()
+  {
+    return [](const RangeType& val) {
+      std::string ret = XT::Common::to_string(val[0], 15);
+      for (size_t ii = 1; ii < val.size(); ++ii)
+        ret += " " + XT::Common::to_string(val[ii], 15);
+      return ret;
+    };
+  } // ... vector_stringifier()
+
+  static std::string rankfile_name(const std::string& prefix, const int rank, const size_t step)
+  {
+    return prefix + "_rank_" + XT::Common::to_string(rank) + "_" + XT::Common::to_string(step) + ".txt";
+  }
+
+  static void write_to_textfile(const GridFunctionType& u_n,
+                                const GridViewType& grid_view,
+                                const std::string& prefix,
+                                const size_t step,
+                                const RangeFieldType t,
+                                const StringifierType& stringifier,
+                                const bool intersection_wise = false)
+  {
+    // write one file per MPI rank
+    std::ofstream rankfile(rankfile_name(prefix, grid_view.comm().rank(), step));
+    const auto local_func = u_n.local_function();
+    for (const auto& entity : elements(grid_view, Dune::Partitions::interiorBorder)) {
+      local_func->bind(entity);
+      const auto entity_center = entity.geometry().center();
+      if (intersection_wise) {
+        for (const auto& intersection : Dune::intersections(grid_view, entity)) {
+          auto position = intersection.geometry().center();
+          assert(position.size() == dimDomain);
+          // avoid ambiguity at interface
+          for (size_t ii = 0; ii < dimDomain; ++ii)
+            if (position[ii] < entity_center[ii])
+              position[ii] += 1e-6 * (entity_center[ii] - position[ii]);
+          const auto val = local_func->evaluate(entity.geometry().local(position), {"t", t});
+          for (size_t ii = 0; ii < dimDomain; ++ii)
+            rankfile << XT::Common::to_string(position[ii], 15) << " ";
+          rankfile << stringifier(val) << std::endl;
+        } // intersections
+      } else {
+        auto position = entity_center;
+        assert(position.size() == dimDomain);
+        // avoid ambiguity at interface
+        const auto val = local_func->evaluate(entity.geometry().local(position), {"t", t});
+        for (size_t ii = 0; ii < dimDomain; ++ii)
+          rankfile << XT::Common::to_string(position[ii], 15) << " ";
+        rankfile << stringifier(val) << std::endl;
+      }
+    }
+    rankfile.close();
+    // Wait till files on all ranks are written
+    grid_view.comm().barrier();
+    // Merge files
+    if (grid_view.comm().rank() == 0) {
+      const std::string mergedfile_name = prefix + "_" + XT::Common::to_string(step) + ".txt";
+      std::remove(mergedfile_name.c_str());
+      std::ofstream merged_file(mergedfile_name, std::ios_base::binary | std::ios_base::app);
+      for (int ii = 0; ii < grid_view.comm().size(); ++ii) {
+        const std::string rankfile_to_merge_name = rankfile_name(prefix, ii, step);
+        std::ifstream rankfile_to_merge(rankfile_to_merge_name, std::ios_base::binary);
+        merged_file << rankfile_to_merge.rdbuf();
+        rankfile_to_merge.close();
+        std::remove(rankfile_to_merge_name.c_str());
+      } // ii
+      merged_file.close();
+    } // if (rank == 0)
+  } // void write_to_textfile()
+
+  static void write_files(const bool visualize,
+                          const bool write_discrete,
+                          const bool write_exact,
+                          const DiscreteFunctionType& discrete_sol,
+                          const GridFunctionType& exact_sol,
+                          const std::string& prefix,
+                          const size_t step,
+                          const RangeFieldType t,
+                          const StringifierType& stringifier,
+                          const VisualizerType& visualizer)
+  {
+    const auto& grid_view = discrete_sol.space().grid_view();
+    if (visualize)
+      discrete_sol.visualize(discrete_sol.space().grid_view(),
+                             prefix + "_" + XT::Common::to_string(step),
+                             false,
+                             VTK::appendedraw,
+                             {},
+                             visualizer);
+    if (write_discrete)
+      write_to_textfile(discrete_sol, grid_view, prefix, step, t, stringifier);
+    if (write_exact)
+      write_to_textfile(exact_sol, grid_view, prefix + "_exact", step, t, stringifier);
+  }
+
+private:
+  RangeFieldType t_;
+  DiscreteFunctionType* u_n_;
+  DiscreteSolutionType* solution_;
+}; // class TimeStepperInterface
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_TIMESTEPPER_INTERFACE_HH
diff --git a/dune/gdt/tools/timestepper/matrix-exponential-kinetic-isotropic.hh b/dune/gdt/tools/timestepper/matrix-exponential-kinetic-isotropic.hh
new file mode 100644
index 0000000000000000000000000000000000000000..b7a1a4aa6effdfc648cfce857de5aad40a0c657c
--- /dev/null
+++ b/dune/gdt/tools/timestepper/matrix-exponential-kinetic-isotropic.hh
@@ -0,0 +1,173 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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:
+//   Rene Milk      (2018)
+//   Tobias Leibner (2017)
+
+#ifndef DUNE_GDT_TIMESTEPPER_KINETIC_ISOTROPIC_HH
+#define DUNE_GDT_TIMESTEPPER_KINETIC_ISOTROPIC_HH
+
+#include <dune/xt/grid/functors/interfaces.hh>
+#include <dune/xt/grid/walker.hh>
+
+#include <dune/xt/functions/interfaces/function.hh>
+
+#include "interface.hh"
+
+namespace Dune {
+namespace GDT {
+
+
+template <class DiscreteFunctionType, class MomentBasis>
+class KineticIsotropicLocalFunctor
+  : public XT::Grid::ElementFunctor<typename DiscreteFunctionType::SpaceType::GridViewType>
+{
+  using GridViewType = typename DiscreteFunctionType::SpaceType::GridViewType;
+  using BaseType = typename XT::Grid::ElementFunctor<GridViewType>;
+  using RangeType = typename DiscreteFunctionType::LocalFunctionType::RangeReturnType;
+  using ScalarFunctionType =
+      XT::Functions::FunctionInterface<MomentBasis::dimDomain, 1, 1, typename MomentBasis::RangeFieldType>;
+  static constexpr size_t dimRange = DiscreteFunctionType::r;
+
+public:
+  using typename BaseType::E;
+
+  KineticIsotropicLocalFunctor(const MomentBasis& basis_functions,
+                               DiscreteFunctionType& solution,
+                               const double dt,
+                               const ScalarFunctionType& sigma_a,
+                               const ScalarFunctionType& sigma_s,
+                               const ScalarFunctionType& Q)
+
+    : basis_functions_(basis_functions)
+    , solution_(solution)
+    , local_solution_(solution_.local_discrete_function())
+    , dt_(dt)
+    , sigma_a_(sigma_a)
+    , sigma_s_(sigma_s)
+    , Q_(Q)
+    , basis_integrated_(basis_functions_.integrated())
+    , u_iso_(basis_functions_.u_iso())
+  {}
+
+  KineticIsotropicLocalFunctor(const KineticIsotropicLocalFunctor& other)
+    : BaseType(other)
+    , basis_functions_(other.basis_functions_)
+    , solution_(other.solution_)
+    , local_solution_(solution_.local_discrete_function())
+    , dt_(other.dt_)
+    , sigma_a_(other.sigma_a_)
+    , sigma_s_(other.sigma_s_)
+    , Q_(other.Q_)
+    , basis_integrated_(other.basis_integrated_)
+    , u_iso_(other.u_iso_)
+  {}
+
+  XT::Grid::ElementFunctor<GridViewType>* copy() override final
+  {
+    return new KineticIsotropicLocalFunctor(*this);
+  }
+
+  void apply_local(const E& entity) override final
+  {
+    local_solution_->bind(entity);
+
+    // get u
+    const auto center = entity.geometry().center();
+    const auto center_local = entity.geometry().local(center);
+    const auto u0 = local_solution_->evaluate(center_local);
+    const auto sigma_a = sigma_a_.evaluate(center)[0];
+    const auto sigma_s = sigma_s_.evaluate(center)[0];
+    const auto Q = Q_.evaluate(center)[0];
+    const auto exp_sigma_a = std::exp(-sigma_a * dt_);
+    const auto exp_sigma_s = std::exp(-sigma_s * dt_);
+    const auto u_iso = u_iso_ * basis_functions_.density(u0);
+
+    auto u = exp_sigma_a * (u0 * exp_sigma_s + u_iso * (1 - exp_sigma_s));
+    if (!XT::Common::is_zero(sigma_a))
+      u += basis_integrated_ * (1 - exp_sigma_a) / sigma_a * Q;
+    else
+      u += basis_integrated_ * dt_ * Q;
+
+    // write to return vector
+    auto& local_vector = local_solution_->dofs();
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      local_vector.set_entry(ii, u[ii]);
+  }
+
+private:
+  const MomentBasis& basis_functions_;
+  DiscreteFunctionType& solution_;
+  std::unique_ptr<typename DiscreteFunctionType::LocalDiscreteFunctionType> local_solution_;
+  const double dt_;
+  const ScalarFunctionType& sigma_a_;
+  const ScalarFunctionType& sigma_s_;
+  const ScalarFunctionType& Q_;
+  const RangeType basis_integrated_;
+  const RangeType u_iso_;
+};
+
+
+/** \brief Time stepper solving linear equation d_t u = Au + b by matrix exponential
+ */
+template <class DiscreteFunctionImp, class MomentBasis>
+class KineticIsotropicTimeStepper : public TimeStepperInterface<DiscreteFunctionImp>
+{
+  typedef KineticIsotropicTimeStepper ThisType;
+  typedef TimeStepperInterface<DiscreteFunctionImp> BaseType;
+
+public:
+  using typename BaseType::DiscreteFunctionType;
+  using typename BaseType::DomainFieldType;
+  using typename BaseType::EntityType;
+  using typename BaseType::RangeFieldType;
+  static const size_t dimDomain = DiscreteFunctionType::d;
+  static const size_t dimRange = DiscreteFunctionType::r;
+  using ScalarFunctionType = XT::Functions::FunctionInterface<dimDomain, 1, 1, RangeFieldType>;
+
+  using BaseType::current_solution;
+  using BaseType::current_time;
+
+  KineticIsotropicTimeStepper(const MomentBasis& basis_functions,
+                              DiscreteFunctionType& initial_values,
+                              const ScalarFunctionType& sigma_a,
+                              const ScalarFunctionType& sigma_s,
+                              const ScalarFunctionType& Q,
+                              const RangeFieldType t_0 = 0.0)
+    : BaseType(t_0, initial_values)
+    , basis_functions_(basis_functions)
+    , sigma_a_(sigma_a)
+    , sigma_s_(sigma_s)
+    , Q_(Q)
+  {}
+
+  RangeFieldType step(const RangeFieldType dt, const RangeFieldType max_dt) override final
+  {
+    const RangeFieldType actual_dt = std::min(dt, max_dt);
+    auto& t = current_time();
+    auto& u_n = current_solution();
+    KineticIsotropicLocalFunctor<DiscreteFunctionType, MomentBasis> functor(
+        basis_functions_, u_n, actual_dt, sigma_a_, sigma_s_, Q_);
+    auto walker = XT::Grid::Walker<typename DiscreteFunctionType::SpaceType::GridViewType>(u_n.space().grid_view());
+    walker.append(functor);
+    walker.walk(true);
+    t += actual_dt;
+    return dt;
+  } // ... step(...)
+
+private:
+  const MomentBasis& basis_functions_;
+  const ScalarFunctionType& sigma_a_;
+  const ScalarFunctionType& sigma_s_;
+  const ScalarFunctionType& Q_;
+};
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_TIMESTEPPER_KINETIC_ISOTROPIC_HH
diff --git a/dune/gdt/type_traits.hh b/dune/gdt/type_traits.hh
index 64c8ff932fe5699ef704a8d4b8372c25eb197fe0..031738a28c319c7ba64ebc94c86725bfc3f21367 100644
--- a/dune/gdt/type_traits.hh
+++ b/dune/gdt/type_traits.hh
@@ -23,6 +23,7 @@ namespace GDT {
 
 enum class SpaceType
 {
+  continuous_flattop,
   continuous_lagrange,
   discontinuous_lagrange,
   finite_volume,
@@ -34,6 +35,8 @@ std::ostream& operator<<(std::ostream& out, const SpaceType& space_type)
 {
   if (space_type == SpaceType::continuous_lagrange)
     out << "continuous_lagrange";
+  else if (space_type == SpaceType::continuous_flattop)
+    out << "continuous_flattop";
   else if (space_type == SpaceType::discontinuous_lagrange)
     out << "discontinuous_lagrange";
   else if (space_type == SpaceType::finite_volume)
@@ -80,6 +83,47 @@ class LocalFiniteElementFamilyInterface;
 template <class GV, size_t r, size_t rC, class R>
 class SpaceInterface;
 
+// from #include <dune/gdt/test/momentmodels/basisfunctions.hh>
+enum class EntropyType;
+
+template <class DomainFieldType,
+          size_t dimDomain,
+          class RangeFieldType,
+          size_t dimRange,
+          size_t dimRangeCols,
+          size_t dimFlux,
+          EntropyType entropy>
+class HatFunctionMomentBasis;
+
+template <class DomainFieldType, class RangeFieldType, size_t order, size_t dimRangeCols, EntropyType entropy>
+class LegendreMomentBasis;
+
+template <class DomainFieldType,
+          size_t dimDomain,
+          class RangeFieldType,
+          size_t dimRange,
+          size_t dimRangeCols,
+          size_t dimFlux,
+          size_t order,
+          EntropyType entropy>
+class PartialMomentBasis;
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t order,
+          size_t fluxDim,
+          bool only_positive,
+          EntropyType entropy>
+class SphericalHarmonicsMomentBasis;
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t order,
+          size_t fluxDim,
+          bool only_even,
+          EntropyType entropy>
+class RealSphericalHarmonicsMomentBasis;
+
 
 namespace internal {
 
@@ -132,6 +176,126 @@ struct is_space<S, true> : public std::is_base_of<SpaceInterface<typename S::GV,
 {};
 
 
+// from #include <dune/gdt/test/momentmodels/basisfunctions.hh>
+template <class T>
+struct is_hatfunction_basis : public std::false_type
+{};
+
+template <class DomainFieldType,
+          size_t dimDomain,
+          class RangeFieldType,
+          size_t dimRange_or_refinements,
+          size_t dimRangeCols,
+          size_t dimFlux,
+          EntropyType entropy>
+struct is_hatfunction_basis<HatFunctionMomentBasis<DomainFieldType,
+                                                   dimDomain,
+                                                   RangeFieldType,
+                                                   dimRange_or_refinements,
+                                                   dimRangeCols,
+                                                   dimFlux,
+                                                   entropy>> : public std::true_type
+{};
+
+template <class T>
+struct is_legendre_basis : public std::false_type
+{};
+
+template <class DomainFieldType, class RangeFieldType, size_t order, size_t dimRangeCols, EntropyType entropy>
+struct is_legendre_basis<LegendreMomentBasis<DomainFieldType, RangeFieldType, order, dimRangeCols, entropy>>
+  : public std::true_type
+{};
+
+template <class T>
+struct is_partial_moment_basis : public std::false_type
+{};
+
+template <class DomainFieldType,
+          size_t dimDomain,
+          class RangeFieldType,
+          size_t dimRange_or_refinements,
+          size_t dimRangeCols,
+          size_t dimFlux,
+          size_t order,
+          EntropyType entropy>
+struct is_partial_moment_basis<PartialMomentBasis<DomainFieldType,
+                                                  dimDomain,
+                                                  RangeFieldType,
+                                                  dimRange_or_refinements,
+                                                  dimRangeCols,
+                                                  dimFlux,
+                                                  order,
+                                                  entropy>> : public std::true_type
+{};
+
+template <class T>
+struct is_1d_partial_moment_basis : public std::false_type
+{};
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t dimRange_or_refinements,
+          size_t dimRangeCols,
+          size_t order,
+          EntropyType entropy>
+struct is_1d_partial_moment_basis<
+    PartialMomentBasis<DomainFieldType, 1, RangeFieldType, dimRange_or_refinements, dimRangeCols, 1, order, entropy>>
+  : public std::true_type
+{};
+
+template <class T>
+struct is_3d_partial_moment_basis : public std::false_type
+{};
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t dimRange_or_refinements,
+          size_t dimRangeCols,
+          size_t order,
+          EntropyType entropy>
+struct is_3d_partial_moment_basis<
+    PartialMomentBasis<DomainFieldType, 3, RangeFieldType, dimRange_or_refinements, dimRangeCols, 3, order, entropy>>
+  : public std::true_type
+{};
+
+template <class T>
+struct is_spherical_harmonics_basis : public std::false_type
+{};
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t order,
+          size_t fluxDim,
+          bool only_positive,
+          EntropyType entropy>
+struct is_spherical_harmonics_basis<
+    SphericalHarmonicsMomentBasis<DomainFieldType, RangeFieldType, order, fluxDim, only_positive, entropy>>
+  : public std::true_type
+{};
+
+template <class T>
+struct is_real_spherical_harmonics_basis : public std::false_type
+{};
+
+template <class DomainFieldType,
+          class RangeFieldType,
+          size_t order,
+          size_t fluxDim,
+          bool only_even,
+          EntropyType entropy>
+struct is_real_spherical_harmonics_basis<
+    RealSphericalHarmonicsMomentBasis<DomainFieldType, RangeFieldType, order, fluxDim, only_even, entropy>>
+  : public std::true_type
+{};
+
+template <class T>
+struct is_full_moment_basis
+{
+  static constexpr bool value = is_legendre_basis<T>::value || is_spherical_harmonics_basis<T>::value
+                                || is_real_spherical_harmonics_basis<T>::value;
+};
+
+
 #if 0
 // from #include <dune/gdt/playground/spaces/restricted.hh>
 template <class S, bool candidate = internal::is_restricted_space_helper<S>::is_candidate>
diff --git a/examples/adaptive_elliptic_swipdg.cc b/examples/adaptive_elliptic_swipdg.cc
index 1e9e995d8e423c0ddb75a2e0de163c04332767c7..bc5a88a1f2df56e322ee63cdd7df85333ac3209e 100644
--- a/examples/adaptive_elliptic_swipdg.cc
+++ b/examples/adaptive_elliptic_swipdg.cc
@@ -198,8 +198,9 @@ int main(int argc, char* argv[])
 
     // problem
     const Test::ESV2007DiffusionProblem<GV> problem;
-    const auto& df = problem.diffusion_factor.as_grid_function<E>();
-    const auto& dt = problem.diffusion_tensor.as_grid_function<E>();
+    const XT::Functions::ConstantFunction<d> diff_factor(1.);
+    const auto& df = diff_factor.as_grid_function<E>();
+    const auto& dt = problem.diffusion.as_grid_function<E>();
     const auto& f = problem.force.as_grid_function<E>();
     const auto& boundary_info = problem.boundary_info;
 
diff --git a/examples/mpi_2019_02_talk_on_hyperbolic_equations.cc b/examples/mpi_2019_02_talk_on_hyperbolic_equations.cc
index 46163ce1827e51c21d1af25257a39abc9c14651b..80225f445c5543dccf79cc80dd5d0445cbacaf2a 100644
--- a/examples/mpi_2019_02_talk_on_hyperbolic_equations.cc
+++ b/examples/mpi_2019_02_talk_on_hyperbolic_equations.cc
@@ -29,7 +29,7 @@
 #include <dune/gdt/local/numerical-fluxes/generic.hh>
 #include <dune/gdt/local/numerical-fluxes/upwind.hh>
 #include <dune/gdt/local/numerical-fluxes/vijayasundaram.hh>
-#include <dune/gdt/local/integrands/elliptic.hh>
+#include <dune/gdt/local/integrands/laplace.hh>
 #include <dune/gdt/local/integrands/product.hh>
 #include <dune/gdt/interpolations.hh>
 #include <dune/gdt/operators/advection-fv.hh>
@@ -188,7 +188,6 @@ XT::LA::ListVectorArray<V> implicit_euler(const DiscreteFunction<V, GV, m>& init
 GTEST_TEST(MPI201902TalkExamples, instationary_heat_equation)
 {
   using G = ONED_1D;
-  static const size_t d = G::dimension;
   auto grid = XT::Grid::make_cube_grid<G>(0., 1., 1024);
   auto grid_view = grid.leaf_view();
   using GV = decltype(grid_view);
@@ -199,7 +198,7 @@ GTEST_TEST(MPI201902TalkExamples, instationary_heat_equation)
   auto cg_space = make_continuous_lagrange_space(grid_view, 1);
   auto dirichlet_constraints = make_dirichlet_constraints(cg_space, boundary_info);
   auto spatial_op = make_matrix_operator<M>(cg_space, Stencil::element);
-  spatial_op.append(LocalElementIntegralBilinearForm<E>(LocalEllipticIntegrand<E>(1.)));
+  spatial_op.append(LocalElementIntegralBilinearForm<E>(LocalLaplaceIntegrand<E>(1.)));
   spatial_op.append(dirichlet_constraints);
   auto l2_op = make_matrix_operator<M>(cg_space, Stencil::element);
   l2_op.append(LocalElementIntegralBilinearForm<E>(LocalElementProductIntegrand<E>(1.)));
@@ -212,14 +211,14 @@ GTEST_TEST(MPI201902TalkExamples, instationary_heat_equation)
   auto time_loop = [&](const double& T_end, const double& dt) {
     XT::LA::ListVectorArray<V> solution(cg_space.mapper().size(), /*length=*/0, /*reserve=*/std::ceil(T_end / (dt)));
     // initial values
-    solution.append(interpolate<V>(0,
-                                   [](const auto& xx, const auto& /*param*/) {
-                                     if (0.25 <= xx[0] && xx[0] <= 0.5)
-                                       return 1.;
-                                     else
-                                       return 0.;
-                                   },
-                                   cg_space)
+    solution.append(default_interpolation<V>(0,
+                                             [](const auto& xx, const auto& /*param*/) {
+                                               if (0.25 <= xx[0] && xx[0] <= 0.5)
+                                                 return 1.;
+                                               else
+                                                 return 0.;
+                                             },
+                                             cg_space)
                         .dofs()
                         .vector(),
                     {"_t", 0.});
@@ -239,7 +238,7 @@ GTEST_TEST(MPI201902TalkExamples, instationary_heat_equation)
     const BochnerSpace<GV> bochner_space(cg_space, time_points_from_vector_array(solution));
     const auto timdomain_solution = make_discrete_bochner_function(bochner_space, solution);
     for (size_t ii = 0; ii < 100; ++ii) {
-      const double time = ii * (T_end / 100);
+      time = ii * (T_end / 100);
       timdomain_solution.evaluate(time).visualize(XT::Common::Test::get_unique_test_name() + "__dt"
                                                   + XT::Common::to_string(dt) + "_solution_"
                                                   + XT::Common::to_string(ii));
@@ -259,7 +258,6 @@ GTEST_TEST(MPI201902TalkExamples, linear_transport)
   auto grid = XT::Grid::make_cube_grid<G>(0., 1., 1024);
   auto grid_view = XT::Grid::make_periodic_grid_view(grid.leaf_view());
   using GV = decltype(grid_view);
-  using E = XT::Grid::extract_entity_t<GV>;
   using I = XT::Grid::extract_intersection_t<GV>;
 
   auto V_h_0 = make_finite_volume_space(grid_view);
@@ -269,17 +267,17 @@ GTEST_TEST(MPI201902TalkExamples, linear_transport)
                                                   "transport_to_the_right",
                                                   {},
                                                   [&](const auto& /*w*/, const auto& /*param*/) { return 1.; });
-  const NumericalUpwindFlux<d, 1> g(f);
+  const NumericalUpwindFlux<I, d, 1> g(f);
   auto L_h = make_advection_fv_operator<M>(grid_view, g, V_h_0, V_h_0);
 
-  auto w_0 = interpolate<V>(0,
-                            [](const auto& xx, const auto& /*param*/) {
-                              if (0.25 <= xx[0] && xx[0] <= 0.5)
-                                return 1.;
-                              else
-                                return 0.;
-                            },
-                            V_h_0);
+  auto w_0 = default_interpolation<V>(0,
+                                      [](const auto& xx, const auto& /*param*/) {
+                                        if (0.25 <= xx[0] && xx[0] <= 0.5)
+                                          return 1.;
+                                        else
+                                          return 0.;
+                                      },
+                                      V_h_0);
 
   // explicit, the right choice
   const double T_end = 1.;
@@ -304,7 +302,6 @@ GTEST_TEST(MPI201902TalkExamples, burgers)
   auto grid = XT::Grid::make_cube_grid<G>(0., 1., 1024);
   auto grid_view = XT::Grid::make_periodic_grid_view(grid.leaf_view());
   using GV = decltype(grid_view);
-  using E = XT::Grid::extract_entity_t<GV>;
   using I = XT::Grid::extract_intersection_t<GV>;
 
   auto V_h_0 = make_finite_volume_space(grid_view);
@@ -314,14 +311,14 @@ GTEST_TEST(MPI201902TalkExamples, burgers)
                                                   "burgers",
                                                   {},
                                                   [&](const auto& w, const auto& /*param*/) { return w; });
-  const NumericalUpwindFlux<d, 1> g(f);
+  const NumericalUpwindFlux<I, d, 1> g(f);
   auto L_h = make_advection_fv_operator<M>(grid_view, g, V_h_0, V_h_0);
 
-  auto w_0 = interpolate<V>(3,
-                            [&](const auto& xx, const auto& /*mu*/) {
-                              return std::exp(-std::pow(xx[0] - 0.33, 2) / (2 * std::pow(0.075, 2)));
-                            },
-                            V_h_0);
+  auto w_0 = default_interpolation<V>(3,
+                                      [&](const auto& xx, const auto& /*mu*/) {
+                                        return std::exp(-std::pow(xx[0] - 0.33, 2) / (2 * std::pow(0.075, 2)));
+                                      },
+                                      V_h_0);
 
   // explicit
   const double T_end = 1.;
@@ -342,7 +339,6 @@ GTEST_TEST(MPI201902TalkExamples, linear_transport__central_differences)
   auto grid = XT::Grid::make_cube_grid<G>(0., 1., 16);
   auto grid_view = XT::Grid::make_periodic_grid_view(grid.leaf_view());
   using GV = decltype(grid_view);
-  using E = XT::Grid::extract_entity_t<GV>;
   using I = XT::Grid::extract_intersection_t<GV>;
 
   auto V_h_0 = make_finite_volume_space(grid_view);
@@ -352,20 +348,20 @@ GTEST_TEST(MPI201902TalkExamples, linear_transport__central_differences)
                                                   "transport_to_the_right",
                                                   {},
                                                   [&](const auto& /*w*/, const auto& /*param*/) { return 1.; });
-  const GenericNumericalFlux<d, 1> g(
-      f, [&](const auto& w_minus, const auto& w_plus, const auto& n, const auto& /*param*/) {
+  const GenericNumericalFlux<I, d, 1> g(
+      f, [&](const auto&, const auto&, const auto& w_minus, const auto& w_plus, const auto& n, const auto& /*param*/) {
         return 0.5 * (f.evaluate(w_minus) + f.evaluate(w_plus)) * n;
       });
   auto L_h = make_advection_fv_operator<M>(grid_view, g, V_h_0, V_h_0);
 
-  auto w_0 = interpolate<V>(0,
-                            [](const auto& xx, const auto& /*param*/) {
-                              if (0.25 <= xx[0] && xx[0] <= 0.5)
-                                return 1.;
-                              else
-                                return 0.;
-                            },
-                            V_h_0);
+  auto w_0 = default_interpolation<V>(0,
+                                      [](const auto& xx, const auto& /*param*/) {
+                                        if (0.25 <= xx[0] && xx[0] <= 0.5)
+                                          return 1.;
+                                        else
+                                          return 0.;
+                                      },
+                                      V_h_0);
 
   // explicit, the right choice
   const double T_end = 1.;
@@ -392,10 +388,11 @@ GTEST_TEST(MPI201902TalkExamples, 2d_euler)
   auto grid = XT::Grid::make_cube_grid<G>(-1., 1., 128);
   auto grid_view = XT::Grid::make_periodic_grid_view(grid.leaf_view());
   using GV = decltype(grid_view);
+  using I = XT::Grid::extract_intersection_t<GV>;
 
   auto V_h_0 = make_finite_volume_space<m>(grid_view);
 
-  const NumericalVijayasundaramFlux<d, m> g(
+  const NumericalVijayasundaramFlux<I, d, m> g(
       f,
       /*flux_eigen_decomposition=*/[&](const auto& /*f*/, const auto& w, const auto& n, const auto&
                                        /*param*/) {
@@ -405,15 +402,15 @@ GTEST_TEST(MPI201902TalkExamples, 2d_euler)
       });
   auto L_h = make_advection_fv_operator<M>(grid_view, g, V_h_0, V_h_0);
 
-  auto w_0 = interpolate<V>(0,
-                            [&](const auto& xx, const auto& /*mu*/) {
-                              if (XT::Common::FloatCmp::ge(xx, DomainType(-0.5))
-                                  && XT::Common::FloatCmp::le(xx, DomainType(0)))
-                                return euler_tools.conservative(/*density=*/4., /*velocity=*/0., /*pressure=*/1.6);
-                              else
-                                return euler_tools.conservative(/*density=*/1., /*velocity=*/0., /*pressure=*/0.4);
-                            },
-                            V_h_0);
+  auto w_0 = default_interpolation<V>(
+      0,
+      [&](const auto& xx, const auto& /*mu*/) {
+        if (XT::Common::FloatCmp::ge(xx, DomainType(-0.5)) && XT::Common::FloatCmp::le(xx, DomainType(0)))
+          return euler_tools.conservative(/*density=*/4., /*velocity=*/0., /*pressure=*/1.6);
+        else
+          return euler_tools.conservative(/*density=*/1., /*velocity=*/0., /*pressure=*/0.4);
+      },
+      V_h_0);
 
   const double T_end = 1.;
   const double dt = estimate_dt_for_hyperbolic_system(grid_view, w_0, f);
@@ -437,6 +434,7 @@ GTEST_TEST(MPI201902TalkExamples, burgers_p1_unstable)
   auto grid = XT::Grid::make_cube_grid<G>(0., 1., 128);
   auto grid_view = XT::Grid::make_periodic_grid_view(grid.leaf_view());
   using GV = decltype(grid_view);
+  using I = XT::Grid::extract_intersection_t<GV>;
 
   const DiscontinuousLagrangeSpace<GV> V_h_1(grid_view, 1);
 
@@ -445,15 +443,15 @@ GTEST_TEST(MPI201902TalkExamples, burgers_p1_unstable)
                                                   "burgers",
                                                   {},
                                                   [&](const auto& w, const auto& /*param*/) { return w; });
-  const NumericalUpwindFlux<d, 1> g(f);
+  const NumericalUpwindFlux<I, d, 1> g(f);
   // unstable DG operator
   const AdvectionDgOperator<M, GV> L_h(grid_view, g, V_h_1, V_h_1, XT::Grid::ApplyOn::NoIntersections<GV>(), 0., 0.);
 
-  auto w_0 = interpolate<V>(3,
-                            [&](const auto& xx, const auto& /*mu*/) {
-                              return std::exp(-std::pow(xx[0] - 0.33, 2) / (2 * std::pow(0.075, 2)));
-                            },
-                            V_h_1);
+  auto w_0 = default_interpolation<V>(3,
+                                      [&](const auto& xx, const auto& /*mu*/) {
+                                        return std::exp(-std::pow(xx[0] - 0.33, 2) / (2 * std::pow(0.075, 2)));
+                                      },
+                                      V_h_1);
 
   const double T_end = 1.;
   const double fv_dt = estimate_dt_for_hyperbolic_system(grid_view, w_0, f);
@@ -469,13 +467,14 @@ GTEST_TEST(MPI201902TalkExamples, burgers_shock_capturing)
   auto grid = XT::Grid::make_cube_grid<G>(0., 1., 16u);
   auto grid_view = XT::Grid::make_periodic_grid_view(grid.leaf_view());
   using GV = decltype(grid_view);
+  using I = XT::Grid::extract_intersection_t<GV>;
 
   const XT::Functions::GenericFunction<1, d, 1> f(2,
                                                   [&](const auto& w, const auto& /*param*/) { return 0.5 * w * w; },
                                                   "burgers",
                                                   {},
                                                   [&](const auto& w, const auto& /*param*/) { return w; });
-  const NumericalEngquistOsherFlux<d, 1> g(f);
+  const NumericalEngquistOsherFlux<I, d, 1> g(f);
 
   auto perform_simulation = [&](const int p, const std::string& prefix, const double& CFL_factor = 0.99) {
     const DiscontinuousLagrangeSpace<GV> V_h_p(grid_view, p);
@@ -483,11 +482,11 @@ GTEST_TEST(MPI201902TalkExamples, burgers_shock_capturing)
     // stabilized DG operator
     const AdvectionDgOperator<M, GV> L_h(grid_view, g, V_h_p, V_h_p);
 
-    auto w_0 = interpolate<V>(3,
-                              [&](const auto& xx, const auto& /*mu*/) {
-                                return std::exp(-std::pow(xx[0] - 0.33, 2) / (2 * std::pow(0.075, 2)));
-                              },
-                              V_h_p);
+    auto w_0 = default_interpolation<V>(3,
+                                        [&](const auto& xx, const auto& /*mu*/) {
+                                          return std::exp(-std::pow(xx[0] - 0.33, 2) / (2 * std::pow(0.075, 2)));
+                                        },
+                                        V_h_p);
 
     const double T_end = 1.;
     const double fv_dt = estimate_dt_for_hyperbolic_system(grid_view, w_0, f);
diff --git a/examples/stationary-heat-equation.cc b/examples/stationary-heat-equation.cc
index f56e7fc57009da611ac4b25aebe24f8f3f318310..463db173b50049b0fe30194cc76e64506e842c57 100644
--- a/examples/stationary-heat-equation.cc
+++ b/examples/stationary-heat-equation.cc
@@ -32,7 +32,7 @@
 #include <dune/gdt/local/bilinear-forms/integrals.hh>
 #include <dune/gdt/local/functionals/integrals.hh>
 #include <dune/gdt/local/integrands/conversion.hh>
-#include <dune/gdt/local/integrands/elliptic.hh>
+#include <dune/gdt/local/integrands/laplace.hh>
 #include <dune/gdt/local/integrands/product.hh>
 #include <dune/gdt/operators/localizable-bilinear-form.hh>
 #include <dune/gdt/operators/matrix-based.hh>
@@ -63,7 +63,6 @@ int main(int argc, char* argv[])
     XT::Common::TimedLogger().create(DXTC_CONFIG_GET("logger.info", 1), DXTC_CONFIG_GET("logger.debug", -1));
     auto logger = XT::Common::TimedLogger().get("main");
 
-    const XT::Functions::ConstantFunction<d> lambda(1);
     const XT::Functions::ConstantFunction<d, d, d> kappa(
         XT::Common::from_string<FieldMatrix<double, d, d>>("[1 0 0; 0 1 0; 0 0 1]"));
     const XT::Functions::GenericFunction<d> force(3, [](const auto& x, const auto& /*param*/) {
@@ -93,8 +92,7 @@ int main(int argc, char* argv[])
     auto space = make_continuous_lagrange_space(grid_view, /*polorder=*/1);
 
     auto lhs_op = make_matrix_operator<M>(space, Stencil::element);
-    lhs_op.append(LocalElementIntegralBilinearForm<E>(
-        LocalEllipticIntegrand<E>(lambda.as_grid_function<E>(), kappa.as_grid_function<E>())));
+    lhs_op.append(LocalElementIntegralBilinearForm<E>(LocalLaplaceIntegrand<E>(kappa.as_grid_function<E>())));
 
     auto rhs_func = make_vector_functional<V>(space);
     rhs_func.append(LocalElementIntegralFunctional<E>(
@@ -117,8 +115,7 @@ int main(int argc, char* argv[])
     const auto error = solution - exact_solution.as_grid_function<E>();
 
     auto h1_prod = make_localizable_bilinear_form(grid_view, error, error);
-    h1_prod.append(LocalElementIntegralBilinearForm<E>(
-        LocalEllipticIntegrand<E>(lambda.as_grid_function<E>(), kappa.as_grid_function<E>())));
+    h1_prod.append(LocalElementIntegralBilinearForm<E>(LocalLaplaceIntegrand<E>(kappa.as_grid_function<E>())));
 
     auto l2_prod = make_localizable_bilinear_form(grid_view, error, error);
     l2_prod.append(LocalElementIntegralBilinearForm<E>(LocalElementProductIntegrand<E>()));
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index d6c2495801cc689b95bc77bbe8d24a1dc6b2cea3..621f4703b6c18aed7895e6b518d8dfefb2bc4934 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -9,5 +9,5 @@
 #   René Fritze (2018)
 # ~~~
 
-add_subdirectory(dune/gdt)
+add_subdirectory(dune)
 dxt_add_python_tests()
diff --git a/python/dune/CMakeLists.txt b/python/dune/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1cb4768f2890209df79dd24a2ec741b8af2444b0
--- /dev/null
+++ b/python/dune/CMakeLists.txt
@@ -0,0 +1,12 @@
+# ~~~
+# This file is part of the dune-gdt project:
+#   https://github.com/dune-community/dune-gdt
+# Copyright 2010-2018 dune-gdt 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:
+#   René Fritze (2018)
+# ~~~
+
+add_subdirectory(gdt)
diff --git a/python/dune/gdt/CMakeLists.txt b/python/dune/gdt/CMakeLists.txt
index ce08e9c3b9bfc0d715d9fc392f0fbe6569e8274c..bc0b3d9b0afcd025b4068cf631d8c6534e23f9e3 100644
--- a/python/dune/gdt/CMakeLists.txt
+++ b/python/dune/gdt/CMakeLists.txt
@@ -18,3 +18,5 @@ dune_pybindxi_add_module(gamm_2019_talk_on_conservative_rb
                          EXCLUDE_FROM_ALL
                          ${header}
                          gamm-2019-talk-on-conservative-rb.cc)
+
+add_subdirectory(discretefunction)
diff --git a/python/dune/gdt/__init__.py b/python/dune/gdt/__init__.py
index 490eb3d68cb8d4359a60cccc8adc148fbbeffa1d..dd488b5818436f31e623e01f9d34a37fdc13c6a7 100644
--- a/python/dune/gdt/__init__.py
+++ b/python/dune/gdt/__init__.py
@@ -9,94 +9,3 @@
 #   Felix Schindler (2017 - 2018)
 #   René Fritze     (2016, 2018)
 # ~~~
-
-from importlib import import_module
-
-import numpy as np
-
-import dune.xt.common
-
-_init_logger_methods = list()
-_test_logger_methods = list()
-_init_mpi_methods = list()
-_other_modules = ('xt.common', 'xt.grid', 'xt.functions', 'xt.la')
-
-_gdt_modules = ()
-#                'spaces_block',
-#                'local_diffusive_flux_estimation_operator',
-#                'local_elliptic_ipdg_operators',
-#                'assembler',
-#                'discretefunction',
-#                'projections',
-#                'functionals_elliptic_ipdg',
-#                'functionals_l2',
-#                'operators_elliptic',
-#                'operators_elliptic_ipdg',
-#                'operators_fluxreconstruction',
-#                'operators_oswaldinterpolation',
-#                'operators_ESV2007',
-#                'operators_OS2015',
-#                'operators_RS2017',
-#                'operators_l2',
-#                'operators_weighted_l2']
-
-for module_name in _gdt_modules:
-    mod = import_module('.__{}'.format(module_name), 'dune.gdt')
-    to_import = [name for name in mod.__dict__ if not name.startswith('_')]
-    globals().update({name: mod.__dict__[name] for name in to_import})
-    _init_logger_methods.append(mod.__dict__['_init_logger'])
-    _test_logger_methods.append(mod.__dict__['_test_logger'])
-    _init_mpi_methods.append(mod.__dict__['_init_mpi'])
-
-del _gdt_modules
-
-
-def init_logger(max_info_level=999,
-                max_debug_level=999,
-                enable_warnings=True,
-                enable_colors=True,
-                info_color='blue',
-                debug_color='darkgray',
-                warning_color='red'):
-    init_logger_methods = _init_logger_methods.copy()
-    for module_name in _other_modules:
-        try:
-            mm = import_module('dune.{}'.format(module_name))
-            for init_logger_method in mm._init_logger_methods:
-                init_logger_methods.append(init_logger_method)
-        except ModuleNotFoundError:
-            pass
-    for init_logger_method in init_logger_methods:
-        init_logger_method(max_info_level, max_debug_level, enable_warnings, enable_colors, info_color, debug_color,
-                           warning_color)
-
-
-def test_logger(info=True, debug=True, warning=True):
-    test_logger_methods = _test_logger_methods.copy()
-    for module_name in _other_modules:
-        try:
-            mm = import_module('dune.{}'.format(module_name))
-            for test_logger_method in mm._test_logger_methods:
-                test_logger_methods.append(test_logger_method)
-        except ModuleNotFoundError:
-            pass
-    for test_logger_method in test_logger_methods:
-        test_logger_method(info, debug, warning)
-
-
-def init_mpi(args=list()):
-    if DEBUG:
-        init_mpi_methods = [
-            _init_mpi_methods[0],
-        ]
-    else:
-        init_mpi_methods = _init_mpi_methods.copy()
-        for module_name in _other_modules:
-            try:
-                mm = import_module('dune.{}'.format(module_name))
-                for init_mpi_method in mm._init_mpi_methods:
-                    init_mpi_methods.append(init_mpi_method)
-            except ModuleNotFoundError:
-                pass
-    for init_mpi_method in init_mpi_methods:
-        init_mpi_method(args)
diff --git a/python/dune/gdt/assembler/system.cc b/python/dune/gdt/assembler/system.cc
deleted file mode 100644
index 355bf905df55a86ad54d6698d2d90e1d9fb24048..0000000000000000000000000000000000000000
--- a/python/dune/gdt/assembler/system.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  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/gdt/shared.hh>
-
-#  include <python/dune/gdt/assembler/system.hh>
-#  include <python/dune/gdt/spaces/constraints.hh>
-
-
-PYBIND11_MODULE(__assembler, m)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.gdt.__spaces");
-  py::module::import("dune.gdt.__local_elliptic_ipdg_operators");
-
-  py::class_<Dune::GDT::bindings::ResultStorage> ResultStorage(m, "ResultStorage", "dune-gdt: ResultStorage");
-  ResultStorage.def(pybind11::init<>());
-  ResultStorage.def_property(
-      "result",
-      [](const Dune::GDT::bindings::ResultStorage& self) { return self.result(); },
-      [](Dune::GDT::bindings::ResultStorage& self, const double& value) { self.result() = value; });
-
-  DUNE_GDT_SPACES_CONSTRAINTS_BIND(m);
-  DUNE_GDT_ASSEMBLER_SYSTEM_BIND(m);
-
-  add_initialization(m, "dune.gdt.assembler");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/assembler/system.hh b/python/dune/gdt/assembler/system.hh
deleted file mode 100644
index db14a2f5c3fc3808bce918e7907cb75c2420f9e1..0000000000000000000000000000000000000000
--- a/python/dune/gdt/assembler/system.hh
+++ /dev/null
@@ -1,394 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_ASSEMBLER_SYSTEM_BINDINGS_HH
-#define PYTHON_DUNE_GDT_ASSEMBLER_SYSTEM_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <dune/xt/common/memory.hh>
-#  include <dune/xt/la/container.hh>
-#  include <dune/xt/grid/grids.hh>
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <python/dune/xt/grid/layers.bindings.hh>
-#  include <dune/xt/grid/walker.hh>
-
-#  include <python/dune/gdt/spaces/bindings.hh>
-#  include <python/dune/gdt/spaces/constraints.hh>
-#  include <dune/gdt/type_traits.hh>
-
-#  include <dune/gdt/assembler/system.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-class ResultStorage
-{
-public:
-  ResultStorage()
-    : result_(0.)
-  {
-  }
-
-  ResultStorage(const ResultStorage& other) = delete;
-  ResultStorage(ResultStorage&& source) = delete;
-
-  ResultStorage& operator=(const ResultStorage& other) = delete;
-  ResultStorage& operator=(ResultStorage&& source) = delete;
-
-  double& result()
-  {
-    return result_;
-  }
-
-  const double& result() const
-  {
-    return result_;
-  }
-
-private:
-  double result_;
-}; // class ResultStorage
-
-
-template <class TP, XT::Grid::Layers grid_layer, XT::Grid::Backends grid_backend>
-class SystemAssembler
-{
-  typedef typename TP::type T;
-  static_assert(is_space<T>::value, "");
-  typedef XT::Grid::extract_grid_t<typename T::GridLayerType> G;
-  typedef typename XT::Grid::Layer<G, grid_layer, grid_backend, XT::Grid::DD::SubdomainGrid<G>>::type GL;
-  typedef XT::Grid::extract_entity_t<GL> E;
-  typedef typename G::ctype D;
-  static const constexpr size_t d = G::dimension;
-
-public:
-  typedef GDT::SystemAssembler<T, GL> type;
-  typedef pybind11::class_<type, XT::Grid::Walker<GL>> bound_type;
-
-private:
-  typedef typename type::TestSpaceType TestSpaceType;
-  typedef typename type::GridLayerType GridLayerType;
-  typedef typename type::AnsatzSpaceType AnsatzSpaceType;
-
-  template <bool do_bind = (std::is_same<TestSpaceType, AnsatzSpaceType>::value
-                            && std::is_same<GridLayerType, typename TestSpaceType::GridLayerType>::value),
-            bool anything = true>
-  struct addbind_ctor_single
-  {
-    void operator()(bound_type& c)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-      c.def(py::init<T>(),
-            "space"_a,
-            "Uses given space as test and ansatz space, and the grid layer of the given space as grid layer.",
-            py::keep_alive<1, 2>());
-    }
-  }; // struct addbind_ctor_single
-
-  template <bool anything>
-  struct addbind_ctor_single<false, anything>
-  {
-    void operator()(bound_type& /*c*/)
-    {
-    }
-  }; // struct addbind_ctor_single
-
-  template <bool same = std::is_same<GL, typename T::GridLayerType>::value, bool anything = true>
-  struct space_and_layer_name
-  {
-    static std::string value()
-    {
-      return space_name<TP>::value();
-    }
-  };
-
-  template <bool anything>
-  struct space_and_layer_name<false, anything>
-  {
-    static std::string value()
-    {
-      return space_name<TP>::value() + "_" + XT::Grid::bindings::layer_name<grid_layer>::value() + "_"
-             + XT::Grid::bindings::backend_name<grid_backend>::value();
-    }
-  };
-
-  template <bool same = std::is_same<GL, typename T::GridLayerType>::value, bool anything = true>
-  struct addbind_factory_methods
-  {
-    void operator()(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      m.def("make_system_assembler",
-            [](const TestSpaceType& space) { return new type(space); },
-            "space"_a,
-            py::keep_alive<0, 1>());
-    }
-  };
-
-  template <bool anything>
-  struct addbind_factory_methods<false, anything>
-  {
-    void operator()(pybind11::module& /*m*/)
-    {
-    }
-  };
-
-  template <XT::LA::Backends la>
-  static void addaddbind_matrixatrix(bound_type& c)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    typedef typename XT::LA::Container<typename T::RangeFieldType, la>::MatrixType M;
-
-    c.def("append",
-          [](type& self,
-             const GDT::LocalBoundaryTwoFormInterface<typename T::BaseFunctionSetType,
-                                                      XT::Grid::extract_intersection_t<GL>>& local_boundary_two_form,
-             M& matrix,
-             const XT::Grid::ApplyOn::WhichIntersection<GL>& which_intersections) {
-            self.append(local_boundary_two_form, matrix, which_intersections.copy());
-          },
-          "local_boundary_two_form"_a,
-          "matrix"_a,
-          "which_intersections"_a = XT::Grid::ApplyOn::AllIntersections<GL>(),
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>());
-    c.def("append",
-          [](type& self,
-             const GDT::LocalCouplingTwoFormInterface<typename T::BaseFunctionSetType,
-                                                      XT::Grid::extract_intersection_t<GL>>& local_coupling_two_form,
-             M& matrix,
-             const XT::Grid::ApplyOn::WhichIntersection<GL>& which_intersections) {
-            self.append(local_coupling_two_form, matrix, which_intersections.copy());
-          },
-          "local_coupling_two_form"_a,
-          "matrix"_a,
-          "which_intersections"_a = XT::Grid::ApplyOn::AllIntersections<GL>(),
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>());
-    c.def("append",
-          [](type& self,
-             const GDT::LocalCouplingTwoFormInterface<typename T::BaseFunctionSetType,
-                                                      XT::Grid::extract_intersection_t<GL>>& local_coupling_two_form,
-             M& matrix_in_in,
-             M& matrix_out_out,
-             M& matrix_in_out,
-             M& matrix_out_in,
-             const XT::Grid::ApplyOn::WhichIntersection<GL>& which_intersections) {
-            self.append(local_coupling_two_form,
-                        matrix_in_in,
-                        matrix_out_out,
-                        matrix_in_out,
-                        matrix_out_in,
-                        which_intersections.copy());
-          },
-          "local_coupling_two_form"_a,
-          "matrix_in_in"_a,
-          "matrix_out_out"_a,
-          "matrix_in_out"_a,
-          "matrix_out_in"_a,
-          "which_intersections"_a = XT::Grid::ApplyOn::AllIntersections<GL>(),
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>(),
-          py::keep_alive<0, 4>(),
-          py::keep_alive<0, 5>());
-  } // ... addaddbind_matrixatrix(...)
-
-public:
-  static bound_type bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    const auto ClassName =
-        XT::Common::to_camel_case(std::string("system_assembler_") + space_and_layer_name<>::value());
-
-    bound_type c(m, ClassName.c_str(), ClassName.c_str());
-    addbind_ctor_single<>()(c);
-
-    c.def("append",
-          [](type& self, type& other, const XT::Grid::ApplyOn::WhichIntersection<GL>& which_intersections) {
-            self.append(other, which_intersections.copy());
-          },
-          "system_assembler"_a,
-          "which_intersections"_a = XT::Grid::ApplyOn::AllIntersections<GL>(),
-          py::keep_alive<1, 2>());
-    c.def("append",
-          [](type& self,
-             XT::Grid::Walker<GridLayerType>& other,
-             const XT::Grid::ApplyOn::WhichIntersection<GL>& which_intersections) {
-            self.append(other, which_intersections.copy());
-          },
-          "grid_walker"_a,
-          "which_intersections"_a = XT::Grid::ApplyOn::AllIntersections<GL>(),
-          py::keep_alive<1, 2>());
-    c.def("assemble",
-          [](type& self, const bool use_tbb) {
-            py::gil_scoped_release DUNE_UNUSED(release);
-            self.assemble(use_tbb);
-          },
-          "use_tbb"_a = false);
-
-    bindings::DirichletConstraints<XT::Grid::extract_intersection_t<typename type::GridLayerType>,
-                                   XT::Grid::extract_grid_t<typename type::GridLayerType>>::addbind(c);
-
-#  if HAVE_DUNE_ISTL
-    addaddbind_matrixatrix<XT::LA::Backends::istl_sparse>(c);
-#  endif
-
-    c.def("append",
-          [](type& self,
-             const GDT::LocalVolumeTwoFormInterface<XT::Functions::LocalfunctionInterface<E, D, d, double, 1>,
-                                                    XT::Functions::LocalfunctionInterface<E, D, d, double, 1>,
-                                                    double>& local_volume_two_form,
-             const XT::Functions::GridFunctionInterface<E, D, d, double, 1>& test_function,
-             const XT::Functions::GridFunctionInterface<E, D, d, double, 1>& ansatz_function,
-             ResultStorage& result /*,
-             const XT::Grid::ApplyOn::WhichEntity<GL>& where*/) {
-            self.append(local_volume_two_form, test_function, ansatz_function, result.result() /*, where.copy()*/);
-          },
-          "local_volume_two_form"_a,
-          "test_function"_a,
-          "ansatz_function"_a,
-          "result"_a /*,
-          "where"_a = XT::Grid::ApplyOn::AllEntities<GL>()*/,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>());
-
-    addbind_factory_methods<>()(m);
-
-    return c;
-  } // ... bind(...)
-}; // class SystemAssembler
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-
-// begin: this is what we need for the lib
-
-#  define _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB(                                                                         \
-      _pre, _G, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)                                 \
-    _pre class Dune::GDT::bindings::SystemAssembler<Dune::GDT::SpaceProvider<_G,                                       \
-                                                                             Dune::XT::Grid::Layers::_s_grid_layer,    \
-                                                                             Dune::GDT::SpaceType::_s_type,            \
-                                                                             Dune::GDT::Backends::_s_backend,          \
-                                                                             _p,                                       \
-                                                                             double,                                   \
-                                                                             _r,                                       \
-                                                                             _rC>,                                     \
-                                                    Dune::XT::Grid::Layers::_g_layer,                                  \
-                                                    Dune::XT::Grid::Backends::_g_backend>
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_ALU_SPACE(                                                             \
-        _pre, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)                                   \
-      _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB(                                                                             \
-          _pre, ALU_2D_SIMPLEX_CONFORMING, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)
-#  else
-#    define _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_ALU_SPACE(                                                             \
-        _pre, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)
-#  endif
-
-#  define _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_YASP_SPACE(                                                              \
-      _pre, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)                                     \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB(                                                                               \
-        _pre, YASP_1D_EQUIDISTANT_OFFSET, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC);      \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB(                                                                               \
-        _pre, YASP_2D_EQUIDISTANT_OFFSET, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)
-
-#  define _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_ALU(_pre, _s_type, _p, _r, _rC)                                          \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_ALU_SPACE(_pre, leaf, view, _s_type, gdt, leaf, _p, 1, 1);                     \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_ALU_SPACE(_pre, level, view, _s_type, gdt, level, _p, 1, 1)
-
-#  define _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_YASP(_pre, _s_type, _p, _r, _rC)                                         \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_YASP_SPACE(_pre, leaf, view, _s_type, gdt, leaf, _p, 1, 1);                    \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_YASP_SPACE(_pre, level, view, _s_type, gdt, level, _p, 1, 1)
-
-#  define DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_ALU(_pre)                                                                 \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_ALU(_pre, fv, 0, 1, 1);                                                        \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_ALU(_pre, cg, 1, 1, 1)
-
-#  define DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_YASP(_pre)                                                                \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_YASP(_pre, fv, 0, 1, 1);                                                       \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_YASP(_pre, cg, 1, 1, 1)
-
-#  define DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB(_pre)                                                                     \
-    DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_ALU(_pre);                                                                      \
-    DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_YASP(_pre)
-
-DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB(extern template);
-
-// end: this is what we need for the lib
-// begin: this is what we need for the .so
-
-
-#  define _DUNE_GDT_ASSEMBLER_SYSTEM_BIND(                                                                             \
-      _m, _G, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)                                   \
-    Dune::GDT::bindings::SystemAssembler<Dune::GDT::SpaceProvider<_G,                                                  \
-                                                                  Dune::XT::Grid::Layers::_s_grid_layer,               \
-                                                                  Dune::GDT::SpaceType::_s_type,                       \
-                                                                  Dune::GDT::Backends::_s_backend,                     \
-                                                                  _p,                                                  \
-                                                                  double,                                              \
-                                                                  _r,                                                  \
-                                                                  _rC>,                                                \
-                                         Dune::XT::Grid::Layers::_g_layer,                                             \
-                                         Dune::XT::Grid::Backends::_g_backend>::bind(_m)
-
-#  define _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_YASP(                                                                        \
-      _m, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)                                       \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND(                                                                                   \
-        _m, YASP_1D_EQUIDISTANT_OFFSET, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC);        \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND(                                                                                   \
-        _m, YASP_2D_EQUIDISTANT_OFFSET, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_ALU(                                                                       \
-        _m, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)                                     \
-      _DUNE_GDT_ASSEMBLER_SYSTEM_BIND(                                                                                 \
-          _m, ALU_2D_SIMPLEX_CONFORMING, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)
-#  else
-#    define _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_ALU(                                                                       \
-        _m, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)
-#  endif
-
-#  define _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_ALL_GRIDS(                                                                   \
-      _m, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)                                       \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_YASP(_m, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC);   \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_ALU(_m, _g_layer, _g_backend, _s_type, _s_backend, _s_grid_layer, _p, _r, _rC)
-
-
-#  define DUNE_GDT_ASSEMBLER_SYSTEM_BIND(_m)                                                                           \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_ALL_GRIDS(_m, dd_subdomain_boundary, view, dg, gdt, dd_subdomain, 1, 1, 1);        \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_ALL_GRIDS(_m, dd_subdomain_coupling, view, dg, gdt, dd_subdomain, 1, 1, 1);        \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_ALL_GRIDS(_m, leaf, view, cg, gdt, leaf, 1, 1, 1);                                 \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_ALL_GRIDS(_m, level, view, cg, gdt, level, 1, 1, 1);                               \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_ALL_GRIDS(_m, leaf, view, fv, gdt, leaf, 0, 1, 1);                                 \
-    _DUNE_GDT_ASSEMBLER_SYSTEM_BIND_ALL_GRIDS(_m, level, view, fv, gdt, level, 0, 1, 1)
-// end: this is what we need for the .so
-
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_ASSEMBLER_SYSTEM_BINDINGS_HH
diff --git a/python/dune/gdt/assembler/system/alu.cc b/python/dune/gdt/assembler/system/alu.cc
deleted file mode 100644
index 68a3a27f8910bab9a5b4dc09705cd39bfe3acdf1..0000000000000000000000000000000000000000
--- a/python/dune/gdt/assembler/system/alu.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI && HAVE_DUNE_ALUGRID
-
-#  include "../system.hh"
-
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_ALU(template);
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-#endif // HAVE_DUNE_PYBINDXI && HAVE_DUNE_ALUGRID
diff --git a/python/dune/gdt/assembler/system/yasp.cc b/python/dune/gdt/assembler/system/yasp.cc
deleted file mode 100644
index 0b1360bfbc0770a963157e1ccd05953a97e5af37..0000000000000000000000000000000000000000
--- a/python/dune/gdt/assembler/system/yasp.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  include "../system.hh"
-
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-DUNE_GDT_ASSEMBLER_SYSTEM_BIND_LIB_YASP(template);
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/discretefunction/CMakeLists.txt b/python/dune/gdt/discretefunction/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..41c2d4c1724a55a3b58e79d73d751bcce82957c3
--- /dev/null
+++ b/python/dune/gdt/discretefunction/CMakeLists.txt
@@ -0,0 +1,16 @@
+# ~~~
+# This file is part of the dune-gdt project:
+#   https://github.com/dune-community/dune-gdt
+# Copyright 2010-2018 dune-gdt 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 (2018)
+#   René Fritze     (2018)
+#   Tobias Leibner  (2018)
+# ~~~
+
+dune_pybindxi_add_module(_discretefunction_1d EXCLUDE_FROM_ALL ${header} discretefunction_1d.cc)
+dune_pybindxi_add_module(_discretefunction_2d EXCLUDE_FROM_ALL ${header} discretefunction_2d.cc)
+dune_pybindxi_add_module(_discretefunction_3d EXCLUDE_FROM_ALL ${header} discretefunction_3d.cc)
diff --git a/python/dune/gdt/discretefunction/__init__.py b/python/dune/gdt/discretefunction/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c2b7cd23b3dfd3bdaf597501c2a446b978a24d73
--- /dev/null
+++ b/python/dune/gdt/discretefunction/__init__.py
@@ -0,0 +1,26 @@
+# ~~~
+# This file is part of the dune-xt-grid project:
+#   https://github.com/dune-community/dune-xt-grid
+# Copyright 2009-2018 dune-xt-grid 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 (2017)
+#   René Fritze     (2018)
+# ~~~
+
+from dune.xt import guarded_import
+
+from ._discretefunction_1d import *
+from ._discretefunction_2d import *
+from ._discretefunction_3d import *
+
+
+def make_discrete_function(space, vector, name="dune.gdt.discretefunction"):
+    for factory in [globals()[s] for s in globals().keys() if s.startswith('make_discrete_function_')]:
+        try:
+            return factory(space, vector, name)
+        except:
+            continue
+    raise TypeError('no matching factory for space \'{}\''.format(space))
diff --git a/python/dune/gdt/discretefunction/bindings.cc b/python/dune/gdt/discretefunction/bindings.cc
deleted file mode 100644
index bfd4e85733ed5b6e3a7e2d643c7a2ca5485a9e58..0000000000000000000000000000000000000000
--- a/python/dune/gdt/discretefunction/bindings.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  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/gdt/discretefunction/bindings.hh>
-#  include <python/dune/gdt/shared.hh>
-
-PYBIND11_MODULE(__discretefunction, m)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.gdt.__spaces");
-  py::module::import("dune.gdt.__spaces_block");
-
-  DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND(m);
-
-  add_initialization(m, "dune.gdt.discretefunction");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/discretefunction/bindings.hh b/python/dune/gdt/discretefunction/bindings.hh
deleted file mode 100644
index 9bf2d74d03bad7014c8d43996d9297a9935a418d..0000000000000000000000000000000000000000
--- a/python/dune/gdt/discretefunction/bindings.hh
+++ /dev/null
@@ -1,330 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BINDINGS_HH
-#define PYTHON_DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <python/dune/xt/common/bindings.hh>
-#  include <dune/xt/grid/dd/subdomains/grid.hh>
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <dune/xt/grid/layers.hh>
-#  include <dune/xt/la/type_traits.hh>
-#  include <python/dune/xt/la/container.bindings.hh>
-
-#  include <dune/gdt/spaces/bindings.hh>
-#  include <dune/gdt/playground/spaces/block.hh>
-#  include <dune/gdt/playground/spaces/restricted.hh>
-#  include <dune/gdt/type_traits.hh>
-
-#  include <dune/gdt/discretefunction/default.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-namespace internal {
-
-
-template <class S, class V>
-class ConstDiscreteFunction
-{
-  static_assert(is_space<S>::value, "");
-  static_assert(XT::LA::is_vector<V>::value, "");
-  typedef XT::Grid::extract_grid_t<typename S::GridLayerType> G;
-
-public:
-  typedef GDT::ConstDiscreteFunction<S, V> type;
-
-private:
-  typedef XT::Functions::GridFunctionInterface<typename S::EntityType,
-                                               typename S::DomainFieldType,
-                                               S::dimDomain,
-                                               typename S::RangeFieldType,
-                                               S::dimRange,
-                                               S::dimRangeCols>
-      BaseType;
-
-public:
-  typedef pybind11::class_<type, BaseType> bound_type;
-
-  static bound_type bind(pybind11::module& m, const std::string& space_name)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    const auto ClassName = XT::Common::to_camel_case("const_discrete_function_" + space_name + "_"
-                                                     + XT::LA::bindings::container_name<V>::value());
-
-    bound_type c(m, ClassName.c_str(), ClassName.c_str());
-    c.def(py::init<const S&, V&, const std::string>(),
-          "space"_a,
-          "vector"_a,
-          "name"_a = "gdt.constdiscretefunction",
-          py::keep_alive<1, 2>(),
-          py::keep_alive<1, 3>());
-    c.def("space", [](type& self) { return self.space(); });
-    c.def("vector_copy", [](type& self) { return self.vector(); });
-    c.def("visualize",
-          [](type& self, const std::string filename, const bool subsampling) {
-            return self.visualize(filename, subsampling);
-          },
-          "filename"_a,
-          "subsampling"_a = (S::polOrder > 1));
-    // these two are copied from <dune/xt/functions/interfaces.pbh>, would be nicer to inherit them
-    c.def("visualize",
-          [](const type& self,
-             const XT::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);
-    c.def("visualize",
-          [](const type& self,
-             const XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-             const std::string& layer,
-             const ssize_t lvl_or_sbdmn,
-             const std::string& path,
-             const bool subsampling) {
-            const auto level_or_subdomain = XT::Common::numeric_cast<int>(lvl_or_sbdmn);
-            if (layer == "leaf")
-              self.visualize(dd_grid_provider.leaf_view(), path, subsampling);
-            else if (layer == "level")
-              self.visualize(dd_grid_provider.template layer<XT::Grid::Layers::level, XT::Grid::Backends::view>(
-                                 level_or_subdomain),
-                             path,
-                             subsampling);
-            else if (layer == "dd_subdomain")
-              self.visualize(dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain, XT::Grid::Backends::view>(
-                                 level_or_subdomain),
-                             path,
-                             subsampling);
-            else if (layer == "dd_subdomain_oversampled")
-              self.visualize(
-                  dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain_oversampled, XT::Grid::Backends::view>(
-                      level_or_subdomain),
-                  path,
-                  subsampling);
-            else
-              DUNE_THROW(
-                  XT::Common::Exceptions::wrong_input_given,
-                  "Given layer has to be one of ('leaf', 'level', 'dd_subdomain', 'dd_subdomain_oversampled'), is '"
-                      << layer
-                      << "'!");
-          },
-          "dd_grid_provider"_a,
-          "layer"_a = "leaf",
-          "level_or_subdomain"_a = -1,
-          "path"_a,
-          "subsampling"_a = true);
-    return c;
-  } // ... bind(...)
-}; // class ConstDiscreteFunction
-
-
-template <class S, class V>
-class DiscreteFunction
-{
-  static_assert(is_space<S>::value, "");
-  static_assert(XT::LA::is_vector<V>::value, "");
-
-  typedef GDT::ConstDiscreteFunction<S, V> BaseType;
-
-public:
-  typedef GDT::DiscreteFunction<S, V> type;
-  typedef pybind11::class_<type, BaseType> bound_type;
-
-  static bound_type bind(pybind11::module& m, const std::string& space_name)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    bindings::internal::ConstDiscreteFunction<S, V>::bind(m, space_name);
-
-    const auto ClassName = XT::Common::to_camel_case("discrete_function_" + space_name + "_"
-                                                     + XT::LA::bindings::container_name<V>::value());
-
-    bound_type c(m, ClassName.c_str(), ClassName.c_str());
-    c.def(py::init<const S&, V&, const std::string>(),
-          "space"_a,
-          "vector"_a,
-          "name"_a = "gdt.discretefunction",
-          py::keep_alive<1, 2>(),
-          py::keep_alive<1, 3>());
-    c.def(
-        py::init<const S&, const std::string>(), "space"_a, "name"_a = "gdt.discretefunction", py::keep_alive<1, 2>());
-
-    m.def(
-        std::string("make_discrete_function").c_str(),
-        [](const S& space, V& vector, const std::string& name) { return make_discrete_function(space, vector, name); },
-        "space"_a,
-        "vector"_a,
-        "name"_a = "gdt.discretefunction",
-        py::keep_alive<0, 1>(),
-        py::keep_alive<0, 2>());
-    m.def(std::string("make_discrete_function").c_str(),
-          [](const S& space, const std::string& name) { return make_discrete_function<V>(space, name); },
-          "space"_a,
-          "name"_a = "gdt.discretefunction",
-          py::keep_alive<0, 1>());
-
-    return c;
-  } // ... bind(...)
-
-}; // class DiscreteFunction
-
-
-} // namespace internal
-
-
-template <class SP, class V>
-class DiscreteFunction
-{
-  typedef typename SP::type S;
-  static_assert(is_space<S>::value, "");
-  static_assert(XT::LA::is_vector<V>::value, "");
-  typedef GDT::ConstDiscreteFunction<S, V> BaseType;
-
-public:
-  typedef GDT::DiscreteFunction<S, V> type;
-  typedef pybind11::class_<type, BaseType> bound_type;
-
-  template <XT::Grid::Backends backend, XT::Grid::Layers layer>
-  static void addbind_restricted(pybind11::module& m, const std::string sp_name)
-  {
-    try { // we might not be the first to add this
-      internal::DiscreteFunction<GDT::RestrictedSpace<S,
-                                                      typename XT::Grid::
-                                                          Layer<XT::Grid::extract_grid_t<typename S::GridLayerType>,
-                                                                layer,
-                                                                backend>::type>,
-                                 V>::bind(m,
-                                          sp_name + "_restricted_to_" + XT::Grid::bindings::layer_name<layer>::value()
-                                              + "_"
-                                              + XT::Grid::bindings::backend_name<backend>::value());
-    } catch (std::runtime_error&) {
-    }
-  } // ... addbind_restricted(...)
-
-  static bound_type bind(pybind11::module& m)
-  {
-    const auto sp_name = space_name<SP>::value();
-    auto c = internal::DiscreteFunction<S, V>::bind(m, sp_name);
-
-    addbind_restricted<XT::Grid::Backends::view, XT::Grid::Layers::dd_subdomain>(m, sp_name);
-    addbind_restricted<XT::Grid::Backends::view, XT::Grid::Layers::dd_subdomain_boundary>(m, sp_name);
-    addbind_restricted<XT::Grid::Backends::view, XT::Grid::Layers::dd_subdomain_coupling>(m, sp_name);
-    addbind_restricted<XT::Grid::Backends::view, XT::Grid::Layers::dd_subdomain_oversampled>(m, sp_name);
-    addbind_restricted<XT::Grid::Backends::view, XT::Grid::Layers::leaf>(m, sp_name);
-    addbind_restricted<XT::Grid::Backends::view, XT::Grid::Layers::level>(m, sp_name);
-
-    return c;
-  }
-}; // class DiscreteFunction
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-
-// begin: this is what we need for the .so
-
-
-#  define _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND(_m, _G, _g_layer, _s_type, _s_backend, _p, _r, _rC, _la)             \
-    Dune::GDT::bindings::DiscreteFunction<                                                                             \
-        Dune::GDT::SpaceProvider<_G,                                                                                   \
-                                 Dune::XT::Grid::Layers::_g_layer,                                                     \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 _r,                                                                                   \
-                                 _rC>,                                                                                 \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>::bind(_m)
-
-#  define _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_COMMON(_m, _G, _g_layer, _s_type, _s_backend, _p, _r, _rC)           \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND(_m, _G, _g_layer, _s_type, _s_backend, _p, _r, _rC, common_dense)
-
-#  if HAVE_EIGEN
-#    define _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_EIGEN(_m, _G, _g_layer, _s_type, _s_backend, _p, _r, _rC)          \
-      _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND(_m, _G, _g_layer, _s_type, _s_backend, _p, _r, _rC, eigen_dense)
-#  else
-#    define _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_EIGEN(_m, _G, _g_layer, _s_type, _s_backend, _p, _r, _rC)
-#  endif
-
-#  if HAVE_DUNE_ISTL
-#    define _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ISTL(_m, _G, _g_layer, _s_type, _s_backend, _p, _r, _rC)           \
-      _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND(_m, _G, _g_layer, _s_type, _s_backend, _p, _r, _rC, istl_dense)
-#  else
-#    define _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ISTL(_m, _G, _g_layer, _s_type, _s_backend, _p, _r, _rC)
-#  endif
-
-#  define _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_LA(_m, _G, _g_layer, _s_type, _s_backend, _p, _r, _rC)           \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_COMMON(_m, _G, _g_layer, _s_type, _s_backend, _p, _r, _rC);                \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_EIGEN(_m, _G, _g_layer, _s_type, _s_backend, _p, _r, _rC);                 \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ISTL(_m, _G, _g_layer, _s_type, _s_backend, _p, _r, _rC)
-
-#  define _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_YASP(_m, _g_layer, _s_type, _s_backend, _p, _r, _rC)                 \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_LA(                                                                    \
-        _m, YASP_1D_EQUIDISTANT_OFFSET, _g_layer, _s_type, _s_backend, _p, _r, _rC);                                   \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_LA(                                                                    \
-        _m, YASP_2D_EQUIDISTANT_OFFSET, _g_layer, _s_type, _s_backend, _p, _r, _rC)
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALU(_m, _g_layer, _s_type, _s_backend, _p, _r, _rC)                \
-      _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_LA(                                                                  \
-          _m, ALU_2D_SIMPLEX_CONFORMING, _g_layer, _s_type, _s_backend, _p, _r, _rC)
-#  else
-#    define _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALU(_m, _g_layer, _s_type, _s_backend, _p, _r, _rC)
-#  endif
-
-#  define _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_GRIDS(_m, _g_layer, _s_type, _s_backend, _p, _r, _rC)            \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_YASP(_m, _g_layer, _s_type, _s_backend, _p, _r, _rC);                      \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALU(_m, _g_layer, _s_type, _s_backend, _p, _r, _rC)
-
-#  define DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND(_m)                                                                   \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_GRIDS(_m, leaf, dg, gdt, 1, 1, 1);                                     \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_GRIDS(_m, level, dg, gdt, 1, 1, 1);                                    \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_GRIDS(_m, leaf, fv, gdt, 0, 1, 1);                                     \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_GRIDS(_m, level, fv, gdt, 0, 1, 1);                                    \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_GRIDS(_m, leaf, cg, gdt, 1, 1, 1);                                     \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_GRIDS(_m, level, cg, gdt, 1, 1, 1);
-
-#  define DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_DD(_m)                                                                \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_GRIDS(_m, dd_subdomain, dg, gdt, 1, 1, 1);                             \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_GRIDS(_m, dd_subdomain, block_dg, gdt, 1, 1, 1);                       \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_GRIDS(_m, dd_subdomain, cg, gdt, 1, 1, 1);                             \
-    _DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BIND_ALL_GRIDS(_m, dd_subdomain, block_cg, gdt, 1, 1, 1);
-
-// end: this is what we need for the .so
-
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BINDINGS_HH
diff --git a/python/dune/gdt/discretefunction/discretefunction.hh b/python/dune/gdt/discretefunction/discretefunction.hh
new file mode 100644
index 0000000000000000000000000000000000000000..76542869ce333cfdeeb31cb5542415cec2a136fd
--- /dev/null
+++ b/python/dune/gdt/discretefunction/discretefunction.hh
@@ -0,0 +1,165 @@
+// This file is part of the dune-gdt project:
+//   https://github.com/dune-community/dune-gdt
+// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
+//   René Fritze     (2018)
+//   Tobias Leibner  (2018)
+
+#ifndef PYTHON_DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BINDINGS_HH
+#define PYTHON_DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BINDINGS_HH
+
+#if HAVE_DUNE_PYBINDXI
+
+#  include <dune/pybindxi/pybind11.h>
+
+#  include <python/dune/xt/common/exceptions.bindings.hh>
+#  include <python/dune/xt/la/container.bindings.hh>
+#  include <python/dune/xt/grid/grids.bindings.hh>
+#  include <python/dune/xt/functions/gridfunction-interface.hh>
+
+#  include <dune/xt/common/python.hh>
+#  include <dune/xt/common/string.hh>
+#  include <dune/xt/la/container.hh>
+#  include <dune/xt/grid/grids.hh>
+#  include <dune/xt/grid/type_traits.hh>
+#  include <dune/xt/functions/interfaces/grid-function.hh>
+
+#  include <dune/gdt/discretefunction/default.hh>
+
+namespace Dune {
+namespace GDT {
+namespace bindings {
+
+
+template <class V, class GV, size_t r = 1, size_t rC = 1, class R = double>
+class DiscreteFunction
+{
+public:
+  using type = GDT::DiscreteFunction<V, GV, r, rC, R>;
+  using base_type = XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<GV>, r, rC, R>;
+  using bound_type = pybind11::class_<type, base_type>;
+
+  static bound_type bind(pybind11::module& m)
+  {
+    namespace py = pybind11;
+    using namespace pybind11::literals;
+    using G = typename GV::Grid;
+    using S = typename type::SpaceType;
+
+    // base_type
+    const auto grid_name = XT::Grid::bindings::grid_name<G>::value();
+    if (std::is_same<R, double>::value)
+      XT::Common::bindings::guarded_bind(
+          [&]() { XT::Functions::bindings::bind_GridFunctionInterface<G, r, rC>(m, grid_name); });
+
+    // type
+    std::string class_name = "discrete_function_" + grid_name;
+    if (r > 1)
+      class_name += "_to" + XT::Common::to_string(r);
+    if (rC > 1)
+      class_name += "x" + XT::Common::to_string(rC);
+    class_name += "_" + XT::LA::bindings::container_name<V>::value();
+    class_name = XT::Common::to_camel_case(class_name);
+    const std::string default_name = "dune.gdt.discretefunction";
+    bound_type c(m, XT::Common::to_camel_case(class_name).c_str(), XT::Common::to_camel_case(class_name).c_str());
+    c.def(py::init<const S&, V&, const std::string&>(),
+          "space"_a,
+          "vector"_a,
+          "name"_a = default_name,
+          py::keep_alive<1, 2>(),
+          py::keep_alive<1, 3>());
+    c.def(py::init<const S&, const std::string&>(), "space"_a, "name"_a = default_name, py::keep_alive<1, 2>());
+    c.def_property_readonly("space", &type::space);
+    c.def_property("dof_vector",
+                   [](const type& self) { return self.dofs().vector(); },
+                   [](type& self, const V& vec) {
+                     DUNE_THROW_IF(vec.size() != self.dofs().vector().size(),
+                                   Exceptions::discrete_function_error,
+                                   "vec.size() = " << vec.size() << "\n   self.dofs().vector().size() = "
+                                                   << self.dofs().vector().size());
+                     self.dofs().vector() = vec;
+                   },
+                   py::return_value_policy::reference_internal);
+    c.def_property_readonly("name", &type::name);
+    c.def("visualize",
+          [](type& self, const std::string& filename) { return self.visualize(filename, VTK::appendedraw); },
+          "filename"_a);
+
+    // factory
+    const std::string factory_name = "make_" + class_name;
+    m.def(
+        factory_name.c_str(),
+        [](const S& space, V& vector, const std::string& name) { return make_discrete_function(space, vector, name); },
+        "space"_a,
+        "vector"_a,
+        "name"_a = default_name,
+        py::keep_alive<0, 1>(),
+        py::keep_alive<0, 2>());
+    m.def(factory_name.c_str(),
+          [](const S& space, const std::string& name) { return make_discrete_function<V>(space, name); },
+          "space"_a,
+          "name"_a = default_name,
+          py::keep_alive<0, 1>());
+
+    return c;
+  } // ... bind(...)
+}; // class DiscreteFunction
+
+
+template <class V, class GridTypes = Dune::XT::Grid::AvailableGridTypes>
+struct DiscreteFunction_for_all_grids
+{
+  using G = typename GridTypes::head_type;
+  using GV = typename G::LeafGridView;
+  static const constexpr size_t d = G::dimension;
+
+  static void bind(pybind11::module& m)
+  {
+    Dune::GDT::bindings::DiscreteFunction<V, GV>::bind(m);
+    if (d > 1)
+      Dune::GDT::bindings::DiscreteFunction<V, GV, d>::bind(m);
+    // add your extra dimensions here
+    // ...
+    DiscreteFunction_for_all_grids<V, typename GridTypes::tail_type>::bind(m);
+  }
+};
+
+template <class V>
+struct DiscreteFunction_for_all_grids<V, boost::tuples::null_type>
+{
+  static void bind(pybind11::module& /*m*/) {}
+};
+
+
+template <class VectorTypes = Dune::XT::LA::AvailableVectorTypes<double>,
+          class GridTypes = Dune::XT::Grid::AvailableGridTypes>
+struct DiscreteFunction_for_all_vectors_and_grids
+
+{
+  static void bind(pybind11::module& m)
+  {
+    using V = typename VectorTypes::head_type;
+    DiscreteFunction_for_all_grids<V, GridTypes>::bind(m);
+    DiscreteFunction_for_all_vectors_and_grids<typename VectorTypes::tail_type, GridTypes>::bind(m);
+  }
+};
+
+template <class GridTypes>
+struct DiscreteFunction_for_all_vectors_and_grids<boost::tuples::null_type, GridTypes>
+{
+  static void bind(pybind11::module&) {}
+};
+
+
+} // namespace bindings
+} // namespace GDT
+} // namespace Dune
+
+
+#endif // HAVE_DUNE_PYBINDXI
+
+#endif // PYTHON_DUNE_GDT_DISCRETEFUNCTION_DEFAULT_BINDINGS_HH
diff --git a/python/dune/gdt/playground/spaces/block.cc b/python/dune/gdt/discretefunction/discretefunction_1d.cc
similarity index 56%
rename from python/dune/gdt/playground/spaces/block.cc
rename to python/dune/gdt/discretefunction/discretefunction_1d.cc
index bc8c22ea76a6b05e4acec36e53bef9d86d1c5e40..5e309a5bea0d0b6d9a4f97faa8e534a0e97a70f3 100644
--- a/python/dune/gdt/playground/spaces/block.cc
+++ b/python/dune/gdt/discretefunction/discretefunction_1d.cc
@@ -5,40 +5,30 @@
 //      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
 //          with "runtime exception" (http://www.dune-project.org/license.html)
 // Authors:
-//   Felix Schindler (2017)
-//   René Fritze     (2018)
+//   Felix Schindler (2019)
 
 #include "config.h"
 
 #if HAVE_DUNE_PYBINDXI
 
-#  include <dune/common/parallel/mpihelper.hh>
+#  include "discretefunction.hh"
 
-#  include <dune/pybindxi/pybind11.h>
-#  include <dune/pybindxi/stl.h>
 
-#  include <python/dune/xt/common/bindings.hh>
-#  include <python/dune/gdt/shared.hh>
-
-#  include "block.hh"
-
-
-PYBIND11_MODULE(__spaces_block, m)
+PYBIND11_MODULE(_discretefunction_1d, m)
 {
   namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
+  using namespace Dune;
+  using namespace Dune::XT;
+  using namespace Dune::GDT;
 
   py::module::import("dune.xt.common");
+  py::module::import("dune.xt.la");
   py::module::import("dune.xt.grid");
   py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.gdt.__spaces");
 
-  DUNE_GDT_SPACES_BLOCK_BIND(m);
-
-  add_initialization(m, "dune.gdt.spaces.block");
+  bindings::DiscreteFunction_for_all_vectors_and_grids<LA::AvailableVectorTypes<double>,
+                                                       XT::Grid::Available1dGridTypes>::bind(m);
 }
 
+
 #endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/spaces/bindings.cc b/python/dune/gdt/discretefunction/discretefunction_2d.cc
similarity index 54%
rename from python/dune/gdt/spaces/bindings.cc
rename to python/dune/gdt/discretefunction/discretefunction_2d.cc
index dcf57a1db6e1a68fd3931aed913013ee0e9d7f61..ba3c7d6539811db974a33492d8101d79ca1d14c8 100644
--- a/python/dune/gdt/spaces/bindings.cc
+++ b/python/dune/gdt/discretefunction/discretefunction_2d.cc
@@ -5,42 +5,30 @@
 //      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
 //          with "runtime exception" (http://www.dune-project.org/license.html)
 // Authors:
-//   Felix Schindler (2017)
-//   René Fritze     (2018)
+//   Felix Schindler (2019)
 
 #include "config.h"
 
 #if HAVE_DUNE_PYBINDXI
 
-#  include <dune/common/parallel/mpihelper.hh>
+#  include "discretefunction.hh"
 
-#  include <dune/pybindxi/pybind11.h>
-#  include <dune/pybindxi/stl.h>
 
-#  include <python/dune/xt/common/bindings.hh>
-#  include <python/dune/gdt/shared.hh>
-
-#  include "bindings.hh"
-
-
-PYBIND11_MODULE(__spaces, m)
+PYBIND11_MODULE(_discretefunction_2d, m)
 {
   namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
+  using namespace Dune;
+  using namespace Dune::XT;
+  using namespace Dune::GDT;
 
   py::module::import("dune.xt.common");
+  py::module::import("dune.xt.la");
   py::module::import("dune.xt.grid");
   py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
 
-  DUNE_GDT_SPACES_CG_BIND(m);
-  DUNE_GDT_SPACES_DG_BIND(m);
-  DUNE_GDT_SPACES_FV_BIND(m);
-  DUNE_GDT_SPACES_RT_BIND(m);
-
-  add_initialization(m, "dune.gdt.spaces");
+  bindings::DiscreteFunction_for_all_vectors_and_grids<LA::AvailableVectorTypes<double>,
+                                                       XT::Grid::Available2dGridTypes>::bind(m);
 }
 
+
 #endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/local/diffusive-flux-estimation-operator.cc b/python/dune/gdt/discretefunction/discretefunction_3d.cc
similarity index 54%
rename from python/dune/gdt/local/diffusive-flux-estimation-operator.cc
rename to python/dune/gdt/discretefunction/discretefunction_3d.cc
index 2b3d7312fc3563ceb04ea8a75357ebc8aabb85fb..449d8b298f5734d092439faa4e41f6ca01b0f3ab 100644
--- a/python/dune/gdt/local/diffusive-flux-estimation-operator.cc
+++ b/python/dune/gdt/discretefunction/discretefunction_3d.cc
@@ -5,39 +5,30 @@
 //      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
 //          with "runtime exception" (http://www.dune-project.org/license.html)
 // Authors:
-//   Felix Schindler (2017)
-//   René Fritze     (2018)
+//   Felix Schindler (2019)
 
 #include "config.h"
 
 #if HAVE_DUNE_PYBINDXI
 
-#  include <dune/common/parallel/mpihelper.hh>
+#  include "discretefunction.hh"
 
-#  include <dune/pybindxi/pybind11.h>
-#  include <dune/pybindxi/stl.h>
 
-#  include <python/dune/xt/common/bindings.hh>
-#  include <python/dune/gdt/shared.hh>
-
-#  include "diffusive-flux-estimation-operator.hh"
-
-
-PYBIND11_MODULE(__local_diffusive_flux_estimation_operator, m)
+PYBIND11_MODULE(_discretefunction_3d, m)
 {
   namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
+  using namespace Dune;
+  using namespace Dune::XT;
+  using namespace Dune::GDT;
 
   py::module::import("dune.xt.common");
+  py::module::import("dune.xt.la");
   py::module::import("dune.xt.grid");
   py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
 
-  DUNE_GDT_LOCAL_DIFFUSIVE_FLUX_ESTIMATION_OPERATOR_BIND(m);
-
-  add_initialization(m, "dune.gdt.assembler");
+  bindings::DiscreteFunction_for_all_vectors_and_grids<LA::AvailableVectorTypes<double>,
+                                                       XT::Grid::Available3dGridTypes>::bind(m);
 }
 
+
 #endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/functionals/base.hh b/python/dune/gdt/functionals/base.hh
deleted file mode 100644
index c527d78461394917deee63b9cfce0b16ed3bfd90..0000000000000000000000000000000000000000
--- a/python/dune/gdt/functionals/base.hh
+++ /dev/null
@@ -1,64 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_FUNCTIONALS_BASE_BINDINGS_H
-#define PYTHON_DUNE_GDT_FUNCTIONALS_BASE_BINDINGS_H
-
-// Todo: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <dune/xt/la/container.hh>
-
-#  include <dune/gdt/assembler/system.hh>
-#  include <dune/gdt/discretefunction/default.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class FunctionalType>
-class VectorFunctionalBase
-{
-public:
-  typedef FunctionalType type;
-  typedef GDT::SystemAssembler<typename FunctionalType::SpaceType, typename FunctionalType::GridLayerType> BaseType;
-  typedef pybind11::class_<type, BaseType> bound_type;
-
-  static bound_type bind(pybind11::module& m, const std::string& class_id)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    typedef typename type::SpaceType S;
-    typedef typename type::VectorType V;
-
-    bound_type c(m, std::string(class_id).c_str(), std::string(class_id).c_str());
-
-    c.def("vector", [](type& self) { return self.vector(); });
-    c.def("space", [](type& self) { return self.space(); });
-    c.def("apply", [](type& self, const V& source) { return self.apply(source); }, "source"_a);
-    c.def(
-        "apply", [](type& self, const ConstDiscreteFunction<S, V>& source) { return self.apply(source); }, "source"_a);
-
-    return c;
-  } // ... bind(...)
-}; // class VectorFunctionalBase
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_FUNCTIONALS_BASE_BINDINGS_H
diff --git a/python/dune/gdt/functionals/elliptic-ipdg/alu_istl.cc b/python/dune/gdt/functionals/elliptic-ipdg/alu_istl.cc
deleted file mode 100644
index 5e99d2925c569095819bd4a9edbb27978ac83a9e..0000000000000000000000000000000000000000
--- a/python/dune/gdt/functionals/elliptic-ipdg/alu_istl.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  include <python/dune/gdt/functionals/elliptic-ipdg/bindings.hh>
-
-
-#  if HAVE_DUNE_ALUGRID && HAVE_DUNE_ISTL
-DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_ALU(template, leaf, view, dg, gdt, 1, istl_sparse);
-DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_ALU(template, level, view, dg, gdt, 1, istl_sparse);
-DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_ALU(template, dd_subdomain, view, dg, gdt, 1, istl_sparse);
-#  endif
-
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/functionals/elliptic-ipdg/bindings.cc b/python/dune/gdt/functionals/elliptic-ipdg/bindings.cc
deleted file mode 100644
index 4dd5054ee945dd7b9fd08e3e8041c32f55ad042f..0000000000000000000000000000000000000000
--- a/python/dune/gdt/functionals/elliptic-ipdg/bindings.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  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/gdt/shared.hh>
-
-#  include <python/dune/gdt/functionals/elliptic-ipdg/bindings.hh>
-
-
-PYBIND11_MODULE(__functionals_elliptic_ipdg, m)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.gdt.__spaces");
-
-// alu_istl.cc
-#  if HAVE_DUNE_ALUGRID && HAVE_DUNE_ISTL
-  DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_ALU(m, leaf, view, dg, gdt, 1, istl_dense);
-  DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_ALU(m, level, view, dg, gdt, 1, istl_dense);
-  DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_ALU(m, dd_subdomain, view, dg, gdt, 1, istl_dense);
-#  endif
-
-// yasp_istl.cc
-#  if HAVE_DUNE_ISTL
-  DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_YASP(m, leaf, view, dg, gdt, 1, istl_dense);
-  DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_YASP(m, level, view, dg, gdt, 1, istl_dense);
-  DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_YASP(m, dd_subdomain, view, dg, gdt, 1, istl_dense);
-#  endif
-
-  add_initialization(m, "dune.gdt.functionals.elliptic-ipdg");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/functionals/elliptic-ipdg/bindings.hh b/python/dune/gdt/functionals/elliptic-ipdg/bindings.hh
deleted file mode 100644
index bc329aa457d6e9497aebcdc5926c448c732f9090..0000000000000000000000000000000000000000
--- a/python/dune/gdt/functionals/elliptic-ipdg/bindings.hh
+++ /dev/null
@@ -1,728 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BINDINGS_HH
-#define PYTHON_DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <dune/xt/grid/type_traits.hh>
-#  include <python/dune/xt/la/container.bindings.hh>
-
-#  include <dune/gdt/spaces/bindings.hh>
-#  include <dune/gdt/type_traits.hh>
-
-#  include <dune/gdt/functionals/elliptic-ipdg.hh>
-#  include <python/dune/gdt/functionals/base.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class DI,
-          class DF,
-          typename DT, // may be void
-          class SP,
-          LocalEllipticIpdgIntegrands::Method method,
-          class V /* = typename XT::LA::Container<typename R::RangeFieldType>::VectorType,
-          class GL = typename RP::type::GridLayerType,
-          class F = typename RP::type::RangeFieldType*/>
-class EllipticIpdgDirichletVectorFunctional
-{
-  typedef typename SP::type S;
-  static_assert(is_space<S>::value, "");
-
-public:
-  typedef GDT::EllipticIpdgDirichletVectorFunctional<DI, DF, DT, S, method, V /*, GL, F*/> type;
-  typedef pybind11::class_<type> bound_type;
-
-private:
-  template <bool single_diffusion = std::is_same<DT, void>::value,
-            bool scalar = (DF::dimRange == 1 && DF::dimRangeCols == 1),
-            bool anything = false>
-  struct diffusion_switch
-  {
-    static std::string suffix()
-    {
-      return "diffusion_factor_and_tensor";
-    }
-
-    template <class C>
-    static void addbind_factory_methods(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      const auto method_name =
-          "make_elliptic_" + LocalEllipticIpdgIntegrands::method_name<method>::value() + "_dirichlet_vector_functional";
-
-      m.def(
-          std::string(method_name + "_" + XT::LA::bindings::container_name<V>::value()).c_str(),
-          [](const DI& dirichlet,
-             const DF& diffusion_factor,
-             const DT& diffusion_tensor,
-             const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<typename S::GridLayerType>>& boundary_info,
-             const S& space,
-             const size_t over_integrate) {
-            return make_elliptic_ipdg_dirichlet_vector_functional<V, method>(
-                       dirichlet, diffusion_factor, diffusion_tensor, boundary_info, space, over_integrate)
-                .release(); //   <- b.c. EllipticIpdgDirichletVectorFunctional is not movable, returning the raw pointer
-          }, //                                                                lets pybind11 correctly manage the memory
-          "dirichlet"_a,
-          "diffusion_factor"_a,
-          "diffusion_tensor"_a,
-          "boundary_info"_a,
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>(),
-          py::keep_alive<0, 4>(),
-          py::keep_alive<0, 5>());
-
-      m.def(
-          method_name.c_str(),
-          [](const DI& dirichlet,
-             const DF& diffusion_factor,
-             const DT& diffusion_tensor,
-             const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<typename S::GridLayerType>>& boundary_info,
-             V& vector,
-             const S& space,
-             const size_t over_integrate) {
-            return make_elliptic_ipdg_dirichlet_vector_functional<method>(
-                       dirichlet, diffusion_factor, diffusion_tensor, boundary_info, vector, space, over_integrate)
-                .release(); //                                                                     <- s.a. for release()
-          },
-          "dirichlet"_a,
-          "diffusion_factor"_a,
-          "diffusion_tensor"_a,
-          "boundary_info"_a,
-          "vector"_a,
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>(),
-          py::keep_alive<0, 4>(),
-          py::keep_alive<0, 5>(),
-          py::keep_alive<0, 6>());
-    } // ... addbind_factory_methods(...)
-  }; // struct diffusion_switch
-
-  struct diffusion_switch_scalar_base
-  {
-    template <class C>
-    static void addbind_factory_methods(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      const auto method_name =
-          "make_elliptic_" + LocalEllipticIpdgIntegrands::method_name<method>::value() + "_dirichlet_vector_functional";
-
-      m.def(
-          std::string(method_name + "_" + XT::LA::bindings::container_name<V>::value()).c_str(),
-          [](const DI& dirichlet,
-             const DF& diffusion,
-             const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<typename S::GridLayerType>>& boundary_info,
-             const S& space,
-             const size_t over_integrate) {
-            return make_elliptic_ipdg_dirichlet_vector_functional<V, method>(
-                       dirichlet, diffusion, boundary_info, space, over_integrate)
-                .release(); //   <- b.c. EllipticIpdgDirichletVectorFunctional is not movable, returning the raw pointer
-          }, //                                                                lets pybind11 correctly manage the memory
-          "dirichlet"_a,
-          "diffusion"_a,
-          "boundary_info"_a,
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>(),
-          py::keep_alive<0, 4>());
-
-      m.def(
-          method_name.c_str(),
-          [](const DI& dirichlet,
-             const DF& diffusion,
-             const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<typename S::GridLayerType>>& boundary_info,
-             V& vector,
-             const S& space,
-             const size_t over_integrate) {
-            return make_elliptic_ipdg_dirichlet_vector_functional<method>(
-                       dirichlet, diffusion, boundary_info, vector, space, over_integrate)
-                .release(); //                                                                     <- s.a. for release()
-          },
-          "dirichlet"_a,
-          "diffusion"_a,
-          "boundary_info"_a,
-          "vector"_a,
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>(),
-          py::keep_alive<0, 4>(),
-          py::keep_alive<0, 5>());
-    } // ... addbind_factory_methods(...)
-  }; // struct diffusion_switch_scalar_base
-
-  template <bool anything>
-  struct diffusion_switch<true, true, anything> : public diffusion_switch_scalar_base
-  {
-    static std::string suffix()
-    {
-      return "single_diffusion_factor";
-    }
-  };
-
-  template <bool anything>
-  struct diffusion_switch<true, false, anything> : public diffusion_switch_scalar_base
-  {
-    static std::string suffix()
-    {
-      return "single_diffusion_tensor";
-    }
-  };
-
-public:
-  static bound_type bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    const auto ClassName = XT::Common::to_camel_case(
-        "elliptic_" + LocalEllipticIpdgIntegrands::method_name<method>::value() + "_dirichlet_vector_functional_"
-        + space_name<SP>::value()
-        + "_"
-        + XT::LA::bindings::container_name<V>::value()
-        + "_"
-        + diffusion_switch<>::suffix());
-
-    auto c = VectorFunctionalBase<type>::bind(m, ClassName.c_str());
-
-    diffusion_switch<>::template addbind_factory_methods<type>(m);
-
-    return c;
-  } // ... bind(...)
-}; // class EllipticIpdgDirichletVectorFunctional
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-
-// begin: this is what we need for the lib
-
-#  define _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_1D(                                                             \
-      _prefix, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, _method)                                       \
-    _prefix class Dune::GDT::bindings::EllipticIpdgDirichletVectorFunctional<                                          \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>;                            \
-    _prefix class Dune::GDT::bindings::EllipticIpdgDirichletVectorFunctional<                                          \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>
-
-#  define _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_D(                                                              \
-      _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, _method)                                   \
-    _prefix class Dune::GDT::bindings::EllipticIpdgDirichletVectorFunctional<                                          \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>;                            \
-    _prefix class Dune::GDT::bindings::EllipticIpdgDirichletVectorFunctional<                                          \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>;                            \
-    _prefix class Dune::GDT::bindings::EllipticIpdgDirichletVectorFunctional<                                          \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>
-
-#  define _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_METHODS_1D(                                                     \
-      _prefix, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)                                                \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_1D(                                                                   \
-        _prefix, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, sipdg);                                      \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_1D(                                                                   \
-        _prefix, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg);                                     \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_1D(                                                                   \
-        _prefix, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_factor);                       \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_1D(                                                                   \
-        _prefix, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_tensor)
-
-#  define _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_METHODS_D(                                                      \
-      _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)                                            \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_D(                                                                    \
-        _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, sipdg);                                  \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_D(                                                                    \
-        _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg);                                 \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_D(                                                                    \
-        _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_factor);                   \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_D(                                                                    \
-        _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_tensor)
-
-/*
-#if HAVE_ALBERTA
-#define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_ALBERTA(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la) \
-  _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_METHODS_D(                                                                \
-      _prefix, 2, ALBERTA_2D, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#else
-*/
-#  define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_ALBERTA(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-//#endif
-
-#  if HAVE_DUNE_ALUGRID
-#    define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_ALU(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la) \
-      _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_METHODS_D(                                                          \
-          _prefix, 2, ALU_2D_SIMPLEX_CONFORMING, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  else
-#    define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_ALU(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  endif
-
-/*
-#if HAVE_DUNE_UGGRID || HAVE_UG
-#define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_UG(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)      \
-  _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_METHODS_D(                                                                \
-      _prefix, 2, UG_2D, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#else
-*/
-#  define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_UG(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-//#endif
-
-#  define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_YASP(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)  \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_METHODS_1D(                                                           \
-        _prefix, YASP_1D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la);                        \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_METHODS_D(                                                            \
-        _prefix, 2, YASP_2D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-
-// alu_istl.cc
-#  if HAVE_DUNE_ALUGRID && HAVE_DUNE_ISTL
-DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_ALU(extern template, leaf, view, cg, gdt, 1, istl_dense);
-DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_ALU(extern template, level, view, cg, gdt, 1, istl_dense);
-// DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_ALU(extern template, dd_subdomain, part, cg, gdt, 1, istl_dense);
-#  endif
-
-// yasp_istl.cc
-#  if HAVE_DUNE_ISTL
-DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_YASP(extern template, leaf, view, cg, gdt, 1, istl_dense);
-DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_YASP(extern template, level, view, cg, gdt, 1, istl_dense);
-// DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_YASP(extern template, dd_subdomain, part, cg, gdt, 1, istl_dense);
-#  endif
-
-// end: this is what we need for the lib
-
-
-// begin: this is what we need for the .so
-
-#  define _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_1D(                                                                 \
-      _m, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, _method)                                            \
-    Dune::GDT::bindings::EllipticIpdgDirichletVectorFunctional<                                                        \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>::bind(_m);                  \
-    Dune::GDT::bindings::EllipticIpdgDirichletVectorFunctional<                                                        \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>::bind(_m)
-
-#  define _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_D(                                                                  \
-      _m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, _method)                                        \
-    Dune::GDT::bindings::EllipticIpdgDirichletVectorFunctional<                                                        \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>::bind(_m);                  \
-    Dune::GDT::bindings::EllipticIpdgDirichletVectorFunctional<                                                        \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>::bind(_m);                  \
-    Dune::GDT::bindings::EllipticIpdgDirichletVectorFunctional<                                                        \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>::bind(_m)
-
-#  define _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_METHODS_1D(                                                         \
-      _m, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)                                                     \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_1D(_m, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, sipdg);   \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_1D(_m, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg);  \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_1D(                                                                       \
-        _m, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_factor);                            \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_1D(                                                                       \
-        _m, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_tensor)
-
-#  define _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_METHODS_D(                                                          \
-      _m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)                                                 \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_D(                                                                        \
-        _m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, sipdg);                                       \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_D(                                                                        \
-        _m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg);                                      \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_D(                                                                        \
-        _m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_factor);                        \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_D(                                                                        \
-        _m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_tensor)
-
-/*
-#if HAVE_ALBERTA
-#define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_ALBERTA(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)          \
-  _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_METHODS_D(_m, 2, ALBERTA_2D, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#else
-*/
-#  define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_ALBERTA(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-//#endif
-
-#  if HAVE_DUNE_ALUGRID
-#    define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_ALU(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)          \
-      _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_METHODS_D(                                                              \
-          _m, 2, ALU_2D_SIMPLEX_CONFORMING, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  else
-#    define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_ALU(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  endif
-
-/*
-#if HAVE_DUNE_UGGRID || HAVE_UG
-#define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_UG(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)               \
-  _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_METHODS_D(_m, 2, UG_2D, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#else
-*/
-#  define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_UG(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-//#endif
-
-#  define DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_YASP(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)           \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_METHODS_1D(                                                               \
-        _m, YASP_1D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la);                             \
-    _DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_METHODS_D(                                                                \
-        _m, 2, YASP_2D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-
-
-// end: this is what we need for the .so
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BINDINGS_HH
diff --git a/python/dune/gdt/functionals/elliptic-ipdg/yasp_istl.cc b/python/dune/gdt/functionals/elliptic-ipdg/yasp_istl.cc
deleted file mode 100644
index b74e62fecbb2f2f71a62cda544fc345b0248e232..0000000000000000000000000000000000000000
--- a/python/dune/gdt/functionals/elliptic-ipdg/yasp_istl.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  include <python/dune/gdt/functionals/elliptic-ipdg/bindings.hh>
-
-
-#  if HAVE_DUNE_ISTL
-DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_YASP(template, leaf, view, dg, gdt, 1, istl_sparse);
-DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_YASP(template, level, view, dg, gdt, 1, istl_sparse);
-DUNE_GDT_FUNCTIONALS_ELLIPTIC_IPDG_BIND_LIB_YASP(template, dd_subdomain, view, dg, gdt, 1, istl_sparse);
-#  endif
-
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/functionals/l2/alu_istl.cc b/python/dune/gdt/functionals/l2/alu_istl.cc
deleted file mode 100644
index f0df4d822b0319bf9247e88e18427af63ffc1263..0000000000000000000000000000000000000000
--- a/python/dune/gdt/functionals/l2/alu_istl.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  include <python/dune/gdt/functionals/l2/bindings.hh>
-
-
-#  if HAVE_DUNE_ALUGRID && HAVE_DUNE_ISTL
-DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_ALU(template, leaf, view, cg, gdt, 1, istl_sparse);
-DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_ALU(template, level, view, cg, gdt, 1, istl_sparse);
-DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_ALU(template, dd_subdomain, view, cg, gdt, 1, istl_sparse);
-#  endif
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/functionals/l2/bindings.cc b/python/dune/gdt/functionals/l2/bindings.cc
deleted file mode 100644
index de92c3fc68ed72c2a30bb3ba3a8e102b5a99b1f2..0000000000000000000000000000000000000000
--- a/python/dune/gdt/functionals/l2/bindings.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  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/gdt/shared.hh>
-
-#  include <python/dune/gdt/functionals/l2/bindings.hh>
-
-
-PYBIND11_MODULE(__functionals_l2, m)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.gdt.__spaces");
-
-  // alu_istl.cc
-  DUNE_GDT_FUNCTIONALS_L2_BIND_ALU(m, leaf, view, dg, gdt, 1, istl_sparse);
-  DUNE_GDT_FUNCTIONALS_L2_BIND_ALU(m, level, view, dg, gdt, 1, istl_sparse);
-  DUNE_GDT_FUNCTIONALS_L2_BIND_ALU(m, dd_subdomain, view, dg, gdt, 1, istl_sparse);
-#  if HAVE_DUNE_ALUGRID && HAVE_DUNE_ISTL
-  DUNE_GDT_FUNCTIONALS_L2_BIND_ALU(m, leaf, view, cg, gdt, 1, istl_sparse);
-  DUNE_GDT_FUNCTIONALS_L2_BIND_ALU(m, level, view, cg, gdt, 1, istl_sparse);
-  DUNE_GDT_FUNCTIONALS_L2_BIND_ALU(m, dd_subdomain, view, cg, gdt, 1, istl_sparse);
-#  endif
-
-// yasp_istl.cc
-#  if HAVE_DUNE_ISTL
-  DUNE_GDT_FUNCTIONALS_L2_BIND_YASP(m, leaf, view, dg, gdt, 1, istl_sparse);
-  DUNE_GDT_FUNCTIONALS_L2_BIND_YASP(m, level, view, dg, gdt, 1, istl_sparse);
-  DUNE_GDT_FUNCTIONALS_L2_BIND_YASP(m, dd_subdomain, view, dg, gdt, 1, istl_sparse);
-  DUNE_GDT_FUNCTIONALS_L2_BIND_YASP(m, leaf, view, cg, gdt, 1, istl_sparse);
-  DUNE_GDT_FUNCTIONALS_L2_BIND_YASP(m, level, view, cg, gdt, 1, istl_sparse);
-  DUNE_GDT_FUNCTIONALS_L2_BIND_YASP(m, dd_subdomain, view, cg, gdt, 1, istl_sparse);
-#  endif
-
-  add_initialization(m, "dune.gdt.functionals.l2");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/functionals/l2/bindings.hh b/python/dune/gdt/functionals/l2/bindings.hh
deleted file mode 100644
index fb1da6478dbe1ba784d5e7ef1073ba4d84a51eb5..0000000000000000000000000000000000000000
--- a/python/dune/gdt/functionals/l2/bindings.hh
+++ /dev/null
@@ -1,316 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_FUNCTIONALS_L2_BINDINGS_HH
-#define PYTHON_DUNE_GDT_FUNCTIONALS_L2_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <python/dune/xt/la/container.bindings.hh>
-
-#  include <dune/gdt/spaces/bindings.hh>
-#  include <dune/gdt/type_traits.hh>
-
-#  include <dune/gdt/functionals/l2.hh>
-#  include <python/dune/gdt/functionals/base.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class F, class SP, class V /*= typename XT::LA::Container<typename SP::type::RangeFieldType>::VectorType,
-          class GridView = typename Space::GridLayerType,
-          class Field = typename Space::RangeFieldType*/>
-class L2VolumeVectorFunctional
-{
-  typedef typename SP::type S;
-  static_assert(is_space<S>::value, "");
-
-public:
-  typedef GDT::L2VolumeVectorFunctional<F, S, V> type;
-  typedef pybind11::class_<type> bound_type;
-
-  static bound_type bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    const auto ClassName = XT::Common::to_camel_case("l2_volume_vector_functional_" + space_name<SP>::value() + "_"
-                                                     + XT::LA::bindings::container_name<V>::value());
-
-    auto c = VectorFunctionalBase<type>::bind(m, ClassName);
-
-    m.def(std::string("make_l2_volume_vector_functional_" + XT::LA::bindings::container_name<V>::value()).c_str(),
-          [](const F& function, const S& space, const size_t over_integrate) {
-            return make_l2_volume_vector_functional<V>(function, space, over_integrate).release(); // <-            b.c.
-          }, //    L2VolumeVectorFunctional is not movable, returning the raw pointer lets pybind11 correctly manage the
-          "function"_a, //                                                                                        memory
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>());
-
-    m.def("make_l2_volume_vector_functional",
-          [](const F& function, V& vector, const S& space, const size_t over_integrate) {
-            return make_l2_volume_vector_functional(function, vector, space, over_integrate).release(); //       <- s.a.
-          },
-          "function"_a,
-          "vector"_a,
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>());
-
-    return c;
-  } // ... bind(...)
-}; // class L2VolumeVectorFunctional
-
-
-template <class F,
-          class SP,
-          class V /*= typename XT::LA::Container<typename SP::type::RangeFieldType>::VectorType*/,
-          class GL /*= typename SP::type::GridLayerType,
-          class Field = typename SP::type::RangeFieldType*/>
-class L2FaceVectorFunctional
-{
-  typedef typename SP::type S;
-  static_assert(is_space<S>::value, "");
-
-public:
-  typedef GDT::L2FaceVectorFunctional<F, S, V> type;
-  typedef pybind11::class_<type> bound_type;
-
-  static bound_type bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    const std::string class_name = "l2_face_vector_functional";
-    const auto ClassName = XT::Common::to_camel_case(class_name + "_" + space_name<SP>::value() + "_"
-                                                     + XT::LA::bindings::container_name<V>::value());
-
-    auto c = VectorFunctionalBase<type>::bind(m, ClassName);
-
-    m.def(std::string("make_" + class_name + "_" + XT::LA::bindings::container_name<V>::value()).c_str(),
-          [](const F& function, const S& space, const size_t over_integrate) {
-            return make_l2_face_vector_functional<V>(function, space, over_integrate).release(); //              <- b.c.
-          }, //      L2FaceVectorFunctional is not movable, returning the raw pointer lets pybind11 correctly manage the
-          "function"_a, //                                                                                        memory
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>());
-    m.def(std::string("make_" + class_name + "_" + XT::LA::bindings::container_name<V>::value()).c_str(),
-          [](const F& function,
-             const S& space,
-             const XT::Grid::ApplyOn::WhichIntersection<GL>& which_intersections,
-             const size_t over_integrate) {
-            return make_l2_face_vector_functional<V>(function, space, over_integrate, which_intersections.copy())
-                .release(); //                                                                                   <- s.a.
-          },
-          "function"_a,
-          "space"_a,
-          "which_intersections"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>());
-
-    m.def(std::string("make_" + class_name).c_str(),
-          [](const F& function, V& vector, const S& space, const size_t over_integrate) {
-            return make_l2_face_vector_functional(function, vector, space, over_integrate).release(); //         <- s.a.
-          },
-          "function"_a,
-          "vector"_a,
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>());
-    m.def(std::string("make_" + class_name).c_str(),
-          [](const F& function,
-             V& vector,
-             const S& space,
-             const XT::Grid::ApplyOn::WhichIntersection<GL>& which_intersections,
-             const size_t over_integrate) {
-            return make_l2_face_vector_functional(function, vector, space, over_integrate, which_intersections.copy())
-                .release(); //                                                                                   <- s.a.
-          },
-          "function"_a,
-          "vector"_a,
-          "space"_a,
-          "which_intersections"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>());
-
-    return c;
-  } // ... bind(...)
-}; // XT::LA::bindings::container_name<V>::value()
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-
-// begin: this is what we need for the lib
-
-#  define _DUNE_GDT_FUNCTIONALS_L2_BIND_LIB(_prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)      \
-    _prefix class Dune::GDT::bindings::L2VolumeVectorFunctional<                                                       \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>;                            \
-    _prefix class Dune::GDT::bindings::L2FaceVectorFunctional<                                                         \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType,                             \
-        typename Dune::XT::Grid::Layer<_GRID,                                                                          \
-                                       Dune::XT::Grid::Layers::_layer,                                                 \
-                                       Dune::XT::Grid::Backends::_g_backend,                                           \
-                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>
-
-
-#  if HAVE_DUNE_ALUGRID
-#    define DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_ALU(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)            \
-      _DUNE_GDT_FUNCTIONALS_L2_BIND_LIB(                                                                               \
-          _prefix, 2, ALU_2D_SIMPLEX_CONFORMING, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  else
-#    define DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_ALU(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  endif
-
-#  define DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_YASP(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)             \
-    _DUNE_GDT_FUNCTIONALS_L2_BIND_LIB(                                                                                 \
-        _prefix, 1, YASP_1D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la);                     \
-    _DUNE_GDT_FUNCTIONALS_L2_BIND_LIB(                                                                                 \
-        _prefix, 2, YASP_2D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-
-// alu_istl.cc
-#  if HAVE_DUNE_ALUGRID && HAVE_DUNE_ISTL
-DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_ALU(extern template, leaf, view, cg, gdt, 1, istl_sparse);
-DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_ALU(extern template, level, view, cg, gdt, 1, istl_sparse);
-DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_ALU(extern template, dd_subdomain, view, cg, gdt, 1, istl_sparse);
-#  endif
-
-// yasp_istl.cc
-#  if HAVE_DUNE_ISTL
-DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_YASP(extern template, leaf, view, cg, gdt, 1, istl_sparse);
-DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_YASP(extern template, level, view, cg, gdt, 1, istl_sparse);
-DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_YASP(extern template, dd_subdomain, view, cg, gdt, 1, istl_sparse);
-#  endif
-
-// end: this is what we need for the lib
-
-
-// begin: this is what we need for the .so
-
-#  define _DUNE_GDT_FUNCTIONALS_L2_BIND(_m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)               \
-    Dune::GDT::bindings::L2VolumeVectorFunctional<                                                                     \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>::bind(_m);                  \
-    Dune::GDT::bindings::L2FaceVectorFunctional<                                                                       \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType,                             \
-        typename Dune::XT::Grid::Layer<_GRID,                                                                          \
-                                       Dune::XT::Grid::Layers::_layer,                                                 \
-                                       Dune::XT::Grid::Backends::_g_backend,                                           \
-                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>::bind(_m)
-
-
-#  if HAVE_DUNE_ALUGRID
-#    define DUNE_GDT_FUNCTIONALS_L2_BIND_ALU(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)                     \
-      _DUNE_GDT_FUNCTIONALS_L2_BIND(_m, 2, ALU_2D_SIMPLEX_CONFORMING, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  else
-#    define DUNE_GDT_FUNCTIONALS_L2_BIND_ALU(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  endif
-
-#  define DUNE_GDT_FUNCTIONALS_L2_BIND_YASP(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)                      \
-    _DUNE_GDT_FUNCTIONALS_L2_BIND(                                                                                     \
-        _m, 1, YASP_1D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la);                          \
-    _DUNE_GDT_FUNCTIONALS_L2_BIND(_m, 2, YASP_2D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-
-
-// end: this is what we need for the .so
-
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_FUNCTIONALS_L2_BINDINGS_HH
diff --git a/python/dune/gdt/functionals/l2/yasp_istl.cc b/python/dune/gdt/functionals/l2/yasp_istl.cc
deleted file mode 100644
index 2229c17e6fcba3af5099952d0dab89f55798da96..0000000000000000000000000000000000000000
--- a/python/dune/gdt/functionals/l2/yasp_istl.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  include <python/dune/gdt/functionals/l2/bindings.hh>
-
-
-#  if HAVE_DUNE_ISTL
-DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_YASP(template, leaf, view, cg, gdt, 1, istl_sparse);
-DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_YASP(template, level, view, cg, gdt, 1, istl_sparse);
-DUNE_GDT_FUNCTIONALS_L2_BIND_LIB_YASP(template, dd_subdomain, view, cg, gdt, 1, istl_sparse);
-#  endif
-
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/gamm-2019-talk-on-conservative-rb.cc b/python/dune/gdt/gamm-2019-talk-on-conservative-rb.cc
index ef4028b4b6d9e9ae81d05e433d149618e63d1b3c..88142231942fe60208b7e3cbb449f835a165eb3d 100644
--- a/python/dune/gdt/gamm-2019-talk-on-conservative-rb.cc
+++ b/python/dune/gdt/gamm-2019-talk-on-conservative-rb.cc
@@ -48,7 +48,15 @@
 using namespace Dune;
 using namespace Dune::GDT;
 
+#if HAVE_DUNE_ALUGRID
 using G = ALU_2D_SIMPLEX_CONFORMING;
+#elif HAVE_DUNE_UGGRID || HAVE_UG
+using G = UG_2D;
+#else
+#  warning Falling back to cubic grid, results will not be reproduced but similar!
+using G = YASP_2D_EQUIDISTANT_OFFSET;
+#endif
+
 using GP = XT::Grid::GridProvider<G>;
 using GV = typename G::LeafGridView;
 using E = XT::Grid::extract_entity_t<GV>;
@@ -301,49 +309,7 @@ PYBIND11_MODULE(gamm_2019_talk_on_conservative_rb, m)
   py::module::import("dune.xt.la");
   py::module::import("dune.xt.grid");
   py::module::import("dune.xt.functions");
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-  Dune::XT::Common::bindings::add_initialization(m, "dune.gdt");
-
-  py::class_<XT::Functions::Spe10::Model1Function<E, d, d, double>,
-             XT::Functions::GridFunctionInterface<E, d, d, double>>
-      spe10_function(m, "Spe10Model1Function", "Spe10Model1Function");
-  spe10_function.def(py::init([](const FieldVector<double, d>& lower_left, const FieldVector<double, d>& upper_right) {
-                       return new XT::Functions::Spe10::Model1Function<E, d, d, double>(
-                           XT::Data::spe10_model1_filename(), lower_left, upper_right);
-                     }),
-                     "lower_left"_a,
-                     "upper_right"_a);
-
-  py::class_<XT::Functions::FunctionAsGridFunctionWrapper<E, 1, 1, double>,
-             XT::Functions::GridFunctionInterface<E, 1, 1, double>>
-      scalar_wrapper(m, "ScalarFunctionAsGridFunctionWrapper", "ScalarFunctionAsGridFunctionWrapper");
-  scalar_wrapper.def(py::init([](const XT::Functions::FunctionInterface<d>& func) {
-                       return new XT::Functions::FunctionAsGridFunctionWrapper<E, 1, 1, double>(func);
-                     }),
-                     py::keep_alive<1, 2>(),
-                     "scalar_function"_a);
-  py::class_<XT::Functions::FunctionAsGridFunctionWrapper<E, d, d, double>,
-             XT::Functions::GridFunctionInterface<E, d, d, double>>
-      matrix_wrapper(m, "MatrixFunctionAsGridFunctionWrapper", "MatrixFunctionAsGridFunctionWrapper");
-  matrix_wrapper.def(py::init([](const XT::Functions::FunctionInterface<d, d, d>& func) {
-                       return new XT::Functions::FunctionAsGridFunctionWrapper<E, d, d, double>(func);
-                     }),
-                     py::keep_alive<1, 2>(),
-                     "matrix_function"_a);
-
-  m.def("function_to_grid_function",
-        [](XT::Functions::FunctionInterface<d>& func) {
-          return std::make_unique<XT::Functions::FunctionAsGridFunctionWrapper<E, 1, 1, double>>(func);
-        },
-        py::keep_alive<0, 1>(),
-        "scalar_function"_a);
-  m.def("function_to_grid_function",
-        [](XT::Functions::FunctionInterface<d, d, d>& func) {
-          return std::make_unique<XT::Functions::FunctionAsGridFunctionWrapper<E, d, d, double>>(func);
-        },
-        py::keep_alive<0, 1>(),
-        "matrix_function"_a);
+  py::module::import("dune.gdt.discretefunction");
 
   m.def("visualize",
         [](GP& grid, XT::Functions::FunctionInterface<d>& func, const std::string& filename) {
@@ -404,24 +370,6 @@ PYBIND11_MODULE(gamm_2019_talk_on_conservative_rb, m)
   rtn_space.def_property_readonly("dimDomain", [](RTN& /*self*/) { return d; });
   rtn_space.def_property_readonly("num_DoFs", [](RTN& self) { return self.mapper().size(); });
 
-  py::class_<ScalarDF> scalar_discrete_function(m, "ScalarDiscreteFunction", "ScalarDiscreteFunction");
-  scalar_discrete_function.def(
-      py::init([](DG& space, V& vec, const std::string& name) { return new ScalarDF(space, vec, name); }),
-      "dg_space"_a,
-      "DoF_vector"_a,
-      "name"_a);
-  scalar_discrete_function.def(
-      "visualize", [](ScalarDF& self, const std::string filename) { self.visualize(filename); }, "filename"_a);
-
-  py::class_<VectorDF> vector_discrete_function(m, "VectorDiscreteFunction", "VectorDiscreteFunction");
-  vector_discrete_function.def(
-      py::init([](RTN& space, V& vec, const std::string& name) { return new VectorDF(space, vec, name); }),
-      "rtn_space"_a,
-      "DoF_vector"_a,
-      "name"_a);
-  vector_discrete_function.def(
-      "visualize", [](VectorDF& self, const std::string filename) { self.visualize(filename); }, "filename"_a);
-
   m.def("make_discrete_function",
         [](DG& dg_space, V& vec, const std::string& name) { return ScalarDF(dg_space, vec, name); },
         "dg_space"_a,
diff --git a/python/dune/gdt/local/diffusive-flux-estimation-operator.hh b/python/dune/gdt/local/diffusive-flux-estimation-operator.hh
deleted file mode 100644
index 3ab96574de33c14ccaafc3c97a70284fd56fecc6..0000000000000000000000000000000000000000
--- a/python/dune/gdt/local/diffusive-flux-estimation-operator.hh
+++ /dev/null
@@ -1,118 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_LOCAL_DIFFUSIVE_FLUX_ESTIMATION_OPERATOR_BINDINGS_HH
-#define PYTHON_DUNE_GDT_LOCAL_DIFFUSIVE_FLUX_ESTIMATION_OPERATOR_BINDINGS_HH
-// Todo: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <dune/xt/functions/interfaces/grid-function.hh>
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <dune/xt/grid/type_traits.hh>
-
-#  include <dune/gdt/local/integrands/ESV2007.hh>
-#  include <dune/gdt/local/operators/integrals.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class G>
-class LocalDiffusiveFluxEstimationOperator
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef typename G::template Codim<0>::Entity E;
-  typedef typename G::ctype D;
-  static const constexpr size_t d = G::dimension;
-  typedef double R;
-
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, 1> ScalarFunction;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, d> VectorFunction;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, d, d> TensorFunction;
-  typedef typename ScalarFunction::LocalfunctionType ScalarLocalFunctionType;
-  typedef GDT::LocalVolumeTwoFormInterface<ScalarLocalFunctionType> InterfaceType;
-
-public:
-  typedef LocalVolumeIntegralOperator<LocalDiffusiveFluxEstimateESV2007Integrand<ScalarFunction,
-                                                                                 VectorFunction,
-                                                                                 TensorFunction>,
-                                      ScalarLocalFunctionType>
-      type;
-  typedef pybind11::class_<type, InterfaceType> bound_type;
-
-  static bound_type bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-    using XT::Common::to_string;
-
-    // bind interface, guard since we might not be the first to do so for this combination
-    try {
-      const auto InterfaceName = XT::Common::to_camel_case(
-          "local_volume_two_form_interface_" + XT::Grid::bindings::grid_name<G>::value() + "_to_1x1");
-      py::class_<InterfaceType>(m, InterfaceName.c_str(), "LocalVolumeTwoFormInterface");
-    } catch (std::runtime_error&) {
-    }
-
-    const std::string class_name = "local_diffusive_flux_estimation_esv2007_operator";
-    const auto ClassName =
-        XT::Common::to_camel_case(class_name + "_" + XT::Grid::bindings::grid_name<G>::value() + "_to_1x1");
-
-    bound_type c(m, ClassName.c_str(), "LocalVolumeIntegralOperator<LocalDiffusiveFluxEstimateESV2007Integrand<...>>");
-
-    m.def(("make_" + class_name + "_to_1x1").c_str(),
-          [](const ScalarFunction& diffusion_factor,
-             const TensorFunction& diffusion_tensor,
-             const VectorFunction& diffusive_flux,
-             const size_t over_integrate) {
-            return type(over_integrate, diffusion_factor, diffusion_tensor, diffusive_flux);
-          },
-          "diffusion_factor"_a,
-          "diffusion_tensor"_a,
-          "diffusive_flux"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>());
-
-    return c;
-  } // ... bind(...)
-}; // class LocalDiffusiveFluxEstimationOperator
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-
-// begin: this is what we need for the .so
-
-
-#  define _DUNE_GDT_LOCAL_DIFFUSIVE_FLUX_ESTIMATION_OPERATOR_BIND(_G, _m)                                              \
-    Dune::GDT::bindings::LocalDiffusiveFluxEstimationOperator<_G>::bind(_m)
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_LOCAL_DIFFUSIVE_FLUX_ESTIMATION_OPERATOR_BIND_ALU(_m)                                            \
-      _DUNE_GDT_LOCAL_DIFFUSIVE_FLUX_ESTIMATION_OPERATOR_BIND(ALU_2D_SIMPLEX_CONFORMING, _m)
-#  else
-#    define _DUNE_GDT_LOCAL_DIFFUSIVE_FLUX_ESTIMATION_OPERATOR_BIND_ALU(_m)
-#  endif
-
-#  define DUNE_GDT_LOCAL_DIFFUSIVE_FLUX_ESTIMATION_OPERATOR_BIND(_m)                                                   \
-    _DUNE_GDT_LOCAL_DIFFUSIVE_FLUX_ESTIMATION_OPERATOR_BIND_ALU(_m)
-
-// end: this is what we need for the .so
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_LOCAL_DIFFUSIVE_FLUX_ESTIMATION_OPERATOR_BINDINGS_HH
diff --git a/python/dune/gdt/local/elliptic-ipdg-operators.cc b/python/dune/gdt/local/elliptic-ipdg-operators.cc
deleted file mode 100644
index 55888904eb8f67b79b3c221359628142c6c96ec3..0000000000000000000000000000000000000000
--- a/python/dune/gdt/local/elliptic-ipdg-operators.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  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/gdt/shared.hh>
-
-#  include "elliptic-ipdg-operators.hh"
-
-
-PYBIND11_MODULE(__local_elliptic_ipdg_operators, m)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.gdt.__spaces");
-
-  DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND(m);
-
-  add_initialization(m, "dune.gdt.assembler");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/local/elliptic-ipdg-operators.hh b/python/dune/gdt/local/elliptic-ipdg-operators.hh
deleted file mode 100644
index 7ea84327e67010ad9908b29b8b30ecad19cb73cb..0000000000000000000000000000000000000000
--- a/python/dune/gdt/local/elliptic-ipdg-operators.hh
+++ /dev/null
@@ -1,686 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BINDINGS_HH
-#define PYTHON_DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <dune/xt/la/container.hh>
-#  include <dune/xt/functions/interfaces.hh>
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <python/dune/xt/grid/layers.bindings.hh>
-#  include <dune/xt/grid/dd/subdomains/grid.hh>
-
-#  include <dune/gdt/spaces.hh>
-#  include <dune/gdt/spaces/bindings.hh>
-#  include <dune/gdt/type_traits.hh>
-
-#  include <dune/gdt/local/integrands/elliptic-ipdg.hh>
-#  include <dune/gdt/local/operators/integrals.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class DF, typename DT, class SP, XT::Grid::Layers layer, LocalEllipticIpdgIntegrands::Method method>
-class LocalEllipticIpdgInnerIntegralOperator
-{
-  static_assert(XT::Functions::is_localizable_function<DF>::value, "");
-  static_assert(XT::Functions::is_localizable_function<DT>::value || std::is_same<DT, void>::value, "");
-  typedef typename SP::type S;
-  static_assert(is_space<S>::value, "");
-  typedef XT::Grid::extract_grid_t<typename S::GridLayerType> G;
-  typedef typename S::BaseFunctionSetType B;
-  typedef XT::Grid::extract_intersection_t<
-      typename XT::Grid::Layer<G, layer, S::layer_backend, XT::Grid::DD::SubdomainGrid<G>>::type>
-      I;
-
-public:
-  typedef GDT::LocalCouplingTwoFormInterface<B, I> InterfaceType;
-  typedef LocalCouplingIntegralOperator<LocalEllipticIpdgIntegrands::Inner<DF, DT, method>, B, I> type;
-  typedef pybind11::class_<type, InterfaceType> bound_type;
-
-private:
-  template <bool intersection_matches_layer = (layer == SP::grid_layer), bool anything = false>
-  struct intersection_postfix
-  {
-    static std::string value()
-    {
-      return "";
-    }
-  };
-
-  template <bool anything>
-  struct intersection_postfix<false, anything>
-  {
-    static std::string value()
-    {
-      return "_" + XT::Grid::bindings::layer_name<layer>::value() + "_intersection";
-    }
-  };
-
-  template <bool single_diffusion = std::is_same<DT, void>::value,
-            bool scalar = (DF::dimRange == 1 && DF::dimRangeCols == 1),
-            bool anything = false>
-  struct diffusion_switch
-  {
-    static std::string suffix()
-    {
-      return "diffusion_factor_and_tensor";
-    }
-
-    static void addbind_factory_methods(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-      using XT::Common::to_string;
-
-      const std::string method_name =
-          "make_local_elliptic_" + LocalEllipticIpdgIntegrands::method_name<method>::value()
-          + "_inner_integral_operator_" + to_string(size_t(S::dimRange)) + "x" + to_string(size_t(S::dimRangeCols))
-          + "_p" + to_string(int(S::polOrder)) + "_" + space_type_name<SP::space_type>::value() + "_"
-          + backend_name<SP::space_backend>::value() + "_space" + intersection_postfix<>::value();
-
-      m.def(method_name.c_str(),
-            [](const DF& diffusion_factor, const DT& diffusion_tensor, const size_t over_integrate) {
-              return type(over_integrate, diffusion_factor, diffusion_tensor);
-            },
-            "diffusion_factor"_a,
-            "diffusion_tensor"_a,
-            "over_integrate"_a = 0,
-            py::keep_alive<0, 1>(),
-            py::keep_alive<0, 2>());
-    } // ... addbind_factory_methods(...)
-  }; // struct diffusion_switch
-
-  struct diffusion_switch_scalar_base
-  {
-    static void addbind_factory_methods(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-      using XT::Common::to_string;
-
-      const std::string method_name =
-          "make_local_elliptic_" + LocalEllipticIpdgIntegrands::method_name<method>::value()
-          + "_inner_integral_operator_" + to_string(size_t(S::dimRange)) + "x" + to_string(size_t(S::dimRangeCols))
-          + "_p" + to_string(int(S::polOrder)) + "_" + space_type_name<SP::space_type>::value() + "_"
-          + backend_name<SP::space_backend>::value() + "_space" + intersection_postfix<>::value();
-
-      m.def(method_name.c_str(),
-            [](const DF& diffusion, const size_t over_integrate) { return type(over_integrate, diffusion); },
-            "diffusion"_a,
-            "over_integrate"_a = 0,
-            py::keep_alive<0, 1>());
-    } // ... addbind_factory_methods(...)
-  }; // struct diffusion_switch_scalar_base
-
-  template <bool anything>
-  struct diffusion_switch<true, true, anything> : public diffusion_switch_scalar_base
-  {
-
-    static std::string suffix()
-    {
-      return "single_diffusion_factor";
-    }
-  };
-
-  template <bool anything>
-  struct diffusion_switch<true, false, anything> : public diffusion_switch_scalar_base
-  {
-
-    static std::string suffix()
-    {
-      return "single_diffusion_tensor";
-    }
-  };
-
-public:
-  static bound_type bind(pybind11::module& m, const std::string& layer_name)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-    using XT::Common::to_string;
-
-    // bind interface, guard since we might not be the first to do so for this intersection
-    try {
-      const auto InterfaceName = XT::Common::to_camel_case(
-          "local_coupling_two_form_interface_" + XT::Grid::bindings::grid_name<G>::value() + layer_name + "_to_"
-          + to_string(size_t(S::dimRange))
-          + "x"
-          + to_string(size_t(S::dimRangeCols))
-          + "_p"
-          + to_string(int(S::polOrder))
-          + "_"
-          + space_type_name<SP::space_type>::value()
-          + "_"
-          + backend_name<SP::space_backend>::value()
-          + "_space"
-          + intersection_postfix<>::value());
-      py::class_<InterfaceType>(m, InterfaceName.c_str(), InterfaceName.c_str());
-    } catch (std::runtime_error&) {
-    }
-
-    const auto ClassName =
-        XT::Common::to_camel_case("local_elliptic_" + LocalEllipticIpdgIntegrands::method_name<method>::value() + "_"
-                                  + diffusion_switch<>::suffix()
-                                  + "_inner_integral_operator_"
-                                  + XT::Grid::bindings::grid_name<G>::value()
-                                  + layer_name
-                                  + "_to_"
-                                  + to_string(size_t(S::dimRange))
-                                  + "x"
-                                  + to_string(size_t(S::dimRangeCols))
-                                  + "_p"
-                                  + to_string(int(S::polOrder))
-                                  + "_"
-                                  + space_type_name<SP::space_type>::value()
-                                  + "_"
-                                  + backend_name<SP::space_backend>::value()
-                                  + "_space"
-                                  + intersection_postfix<>::value());
-
-    bound_type c(m, ClassName.c_str());
-
-    diffusion_switch<>::addbind_factory_methods(m);
-
-    return c;
-  } // ... bind(...)
-}; // class LocalEllipticIpdgInnerIntegralOperator
-
-
-template <class DF, typename DT, class SP, XT::Grid::Layers layer, LocalEllipticIpdgIntegrands::Method method>
-class LocalEllipticIpdgBoundaryIntegralOperator
-{
-  static_assert(XT::Functions::is_localizable_function<DF>::value, "");
-  static_assert(XT::Functions::is_localizable_function<DT>::value || std::is_same<DT, void>::value, "");
-  typedef typename SP::type S;
-  static_assert(is_space<S>::value, "");
-  typedef XT::Grid::extract_grid_t<typename S::GridLayerType> G;
-  typedef typename S::BaseFunctionSetType B;
-  typedef XT::Grid::extract_intersection_t<
-      typename XT::Grid::Layer<G, layer, S::layer_backend, XT::Grid::DD::SubdomainGrid<G>>::type>
-      I;
-
-public:
-  typedef GDT::LocalBoundaryTwoFormInterface<B, I> InterfaceType;
-  typedef LocalBoundaryIntegralOperator<LocalEllipticIpdgIntegrands::BoundaryLHS<DF, DT, method>, B, I> type;
-  typedef pybind11::class_<type, InterfaceType> bound_type;
-
-private:
-  template <bool intersection_matches_layer = (layer == SP::grid_layer), bool anything = false>
-  struct intersection_postfix
-  {
-    static std::string value()
-    {
-      return "";
-    }
-  };
-
-  template <bool anything>
-  struct intersection_postfix<false, anything>
-  {
-    static std::string value()
-    {
-      return "_" + XT::Grid::bindings::layer_name<layer>::value() + "_intersection";
-    }
-  };
-
-  template <bool single_diffusion = std::is_same<DT, void>::value,
-            bool scalar = (DF::dimRange == 1 && DF::dimRangeCols == 1),
-            bool anything = false>
-  struct diffusion_switch
-  {
-    static std::string suffix()
-    {
-      return "diffusion_factor_and_tensor";
-    }
-
-    static void addbind_factory_methods(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-      using XT::Common::to_string;
-
-      const std::string method_name =
-          "make_local_elliptic_" + LocalEllipticIpdgIntegrands::method_name<method>::value()
-          + "_boundary_integral_operator_" + to_string(size_t(S::dimRange)) + "x" + to_string(size_t(S::dimRangeCols))
-          + "_p" + to_string(int(S::polOrder)) + "_" + space_type_name<SP::space_type>::value() + "_"
-          + backend_name<SP::space_backend>::value() + "_space" + intersection_postfix<>::value();
-
-      m.def(method_name.c_str(),
-            [](const DF& diffusion_factor, const DT& diffusion_tensor, const size_t over_integrate) {
-              return type(over_integrate, diffusion_factor, diffusion_tensor);
-            },
-            "diffusion_factor"_a,
-            "diffusion_tensor"_a,
-            "over_integrate"_a = 0,
-            py::keep_alive<0, 1>(),
-            py::keep_alive<0, 2>());
-    } // ... addbind_factory_methods(...)
-  }; // struct diffusion_switch
-
-  struct diffusion_switch_scalar_base
-  {
-    static void addbind_factory_methods(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-      using XT::Common::to_string;
-
-      const std::string method_name =
-          "make_local_elliptic_" + LocalEllipticIpdgIntegrands::method_name<method>::value()
-          + "_boundary_integral_operator_" + to_string(size_t(S::dimRange)) + "x" + to_string(size_t(S::dimRangeCols))
-          + "_p" + to_string(int(S::polOrder)) + "_" + space_type_name<SP::space_type>::value() + "_"
-          + backend_name<SP::space_backend>::value() + "_space" + intersection_postfix<>::value();
-
-      m.def(method_name.c_str(),
-            [](const DF& diffusion, const size_t over_integrate) { return type(over_integrate, diffusion); },
-            "diffusion"_a,
-            "over_integrate"_a = 0,
-            py::keep_alive<0, 1>());
-    } // ... addbind_factory_methods(...)
-  }; // struct diffusion_switch_scalar_base
-
-  template <bool anything>
-  struct diffusion_switch<true, true, anything> : public diffusion_switch_scalar_base
-  {
-
-    static std::string suffix()
-    {
-      return "single_diffusion_factor";
-    }
-  };
-
-  template <bool anything>
-  struct diffusion_switch<true, false, anything> : public diffusion_switch_scalar_base
-  {
-
-    static std::string suffix()
-    {
-      return "single_diffusion_tensor";
-    }
-  };
-
-public:
-  static bound_type bind(pybind11::module& m, const std::string& layer_name)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-    using XT::Common::to_string;
-
-    // bind interface, guard since we might not be the first to do so for this intersection
-    try {
-      const auto InterfaceName = XT::Common::to_camel_case(
-          "local_boundary_two_form_interface_" + XT::Grid::bindings::grid_name<G>::value() + layer_name + "_to_"
-          + to_string(size_t(S::dimRange))
-          + "x"
-          + to_string(size_t(S::dimRangeCols))
-          + "_p"
-          + to_string(int(S::polOrder))
-          + "_"
-          + space_type_name<SP::space_type>::value()
-          + "_"
-          + backend_name<SP::space_backend>::value()
-          + "_space"
-          + intersection_postfix<>::value());
-      py::class_<InterfaceType>(m, InterfaceName.c_str(), InterfaceName.c_str());
-    } catch (std::runtime_error&) {
-    }
-
-    const auto ClassName =
-        XT::Common::to_camel_case("local_elliptic_" + LocalEllipticIpdgIntegrands::method_name<method>::value() + "_"
-                                  + diffusion_switch<>::suffix()
-                                  + "_boundary_integral_operator_"
-                                  + XT::Grid::bindings::grid_name<G>::value()
-                                  + layer_name
-                                  + "_to_"
-                                  + to_string(size_t(S::dimRange))
-                                  + "x"
-                                  + to_string(size_t(S::dimRangeCols))
-                                  + "_p"
-                                  + to_string(int(S::polOrder))
-                                  + "_"
-                                  + space_type_name<SP::space_type>::value()
-                                  + "_"
-                                  + backend_name<SP::space_backend>::value()
-                                  + "_space"
-                                  + intersection_postfix<>::value());
-
-    bound_type c(m, ClassName.c_str());
-
-    diffusion_switch<>::addbind_factory_methods(m);
-
-    return c;
-  } // ... bind(...)
-}; // class LocalEllipticIpdgBoundaryIntegralOperator
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-
-// begin: this is what we need for the .so
-
-#  define _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_1D(                                                             \
-      _m, _G, _d, _g_layer, _g_backend, _s_type, _s_backend, _i_layer_type, _p, _R, _r, _rC, _layer_name, _method)     \
-    Dune::GDT::bindings::LocalEllipticIpdgInnerIntegralOperator<                                                       \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _G,                                                             \
-                                                       Dune::XT::Grid::Layers::_g_layer,                               \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_G>>::type>,                  \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _G,                                                             \
-                                                       Dune::XT::Grid::Layers::_g_layer,                               \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_G>>::type>,                  \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        Dune::GDT::SpaceProvider<_G,                                                                                   \
-                                 Dune::XT::Grid::Layers::_g_layer,                                                     \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 _R,                                                                                   \
-                                 _r,                                                                                   \
-                                 _rC>,                                                                                 \
-        Dune::XT::Grid::Layers::_i_layer_type,                                                                         \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method>::bind(_m, _layer_name);                               \
-    Dune::GDT::bindings::LocalEllipticIpdgInnerIntegralOperator<                                                       \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _G,                                                             \
-                                                       Dune::XT::Grid::Layers::_g_layer,                               \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_G>>::type>,                  \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_G,                                                                                   \
-                                 Dune::XT::Grid::Layers::_g_layer,                                                     \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 _R,                                                                                   \
-                                 _r,                                                                                   \
-                                 _rC>,                                                                                 \
-        Dune::XT::Grid::Layers::_i_layer_type,                                                                         \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method>::bind(_m, _layer_name);                               \
-    Dune::GDT::bindings::LocalEllipticIpdgBoundaryIntegralOperator<                                                    \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _G,                                                             \
-                                                       Dune::XT::Grid::Layers::_g_layer,                               \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_G>>::type>,                  \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _G,                                                             \
-                                                       Dune::XT::Grid::Layers::_g_layer,                               \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_G>>::type>,                  \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        Dune::GDT::SpaceProvider<_G,                                                                                   \
-                                 Dune::XT::Grid::Layers::_g_layer,                                                     \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 _R,                                                                                   \
-                                 _r,                                                                                   \
-                                 _rC>,                                                                                 \
-        Dune::XT::Grid::Layers::_i_layer_type,                                                                         \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method>::bind(_m, _layer_name);                               \
-    Dune::GDT::bindings::LocalEllipticIpdgBoundaryIntegralOperator<                                                    \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _G,                                                             \
-                                                       Dune::XT::Grid::Layers::_g_layer,                               \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_G>>::type>,                  \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_G,                                                                                   \
-                                 Dune::XT::Grid::Layers::_g_layer,                                                     \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 _R,                                                                                   \
-                                 _r,                                                                                   \
-                                 _rC>,                                                                                 \
-        Dune::XT::Grid::Layers::_i_layer_type,                                                                         \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method>::bind(_m, _layer_name)
-
-#  define _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_DD(                                                             \
-      _m, _G, _d, _g_layer, _g_backend, _s_type, _s_backend, _i_layer_type, _p, _R, _r, _rC, _layer_name, _method)     \
-    _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_1D(                                                                   \
-        _m, _G, _d, _g_layer, _g_backend, _s_type, _s_backend, _i_layer_type, _p, _R, _r, _rC, _layer_name, _method);  \
-    Dune::GDT::bindings::LocalEllipticIpdgInnerIntegralOperator<                                                       \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _G,                                                             \
-                                                       Dune::XT::Grid::Layers::_g_layer,                               \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_G>>::type>,                  \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_G,                                                                                   \
-                                 Dune::XT::Grid::Layers::_g_layer,                                                     \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 _R,                                                                                   \
-                                 _r,                                                                                   \
-                                 _rC>,                                                                                 \
-        Dune::XT::Grid::Layers::_i_layer_type,                                                                         \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method>::bind(_m, _layer_name);                               \
-    Dune::GDT::bindings::LocalEllipticIpdgBoundaryIntegralOperator<                                                    \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _G,                                                             \
-                                                       Dune::XT::Grid::Layers::_g_layer,                               \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_G>>::type>,                  \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_G,                                                                                   \
-                                 Dune::XT::Grid::Layers::_g_layer,                                                     \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 _R,                                                                                   \
-                                 _r,                                                                                   \
-                                 _rC>,                                                                                 \
-        Dune::XT::Grid::Layers::_i_layer_type,                                                                         \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method>::bind(_m, _layer_name)
-
-#  define _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_METHODS_1D(                                                     \
-      _m, _G, _d, _g_layer, _g_backend, _s_type, _s_backend, _i_layer_type, _p, _R, _r, _rC, _layer_name)              \
-    _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_1D(                                                                   \
-        _m, _G, _d, _g_layer, _g_backend, _s_type, _s_backend, _i_layer_type, _p, _R, _r, _rC, _layer_name, sipdg);    \
-    _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_1D(                                                                   \
-        _m, _G, _d, _g_layer, _g_backend, _s_type, _s_backend, _i_layer_type, _p, _R, _r, _rC, _layer_name, swipdg);   \
-    _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_1D(_m,                                                                \
-                                                    _G,                                                                \
-                                                    _d,                                                                \
-                                                    _g_layer,                                                          \
-                                                    _g_backend,                                                        \
-                                                    _s_type,                                                           \
-                                                    _s_backend,                                                        \
-                                                    _i_layer_type,                                                     \
-                                                    _p,                                                                \
-                                                    _R,                                                                \
-                                                    _r,                                                                \
-                                                    _rC,                                                               \
-                                                    _layer_name,                                                       \
-                                                    swipdg_affine_factor);                                             \
-    _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_1D(_m,                                                                \
-                                                    _G,                                                                \
-                                                    _d,                                                                \
-                                                    _g_layer,                                                          \
-                                                    _g_backend,                                                        \
-                                                    _s_type,                                                           \
-                                                    _s_backend,                                                        \
-                                                    _i_layer_type,                                                     \
-                                                    _p,                                                                \
-                                                    _R,                                                                \
-                                                    _r,                                                                \
-                                                    _rC,                                                               \
-                                                    _layer_name,                                                       \
-                                                    swipdg_affine_tensor)
-
-#  define _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_METHODS_DD(                                                     \
-      _m, _G, _d, _g_layer, _g_backend, _s_type, _s_backend, _i_layer_type, _p, _R, _r, _rC, _layer_name)              \
-    _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_DD(                                                                   \
-        _m, _G, _d, _g_layer, _g_backend, _s_type, _s_backend, _i_layer_type, _p, _R, _r, _rC, _layer_name, sipdg);    \
-    _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_DD(                                                                   \
-        _m, _G, _d, _g_layer, _g_backend, _s_type, _s_backend, _i_layer_type, _p, _R, _r, _rC, _layer_name, swipdg);   \
-    _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_DD(_m,                                                                \
-                                                    _G,                                                                \
-                                                    _d,                                                                \
-                                                    _g_layer,                                                          \
-                                                    _g_backend,                                                        \
-                                                    _s_type,                                                           \
-                                                    _s_backend,                                                        \
-                                                    _i_layer_type,                                                     \
-                                                    _p,                                                                \
-                                                    _R,                                                                \
-                                                    _r,                                                                \
-                                                    _rC,                                                               \
-                                                    _layer_name,                                                       \
-                                                    swipdg_affine_factor);                                             \
-    _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_DD(_m,                                                                \
-                                                    _G,                                                                \
-                                                    _d,                                                                \
-                                                    _g_layer,                                                          \
-                                                    _g_backend,                                                        \
-                                                    _s_type,                                                           \
-                                                    _s_backend,                                                        \
-                                                    _i_layer_type,                                                     \
-                                                    _p,                                                                \
-                                                    _R,                                                                \
-                                                    _r,                                                                \
-                                                    _rC,                                                               \
-                                                    _layer_name,                                                       \
-                                                    swipdg_affine_tensor)
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_ALU(                                                          \
-        _m, _g_layer, _g_backend, _s_type, _s_backend, _i_layer_type, _p, _R, _r, _rC, _layer_name)                    \
-      _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_METHODS_DD(_m,                                                      \
-                                                              ALU_2D_SIMPLEX_CONFORMING,                               \
-                                                              2,                                                       \
-                                                              _g_layer,                                                \
-                                                              _g_backend,                                              \
-                                                              _s_type,                                                 \
-                                                              _s_backend,                                              \
-                                                              _i_layer_type,                                           \
-                                                              _p,                                                      \
-                                                              _R,                                                      \
-                                                              _r,                                                      \
-                                                              _rC,                                                     \
-                                                              _layer_name)
-#  else
-#    define _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_ALU(                                                          \
-        _m, _g_layer, _g_backend, _s_type, _s_backend, _i_layer_type, _p, _R, _r, _rC, _layer_name)
-#  endif
-
-#  define _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_YASP(                                                           \
-      _m, _g_layer, _g_backend, _s_type, _s_backend, _i_layer_type, _p, _R, _r, _rC, _layer_name)                      \
-    _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_METHODS_1D(_m,                                                        \
-                                                            YASP_1D_EQUIDISTANT_OFFSET,                                \
-                                                            1,                                                         \
-                                                            _g_layer,                                                  \
-                                                            _g_backend,                                                \
-                                                            _s_type,                                                   \
-                                                            _s_backend,                                                \
-                                                            _i_layer_type,                                             \
-                                                            _p,                                                        \
-                                                            _R,                                                        \
-                                                            _r,                                                        \
-                                                            _rC,                                                       \
-                                                            _layer_name);                                              \
-    _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_METHODS_DD(_m,                                                        \
-                                                            YASP_2D_EQUIDISTANT_OFFSET,                                \
-                                                            2,                                                         \
-                                                            _g_layer,                                                  \
-                                                            _g_backend,                                                \
-                                                            _s_type,                                                   \
-                                                            _s_backend,                                                \
-                                                            _i_layer_type,                                             \
-                                                            _p,                                                        \
-                                                            _R,                                                        \
-                                                            _r,                                                        \
-                                                            _rC,                                                       \
-                                                            _layer_name);                                              \
-    _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_METHODS_DD(_m,                                                        \
-                                                            YASP_3D_EQUIDISTANT_OFFSET,                                \
-                                                            3,                                                         \
-                                                            _g_layer,                                                  \
-                                                            _g_backend,                                                \
-                                                            _s_type,                                                   \
-                                                            _s_backend,                                                \
-                                                            _i_layer_type,                                             \
-                                                            _p,                                                        \
-                                                            _R,                                                        \
-                                                            _r,                                                        \
-                                                            _rC,                                                       \
-                                                            _layer_name)
-
-                                                                 /*
-_DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_ALU(                                                                    \
-    _m, leaf, part, _s_type, _s_backend, leaf, _p, _R, _r, _rC, "_leaf_");                                           \
-_DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_ALU(                                                                    \
-    _m, level, part, _s_type, _s_backend, level, _p, _R, _r, _rC, "_level_");                                        \
-_DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_YASP(_m, leaf, part, _s_type, _s_backend, leaf, _p, _R, _r, _rC, "");   \
-_DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_YASP(                                                                   \
-    _m, dd_subdomain, part, _s_type, _s_backend, dd_subdomain_coupling, _p, _R, _r, _rC, "_dd_subdomain_")
-*/
-
-#  define DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND(_m)                                                              \
-    _DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BIND_ALU(                                                                  \
-        _m, dd_subdomain, view, dg, gdt, dd_subdomain_coupling, 1, double, 1, 1, "_dd_subdomain_")
-
-// end: this is what we need for the .so
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_LOCAL_ELLIPTIC_IPDG_OPERATORS_BINDINGS_HH
diff --git a/python/dune/gdt/operators/base.hh b/python/dune/gdt/operators/base.hh
deleted file mode 100644
index b5b1c448bb9dca770acfa37dd140dad4ed7894ee..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/base.hh
+++ /dev/null
@@ -1,196 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_OPERATORS_BASE_BINDINGS_HH
-#define PYTHON_DUNE_GDT_OPERATORS_BASE_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <dune/xt/la/container.hh>
-
-#  include <dune/gdt/type_traits.hh>
-
-#  include <dune/gdt/operators/base.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class OperatorType>
-class MatrixOperatorBase
-{
-  static_assert(is_matrix_operator<OperatorType>::value, "");
-
-public:
-  typedef OperatorType type;
-  typedef GDT::SystemAssembler<typename OperatorType::RangeSpaceType,
-                               typename OperatorType::GridLayerType,
-                               typename OperatorType::SourceSpaceType>
-      BaseType;
-  typedef pybind11::class_<type, BaseType> bound_type;
-
-private:
-  typedef typename type::RangeSpaceType R;
-  typedef typename type::SourceSpaceType S;
-  typedef typename XT::LA::Container<typename type::FieldType, type::MatrixType::vector_type>::VectorType V;
-
-public:
-  template <bool same_spaces =
-                std::is_same<typename OperatorType::RangeSpaceType, typename OperatorType::SourceSpaceType>::value,
-            bool anything = true>
-  struct induced_norm
-  {
-    static void addbind(bound_type& c)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      c.def("induced_norm",
-            [](type& self, const V& range) {
-              py::gil_scoped_release DUNE_UNUSED(release);
-              return self.induced_norm(range);
-            },
-            "range"_a);
-      c.def("induced_norm",
-            [](type& self, const GDT::ConstDiscreteFunction<R, V>& range) {
-              py::gil_scoped_release DUNE_UNUSED(release);
-              return self.induced_norm(range);
-            },
-            "range"_a);
-    }
-  }; // struct induced_norm
-
-  template <bool anything>
-  struct induced_norm<false, anything>
-  {
-    static void addbind(bound_type& /*c*/)
-    {
-    }
-  };
-
-  static bound_type bind(pybind11::module& m, const std::string& class_id)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    bound_type c(m, std::string(class_id).c_str(), std::string(class_id).c_str());
-
-    // Does not work if the grid layer of the operator is not the same as the one from the space:
-    // c.def_static("pattern", [](const R& space) { return type::pattern(space); });
-
-    // from MatrixOperatorBase
-    c.def("pattern",
-          [](type& self) { return self.pattern(self.range_space(), self.source_space(), self.grid_layer()); });
-    c.def("matrix", [](type& self) { return self.matrix(); });
-    c.def("source_space", [](type& self) { return self.source_space(); });
-    c.def("range_space", [](type& self) { return self.range_space(); });
-    c.def("apply",
-          [](type& self, const V& source, V& range) {
-            py::gil_scoped_release DUNE_UNUSED(release);
-            self.apply(source, range);
-          },
-          "source"_a,
-          "range"_a);
-    c.def("apply",
-          [](type& self, const GDT::ConstDiscreteFunction<S, V>& source, GDT::DiscreteFunction<R, V>& range) {
-            py::gil_scoped_release DUNE_UNUSED(release);
-            self.apply(source, range);
-          },
-          "source"_a,
-          "range"_a);
-    c.def("apply2",
-          [](type& self, const V& range, const V& source) {
-            py::gil_scoped_release DUNE_UNUSED(release);
-            return self.apply2(range, source);
-          },
-          "range"_a,
-          "source"_a);
-    c.def(
-        "apply2",
-        [](type& self, const GDT::ConstDiscreteFunction<R, V>& range, const GDT::ConstDiscreteFunction<S, V>& source) {
-          py::gil_scoped_release DUNE_UNUSED(release);
-          return self.apply2(range, source);
-        },
-        "range"_a,
-        "source"_a);
-    c.def("apply_inverse",
-          [](type& self, const V& range, V& source, const XT::Common::Configuration& opts) {
-            py::gil_scoped_release DUNE_UNUSED(release);
-            self.apply_inverse(range, source, opts);
-          },
-          "range"_a,
-          "source"_a,
-          "opts"_a);
-    c.def("apply_inverse",
-          [](type& self,
-             const GDT::ConstDiscreteFunction<R, V>& range,
-             GDT::ConstDiscreteFunction<S, V>& source,
-             const XT::Common::Configuration& opts) {
-            py::gil_scoped_release DUNE_UNUSED(release);
-            self.apply_inverse(range, source, opts);
-          },
-          "range"_a,
-          "source"_a,
-          "opts"_a);
-    c.def("invert_options", [](type& self) { return self.invert_options(); });
-    c.def("invert_options", [](type& self, const std::string& type) { return self.invert_options(type); }, "type"_a);
-
-    // from OperatorInterface
-    c.def("apply_inverse",
-          [](type& self, const V& range, V& source, const std::string& type) {
-            py::gil_scoped_release DUNE_UNUSED(release);
-            self.apply_inverse(range, source, type);
-          },
-          "range"_a,
-          "source"_a,
-          "type"_a);
-    c.def("apply_inverse",
-          [](type& self,
-             const GDT::ConstDiscreteFunction<R, V>& range,
-             GDT::ConstDiscreteFunction<S, V>& source,
-             const std::string& type) {
-            py::gil_scoped_release DUNE_UNUSED(release);
-            self.apply_inverse(range, source, type);
-          },
-          "range"_a,
-          "source"_a,
-          "type"_a);
-    c.def("apply_inverse",
-          [](type& self, const V& range, V& source) {
-            py::gil_scoped_release DUNE_UNUSED(release);
-            self.apply_inverse(range, source);
-          },
-          "range"_a,
-          "source"_a);
-    c.def("apply_inverse",
-          [](type& self, const GDT::ConstDiscreteFunction<R, V>& range, GDT::ConstDiscreteFunction<S, V>& source) {
-            py::gil_scoped_release DUNE_UNUSED(release);
-            self.apply_inverse(range, source);
-          },
-          "range"_a,
-          "source"_a);
-
-    induced_norm<>::addbind(c);
-
-    return c;
-  } // ... bind(...)
-}; // class MatrixOperatorBase
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_OPERATORS_BASE_BINDINGS_HH
diff --git a/python/dune/gdt/operators/elliptic-ipdg/alu_istl.cc b/python/dune/gdt/operators/elliptic-ipdg/alu_istl.cc
deleted file mode 100644
index 6e37864063fbbb514e4715b542729a89835bedaf..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/elliptic-ipdg/alu_istl.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  include <python/dune/gdt/operators/elliptic-ipdg/bindings.hh>
-
-
-#  if HAVE_DUNE_ALUGRID && HAVE_DUNE_ISTL
-DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_ALU(template, leaf, view, dg, gdt, 1, istl_sparse);
-DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_ALU(template, level, view, dg, gdt, 1, istl_sparse);
-DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_ALU(template, dd_subdomain, view, dg, gdt, 1, istl_sparse);
-#  endif
-
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/operators/elliptic-ipdg/bindings.cc b/python/dune/gdt/operators/elliptic-ipdg/bindings.cc
deleted file mode 100644
index 79069357a31408935d64761a0ce4f40f2565c95c..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/elliptic-ipdg/bindings.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  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/gdt/shared.hh>
-
-#  include <python/dune/gdt/operators/elliptic-ipdg/bindings.hh>
-
-
-PYBIND11_MODULE(__operators_elliptic_ipdg, m)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.gdt.__spaces");
-  py::module::import("dune.gdt.__discretefunction");
-
-// alu_istl.cc
-#  if HAVE_DUNE_ALUGRID && HAVE_DUNE_ISTL
-  DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_ALU(m, leaf, view, dg, gdt, 1, istl_sparse);
-  DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_ALU(m, level, view, dg, gdt, 1, istl_sparse);
-  DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_ALU(m, dd_subdomain, view, dg, gdt, 1, istl_sparse);
-#  endif
-
-// yasp_istl.cc
-#  if HAVE_DUNE_ISTL
-  DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_YASP(m, leaf, view, dg, gdt, 1, istl_sparse);
-  DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_YASP(m, level, view, dg, gdt, 1, istl_sparse);
-  DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_YASP(m, dd_subdomain, view, dg, gdt, 1, istl_sparse);
-#  endif
-
-  add_initialization(m, "dune.gdt.operators.elliptic.ipdg");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/operators/elliptic-ipdg/bindings.hh b/python/dune/gdt/operators/elliptic-ipdg/bindings.hh
deleted file mode 100644
index 1ed981e6070f455846a3fd3cdcdb1558a76066fe..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/elliptic-ipdg/bindings.hh
+++ /dev/null
@@ -1,565 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BINDINGS_HH
-#define PYTHON_DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <dune/xt/common/string.hh>
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <dune/xt/grid/type_traits.hh>
-#  include <python/dune/xt/la/container.bindings.hh>
-
-#  include <python/dune/gdt/spaces/bindings.hh>
-#  include <dune/gdt/type_traits.hh>
-
-#  include <dune/gdt/operators/elliptic-ipdg.hh>
-#  include <python/dune/gdt/operators/base.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class DF,
-          typename DT, // may be void
-          class RP,
-          LocalEllipticIpdgIntegrands::Method method,
-          class M /* = typename XT::LA::Container<typename R::RangeFieldType>::MatrixType,
-          class GL = typename RP::type::GridLayerType,
-          class SP = RP,
-          class F = typename RP::type::RangeFieldType*/>
-class EllipticIpdgMatrixOperator
-{
-  typedef typename RP::type R;
-  static_assert(is_space<R>::value, "");
-
-public:
-  typedef GDT::EllipticIpdgMatrixOperator<DF, DT, R, method, M /*, GL, S, F*/> type;
-  typedef pybind11::class_<type> bound_type;
-
-private:
-  template <bool single_diffusion = std::is_same<DT, void>::value,
-            bool scalar = (DF::dimRange == 1 && DF::dimRangeCols == 1),
-            bool anything = false>
-  struct diffusion_switch
-  {
-    static std::string suffix()
-    {
-      return "diffusion_factor_and_tensor";
-    }
-
-    template <class C>
-    static void addbind_factory_methods(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      const auto method_name =
-          "make_elliptic_" + LocalEllipticIpdgIntegrands::method_name<method>::value() + "_matrix_operator";
-
-      m.def(
-          std::string(method_name + "_" + XT::LA::bindings::container_name<M>::value()).c_str(),
-          [](const DF& diffusion_factor,
-             const DT& diffusion_tensor,
-             const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<typename R::GridLayerType>>& boundary_info,
-             const R& space,
-             const size_t over_integrate) {
-            return make_elliptic_ipdg_matrix_operator<M, method>(
-                       diffusion_factor, diffusion_tensor, boundary_info, space, over_integrate)
-                .release(); //         <- b.c. EllipticIpdgMatrixOperator is not movable, returning the raw pointer lets
-          }, //                                                                     pybind11 correctly manage the memory
-          "diffusion_factor"_a,
-          "diffusion_tensor"_a,
-          "boundary_info"_a,
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>(),
-          py::keep_alive<0, 4>());
-
-      m.def(
-          method_name.c_str(),
-          [](const DF& diffusion_factor,
-             const DT& diffusion_tensor,
-             const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<typename R::GridLayerType>>& boundary_info,
-             M& matrix,
-             const R& space,
-             const size_t over_integrate) {
-            return make_elliptic_ipdg_matrix_operator<method>(
-                       diffusion_factor, diffusion_tensor, boundary_info, matrix, space, over_integrate)
-                .release(); //                                                                     <- s.a. for release()
-          },
-          "diffusion_factor"_a,
-          "diffusion_tensor"_a,
-          "boundary_info"_a,
-          "matrix"_a,
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>(),
-          py::keep_alive<0, 4>(),
-          py::keep_alive<0, 5>());
-    } // ... addbind_factory_methods(...)
-
-  }; // struct diffusion_switch
-
-  struct diffusion_switch_scalar_base
-  {
-    template <class C>
-    static void addbind_factory_methods(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      const auto method_name =
-          "make_elliptic_" + LocalEllipticIpdgIntegrands::method_name<method>::value() + "_matrix_operator";
-
-      m.def(
-          std::string(method_name + "_" + XT::LA::bindings::container_name<M>::value()).c_str(),
-          [](const DF& diffusion,
-             const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<typename R::GridLayerType>>& boundary_info,
-             const R& space,
-             const size_t over_integrate) {
-            return make_elliptic_ipdg_matrix_operator<M, method>(diffusion, boundary_info, space, over_integrate)
-                .release(); //                                                                     <- s.a. for release()
-          },
-          "diffusion"_a,
-          "boundary_info"_a,
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>());
-
-      m.def(
-          method_name.c_str(),
-          [](const DF& diffusion,
-             const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<typename R::GridLayerType>>& boundary_info,
-             M& matrix,
-             const R& space,
-             const size_t over_integrate) {
-            return make_elliptic_ipdg_matrix_operator<method>(diffusion, boundary_info, matrix, space, over_integrate)
-                .release(); //                                                                     <- s.a. for release()
-          },
-          "diffusion"_a,
-          "boundary_info"_a,
-          "matrix"_a,
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>(),
-          py::keep_alive<0, 4>());
-    } // ... addbind_factory_methods(...)
-  }; // struct diffusion_switch<..., void>
-
-  template <bool anything>
-  struct diffusion_switch<true, true, anything> : public diffusion_switch_scalar_base
-  {
-    static std::string suffix()
-    {
-      return "single_diffusion_factor";
-    }
-  };
-
-  template <bool anything>
-  struct diffusion_switch<true, false, anything> : public diffusion_switch_scalar_base
-  {
-    static std::string suffix()
-    {
-      return "single_diffusion_tensor";
-    }
-  };
-
-public:
-  static bound_type bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    const auto ClassName = XT::Common::to_camel_case(
-        "elliptic_" + LocalEllipticIpdgIntegrands::method_name<method>::value() + "_matrix_operator_"
-        + space_name<RP>::value()
-        + "_"
-        + XT::LA::bindings::container_name<M>::value()
-        + "_"
-        + diffusion_switch<>::suffix());
-
-    auto c = MatrixOperatorBase<type>::bind(m, ClassName.c_str());
-
-    diffusion_switch<>::template addbind_factory_methods<type>(m);
-
-    return c;
-  } // ... bind(...)
-}; // EllipticIpdgMatrixOperator
-
-
-} // naemspace bindings
-} // namespace GDT
-} // namespace Dune
-
-
-// begin: this is what we need for the lib
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_1D(                                                               \
-      _prefix, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, _method)                                       \
-    _prefix class Dune::GDT::bindings::EllipticIpdgMatrixOperator<                                                     \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>;                            \
-    _prefix class Dune::GDT::bindings::EllipticIpdgMatrixOperator<                                                     \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_D(                                                                \
-      _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, _method)                                   \
-    _prefix class Dune::GDT::bindings::EllipticIpdgMatrixOperator<                                                     \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>;                            \
-    _prefix class Dune::GDT::bindings::EllipticIpdgMatrixOperator<                                                     \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>;                            \
-    _prefix class Dune::GDT::bindings::EllipticIpdgMatrixOperator<                                                     \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_METHODS_1D(                                                       \
-      _prefix, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)                                                \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_1D(                                                                     \
-        _prefix, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, sipdg);                                      \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_1D(                                                                     \
-        _prefix, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg);                                     \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_1D(                                                                     \
-        _prefix, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_factor);                       \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_1D(                                                                     \
-        _prefix, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_tensor)
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_METHODS_D(                                                        \
-      _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)                                            \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_D(                                                                      \
-        _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, sipdg);                                  \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_D(                                                                      \
-        _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg);                                 \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_D(                                                                      \
-        _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_factor);                   \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_D(                                                                      \
-        _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_tensor)
-
-/*
-#if HAVE_ALBERTA
-#define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_ALBERTA(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la) \
-  _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_METHODS_D(                                                                \
-      _prefix, 2, ALBERTA_2D, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#else
-*/
-#  define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_ALBERTA(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-//#endif
-
-#  if HAVE_DUNE_ALUGRID
-#    define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_ALU(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)   \
-      _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_METHODS_D(                                                            \
-          _prefix, 2, ALU_2D_SIMPLEX_CONFORMING, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  else
-#    define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_ALU(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  endif
-
-/*
-#if HAVE_DUNE_UGGRID || HAVE_UG
-#define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_UG(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)      \
-  _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_METHODS_D(                                                                \
-      _prefix, 2, UG_2D, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#else
-*/
-#  define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_UG(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-//#endif
-
-#  define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_YASP(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)    \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_METHODS_1D(                                                             \
-        _prefix, YASP_1D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la);                        \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_METHODS_D(                                                              \
-        _prefix, 2, YASP_2D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-
-// alu_istl.cc
-#  if HAVE_DUNE_ALUGRID && HAVE_DUNE_ISTL
-DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_ALU(extern template, leaf, view, cg, gdt, 1, istl_sparse);
-DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_ALU(extern template, level, view, cg, gdt, 1, istl_sparse);
-// DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_ALU(extern template, dd_subdomain, view, cg, gdt, 1, istl_sparse);
-#  endif
-
-// yasp_istl.cc
-#  if HAVE_DUNE_ISTL
-DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_YASP(extern template, leaf, view, cg, gdt, 1, istl_sparse);
-DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_YASP(extern template, level, view, cg, gdt, 1, istl_sparse);
-// DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_YASP(extern template, dd_subdomain, view, cg, gdt, 1, istl_sparse);
-#  endif
-
-// end: this is what we need for the lib
-
-
-// begin: this is what we need for the .so
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_1D(                                                                   \
-      _m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, _method)                                        \
-    Dune::GDT::bindings::EllipticIpdgMatrixOperator<                                                                   \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>::bind(_m);                  \
-    Dune::GDT::bindings::EllipticIpdgMatrixOperator<                                                                   \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>::bind(_m)
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_D(                                                                    \
-      _m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, _method)                                        \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_1D(                                                                         \
-        _m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, _method);                                     \
-    Dune::GDT::bindings::EllipticIpdgMatrixOperator<                                                                   \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        Dune::GDT::LocalEllipticIpdgIntegrands::Method::_method,                                                       \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>::bind(_m)
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_METHODS_1D(                                                           \
-      _m, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)                                                     \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_1D(_m, 1, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, sipdg);  \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_1D(_m, 1, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg); \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_1D(                                                                         \
-        _m, 1, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_factor);                         \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_1D(                                                                         \
-        _m, 1, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_tensor)
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_METHODS_D(                                                            \
-      _m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)                                                 \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_D(_m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, sipdg);  \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_D(_m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg); \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_D(                                                                          \
-        _m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_factor);                        \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_D(                                                                          \
-        _m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la, swipdg_affine_tensor)
-
-/*
-#if HAVE_ALBERTA
-#define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_ALBERTA(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)          \
-  _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_METHODS_D(_m, 2, ALBERTA_2D, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#else
-*/
-#  define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_ALBERTA(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-//#endif
-
-#  if HAVE_DUNE_ALUGRID
-#    define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_ALU(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)            \
-      _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_METHODS_D(                                                                \
-          _m, 2, ALU_2D_SIMPLEX_CONFORMING, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  else
-#    define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_ALU(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  endif
-
-/*
-#if HAVE_DUNE_UGGRID || HAVE_UG
-#define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_UG(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)               \
-  _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_METHODS_D(_m, 2, UG_2D, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#else
-*/
-#  define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_UG(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-//#endif
-
-#  define DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_YASP(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)             \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_METHODS_1D(                                                                 \
-        _m, YASP_1D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la);                             \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_METHODS_D(                                                                  \
-        _m, 2, YASP_2D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-
-
-// end: this is what we need for the .so
-
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BINDINGS_HH
diff --git a/python/dune/gdt/operators/elliptic-ipdg/yasp_istl.cc b/python/dune/gdt/operators/elliptic-ipdg/yasp_istl.cc
deleted file mode 100644
index 22199e07861a5157001fee84413445ee2899dfda..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/elliptic-ipdg/yasp_istl.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  include <python/dune/gdt/operators/elliptic-ipdg/bindings.hh>
-
-
-#  if HAVE_DUNE_ISTL
-DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_YASP(template, leaf, view, dg, gdt, 1, istl_sparse);
-DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_YASP(template, level, view, dg, gdt, 1, istl_sparse);
-DUNE_GDT_OPERATORS_ELLIPTIC_IPDG_BIND_LIB_YASP(template, dd_subdomain, view, dg, gdt, 1, istl_sparse);
-#  endif
-
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/operators/elliptic/bindings.cc b/python/dune/gdt/operators/elliptic/bindings.cc
deleted file mode 100644
index 30a17de5d33bea4bba60f698167dc5a50550fb50..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/elliptic/bindings.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  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/gdt/shared.hh>
-
-#  include <python/dune/gdt/operators/elliptic/bindings.hh>
-
-
-PYBIND11_MODULE(__operators_elliptic, m)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.gdt.__spaces");
-  py::module::import("dune.gdt.__discretefunction");
-
-  DUNE_GDT_OPERATORS_ELLIPTIC_BIND_ISTL(m);
-
-  add_initialization(m, "dune.gdt.operators.elliptic");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/operators/elliptic/bindings.hh b/python/dune/gdt/operators/elliptic/bindings.hh
deleted file mode 100644
index 4f130da92fd291f47b4c54ab65f3fff8a324b106..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/elliptic/bindings.hh
+++ /dev/null
@@ -1,465 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_OPERATORS_ELLIPTIC_BINDINGS_HH
-#define PYTHON_DUNE_GDT_OPERATORS_ELLIPTIC_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <dune/xt/grid/type_traits.hh>
-#  include <python/dune/xt/la/container.bindings.hh>
-
-#  include <python/dune/gdt/spaces/bindings.hh>
-#  include <dune/gdt/type_traits.hh>
-
-#  include <python/dune/gdt/operators/base.hh>
-#  include <dune/gdt/operators/elliptic.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class DF,
-          typename DT, // may be void
-          class RP,
-          class M /* = typename XT::LA::Container<typename R::RangeFieldType>::MatrixType,
-          class GL = typename R::GridLayerType,
-          class S = R,
-          class F = typename R::RangeFieldType*/>
-class EllipticMatrixOperator
-{
-  typedef typename RP::type R;
-  static_assert(is_space<R>::value, "");
-
-public:
-  typedef GDT::EllipticMatrixOperator<DF, DT, R, M /*, GL, S, F*/> type;
-  typedef pybind11::class_<type> bound_type;
-
-private:
-  template <bool single_diffusion = std::is_same<DT, void>::value,
-            bool scalar = (DF::dimRange == 1 && DF::dimRangeCols == 1),
-            bool anything = false>
-  struct diffusion_switch
-  {
-    static std::string suffix()
-    {
-      return "diffusion_factor_and_tensor";
-    }
-
-    template <class C>
-    static void addbind_factory_methods(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      const std::string method_name = "make_elliptic_matrix_operator_" + XT::LA::bindings::container_name<M>::value();
-
-      m.def(
-          method_name.c_str(),
-          [](const DF& diffusion_factor, const DT& diffusion_tensor, const R& space, const size_t over_integrate) {
-            return make_elliptic_matrix_operator<M>(diffusion_factor, diffusion_tensor, space, over_integrate)
-                .release(); //    <- b.c. EllipticMatrixOperator is not movable, returning the raw pointer lets pybind11
-          }, //                                                                              correctly manage the memory
-          "diffusion_factor"_a,
-          "diffusion_tensor"_a,
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>());
-
-      m.def(
-          std::string(method_name).c_str(),
-          [](const DF& diffusion_factor,
-             const DT& diffusion_tensor,
-             M& matrix,
-             const R& space,
-             const size_t over_integrate) {
-            return make_elliptic_matrix_operator(diffusion_factor, diffusion_tensor, matrix, space, over_integrate)
-                .release(); //                                                                     <- s.a. for release()
-          },
-          "diffusion_factor"_a,
-          "diffusion_tensor"_a,
-          "matrix"_a,
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>());
-    } // ... addbind_factory_methods(...)
-  }; // struct diffusion_switch
-
-  struct diffusion_switch_scalar_base
-  {
-    template <class C>
-    static void addbind_factory_methods(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      const std::string method_name = "make_elliptic_matrix_operator_" + XT::LA::bindings::container_name<M>::value();
-
-      m.def(method_name.c_str(),
-            [](const DF& diffusion, const R& space, const size_t over_integrate) {
-              return make_elliptic_matrix_operator<M>(diffusion, space, over_integrate).release(); // <- s.a.
-            },
-            "diffusion"_a,
-            "space"_a,
-            "over_integrate"_a = 0,
-            py::keep_alive<0, 1>(),
-            py::keep_alive<0, 2>());
-
-      m.def(std::string(method_name).c_str(),
-            [](const DF& diffusion, M& matrix, const R& space, const size_t over_integrate) {
-              return make_elliptic_matrix_operator(diffusion, matrix, space, over_integrate).release(); // <- s.a.
-            },
-            "diffusion"_a,
-            "matrix"_a,
-            "space"_a,
-            "over_integrate"_a = 0,
-            py::keep_alive<0, 1>(),
-            py::keep_alive<0, 2>());
-    } // ... addbind_factory_methods(...)
-  }; // struct diffusion_switch<..., void>
-
-  template <bool anything>
-  struct diffusion_switch<true, true, anything> : public diffusion_switch_scalar_base
-  {
-
-    static std::string suffix()
-    {
-      return "single_diffusion_factor";
-    }
-  };
-
-  template <bool anything>
-  struct diffusion_switch<true, false, anything> : public diffusion_switch_scalar_base
-  {
-
-    static std::string suffix()
-    {
-      return "single_diffusion_tensor";
-    }
-  };
-
-public:
-  static bound_type bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    const auto ClassName = XT::Common::to_camel_case(
-        "elliptic_matrix_operator_" + space_name<RP>::value() + "_" + XT::LA::bindings::container_name<M>::value() + "_"
-        + diffusion_switch<>::suffix());
-
-    auto c = MatrixOperatorBase<type>::bind(m, ClassName);
-
-    diffusion_switch<>::template addbind_factory_methods<type>(m);
-
-    return c;
-  } // ... bind(...)
-}; // class EllipticMatrixOperator
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-
-// begin: this is what we need for the lib
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_1D(                                                                    \
-      _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)                                            \
-    _prefix class Dune::GDT::bindings::EllipticMatrixOperator<                                                         \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>;                            \
-    _prefix class Dune::GDT::bindings::EllipticMatrixOperator<                                                         \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_D(                                                                     \
-      _prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)                                            \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_1D(_prefix, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la);    \
-    _prefix class Dune::GDT::bindings::EllipticMatrixOperator<                                                         \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>
-
-/*
-#if HAVE_ALBERTA
-#define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_ALBERTA(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)              \
-  _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_D(_prefix, 2, ALBERTA_2D, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#else
-*/
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_ALBERTA(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-//#endif
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_ALU(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)       \
-      _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_D(                                                                         \
-          _prefix, 2, ALU_2D_SIMPLEX_CONFORMING, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  else
-#    define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_ALU(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  endif
-
-/*
-#if HAVE_DUNE_UGGRID || HAVE_UG
-#define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_UG(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)                   \
-  _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_D(_prefix, 2, UG_2D, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#else
-*/
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_UG(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-//#endif
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_YASP(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)        \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_1D(                                                                          \
-        _prefix, 1, YASP_1D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la);                     \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_D(                                                                           \
-        _prefix, 2, YASP_2D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_GRIDS(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)       \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_ALBERTA(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la);          \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_ALU(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la);              \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_UG(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la);               \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_YASP(_prefix, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB(_prefix, _la)                                                          \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_GRIDS(_prefix, leaf, view, dg, gdt, 1, _la);                                 \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_GRIDS(_prefix, level, view, dg, gdt, 1, _la);                                \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_GRIDS(_prefix, dd_subdomain, view, dg, gdt, 1, _la);                         \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_GRIDS(_prefix, dd_subdomain, view, cg, gdt, 1, _la)
-
-#  if HAVE_DUNE_ISTL
-#    define DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_ISTL(_prefix)                                                         \
-      _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB(_prefix, istl_sparse)
-#  else
-#    define DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_ISTL(_prefix)
-#  endif
-
-// istl.cc
-DUNE_GDT_OPERATORS_ELLIPTIC_BIND_LIB_ISTL(extern template);
-
-// end: this is what we need for the lib
-
-
-// begin: this is what we need for the .so
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_1D(_m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)        \
-    Dune::GDT::bindings::EllipticMatrixOperator<                                                                       \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>::bind(_m);                  \
-    Dune::GDT::bindings::EllipticMatrixOperator<                                                                       \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   1,                                                                  \
-                                                   1>,                                                                 \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>::bind(_m)
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_D(_m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la)         \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_1D(_m, _d, _GRID, _layer, _g_backend, _s_type, _s_backend, _p, _la);             \
-    Dune::GDT::bindings::EllipticMatrixOperator<                                                                       \
-        Dune::XT::Functions::GridFunctionInterface<Dune::XT::Grid::extract_entity_t<typename Dune::XT::Grid::Layer<    \
-                                                       _GRID,                                                          \
-                                                       Dune::XT::Grid::Layers::_layer,                                 \
-                                                       Dune::XT::Grid::Backends::_g_backend,                           \
-                                                       Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,               \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   double,                                                             \
-                                                   _d,                                                                 \
-                                                   _d>,                                                                \
-        void,                                                                                                          \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_layer,                                                       \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::MatrixType>::bind(_m)
-
-/*
-#if HAVE_ALBERTA
-#define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_ALBERTA(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)              \
-  _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_D(_m, 2, ALBERTA_2D, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#else
-*/
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_ALBERTA(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-//#endif
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_ALU(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)                \
-      _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_D(                                                                             \
-          _m, 2, ALU_2D_SIMPLEX_CONFORMING, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  else
-#    define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_ALU(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#  endif
-
-/*
-#if HAVE_DUNE_UGGRID || HAVE_UG
-#define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_UG(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)                   \
-  _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_D(_m, 2, UG_2D, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-#else
-*/
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_UG(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-//#endif
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_YASP(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)                 \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_1D(                                                                              \
-        _m, 1, YASP_1D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la);                          \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_D(                                                                               \
-        _m, 2, YASP_2D_EQUIDISTANT_OFFSET, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_GRIDS(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)                \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_ALBERTA(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la);                   \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_ALU(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la);                       \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_UG(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la);                        \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_YASP(_m, _layer, _g_backend, _s_type, _s_backend, _p, _la)
-
-#  define _DUNE_GDT_OPERATORS_ELLIPTIC_BIND(_m, _la)                                                                   \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_GRIDS(_m, leaf, view, cg, gdt, 1, _la);                                          \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_GRIDS(_m, level, view, cg, gdt, 1, _la);                                         \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_GRIDS(_m, leaf, view, dg, gdt, 1, _la);                                          \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_GRIDS(_m, level, view, dg, gdt, 1, _la);                                         \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_GRIDS(_m, dd_subdomain, view, dg, gdt, 1, _la);                                  \
-    _DUNE_GDT_OPERATORS_ELLIPTIC_BIND_GRIDS(_m, dd_subdomain, view, dg, gdt, 1, _la)
-#  define DUNE_GDT_OPERATORS_ELLIPTIC_BIND_COMMON(_m)
-//_DUNE_GDT_OPERATORS_ELLIPTIC_BIND(_m, common_dense)
-                                                       /*
-#if HAVE_EIGEN
-#define DUNE_GDT_OPERATORS_ELLIPTIC_BIND_EIGEN(_m)                                                               \
-  _DUNE_GDT_OPERATORS_ELLIPTIC_BIND(_m, eigen_dense);                                                            \
-  _DUNE_GDT_OPERATORS_ELLIPTIC_BIND(_m, eigen_sparse)
-#else
-*/
-#  define DUNE_GDT_OPERATORS_ELLIPTIC_BIND_EIGEN(_m)
-//#endif
-#  if HAVE_DUNE_ISTL
-#    define DUNE_GDT_OPERATORS_ELLIPTIC_BIND_ISTL(_m) _DUNE_GDT_OPERATORS_ELLIPTIC_BIND(_m, istl_sparse)
-#  else
-#    define DUNE_GDT_OPERATORS_ELLIPTIC_BIND_ISTL(_m)
-#  endif
-
-// end: this is what we need for the .so
-
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_OPERATORS_ELLIPTIC_BINDINGS_HH
diff --git a/python/dune/gdt/operators/fluxreconstruction.cc b/python/dune/gdt/operators/fluxreconstruction.cc
deleted file mode 100644
index 5fee70ab9259928d023896eda91eaff42a4777d2..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/fluxreconstruction.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  include <dune/common/parallel/mpihelper.hh>
-
-#  include <dune/pybindxi/pybind11.h>
-#  include <python/dune/gdt/shared.hh>
-#  include <dune/pybindxi/stl.h>
-
-#  include <python/dune/xt/common/bindings.hh>
-#  include <dune/xt/grid/dd/subdomains/grid.hh>
-#  include <dune/xt/grid/grids.hh>
-
-#  include <dune/gdt/spaces.hh>
-#  include <python/dune/gdt/operators/fluxreconstruction.hh>
-
-
-PYBIND11_MODULE(__operators_fluxreconstruction, m)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.gdt.__spaces");
-  py::module::import("dune.gdt.__discretefunction");
-
-#  if HAVE_DUNE_ALUGRID && HAVE_DUNE_ISTL
-  Dune::GDT::bindings::DiffusiveFluxReconstructionOperator<ALU_2D_SIMPLEX_CONFORMING,
-                                                           Dune::GDT::SpaceType::rt,
-                                                           Dune::GDT::Backends::gdt,
-                                                           Dune::XT::Grid::Layers::leaf,
-                                                           0,
-                                                           double,
-                                                           2,
-                                                           Dune::XT::LA::Backends::istl_dense>::bind(m);
-#  endif // HAVE_DUNE_ALUGRID && HAVE_DUNE_ISTL
-
-  add_initialization(m, "dune.gdt.operators.elliptic");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/operators/fluxreconstruction.hh b/python/dune/gdt/operators/fluxreconstruction.hh
deleted file mode 100644
index 030948aefcf084670f3e147ea18bb7a0f524a8c3..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/fluxreconstruction.hh
+++ /dev/null
@@ -1,99 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_OPERATORS_FLUXRECONSTRUCTION_BINDINGS_HH
-#define PYTHON_DUNE_GDT_OPERATORS_FLUXRECONSTRUCTION_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <dune/xt/la/container.hh>
-
-#  include <dune/gdt/spaces.hh>
-
-#  include <dune/gdt/operators/fluxreconstruction.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class G,
-          SpaceType space_type,
-          Backends space_backend,
-          XT::Grid::Layers space_layer_type,
-          int p,
-          class R,
-          size_t r,
-          XT::LA::Backends la_backend /*,
-          XT::Grid::Layers layer_type = space_layer_type,
-          XT::Grid::Backends layer_backend =
-              SpaceProvider<G, space_layer_type, space_type, space_backend, p, R, r>::layer_backend*/>
-class DiffusiveFluxReconstructionOperator
-{
-  typedef typename SpaceProvider<G, space_layer_type, space_type, space_backend, p, R, r>::type S;
-  typedef typename S::GridLayerType GL;
-  typedef typename XT::LA::Container<R, la_backend>::VectorType V;
-  typedef typename S::EntityType E;
-  typedef typename S::DomainFieldType D;
-  static const size_t d = S::dimDomain;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, 1> ScalarFunctionType;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, d, d> TensorFunctionType;
-
-public:
-  static void bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    //    m.def("apply_diffusive_flux_reconstruction_operator",
-    //          [](const ScalarFunctionType& diffusion_factor,
-    //             const ScalarFunctionType& source,
-    //             DiscreteFunction<S, V>& range) {
-    //            py::gil_scoped_release DUNE_UNUSED(release);
-    //            GDT::DiffusiveFluxReconstructionOperator<GL,
-    //                                                     ScalarFunctionType,
-    //                                                     void,
-    //                                                     LocalEllipticIpdgIntegrands::Method::swipdg_affine_factor>(
-    //                range.space().grid_layer(), diffusion_factor)
-    //                .apply(source, range);
-    //          },
-    //          "diffusion_tensor"_a,
-    //          "source"_a,
-    //          "range"_a);
-    m.def("apply_diffusive_flux_reconstruction_operator",
-          [](const ScalarFunctionType& diffusion_factor,
-             const TensorFunctionType& diffusion_tensor,
-             const ScalarFunctionType& source,
-             DiscreteFunction<S, V>& range) {
-            py::gil_scoped_release DUNE_UNUSED(release);
-            GDT::DiffusiveFluxReconstructionOperator<GL,
-                                                     ScalarFunctionType,
-                                                     TensorFunctionType,
-                                                     LocalEllipticIpdgIntegrands::Method::swipdg_affine_factor>(
-                range.space().grid_layer(), diffusion_factor, diffusion_tensor)
-                .apply(source, range);
-          },
-          "diffusion_factor"_a,
-          "diffusion_tensor"_a,
-          "source"_a,
-          "range"_a);
-  }
-}; // class DiffusiveFluxReconstructionOperator
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_OPERATORS_FLUXRECONSTRUCTION_BINDINGS_HH
diff --git a/python/dune/gdt/operators/l2.cc b/python/dune/gdt/operators/l2.cc
deleted file mode 100644
index 7acf814ebf2a3d0720acd3f90f53ff40847a132e..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/l2.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  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/gdt/shared.hh>
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <dune/xt/grid/layers.hh>
-
-#  include <python/dune/gdt/operators/l2.hh>
-#  include <dune/gdt/playground/spaces/restricted.hh>
-
-using namespace Dune;
-namespace py = pybind11;
-using namespace pybind11::literals;
-using Dune::XT::Grid::Layers;
-using namespace Dune::XT;
-using Dune::GDT::SpaceType;
-
-
-template <class G,
-          XT::Grid::Layers layer_type,
-          XT::Grid::Backends layer_backend,
-          size_t range_r = 1,
-          size_t range_rC = 1,
-          size_t source_r = range_r,
-          size_t source_rC = range_rC>
-void bind_l2_localizable_product(py::module& m)
-{
-  try {
-    GDT::bindings::L2LocalizableProduct<G, layer_type, layer_backend, range_r, range_rC, source_r, source_rC>::bind(m);
-  } catch (std::runtime_error&) {
-  }
-}
-
-
-PYBIND11_MODULE(__operators_l2, m)
-{
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.gdt.__spaces");
-  py::module::import("dune.gdt.__discretefunction");
-
-#  if HAVE_DUNE_ALUGRID
-  bind_l2_localizable_product<ALU_2D_SIMPLEX_CONFORMING, Layers::dd_subdomain, XT::Grid::Backends::view>(m);
-
-  Dune::GDT::bindings::L2MatrixOperator<ALU_2D_SIMPLEX_CONFORMING,
-                                        Layers::dd_subdomain,
-                                        SpaceType::dg,
-                                        GDT::Backends::gdt,
-                                        1,
-                                        1,
-                                        LA::Backends::istl_sparse>::bind(m);
-  Dune::GDT::bindings::L2MatrixOperator<ALU_2D_SIMPLEX_CONFORMING,
-                                        Layers::leaf,
-                                        SpaceType::dg,
-                                        GDT::Backends::gdt,
-                                        1,
-                                        1,
-                                        LA::Backends::istl_sparse>::bind(m);
-  Dune::GDT::bindings::L2MatrixOperator<ALU_2D_SIMPLEX_CONFORMING,
-                                        Layers::level,
-                                        SpaceType::dg,
-                                        GDT::Backends::gdt,
-                                        1,
-                                        1,
-                                        LA::Backends::istl_sparse>::bind(m);
-  Dune::GDT::bindings::internal::L2MatrixOperator<
-      GDT::RestrictedSpace<typename GDT::SpaceProvider<ALU_2D_SIMPLEX_CONFORMING,
-                                                       Layers::leaf,
-                                                       GDT::SpaceType::rt,
-                                                       GDT::Backends::gdt,
-                                                       0,
-                                                       double,
-                                                       2>::type,
-                           typename XT::Grid::Layer<ALU_2D_SIMPLEX_CONFORMING,
-                                                    Layers::dd_subdomain,
-                                                    XT::Grid::Backends::view,
-                                                    XT::Grid::DD::SubdomainGrid<ALU_2D_SIMPLEX_CONFORMING>>::type>,
-      XT::LA::IstlRowMajorSparseMatrix<double>>::bind(m,
-                                                      "RtAlu2dSimplexLeafRestrictedSubdomainPartSpace",
-                                                      "istl_row_major_sparse_matrix_double");
-
-#  endif // HAVE_DUNE_ALUGRID
-
-  add_initialization(m, "dune.gdt.operators.elliptic");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/operators/l2.hh b/python/dune/gdt/operators/l2.hh
deleted file mode 100644
index efcd95ea05fc0a3da48841ae81f9f92cff62ce95..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/l2.hh
+++ /dev/null
@@ -1,245 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_OPERATORS_L2_BINDINGS_HH
-#define PYTHON_DUNE_GDT_OPERATORS_L2_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <dune/xt/grid/type_traits.hh>
-#  include <python/dune/xt/la/container.bindings.hh>
-
-#  include <dune/gdt/spaces.hh>
-#  include <dune/gdt/spaces/bindings.hh>
-#  include <dune/gdt/type_traits.hh>
-
-#  include <python/dune/gdt/operators/base.hh>
-#  include <dune/gdt/operators/l2.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-namespace internal {
-
-
-template <class R, class M>
-class L2MatrixOperator
-{
-public:
-  typedef GDT::L2MatrixOperator<R, M> type;
-  typedef pybind11::class_<type, XT::Grid::Walker<typename R::GridLayerType>> bound_type;
-
-public:
-  static bound_type bind(pybind11::module& m, const std::string& space_name, const std::string& container_name)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    const std::string class_name = "l2_matrix_operator";
-    const auto ClassName = XT::Common::to_camel_case(class_name + "_" + space_name + "_" + container_name);
-
-    bound_type c(m, ClassName.c_str());
-    c.def("assemble", [](type& self) { self.assemble(); });
-    c.def("matrix", [](type& self) { return self.matrix(); });
-
-    m.def(std::string("make_" + class_name + "_" + container_name).c_str(),
-          [](const R& space, const size_t over_integrate) {
-            return make_l2_matrix_operator<M>(space, over_integrate).release();
-          },
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>());
-
-    m.def(std::string("make_" + class_name).c_str(),
-          [](M& matrix, const R& space, const size_t over_integrate) {
-            return make_l2_matrix_operator(matrix, space, over_integrate).release();
-          },
-          "matrix"_a,
-          "space"_a,
-          "over_integrate"_a = 0,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>());
-
-    return c;
-  } // ... bind(...)
-}; // class L2MatrixOperator
-
-
-} // namespace internal
-
-
-template <class G,
-          XT::Grid::Layers layer_type,
-          GDT::SpaceType space_type,
-          GDT::Backends space_backend,
-          int p,
-          size_t r,
-          XT::LA::Backends la_backend>
-class L2MatrixOperator
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef GDT::SpaceProvider<G, layer_type, space_type, space_backend, p, double, r, 1> RP;
-  typedef typename RP::type R;
-  typedef typename XT::LA::Container<double, la_backend>::MatrixType M;
-
-  typedef internal::L2MatrixOperator<R, M> binder;
-
-public:
-  typedef typename binder::type type;
-  typedef typename binder::bound_type bound_type;
-
-public:
-  static bound_type bind(pybind11::module& m)
-  {
-    return binder::bind(m, space_name<RP>::value(), XT::LA::bindings::container_name<M>::value());
-  }
-}; // class L2MatrixOperator
-
-
-template <class G,
-          XT::Grid::Layers layer_type,
-          XT::Grid::Backends layer_backend,
-          size_t range_r = 1,
-          size_t range_rC = 1,
-          size_t source_r = range_r,
-          size_t source_rC = range_rC>
-class L2LocalizableProduct
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-
-  template <bool is_dd = layer_type == XT::Grid::Layers::dd_subdomain
-                         || layer_type == XT::Grid::Layers::dd_subdomain_boundary
-                         || layer_type == XT::Grid::Layers::dd_subdomain_coupling
-                         || layer_type == XT::Grid::Layers::dd_subdomain_oversampled,
-            bool anything = true>
-  struct GridLayer
-  {
-    typedef typename XT::Grid::Layer<G, layer_type, layer_backend, XT::Grid::DD::SubdomainGrid<G>>::type type;
-  };
-
-  template <bool anything>
-  struct GridLayer<false, anything>
-  {
-    typedef typename XT::Grid::Layer<G, layer_type, layer_backend>::type type;
-  };
-
-  typedef typename GridLayer<>::type GL;
-  typedef XT::Grid::extract_entity_t<GL> E;
-  typedef typename G::ctype D;
-  static const size_t d = G::dimension;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, double, range_r, range_rC> R;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, double, source_r, source_rC> S;
-
-public:
-  typedef GDT::L2LocalizableProduct<GL, R, S> type;
-  typedef pybind11::class_<type, XT::Grid::Walker<GL>> bound_type;
-
-private:
-  static std::string class_name()
-  {
-    return "l2_localizable_product_on_" + XT::Grid::bindings::layer_name<layer_type>::value() + "_"
-           + XT::Grid::bindings::backend_name<layer_backend>::value() + "_for_" + XT::Common::to_string(range_r) + "x"
-           + XT::Common::to_string(range_rC) + "_range_times_" + XT::Common::to_string(source_r) + "x"
-           + XT::Common::to_string(source_rC) + "_source";
-  }
-
-  template <bool is_dd = layer_type == XT::Grid::Layers::dd_subdomain
-                         || layer_type == XT::Grid::Layers::dd_subdomain_boundary
-                         || layer_type == XT::Grid::Layers::dd_subdomain_coupling
-                         || layer_type == XT::Grid::Layers::dd_subdomain_oversampled,
-            bool anything = true>
-  struct FactoryMethods
-  {
-    static void addbind(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      m.def(std::string("make_" + class_name()).c_str(),
-            [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& grid_provider,
-               const int level_or_subdomain,
-               const R& range,
-               const S& source,
-               const size_t over_integrate) {
-              return make_l2_localizable_product(
-                         grid_provider.template layer<layer_type, layer_backend>(level_or_subdomain),
-                         range,
-                         source,
-                         over_integrate)
-                  .release();
-            },
-            "grid_provider"_a,
-            "level_or_subdomain"_a,
-            "range"_a,
-            "source"_a,
-            "over_integrate"_a = 0,
-            py::keep_alive<0, 3>(),
-            py::keep_alive<0, 4>());
-    }
-  }; // struct FactoryMethods<true, ...>
-
-  template <bool anything>
-  struct FactoryMethods<false, anything>
-  {
-    static void addbind(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      m.def(std::string("make_" + class_name()).c_str(),
-            [](XT::Grid::GridProvider<G>& grid_provider,
-               const int level,
-               const R& range,
-               const S& source,
-               const size_t over_integrate) {
-              return make_l2_localizable_product(
-                         grid_provider.template layer<layer_type, layer_backend>(level), range, source, over_integrate)
-                  .release();
-            },
-            "grid_provider"_a,
-            "level"_a,
-            "range"_a,
-            "source"_a,
-            "over_integrate"_a = 0,
-            py::keep_alive<0, 3>(),
-            py::keep_alive<0, 4>());
-
-      FactoryMethods<true>::addbind(m);
-    }
-  }; // struct FactoryMethods<false, ...>
-
-public:
-  static bound_type bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    FactoryMethods<>::addbind(m); // needs to come first, as the code below may fail
-
-    const std::string cn = class_name();
-    bound_type c(m, XT::Common::to_camel_case(cn).c_str());
-    c.def("apply2", [](type& self) { return self.apply2(); });
-    c.def("result", [](type& self) { return self.result(); });
-
-    return c;
-  } // ... bind(...)
-}; // class L2LocalizableProduct
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_OPERATORS_L2_BINDINGS_HH
diff --git a/python/dune/gdt/operators/oswaldinterpolation.cc b/python/dune/gdt/operators/oswaldinterpolation.cc
deleted file mode 100644
index 354ae3c409255dcce4423a6f63937e59bf9e149f..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/oswaldinterpolation.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  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/gdt/shared.hh>
-#  include <dune/xt/grid/dd/subdomains/grid.hh>
-#  include <dune/xt/grid/grids.hh>
-
-#  include <dune/gdt/spaces.hh>
-#  include <python/dune/gdt/operators/oswaldinterpolation.hh>
-
-
-PYBIND11_MODULE(__operators_oswaldinterpolation, m)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.gdt.__spaces");
-  py::module::import("dune.gdt.__discretefunction");
-
-#  if HAVE_DUNE_ALUGRID && HAVE_DUNE_ISTL
-  Dune::GDT::bindings::OswaldInterpolationOperator<ALU_2D_SIMPLEX_CONFORMING,
-                                                   Dune::GDT::SpaceType::block_dg,
-                                                   Dune::GDT::Backends::gdt,
-                                                   Dune::XT::Grid::Layers::dd_subdomain,
-                                                   1,
-                                                   double,
-                                                   1,
-                                                   Dune::XT::LA::Backends::istl_dense,
-                                                   Dune::XT::Grid::Layers::dd_subdomain_oversampled>::bind(m);
-#  endif // HAVE_DUNE_ALUGRID && HAVE_DUNE_ISTL
-
-  add_initialization(m, "dune.gdt.operators.elliptic");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/operators/oswaldinterpolation.hh b/python/dune/gdt/operators/oswaldinterpolation.hh
deleted file mode 100644
index 8f0bac8fff72c8d2902821214a10a12cd709e3f5..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/oswaldinterpolation.hh
+++ /dev/null
@@ -1,84 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2017 - 2018)
-
-#ifndef PYTHON_DUNE_GDT_OPERATORS_OSWALDINTERPOLATION_BINDINGS_HH
-#define PYTHON_DUNE_GDT_OPERATORS_OSWALDINTERPOLATION_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <dune/xt/la/container.hh>
-#  include <dune/xt/grid/gridprovider/provider.hh>
-
-#  include <dune/gdt/spaces.hh>
-
-#  include <dune/gdt/operators/oswaldinterpolation.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class G,
-          SpaceType space_type,
-          Backends space_backend,
-          XT::Grid::Layers space_layer_type,
-          int p,
-          class R,
-          size_t r,
-          XT::LA::Backends la_backend,
-          XT::Grid::Layers interpolation_layer_type = space_layer_type,
-          XT::Grid::Backends interpolation_layer_backend =
-              SpaceProvider<G, space_layer_type, space_type, space_backend, p, R, r>::layer_backend>
-class OswaldInterpolationOperator
-{
-  typedef typename SpaceProvider<G, space_layer_type, space_type, space_backend, p, R, r>::type S;
-  typedef typename S::GridLayerType GL;
-  typedef typename XT::LA::Container<R, la_backend>::VectorType V;
-  typedef typename XT::Grid::
-      Layer<G, interpolation_layer_type, interpolation_layer_backend, XT::Grid::DD::SubdomainGrid<G>>::type
-          InterpolationLayerType;
-
-public:
-  static void bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    m.def("apply_oswald_interpolation_operator",
-          [](const XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-             const ssize_t layer_level_or_subdomain,
-             const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<InterpolationLayerType>>& boundary_info,
-             const GDT::ConstDiscreteFunction<S, V>& source,
-             GDT::DiscreteFunction<S, V>& range) {
-            GDT::OswaldInterpolationOperator<InterpolationLayerType, R>(
-                dd_grid_provider.template layer<interpolation_layer_type, interpolation_layer_backend>(
-                    layer_level_or_subdomain),
-                boundary_info)
-                .apply(source, range);
-          },
-          "dd_grid_provider"_a,
-          "layer_level_or_subdomain"_a = -1,
-          "boundary_info"_a =
-              XT::Grid::AllDirichletBoundaryInfo<XT::Grid::extract_intersection_t<InterpolationLayerType>>(),
-          "source"_a,
-          "range"_a);
-  }
-}; // class OswaldInterpolationOperator
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_OPERATORS_OSWALDINTERPOLATION_BINDINGS_HH
diff --git a/python/dune/gdt/operators/weighted-l2.cc b/python/dune/gdt/operators/weighted-l2.cc
deleted file mode 100644
index d53fbf214fdbfa950c146f05c2dce4caf0ea5a31..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/weighted-l2.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2017)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  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/gdt/shared.hh>
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <dune/xt/grid/layers.hh>
-
-#  include <python/dune/gdt/operators/weighted-l2.hh>
-
-
-PYBIND11_MODULE(__operators_weighted_l2, m)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-  using Dune::XT::Grid::Backends;
-  using Dune::XT::Grid::Layers;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.gdt.__spaces");
-  py::module::import("dune.gdt.__discretefunction");
-
-#  if HAVE_DUNE_ALUGRID
-  Dune::GDT::bindings::WeightedL2LocalizableProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::leaf, Backends::view>::bind(m);
-  Dune::GDT::bindings::WeightedL2LocalizableProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::level, Backends::view>::bind(m);
-  Dune::GDT::bindings::WeightedL2LocalizableProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::dd_subdomain, Backends::view>::
-      bind(m);
-#  endif // HAVE_DUNE_ALUGRID
-
-  add_initialization(m, "dune.gdt.operators.elliptic");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/operators/weighted-l2.hh b/python/dune/gdt/operators/weighted-l2.hh
deleted file mode 100644
index 2da773f33cf64bd8367671780032e1a967a77082..0000000000000000000000000000000000000000
--- a/python/dune/gdt/operators/weighted-l2.hh
+++ /dev/null
@@ -1,143 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_OPERATORS_WEIGHTED_L2_BINDINGS_HH
-#define PYTHON_DUNE_GDT_OPERATORS_WEIGHTED_L2_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <dune/xt/grid/dd/subdomains/grid.hh>
-#  include <dune/xt/grid/gridprovider/provider.hh>
-#  include <python/dune/xt/grid/layers.bindings.hh>
-#  include <dune/xt/functions/interfaces/grid-function.hh>
-
-#  include <dune/gdt/spaces/interface.hh>
-
-#  include <dune/gdt/operators/weighted-l2.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class G, XT::Grid::Layers layer_type, XT::Grid::Backends layer_backend>
-class WeightedL2LocalizableProduct
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef typename XT::Grid::Layer<G, layer_type, layer_backend, XT::Grid::DD::SubdomainGrid<G>>::type GL;
-
-  typedef XT::Grid::extract_entity_t<GL> E;
-  typedef typename G::ctype D;
-  static const size_t d = G::dimension;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, double, 1, 1> F;
-
-  template <bool is_dd_subdomain = (layer_type == XT::Grid::Layers::dd_subdomain)
-                                   || (layer_type == XT::Grid::Layers::dd_subdomain_boundary)
-                                   || (layer_type == XT::Grid::Layers::dd_subdomain_coupling),
-            bool anything = true>
-  struct helper
-  {
-    static void bind(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      m.def(std::string("apply_weighted_l2_product_" + XT::Grid::bindings::layer_name<layer_type>::value() + "_"
-                        + XT::Grid::bindings::backend_name<layer_backend>::value())
-                .c_str(),
-            [](const F& weight,
-               const F& range,
-               const F& source,
-               const XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& grid,
-               const int level_or_subdomain,
-               const size_t over_integrate) {
-              return GDT::WeightedL2LocalizableProduct<F, GL, F, F>(
-                         over_integrate,
-                         weight,
-                         grid.template layer<layer_type, layer_backend>(level_or_subdomain),
-                         range,
-                         source)
-                  .apply2();
-            },
-            "weight"_a,
-            "range"_a,
-            "source"_a,
-            "grid"_a,
-            "level_or_subdomain"_a = -1,
-            "over_integrate"_a = 0);
-    } // ... bind(...)
-  }; // struct helper<true, ...>
-
-  template <bool anything>
-  struct helper<false, anything>
-  {
-    static void bind(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      m.def(std::string("apply_weighted_l2_product_" + XT::Grid::bindings::layer_name<layer_type>::value() + "_"
-                        + XT::Grid::bindings::backend_name<layer_backend>::value())
-                .c_str(),
-            [](const F& weight,
-               const F& range,
-               const F& source,
-               const XT::Grid::GridProvider<G>& grid,
-               const int level,
-               const size_t over_integrate) {
-              return GDT::WeightedL2LocalizableProduct<F, GL, F, F>(
-                         over_integrate, weight, grid.template layer<layer_type, layer_backend>(level), range, source)
-                  .apply2();
-            },
-            "weight"_a,
-            "range"_a,
-            "source"_a,
-            "grid"_a,
-            "level"_a = -1,
-            "over_integrate"_a = 0);
-      m.def(std::string("apply_weighted_l2_product_" + XT::Grid::bindings::layer_name<layer_type>::value() + "_"
-                        + XT::Grid::bindings::backend_name<layer_backend>::value())
-                .c_str(),
-            [](const F& weight,
-               const F& range,
-               const F& source,
-               const XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& grid,
-               const int layer,
-               const size_t over_integrate) {
-              return GDT::WeightedL2LocalizableProduct<F, GL, F, F>(
-                         over_integrate, weight, grid.template layer<layer_type, layer_backend>(layer), range, source)
-                  .apply2();
-            },
-            "weight"_a,
-            "range"_a,
-            "source"_a,
-            "grid"_a,
-            "layer"_a = -1,
-            "over_integrate"_a = 0);
-    } // ... bind(...)
-  }; // struct helper<false, ...>
-
-public:
-  static void bind(pybind11::module& m)
-  {
-    helper<>::bind(m);
-  }
-}; // class WeightedL2LocalizableProduct
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_OPERATORS_WEIGHTED_L2_BINDINGS_HH
diff --git a/python/dune/gdt/playground/operators/ESV2007.cc b/python/dune/gdt/playground/operators/ESV2007.cc
deleted file mode 100644
index 95caa37d448b3c92a737679d4e18e8460fccfcaa..0000000000000000000000000000000000000000
--- a/python/dune/gdt/playground/operators/ESV2007.cc
+++ /dev/null
@@ -1,525 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  include <memory>
-
-#  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/gdt/shared.hh>
-#  include <dune/xt/common/numeric_cast.hh>
-#  include <dune/xt/grid/dd/subdomains/grid.hh>
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <python/dune/xt/grid/layers.bindings.hh>
-#  include <dune/xt/grid/gridprovider/provider.hh>
-#  include <dune/xt/grid/walker.hh>
-#  include <dune/xt/grid/type_traits.hh>
-
-#  include <python/dune/gdt/playground/operators/ESV2007.hh>
-
-using namespace Dune;
-using XT::Grid::Backends;
-using XT::Grid::Layers;
-namespace py = pybind11;
-
-
-template <class G, Layers layer_type, Backends layer_backend, Layers interpolation_layer_type = layer_type>
-struct NonconformityProduct
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef typename XT::Grid::Layer<G, layer_type, layer_backend, XT::Grid::DD::SubdomainGrid<G>>::type GL;
-  typedef
-      typename XT::Grid::Layer<G, interpolation_layer_type, Backends::view, XT::Grid::DD::SubdomainGrid<G>>::type IGL;
-
-  typedef GDT::ESV2007::NonconformityProduct<GL, IGL> type;
-  typedef py::class_<type, XT::Grid::Walker<GL>> bound_type;
-
-  template <bool is_same = (interpolation_layer_type == layer_type) && (layer_backend == Backends::view),
-            bool anything = true>
-  struct interpolation_layer_suffix
-  {
-    static std::string value()
-    {
-      return "";
-    }
-  }; // struct interpolation_layer_suffix<true, ...>
-
-  template <bool anything>
-  struct interpolation_layer_suffix<false, anything>
-  {
-    static std::string value()
-    {
-      return "_" + XT::Grid::bindings::layer_name<interpolation_layer_type>::value() + "_"
-             + XT::Grid::bindings::backend_name<Backends::view>::value();
-    }
-  }; // struct interpolation_layer_suffix<false, ...>
-
-  static std::string class_name()
-  {
-    return "ESV2007_nonconformity_product";
-  }
-
-  static std::string layer_suffix()
-  {
-    return XT::Grid::bindings::layer_name<layer_type>::value() + "_"
-           + XT::Grid::bindings::backend_name<layer_backend>::value() + interpolation_layer_suffix<>::value();
-  }
-
-  template <bool is_dd = (layer_type == Layers::dd_subdomain) || (layer_type == Layers::dd_subdomain_boundary)
-                         || (layer_type == Layers::dd_subdomain_coupling)
-                         || (layer_type == Layers::dd_subdomain_oversampled)
-                         || (interpolation_layer_type == Layers::dd_subdomain)
-                         || (interpolation_layer_type == Layers::dd_subdomain_boundary)
-                         || (interpolation_layer_type == Layers::dd_subdomain_coupling)
-                         || (interpolation_layer_type == Layers::dd_subdomain_oversampled),
-            bool anything = true>
-  struct factory_method
-  {
-    static void addbind(py::module& m)
-    {
-      using namespace pybind11::literals;
-
-      m.def(std::string("make_" + class_name() + "_" + layer_suffix()).c_str(),
-            [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-               const ssize_t layer_level_or_subdomain,
-               const ssize_t interpolation_layer_level_or_subdomain,
-               const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<IGL>>& interpolation_boundary_info,
-               const typename type::ScalarFunctionType& lambda,
-               const typename type::TensorFunctionType& kappa,
-               const typename type::ScalarFunctionType& u,
-               const typename type::ScalarFunctionType& v,
-               const ssize_t over_integrate) {
-              return new type(dd_grid_provider.template layer<layer_type, layer_backend>(
-                                  XT::Common::numeric_cast<int>(layer_level_or_subdomain)),
-                              dd_grid_provider.template layer<interpolation_layer_type, Backends::view>(
-                                  XT::Common::numeric_cast<int>(interpolation_layer_level_or_subdomain)),
-                              interpolation_boundary_info,
-                              lambda,
-                              kappa,
-                              u,
-                              v,
-                              XT::Common::numeric_cast<size_t>(over_integrate));
-            },
-            "dd_grid_provider"_a,
-            "layer_level_or_subdomain"_a = -1,
-            "interpolation_layer_level_or_subdomain"_a = -1,
-            "interpolation_boundary_info"_a,
-            "lambda"_a,
-            "kappa"_a,
-            "u"_a,
-            "v"_a,
-            "over_integrate"_a = 2);
-    }
-  }; // struct factory_method<true, ...>
-
-  template <bool anything>
-  struct factory_method<false, anything>
-  {
-    static void addbind(py::module& m)
-    {
-      using namespace pybind11::literals;
-
-      m.def(std::string("make_" + class_name() + "_" + layer_suffix()).c_str(),
-            [](XT::Grid::GridProvider<G>& grid_provider,
-               const ssize_t layer_level,
-               const ssize_t interpolation_layer_level,
-               const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<IGL>>& interpolation_boundary_info,
-               const typename type::ScalarFunctionType& lambda,
-               const typename type::TensorFunctionType& kappa,
-               const typename type::ScalarFunctionType& u,
-               const typename type::ScalarFunctionType& v,
-               const ssize_t over_integrate) {
-              return new type(
-                  grid_provider.template layer<layer_type, layer_backend>(XT::Common::numeric_cast<int>(layer_level)),
-                  grid_provider.template layer<interpolation_layer_type, Backends::view>(
-                      XT::Common::numeric_cast<int>(interpolation_layer_level)),
-                  interpolation_boundary_info,
-                  lambda,
-                  kappa,
-                  u,
-                  v,
-                  XT::Common::numeric_cast<size_t>(over_integrate));
-            },
-            "grid_provider"_a,
-            "layer_level"_a = -1,
-            "interpolation_layer_level"_a = -1,
-            "interpolation_boundary_info"_a,
-            "lambda"_a,
-            "kappa"_a,
-            "u"_a,
-            "v"_a,
-            "over_integrate"_a = 2);
-
-      factory_method<true>::addbind(m);
-    }
-  }; // struct factory_method<false, ...>
-
-  static void bind(py::module& m)
-  {
-    using namespace pybind11::literals;
-
-    try { // we might not be the first ones to add this type
-      bound_type c(m,
-                   XT::Common::to_camel_case(class_name() + "_" + XT::Grid::bindings::grid_name<G>::value() + "_"
-                                             + layer_suffix())
-                       .c_str(),
-                   "ESV2007::NonconformityProduct");
-      c.def("apply2", [](type& self) { return self.apply2(); });
-      c.def("result", [](type& self) { return self.apply2(); });
-    } catch (std::runtime_error& ee) {
-    }
-
-    factory_method<>::addbind(m);
-  } // ... bind(...)
-}; // struct NonconformityProduct
-
-
-template <class G, Layers layer_type, Backends layer_backend, Layers reconstruction_layer_type = layer_type>
-struct ResidualProduct
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef typename XT::Grid::Layer<G, layer_type, layer_backend, XT::Grid::DD::SubdomainGrid<G>>::type GL;
-  typedef
-      typename XT::Grid::Layer<G, reconstruction_layer_type, Backends::view, XT::Grid::DD::SubdomainGrid<G>>::type RGL;
-
-  typedef GDT::ESV2007::ResidualProduct<GL, RGL> type;
-  typedef py::class_<type, XT::Grid::Walker<GL>> bound_type;
-
-  template <bool is_same = (reconstruction_layer_type == layer_type) && (layer_backend == Backends::view),
-            bool anything = true>
-  struct reconstruction_layer_suffix
-  {
-    static std::string value()
-    {
-      return "";
-    }
-  }; // struct reconstruction_layer_suffix<true, ...>
-
-  template <bool anything>
-  struct reconstruction_layer_suffix<false, anything>
-  {
-    static std::string value()
-    {
-      return "_" + XT::Grid::bindings::layer_name<reconstruction_layer_type>::value() + "_"
-             + XT::Grid::bindings::backend_name<Backends::view>::value();
-    }
-  }; // struct reconstruction_layer_suffix<false, ...>
-
-  static std::string class_name()
-  {
-    return "ESV2007_residual_product";
-  }
-
-  static std::string layer_suffix()
-  {
-    return XT::Grid::bindings::layer_name<layer_type>::value() + "_"
-           + XT::Grid::bindings::backend_name<layer_backend>::value() + reconstruction_layer_suffix<>::value();
-  }
-
-  template <bool is_dd = (layer_type == Layers::dd_subdomain) || (layer_type == Layers::dd_subdomain_boundary)
-                         || (layer_type == Layers::dd_subdomain_coupling)
-                         || (layer_type == Layers::dd_subdomain_oversampled)
-                         || (reconstruction_layer_type == Layers::dd_subdomain)
-                         || (reconstruction_layer_type == Layers::dd_subdomain_boundary)
-                         || (reconstruction_layer_type == Layers::dd_subdomain_coupling)
-                         || (reconstruction_layer_type == Layers::dd_subdomain_oversampled),
-            bool anything = true>
-  struct factory_method
-  {
-    static void addbind(py::module& m)
-    {
-      using namespace pybind11::literals;
-
-      m.def(std::string("make_" + class_name() + "_" + layer_suffix()).c_str(),
-            [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-               const ssize_t layer_level_or_subdomain,
-               const ssize_t reconstruction_layer_level_or_subdomain,
-               const typename type::ScalarFunctionType& lambda,
-               const typename type::TensorFunctionType& kappa,
-               const typename type::ScalarFunctionType& f,
-               const typename type::ScalarFunctionType& u,
-               const typename type::ScalarFunctionType& v,
-               const ssize_t over_integrate,
-               const double& poincare_constant) {
-              return new type(dd_grid_provider.template layer<layer_type, layer_backend>(
-                                  XT::Common::numeric_cast<int>(layer_level_or_subdomain)),
-                              dd_grid_provider.template layer<reconstruction_layer_type, Backends::view>(
-                                  XT::Common::numeric_cast<int>(reconstruction_layer_level_or_subdomain)),
-                              lambda,
-                              kappa,
-                              f,
-                              u,
-                              v,
-                              poincare_constant,
-                              XT::Common::numeric_cast<size_t>(over_integrate));
-            },
-            "dd_grid_provider"_a,
-            "layer_level"_a = -1,
-            "reconstruction_layer_level"_a = -1,
-            "lambda"_a,
-            "kappa"_a,
-            "f"_a,
-            "u"_a,
-            "v"_a,
-            "over_integrate"_a = 2,
-            "poincare_constant"_a = 1.0 / (M_PIl * M_PIl));
-    }
-  }; // struct factory_method<true, ...>
-
-  template <bool anything>
-  struct factory_method<false, anything>
-  {
-    static void addbind(py::module& m)
-    {
-      using namespace pybind11::literals;
-
-      m.def(std::string("make_" + class_name() + "_" + layer_suffix()).c_str(),
-            [](XT::Grid::GridProvider<G>& grid_provider,
-               const ssize_t layer_level,
-               const ssize_t reconstruction_layer_level,
-               const typename type::ScalarFunctionType& lambda,
-               const typename type::TensorFunctionType& kappa,
-               const typename type::ScalarFunctionType& f,
-               const typename type::ScalarFunctionType& u,
-               const typename type::ScalarFunctionType& v,
-               const ssize_t over_integrate,
-               const double& poincare_constant) {
-              return new type(
-                  grid_provider.template layer<layer_type, layer_backend>(XT::Common::numeric_cast<int>(layer_level)),
-                  grid_provider.template layer<reconstruction_layer_type, Backends::view>(
-                      XT::Common::numeric_cast<int>(reconstruction_layer_level)),
-                  lambda,
-                  kappa,
-                  f,
-                  u,
-                  v,
-                  poincare_constant,
-                  XT::Common::numeric_cast<size_t>(over_integrate));
-            },
-            "grid_provider"_a,
-            "layer_level"_a = -1,
-            "reconstruction_layer_level"_a = -1,
-            "lambda"_a,
-            "kappa"_a,
-            "f"_a,
-            "u"_a,
-            "v"_a,
-            "over_integrate"_a = 2,
-            "poincare_constant"_a = 1.0 / (M_PIl * M_PIl));
-
-      factory_method<true>::addbind(m);
-    }
-  }; // struct factory_method<false, ...>
-
-  static void bind(py::module& m)
-  {
-    using namespace pybind11::literals;
-
-    try { // we might not be the first ones to add this type
-      bound_type c(m,
-                   XT::Common::to_camel_case(class_name() + "_" + XT::Grid::bindings::grid_name<G>::value() + "_"
-                                             + layer_suffix())
-                       .c_str(),
-                   "ESV2007::ResidualProduct");
-      c.def("apply2", [](type& self) { return self.apply2(); });
-      c.def("result", [](type& self) { return self.apply2(); });
-    } catch (std::runtime_error& ee) {
-    }
-
-    factory_method<>::addbind(m);
-  } // ... bind(...)
-}; // struct ResidualProduct
-
-
-template <class G, Layers layer_type, Backends layer_backend, Layers reconstruction_layer_type = layer_type>
-struct DiffusiveFluxProduct
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef typename XT::Grid::Layer<G, layer_type, layer_backend, XT::Grid::DD::SubdomainGrid<G>>::type GL;
-  typedef
-      typename XT::Grid::Layer<G, reconstruction_layer_type, Backends::view, XT::Grid::DD::SubdomainGrid<G>>::type RGL;
-
-  typedef GDT::ESV2007::DiffusiveFluxProduct<GL, RGL> type;
-  typedef py::class_<type, XT::Grid::Walker<GL>> bound_type;
-
-  template <bool is_same = (reconstruction_layer_type == layer_type) && (layer_backend == Backends::view),
-            bool anything = true>
-  struct reconstruction_layer_suffix
-  {
-    static std::string value()
-    {
-      return "";
-    }
-  }; // struct reconstruction_layer_suffix<true, ...>
-
-  template <bool anything>
-  struct reconstruction_layer_suffix<false, anything>
-  {
-    static std::string value()
-    {
-      return "_" + XT::Grid::bindings::layer_name<reconstruction_layer_type>::value() + "_"
-             + XT::Grid::bindings::backend_name<Backends::view>::value();
-    }
-  }; // struct reconstruction_layer_suffix<false, ...>
-
-  static std::string class_name()
-  {
-    return "ESV2007_diffusive_flux_product";
-  }
-
-  static std::string layer_suffix()
-  {
-    return XT::Grid::bindings::layer_name<layer_type>::value() + "_"
-           + XT::Grid::bindings::backend_name<layer_backend>::value() + reconstruction_layer_suffix<>::value();
-  }
-
-  template <bool is_dd = (layer_type == Layers::dd_subdomain) || (layer_type == Layers::dd_subdomain_boundary)
-                         || (layer_type == Layers::dd_subdomain_coupling)
-                         || (layer_type == Layers::dd_subdomain_oversampled)
-                         || (reconstruction_layer_type == Layers::dd_subdomain)
-                         || (reconstruction_layer_type == Layers::dd_subdomain_boundary)
-                         || (reconstruction_layer_type == Layers::dd_subdomain_coupling)
-                         || (reconstruction_layer_type == Layers::dd_subdomain_oversampled),
-            bool anything = true>
-  struct factory_method
-  {
-    static void addbind(py::module& m)
-    {
-      using namespace pybind11::literals;
-
-      m.def(std::string("make_" + class_name() + "_" + layer_suffix()).c_str(),
-            [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-               const ssize_t layer_level_or_subdomain,
-               const ssize_t reconstruction_layer_level_or_subdomain,
-               const typename type::ScalarFunctionType& lambda,
-               const typename type::TensorFunctionType& kappa,
-               const typename type::ScalarFunctionType& u,
-               const typename type::ScalarFunctionType& v,
-               const ssize_t over_integrate) {
-              return new type(dd_grid_provider.template layer<layer_type, layer_backend>(
-                                  XT::Common::numeric_cast<int>(layer_level_or_subdomain)),
-                              dd_grid_provider.template layer<reconstruction_layer_type, Backends::view>(
-                                  XT::Common::numeric_cast<int>(reconstruction_layer_level_or_subdomain)),
-                              lambda,
-                              kappa,
-                              u,
-                              v,
-                              XT::Common::numeric_cast<size_t>(over_integrate));
-            },
-            "dd_grid_provider"_a,
-            "layer_level"_a = -1,
-            "reconstruction_layer_level"_a = -1,
-            "lambda"_a,
-            "kappa"_a,
-            "u"_a,
-            "v"_a,
-            "over_integrate"_a = 2);
-    }
-  }; // struct factory_method<true, ...>
-
-  template <bool anything>
-  struct factory_method<false, anything>
-  {
-    static void addbind(py::module& m)
-    {
-      using namespace pybind11::literals;
-
-      m.def(std::string("make_" + class_name() + "_" + layer_suffix()).c_str(),
-            [](XT::Grid::GridProvider<G>& grid_provider,
-               const ssize_t layer_level,
-               const ssize_t reconstruction_layer_level,
-               const typename type::ScalarFunctionType& lambda,
-               const typename type::TensorFunctionType& kappa,
-               const typename type::ScalarFunctionType& u,
-               const typename type::ScalarFunctionType& v,
-               const ssize_t over_integrate) {
-              return new type(
-                  grid_provider.template layer<layer_type, layer_backend>(XT::Common::numeric_cast<int>(layer_level)),
-                  grid_provider.template layer<reconstruction_layer_type, Backends::view>(
-                      XT::Common::numeric_cast<int>(reconstruction_layer_level)),
-                  lambda,
-                  kappa,
-                  u,
-                  v,
-                  XT::Common::numeric_cast<size_t>(over_integrate));
-            },
-            "grid_provider"_a,
-            "layer_level"_a = -1,
-            "reconstruction_layer_level"_a = -1,
-            "lambda"_a,
-            "kappa"_a,
-            "u"_a,
-            "v"_a,
-            "over_integrate"_a = 2);
-
-      factory_method<true>::addbind(m);
-    }
-  }; // struct factory_method<false, ...>
-
-  static void bind(py::module& m)
-  {
-    using namespace pybind11::literals;
-
-    try { // we might not be the first ones to add this type
-      bound_type c(m,
-                   XT::Common::to_camel_case(class_name() + "_" + XT::Grid::bindings::grid_name<G>::value() + "_"
-                                             + layer_suffix())
-                       .c_str(),
-                   "ESV2007::DiffusiveFluxProduct");
-      c.def("apply2", [](type& self) { return self.apply2(); });
-      c.def("result", [](type& self) { return self.apply2(); });
-    } catch (std::runtime_error& ee) {
-    }
-
-    factory_method<>::addbind(m);
-  } // ... bind(...)
-}; // struct DiffusiveFluxProduct
-
-
-PYBIND11_MODULE(__operators_ESV2007, m)
-{
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-
-#  if HAVE_DUNE_ALUGRID
-  NonconformityProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::leaf, Backends::view>::bind(m);
-  NonconformityProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::dd_subdomain, Backends::view>::bind(m);
-  NonconformityProduct<ALU_2D_SIMPLEX_CONFORMING,
-                       Layers::dd_subdomain,
-                       Backends::view,
-                       Layers::dd_subdomain_oversampled>::bind(m);
-  ResidualProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::leaf, Backends::view>::bind(m);
-  ResidualProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::leaf, Backends::view>::bind(m);
-  //                        on a dd_subdomain_oversampled grid view is broken, if based on
-  //                        a 2d simplex alugrid.
-  ResidualProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::dd_subdomain, Backends::view, Layers::leaf>::bind(m);
-  DiffusiveFluxProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::leaf, Backends::view>::bind(m);
-  DiffusiveFluxProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::leaf, Backends::view>::bind(m);
-  // s.a.
-  DiffusiveFluxProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::dd_subdomain, Backends::view, Layers::leaf>::bind(m);
-#  endif
-
-  add_initialization(m, "dune.gdt.operators.elliptic");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/playground/operators/ESV2007.hh b/python/dune/gdt/playground/operators/ESV2007.hh
deleted file mode 100644
index cc6ed6446d873e3983ce688fa294f19146c00b29..0000000000000000000000000000000000000000
--- a/python/dune/gdt/playground/operators/ESV2007.hh
+++ /dev/null
@@ -1,389 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef DUNE_GDT_PLAYGROUND_OPERATORS_RS2017_HH
-#define DUNE_GDT_PLAYGROUND_OPERATORS_RS2017_HH
-
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/common/typetraits.hh>
-
-#  include <dune/xt/common/fmatrix.hh>
-#  include <dune/xt/la/eigen-solver.hh>
-#  include <dune/xt/grid/boundaryinfo/interfaces.hh>
-#  include <dune/xt/grid/entity.hh>
-#  include <dune/xt/grid/type_traits.hh>
-#  include <dune/xt/functions/derived.hh>
-#  include <dune/xt/functions/interfaces/grid-function.hh>
-
-#  include <dune/gdt/discretefunction/default.hh>
-#  include <dune/gdt/local/operators/integrals.hh>
-#  include <dune/gdt/local/integrands/lambda.hh>
-#  include <dune/gdt/operators/base.hh>
-#  include <dune/gdt/operators/fluxreconstruction.hh>
-#  include <dune/gdt/operators/oswaldinterpolation.hh>
-#  include <dune/gdt/spaces/dg/default.hh>
-#  include <dune/gdt/spaces/rt/default.hh>
-
-namespace Dune {
-namespace GDT {
-namespace ESV2007 {
-
-
-template <class ProductGridLayer, class InterpolationGridLayerType>
-class NonconformityProduct
-    : public LocalizableProductBase<ProductGridLayer,
-                                    XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<ProductGridLayer>,
-                                                                         typename ProductGridLayer::ctype,
-                                                                         ProductGridLayer::dimension,
-                                                                         double,
-                                                                         1>>
-{
-  static_assert(XT::Grid::is_layer<ProductGridLayer>::value, "");
-  static_assert(XT::Grid::is_layer<InterpolationGridLayerType>::value, "");
-  typedef XT::Grid::extract_entity_t<ProductGridLayer> E;
-  static_assert(std::is_same<XT::Grid::extract_entity_t<InterpolationGridLayerType>, E>::value, "");
-  typedef typename ProductGridLayer::ctype D;
-  static const constexpr size_t d = ProductGridLayer::dimension;
-  typedef double R;
-
-public:
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, 1> ScalarFunctionType;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, d, d> TensorFunctionType;
-
-private:
-  typedef LocalizableProductBase<ProductGridLayer, ScalarFunctionType> BaseType;
-  typedef NonconformityProduct<ProductGridLayer, InterpolationGridLayerType> ThisType;
-  typedef LocalVolumeIntegralOperator<LocalLambdaBinaryVolumeIntegrand<E>,
-                                      typename ScalarFunctionType::LocalfunctionType>
-      LocalProductType;
-  typedef DiscontinuousLagrangeSpace<InterpolationGridLayerType, 1, R> DgSpaceType;
-  typedef DiscreteFunction<DgSpaceType> DiscreteFunctionType;
-
-public:
-  using typename BaseType::GridLayerType;
-
-  NonconformityProduct(GridLayerType product_grid_layer,
-                       InterpolationGridLayerType interpolation_grid_layer,
-                       const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<InterpolationGridLayerType>>&
-                           interpolation_boundary_info,
-                       const ScalarFunctionType& lambda,
-                       const TensorFunctionType& kappa,
-                       const ScalarFunctionType& u,
-                       const ScalarFunctionType& v,
-                       const size_t over_integrate = 2)
-    : BaseType(product_grid_layer, u, v)
-    , interpolation_grid_layer_(interpolation_grid_layer)
-    , lambda_(lambda)
-    , kappa_(kappa)
-    , dg_space_(interpolation_grid_layer_)
-    , interpolated_u_(dg_space_)
-    , interpolated_v_(dg_space_)
-    , over_integrate_(over_integrate)
-    , local_product_(
-          // the order lambda
-          [&](const auto& local_u, const auto& local_v) {
-            const auto& entity = local_u.entity();
-            const auto local_lambda = lambda_.local_function(entity);
-            const auto local_kappa = kappa_.local_function(entity);
-            return local_lambda->order() + local_kappa->order()
-                   + size_t(std::max(ssize_t(local_u.order()) - 1, ssize_t(0))
-                            + std::max(ssize_t(local_v.order()) - 1, ssize_t(0)))
-                   + over_integrate_;
-          },
-          // the evaluate lambda
-          [&](const auto& local_u, const auto& local_v, const auto& local_point, auto& ret) {
-            const auto& entity = local_u.entity();
-            XT::Common::FieldMatrix<R, d, d> diffusion = kappa_.local_function(entity)->evaluate(local_point);
-            diffusion *= lambda_.local_function(entity)->evaluate(local_point);
-            const auto grad_u = local_u.jacobian(local_point).at(0)[0];
-            const auto grad_interpolated_u = interpolated_u_.local_function(entity)->jacobian(local_point)[0];
-            const auto grad_v = local_v.jacobian(local_point).at(0)[0];
-            const auto grad_interpolated_v = interpolated_v_.local_function(entity)->jacobian(local_point)[0];
-            ret[0][0] = (diffusion * (grad_u - grad_interpolated_u)) * (grad_v - grad_interpolated_v);
-          })
-  {
-    OswaldInterpolationOperator<InterpolationGridLayerType> oswald_interpolation(interpolation_grid_layer_,
-                                                                                 interpolation_boundary_info);
-    oswald_interpolation.apply(this->range(), interpolated_u_);
-    oswald_interpolation.apply(this->source(), interpolated_v_);
-    this->append(local_product_);
-  }
-
-  NonconformityProduct(const ThisType&) = delete;
-  NonconformityProduct(ThisType&&) = delete;
-
-private:
-  const InterpolationGridLayerType interpolation_grid_layer_;
-  const ScalarFunctionType& lambda_;
-  const TensorFunctionType& kappa_;
-  const DgSpaceType dg_space_;
-  DiscreteFunctionType interpolated_u_;
-  DiscreteFunctionType interpolated_v_;
-  const size_t over_integrate_;
-  const LocalProductType local_product_;
-}; // class NonconformityProduct
-
-
-namespace internal {
-
-
-template <class ProductGridLayer, class ReconstructionGridLayer>
-class ResidualProductBase
-{
-  static_assert(XT::Grid::is_layer<ProductGridLayer>::value, "");
-  static_assert(XT::Grid::is_layer<ReconstructionGridLayer>::value, "");
-
-protected:
-  typedef XT::Grid::extract_entity_t<ProductGridLayer> E;
-  typedef typename ProductGridLayer::ctype D;
-  static const constexpr size_t d = ProductGridLayer::dimension;
-  typedef double R;
-
-private:
-  static_assert(std::is_same<XT::Grid::extract_entity_t<ReconstructionGridLayer>, E>::value, "");
-  typedef ResidualProductBase<ProductGridLayer, ReconstructionGridLayer> ThisType;
-
-public:
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, 1> ScalarFunctionType;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, d, d> TensorFunctionType;
-
-private:
-  typedef RaviartThomasSpace<ReconstructionGridLayer, 0, R> RtSpaceType;
-  typedef DiscreteFunction<RtSpaceType> FluxReconstructionType;
-  typedef XT::Functions::DivergenceFunction<FluxReconstructionType> DivergenceOfFluxReconstructionType;
-  typedef typename ScalarFunctionType::DifferenceType DifferenceType;
-
-public:
-  ResidualProductBase(ReconstructionGridLayer reconstruction_grid_layer,
-                      const ScalarFunctionType& lambda,
-                      const TensorFunctionType& kappa,
-                      const ScalarFunctionType& f,
-                      const ScalarFunctionType& u,
-                      const ScalarFunctionType& v)
-    : f_(f)
-    , rt_space_(reconstruction_grid_layer)
-    , reconstructed_u_(rt_space_)
-    , reconstructed_v_(rt_space_)
-    , divergence_of_reconstructed_u_(reconstructed_u_)
-    , divergence_of_reconstructed_v_(reconstructed_v_)
-    , f_minus_divergence_of_reconstructed_u_(f_ - divergence_of_reconstructed_u_)
-    , f_minus_divergence_of_reconstructed_v_(f_ - divergence_of_reconstructed_v_)
-  {
-    DiffusiveFluxReconstructionOperator<ReconstructionGridLayer, ScalarFunctionType, TensorFunctionType>
-        flux_reconstruction(reconstruction_grid_layer, lambda, kappa);
-    flux_reconstruction.apply(u, reconstructed_u_);
-    flux_reconstruction.apply(v, reconstructed_v_);
-  }
-
-  ResidualProductBase(const ThisType&) = delete;
-  ResidualProductBase(ThisType&&) = delete;
-
-protected:
-  const ScalarFunctionType& f_;
-  const RtSpaceType rt_space_;
-  FluxReconstructionType reconstructed_u_;
-  FluxReconstructionType reconstructed_v_;
-  const DivergenceOfFluxReconstructionType divergence_of_reconstructed_u_;
-  const DivergenceOfFluxReconstructionType divergence_of_reconstructed_v_;
-  const DifferenceType f_minus_divergence_of_reconstructed_u_;
-  const DifferenceType f_minus_divergence_of_reconstructed_v_;
-}; // class ResidualProductBase
-
-
-} // namespace internal
-
-
-template <class ProductGridLayer, class ReconstructionGridLayer>
-class ResidualProduct
-    : internal::ResidualProductBase<ProductGridLayer, ReconstructionGridLayer>,
-      public LocalizableProductBase<ProductGridLayer,
-                                    XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<ProductGridLayer>,
-                                                                         typename ProductGridLayer::ctype,
-                                                                         ProductGridLayer::dimension,
-                                                                         double,
-                                                                         1>>
-{
-  typedef internal::ResidualProductBase<ProductGridLayer, ReconstructionGridLayer> ResidualProductBaseType;
-  typedef LocalizableProductBase<ProductGridLayer,
-                                 XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<ProductGridLayer>,
-                                                                      typename ProductGridLayer::ctype,
-                                                                      ProductGridLayer::dimension,
-                                                                      double,
-                                                                      1>>
-      LocalizableProductBaseType;
-
-public:
-  using typename ResidualProductBaseType::ScalarFunctionType;
-  using typename ResidualProductBaseType::TensorFunctionType;
-
-private:
-  using typename ResidualProductBaseType::E;
-  using typename ResidualProductBaseType::R;
-  typedef LocalVolumeIntegralOperator<LocalLambdaBinaryVolumeIntegrand<E>,
-                                      typename ScalarFunctionType::LocalfunctionType>
-      LocalProductType;
-
-public:
-  ResidualProduct(ProductGridLayer product_grid_layer,
-                  ReconstructionGridLayer reconstruction_grid_layer,
-                  const ScalarFunctionType& lambda,
-                  const TensorFunctionType& kappa,
-                  const ScalarFunctionType& f,
-                  const ScalarFunctionType& u,
-                  const ScalarFunctionType& v,
-                  const double& poincare_constant = 1.0 / (M_PIl * M_PIl),
-                  const size_t over_integrate = 2)
-    : ResidualProductBaseType(reconstruction_grid_layer, lambda, kappa, f, u, v)
-    , LocalizableProductBaseType(product_grid_layer,
-                                 this->f_minus_divergence_of_reconstructed_u_,
-                                 this->f_minus_divergence_of_reconstructed_v_)
-    , lambda_(lambda)
-    , kappa_(kappa)
-    , poincare_constant_(poincare_constant)
-    , over_integrate_(over_integrate)
-    , local_product_(
-          // the order lambda
-          [&](const auto& local_f_minus_divergence_of_reconstructed_u,
-              const auto& local_f_minus_divergence_of_reconstructed_v) {
-            return local_f_minus_divergence_of_reconstructed_u.order()
-                   + local_f_minus_divergence_of_reconstructed_v.order() + over_integrate_;
-          },
-          // the evaluate lambda
-          [&](const auto& local_f_minus_divergence_of_reconstructed_u,
-              const auto& local_f_minus_divergence_of_reconstructed_v,
-              const auto& local_point,
-              auto& ret) {
-            const auto& entity = local_f_minus_divergence_of_reconstructed_u.entity();
-            // we need the min_ev for this entity, so we just evaluate in one point
-            const auto center = entity.geometry().local(entity.geometry().center());
-            auto diffusion = kappa_.local_function(entity)->evaluate(center);
-            diffusion *= lambda_.local_function(entity)->evaluate(center);
-            const auto min_ev = XT::LA::make_eigen_solver(
-                                    diffusion, XT::Common::Configuration{{"assert_positive_eigenvalues"}, {1e-15}})
-                                    .min_eigenvalues(1)
-                                    .at(0);
-            const auto h = XT::Grid::entity_diameter(entity);
-            ret[0][0] = (poincare_constant_ / min_ev) * h * h
-                        * local_f_minus_divergence_of_reconstructed_u.evaluate(local_point).at(0)[0]
-                        * local_f_minus_divergence_of_reconstructed_v.evaluate(local_point).at(0)[0];
-          })
-  {
-    this->append(local_product_);
-  }
-
-private:
-  const ScalarFunctionType& lambda_;
-  const TensorFunctionType& kappa_;
-  const double poincare_constant_;
-  const size_t over_integrate_;
-  const LocalProductType local_product_;
-}; // class ResidualProduct
-
-
-template <class ProductGridLayer, class ReconstructionGridLayer>
-class DiffusiveFluxProduct
-    : public LocalizableProductBase<ProductGridLayer,
-                                    XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<ProductGridLayer>,
-                                                                         typename ProductGridLayer::ctype,
-                                                                         ProductGridLayer::dimension,
-                                                                         double,
-                                                                         1>>
-{
-  static_assert(XT::Grid::is_layer<ProductGridLayer>::value, "");
-  static_assert(XT::Grid::is_layer<ReconstructionGridLayer>::value, "");
-  typedef XT::Grid::extract_entity_t<ProductGridLayer> E;
-  static_assert(std::is_same<XT::Grid::extract_entity_t<ReconstructionGridLayer>, E>::value, "");
-  typedef typename ProductGridLayer::ctype D;
-  static const constexpr size_t d = ProductGridLayer::dimension;
-  typedef double R;
-  typedef DiffusiveFluxProduct<ProductGridLayer, ReconstructionGridLayer> ThisType;
-
-public:
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, 1> ScalarFunctionType;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, d, d> TensorFunctionType;
-
-private:
-  typedef RaviartThomasSpace<ReconstructionGridLayer, 0, R> RtSpaceType;
-  typedef DiscreteFunction<RtSpaceType> FluxReconstructionType;
-  typedef LocalizableProductBase<ProductGridLayer, XT::Functions::GridFunctionInterface<E, D, d, R, 1>> BaseType;
-  typedef LocalVolumeIntegralOperator<LocalLambdaBinaryVolumeIntegrand<E>,
-                                      typename ScalarFunctionType::LocalfunctionType>
-      LocalProductType;
-
-public:
-  DiffusiveFluxProduct(ProductGridLayer product_grid_layer,
-                       ReconstructionGridLayer reconstruction_grid_layer,
-                       const ScalarFunctionType& lambda,
-                       const TensorFunctionType& kappa,
-                       const ScalarFunctionType& u,
-                       const ScalarFunctionType& v,
-                       const size_t over_integrate = 2)
-    : BaseType(product_grid_layer, u, v)
-    , lambda_(lambda)
-    , kappa_(kappa)
-    , rt_space_(reconstruction_grid_layer)
-    , reconstructed_u_(rt_space_)
-    , reconstructed_v_(rt_space_)
-    , over_integrate_(over_integrate)
-    , local_product_(
-          // the order lambda
-          [&](const auto& local_u, const auto& local_v) {
-            const auto& entity = local_u.entity();
-            const size_t diffusion_order =
-                lambda_.local_function(entity)->order() + kappa_.local_function(entity)->order();
-            return 3 * diffusion_order + size_t(std::max(ssize_t(local_u.order()) - 1, ssize_t(0)))
-                   + size_t(std::max(ssize_t(local_v.order()) - 1, ssize_t(0))) + over_integrate_;
-          },
-          // the evaluate lambda
-          [&](const auto& local_u, const auto& local_v, const auto& local_point, auto& ret) {
-            const auto& entity = local_u.entity();
-            XT::Common::FieldMatrix<R, d, d> diffusion = kappa_.local_function(entity)->evaluate(local_point);
-            diffusion *= lambda_.local_function(entity)->evaluate(local_point);
-            XT::Common::FieldMatrix<R, d, d> one_over_diffusion = diffusion;
-            one_over_diffusion.invert(); // there is no documented way to assert that the inversion was successfull
-            const auto grad_u = local_u.jacobian(local_point).at(0)[0];
-            const auto grad_v = local_v.jacobian(local_point).at(0)[0];
-            const auto val_reconstructed_u = reconstructed_u_.local_function(entity)->evaluate(local_point);
-            const auto val_reconstructed_v = reconstructed_v_.local_function(entity)->evaluate(local_point);
-            ret[0][0] = (one_over_diffusion * ((diffusion * grad_u) + val_reconstructed_u)) // clang-format off
-                                            * ((diffusion * grad_v) + val_reconstructed_v); // clang-format on
-          })
-  {
-    DiffusiveFluxReconstructionOperator<ReconstructionGridLayer, ScalarFunctionType, TensorFunctionType>
-        flux_reconstruction(reconstruction_grid_layer, lambda, kappa);
-    flux_reconstruction.apply(u, reconstructed_u_);
-    flux_reconstruction.apply(v, reconstructed_v_);
-    this->append(local_product_);
-  }
-
-  DiffusiveFluxProduct(const ThisType&) = delete;
-  DiffusiveFluxProduct(ThisType&&) = delete;
-
-private:
-  const ScalarFunctionType& lambda_;
-  const TensorFunctionType& kappa_;
-  const RtSpaceType rt_space_;
-  FluxReconstructionType reconstructed_u_;
-  FluxReconstructionType reconstructed_v_;
-  const size_t over_integrate_;
-  const LocalProductType local_product_;
-}; // class DiffusiveFluxProduct
-
-
-} // namespace ESV2007
-} // namespace GDT
-} // namespace Dune
-
-#endif // 0
-
-#endif // DUNE_GDT_PLAYGROUND_OPERATORS_RS2017_HH
diff --git a/python/dune/gdt/playground/operators/OS2015.cc b/python/dune/gdt/playground/operators/OS2015.cc
deleted file mode 100644
index 4f2be0ec68f68c9918466b51e0c916cae0056d39..0000000000000000000000000000000000000000
--- a/python/dune/gdt/playground/operators/OS2015.cc
+++ /dev/null
@@ -1,379 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  include <memory>
-
-#  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/gdt/shared.hh>
-#  include <dune/xt/common/numeric_cast.hh>
-#  include <dune/xt/grid/dd/subdomains/grid.hh>
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <python/dune/xt/grid/layers.bindings.hh>
-#  include <dune/xt/grid/gridprovider/provider.hh>
-#  include <dune/xt/grid/walker.hh>
-#  include <dune/xt/grid/type_traits.hh>
-
-#  include <python/dune/gdt/playground/operators/OS2015.hh>
-
-using namespace Dune;
-using XT::Grid::Backends;
-using XT::Grid::Layers;
-namespace py = pybind11;
-
-
-template <class G, Layers layer_type, Backends layer_backend, Layers reconstruction_layer_type = layer_type>
-struct ResidualProduct
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef typename XT::Grid::Layer<G, layer_type, layer_backend, XT::Grid::DD::SubdomainGrid<G>>::type GL;
-  typedef
-      typename XT::Grid::Layer<G, reconstruction_layer_type, Backends::view, XT::Grid::DD::SubdomainGrid<G>>::type RGL;
-
-  typedef GDT::OS2015::ResidualProduct<GL, RGL> type;
-  typedef py::class_<type, XT::Grid::Walker<GL>> bound_type;
-
-  template <bool is_same = (reconstruction_layer_type == layer_type) && (layer_backend == Backends::view),
-            bool anything = true>
-  struct reconstruction_layer_suffix
-  {
-    static std::string value()
-    {
-      return "";
-    }
-  }; // struct reconstruction_layer_suffix<true, ...>
-
-  template <bool anything>
-  struct reconstruction_layer_suffix<false, anything>
-  {
-    static std::string value()
-    {
-      return "_" + XT::Grid::bindings::layer_name<reconstruction_layer_type>::value() + "_"
-             + XT::Grid::bindings::backend_name<Backends::view>::value();
-    }
-  }; // struct reconstruction_layer_suffix<false, ...>
-
-  static std::string class_name()
-  {
-    return "OS2015_residual_product";
-  }
-
-  static std::string layer_suffix()
-  {
-    return XT::Grid::bindings::layer_name<layer_type>::value() + "_"
-           + XT::Grid::bindings::backend_name<layer_backend>::value() + reconstruction_layer_suffix<>::value();
-  }
-
-  template <bool is_dd = (layer_type == Layers::dd_subdomain) || (layer_type == Layers::dd_subdomain_boundary)
-                         || (layer_type == Layers::dd_subdomain_coupling)
-                         || (layer_type == Layers::dd_subdomain_oversampled)
-                         || (reconstruction_layer_type == Layers::dd_subdomain)
-                         || (reconstruction_layer_type == Layers::dd_subdomain_boundary)
-                         || (reconstruction_layer_type == Layers::dd_subdomain_coupling)
-                         || (reconstruction_layer_type == Layers::dd_subdomain_oversampled),
-            bool anything = true>
-  struct factory_method
-  {
-    static void addbind(py::module& m)
-    {
-      using namespace pybind11::literals;
-
-      m.def(std::string("make_" + class_name() + "_" + layer_suffix()).c_str(),
-            [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-               const ssize_t layer_level_or_subdomain,
-               const ssize_t reconstruction_layer_level_or_subdomain,
-               const typename type::ScalarFunctionType& lambda,
-               const typename type::ScalarFunctionType& lambda_hat,
-               const typename type::TensorFunctionType& kappa,
-               const typename type::ScalarFunctionType& f,
-               const typename type::ScalarFunctionType& u,
-               const typename type::ScalarFunctionType& v,
-               const ssize_t over_integrate,
-               const double& poincare_constant) {
-              return new type(dd_grid_provider.template layer<layer_type, layer_backend>(
-                                  XT::Common::numeric_cast<int>(layer_level_or_subdomain)),
-                              dd_grid_provider.template layer<reconstruction_layer_type, Backends::view>(
-                                  XT::Common::numeric_cast<int>(reconstruction_layer_level_or_subdomain)),
-                              lambda,
-                              lambda_hat,
-                              kappa,
-                              f,
-                              u,
-                              v,
-                              poincare_constant,
-                              XT::Common::numeric_cast<size_t>(over_integrate));
-            },
-            "dd_grid_provider"_a,
-            "layer_level"_a = -1,
-            "reconstruction_layer_level"_a = -1,
-            "lambda"_a,
-            "lambda_hat"_a,
-            "kappa"_a,
-            "f"_a,
-            "u"_a,
-            "v"_a,
-            "over_integrate"_a = 2,
-            "poincare_constant"_a = 1.0 / (M_PIl * M_PIl));
-    }
-  }; // struct factory_method<true, ...>
-
-  template <bool anything>
-  struct factory_method<false, anything>
-  {
-    static void addbind(py::module& m)
-    {
-      using namespace pybind11::literals;
-
-      m.def(std::string("make_" + class_name() + "_" + layer_suffix()).c_str(),
-            [](XT::Grid::GridProvider<G>& grid_provider,
-               const ssize_t layer_level,
-               const ssize_t reconstruction_layer_level,
-               const typename type::ScalarFunctionType& lambda,
-               const typename type::ScalarFunctionType& lambda_hat,
-               const typename type::TensorFunctionType& kappa,
-               const typename type::ScalarFunctionType& f,
-               const typename type::ScalarFunctionType& u,
-               const typename type::ScalarFunctionType& v,
-               const ssize_t over_integrate,
-               const double& poincare_constant) {
-              return new type(
-                  grid_provider.template layer<layer_type, layer_backend>(XT::Common::numeric_cast<int>(layer_level)),
-                  grid_provider.template layer<reconstruction_layer_type, Backends::view>(
-                      XT::Common::numeric_cast<int>(reconstruction_layer_level)),
-                  lambda,
-                  lambda_hat,
-                  kappa,
-                  f,
-                  u,
-                  v,
-                  poincare_constant,
-                  XT::Common::numeric_cast<size_t>(over_integrate));
-            },
-            "grid_provider"_a,
-            "layer_level"_a = -1,
-            "reconstruction_layer_level"_a = -1,
-            "lambda"_a,
-            "lambda_hat"_a,
-            "kappa"_a,
-            "f"_a,
-            "u"_a,
-            "v"_a,
-            "over_integrate"_a = 2,
-            "poincare_constant"_a = 1.0 / (M_PIl * M_PIl));
-
-      factory_method<true>::addbind(m);
-    }
-  }; // struct factory_method<false, ...>
-
-  static void bind(py::module& m)
-  {
-    using namespace pybind11::literals;
-
-    try { // we might not be the first ones to add this type
-      bound_type c(m,
-                   XT::Common::to_camel_case(class_name() + "_" + XT::Grid::bindings::grid_name<G>::value() + "_"
-                                             + layer_suffix())
-                       .c_str(),
-                   "OS2015::ResidualProduct");
-      c.def("apply2", [](type& self) { return self.apply2(); });
-      c.def("result", [](type& self) { return self.apply2(); });
-    } catch (std::runtime_error& ee) {
-    }
-
-    factory_method<>::addbind(m);
-  } // ... bind(...)
-}; // struct ResidualProduct
-
-
-template <class G, Layers layer_type, Backends layer_backend, Layers reconstruction_layer_type = layer_type>
-struct DiffusiveFluxProduct
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef typename XT::Grid::Layer<G, layer_type, layer_backend, XT::Grid::DD::SubdomainGrid<G>>::type GL;
-  typedef
-      typename XT::Grid::Layer<G, reconstruction_layer_type, Backends::view, XT::Grid::DD::SubdomainGrid<G>>::type RGL;
-
-  typedef GDT::OS2015::DiffusiveFluxProduct<GL, RGL> type;
-  typedef py::class_<type, XT::Grid::Walker<GL>> bound_type;
-
-  template <bool is_same = (reconstruction_layer_type == layer_type) && (layer_backend == Backends::view),
-            bool anything = true>
-  struct reconstruction_layer_suffix
-  {
-    static std::string value()
-    {
-      return "";
-    }
-  }; // struct reconstruction_layer_suffix<true, ...>
-
-  template <bool anything>
-  struct reconstruction_layer_suffix<false, anything>
-  {
-    static std::string value()
-    {
-      return "_" + XT::Grid::bindings::layer_name<reconstruction_layer_type>::value() + "_"
-             + XT::Grid::bindings::backend_name<Backends::view>::value();
-    }
-  }; // struct reconstruction_layer_suffix<false, ...>
-
-  static std::string class_name()
-  {
-    return "OS2015_diffusive_flux_product";
-  }
-
-  static std::string layer_suffix()
-  {
-    return XT::Grid::bindings::layer_name<layer_type>::value() + "_"
-           + XT::Grid::bindings::backend_name<layer_backend>::value() + reconstruction_layer_suffix<>::value();
-  }
-
-  template <bool is_dd = (layer_type == Layers::dd_subdomain) || (layer_type == Layers::dd_subdomain_boundary)
-                         || (layer_type == Layers::dd_subdomain_coupling)
-                         || (layer_type == Layers::dd_subdomain_oversampled)
-                         || (reconstruction_layer_type == Layers::dd_subdomain)
-                         || (reconstruction_layer_type == Layers::dd_subdomain_boundary)
-                         || (reconstruction_layer_type == Layers::dd_subdomain_coupling)
-                         || (reconstruction_layer_type == Layers::dd_subdomain_oversampled),
-            bool anything = true>
-  struct factory_method
-  {
-    static void addbind(py::module& m)
-    {
-      using namespace pybind11::literals;
-
-      m.def(std::string("make_" + class_name() + "_" + layer_suffix()).c_str(),
-            [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-               const ssize_t layer_level_or_subdomain,
-               const ssize_t reconstruction_layer_level_or_subdomain,
-               const typename type::ScalarFunctionType& lambda,
-               const typename type::ScalarFunctionType& lambda_hat,
-               const typename type::TensorFunctionType& kappa,
-               const typename type::ScalarFunctionType& u,
-               const typename type::ScalarFunctionType& v,
-               const ssize_t over_integrate) {
-              return new type(dd_grid_provider.template layer<layer_type, layer_backend>(
-                                  XT::Common::numeric_cast<int>(layer_level_or_subdomain)),
-                              dd_grid_provider.template layer<reconstruction_layer_type, Backends::view>(
-                                  XT::Common::numeric_cast<int>(reconstruction_layer_level_or_subdomain)),
-                              lambda,
-                              lambda_hat,
-                              kappa,
-                              u,
-                              v,
-                              XT::Common::numeric_cast<size_t>(over_integrate));
-            },
-            "dd_grid_provider"_a,
-            "layer_level"_a = -1,
-            "reconstruction_layer_level"_a = -1,
-            "lambda"_a,
-            "lambda_hat"_a,
-            "kappa"_a,
-            "u"_a,
-            "v"_a,
-            "over_integrate"_a = 2);
-    }
-  }; // struct factory_method<true, ...>
-
-  template <bool anything>
-  struct factory_method<false, anything>
-  {
-    static void addbind(py::module& m)
-    {
-      using namespace pybind11::literals;
-
-      m.def(std::string("make_" + class_name() + "_" + layer_suffix()).c_str(),
-            [](XT::Grid::GridProvider<G>& grid_provider,
-               const ssize_t layer_level,
-               const ssize_t reconstruction_layer_level,
-               const typename type::ScalarFunctionType& lambda,
-               const typename type::ScalarFunctionType& lambda_hat,
-               const typename type::TensorFunctionType& kappa,
-               const typename type::ScalarFunctionType& u,
-               const typename type::ScalarFunctionType& v,
-               const ssize_t over_integrate) {
-              return new type(
-                  grid_provider.template layer<layer_type, layer_backend>(XT::Common::numeric_cast<int>(layer_level)),
-                  grid_provider.template layer<reconstruction_layer_type, Backends::view>(
-                      XT::Common::numeric_cast<int>(reconstruction_layer_level)),
-                  lambda,
-                  lambda_hat,
-                  kappa,
-                  u,
-                  v,
-                  XT::Common::numeric_cast<size_t>(over_integrate));
-            },
-            "grid_provider"_a,
-            "layer_level"_a = -1,
-            "reconstruction_layer_level"_a = -1,
-            "lambda"_a,
-            "lambda_hat"_a,
-            "kappa"_a,
-            "u"_a,
-            "v"_a,
-            "over_integrate"_a = 2);
-
-      factory_method<true>::addbind(m);
-    }
-  }; // struct factory_method<false, ...>
-
-  static void bind(py::module& m)
-  {
-    using namespace pybind11::literals;
-
-    try { // we might not be the first ones to add this type
-      bound_type c(m,
-                   XT::Common::to_camel_case(class_name() + "_" + XT::Grid::bindings::grid_name<G>::value() + "_"
-                                             + layer_suffix())
-                       .c_str(),
-                   "OS2015::DiffusiveFluxProduct");
-      c.def("apply2", [](type& self) { return self.apply2(); });
-      c.def("result", [](type& self) { return self.apply2(); });
-    } catch (std::runtime_error& ee) {
-    }
-
-    factory_method<>::addbind(m);
-  } // ... bind(...)
-}; // struct DiffusiveFluxProduct
-
-
-PYBIND11_MODULE(__operators_OS2015, m)
-{
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-
-#  if HAVE_DUNE_ALUGRID
-  // This is not efficient: we reconstruct on the whole leaf instead of only the neighborhood, but the rt space
-  //                        on a dd_subdomain_oversampled grid view (which is a wrapped part) is broken, if based on
-  //                        a 2d simplex alugrid.
-  ResidualProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::dd_subdomain, Backends::view, Layers::leaf>::bind(m);
-  DiffusiveFluxProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::leaf, Backends::view>::bind(m);
-  DiffusiveFluxProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::leaf, Backends::view>::bind(m);
-  // s.a.
-  DiffusiveFluxProduct<ALU_2D_SIMPLEX_CONFORMING, Layers::dd_subdomain, Backends::view, Layers::leaf>::bind(m);
-#  endif
-
-  add_initialization(m, "dune.gdt.operators.elliptic");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/playground/operators/OS2015.hh b/python/dune/gdt/playground/operators/OS2015.hh
deleted file mode 100644
index b9b368337af7c5fdac5c897d7bbe1f48dfe2e93a..0000000000000000000000000000000000000000
--- a/python/dune/gdt/playground/operators/OS2015.hh
+++ /dev/null
@@ -1,351 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef DUNE_GDT_PLAYGROUND_OPERATORS_OS2015_HH
-#define DUNE_GDT_PLAYGROUND_OPERATORS_OS2015_HH
-
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/common/typetraits.hh>
-
-#  include <dune/geometry/quadraturerules.hh>
-#  include <dune/geometry/referenceelements.hh>
-
-#  include <dune/xt/common/fmatrix.hh>
-#  include <dune/xt/la/container/eigen.hh>
-#  include <dune/xt/la/eigen-solver.hh>
-#  include <dune/xt/grid/boundaryinfo/interfaces.hh>
-#  include <dune/xt/grid/entity.hh>
-#  include <dune/xt/grid/type_traits.hh>
-#  include <dune/xt/functions/derived.hh>
-#  include <dune/xt/functions/interfaces/grid-function.hh>
-
-#  include <dune/gdt/discretefunction/default.hh>
-#  include <dune/gdt/local/operators/integrals.hh>
-#  include <dune/gdt/local/integrands/lambda.hh>
-#  include <dune/gdt/operators/base.hh>
-#  include <dune/gdt/operators/fluxreconstruction.hh>
-#  include <dune/gdt/spaces/rt/default.hh>
-
-namespace Dune {
-namespace GDT {
-namespace OS2015 {
-namespace internal {
-
-
-template <class ProductGridLayer, class ReconstructionGridLayer>
-class ResidualProductBase
-{
-  static_assert(XT::Grid::is_layer<ProductGridLayer>::value, "");
-  static_assert(XT::Grid::is_layer<ReconstructionGridLayer>::value, "");
-
-protected:
-  typedef XT::Grid::extract_entity_t<ProductGridLayer> E;
-  typedef typename ProductGridLayer::ctype D;
-  static const constexpr size_t d = ProductGridLayer::dimension;
-  typedef double R;
-
-private:
-  static_assert(std::is_same<XT::Grid::extract_entity_t<ReconstructionGridLayer>, E>::value, "");
-  typedef ResidualProductBase<ProductGridLayer, ReconstructionGridLayer> ThisType;
-
-public:
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, 1> ScalarFunctionType;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, d, d> TensorFunctionType;
-
-private:
-  typedef RaviartThomasSpace<ReconstructionGridLayer, 0, R> RtSpaceType;
-  typedef DiscreteFunction<RtSpaceType> FluxReconstructionType;
-  typedef XT::Functions::DivergenceFunction<FluxReconstructionType> DivergenceOfFluxReconstructionType;
-  typedef typename ScalarFunctionType::DifferenceType DifferenceType;
-
-public:
-  ResidualProductBase(ReconstructionGridLayer reconstruction_grid_layer,
-                      const ScalarFunctionType& lambda,
-                      const TensorFunctionType& kappa,
-                      const ScalarFunctionType& f,
-                      const ScalarFunctionType& u,
-                      const ScalarFunctionType& v)
-    : f_(f)
-    , rt_space_(reconstruction_grid_layer)
-    , reconstructed_u_(rt_space_)
-    , reconstructed_v_(rt_space_)
-    , divergence_of_reconstructed_u_(reconstructed_u_)
-    , divergence_of_reconstructed_v_(reconstructed_v_)
-    , f_minus_divergence_of_reconstructed_u_(f_ - divergence_of_reconstructed_u_)
-    , f_minus_divergence_of_reconstructed_v_(f_ - divergence_of_reconstructed_v_)
-  {
-    DiffusiveFluxReconstructionOperator<ReconstructionGridLayer,
-                                        ScalarFunctionType,
-                                        TensorFunctionType,
-                                        LocalEllipticIpdgIntegrands::Method::swipdg_affine_factor>
-        flux_reconstruction(reconstruction_grid_layer, lambda, kappa);
-    flux_reconstruction.apply(u, reconstructed_u_);
-    flux_reconstruction.apply(v, reconstructed_v_);
-  }
-
-  ResidualProductBase(const ThisType&) = delete;
-  ResidualProductBase(ThisType&&) = delete;
-
-protected:
-  const ScalarFunctionType& f_;
-  const RtSpaceType rt_space_;
-  FluxReconstructionType reconstructed_u_;
-  FluxReconstructionType reconstructed_v_;
-  const DivergenceOfFluxReconstructionType divergence_of_reconstructed_u_;
-  const DivergenceOfFluxReconstructionType divergence_of_reconstructed_v_;
-  const DifferenceType f_minus_divergence_of_reconstructed_u_;
-  const DifferenceType f_minus_divergence_of_reconstructed_v_;
-}; // class ResidualProductBase
-
-
-} // namespace internal
-
-
-template <class ProductGridLayer, class ReconstructionGridLayer>
-class ResidualProduct
-    : internal::ResidualProductBase<ProductGridLayer, ReconstructionGridLayer>,
-      public LocalizableProductBase<ProductGridLayer,
-                                    XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<ProductGridLayer>,
-                                                                         typename ProductGridLayer::ctype,
-                                                                         ProductGridLayer::dimension,
-                                                                         double,
-                                                                         1>>
-{
-  typedef internal::ResidualProductBase<ProductGridLayer, ReconstructionGridLayer> ResidualProductBaseType;
-  typedef LocalizableProductBase<ProductGridLayer,
-                                 XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<ProductGridLayer>,
-                                                                      typename ProductGridLayer::ctype,
-                                                                      ProductGridLayer::dimension,
-                                                                      double,
-                                                                      1>>
-      LocalizableProductBaseType;
-
-public:
-  using typename ResidualProductBaseType::ScalarFunctionType;
-  using typename ResidualProductBaseType::TensorFunctionType;
-
-private:
-  using typename ResidualProductBaseType::E;
-  using typename ResidualProductBaseType::D;
-  using ResidualProductBaseType::d;
-  using typename ResidualProductBaseType::R;
-  typedef LocalVolumeIntegralOperator<LocalLambdaBinaryVolumeIntegrand<E>,
-                                      typename ScalarFunctionType::LocalfunctionType>
-      LocalProductType;
-
-public:
-  ResidualProduct(ProductGridLayer product_grid_layer,
-                  ReconstructionGridLayer reconstruction_grid_layer,
-                  const ScalarFunctionType& lambda,
-                  const ScalarFunctionType& lambda_hat,
-                  const TensorFunctionType& kappa,
-                  const ScalarFunctionType& f,
-                  const ScalarFunctionType& u,
-                  const ScalarFunctionType& v,
-                  const double& poincare_constant = 1.0 / (M_PIl * M_PIl),
-                  const size_t over_integrate = 2)
-    : ResidualProductBaseType(reconstruction_grid_layer, lambda, kappa, f, u, v)
-    , LocalizableProductBaseType(product_grid_layer,
-                                 this->f_minus_divergence_of_reconstructed_u_,
-                                 this->f_minus_divergence_of_reconstructed_v_)
-    , lambda_(lambda_hat)
-    , kappa_(kappa)
-    , poincare_constant_(poincare_constant)
-    , over_integrate_(over_integrate)
-    , local_product_(
-          // the order lambda
-          [&](const auto& local_f_minus_divergence_of_reconstructed_u,
-              const auto& local_f_minus_divergence_of_reconstructed_v) {
-            return local_f_minus_divergence_of_reconstructed_u.order()
-                   + local_f_minus_divergence_of_reconstructed_v.order() + over_integrate_;
-          },
-          // the evaluate lambda
-          [&](const auto& local_f_minus_divergence_of_reconstructed_u,
-              const auto& local_f_minus_divergence_of_reconstructed_v,
-              const auto& local_point,
-              auto& ret) {
-            ret[0][0] = local_f_minus_divergence_of_reconstructed_u.evaluate(local_point).at(0)[0]
-                        * local_f_minus_divergence_of_reconstructed_v.evaluate(local_point).at(0)[0];
-          })
-    , min_diffusion_ev_(std::numeric_limits<R>::max())
-    , subdomain_vertices_()
-    , finalized_(false)
-  {
-    this->append(local_product_);
-
-    // the functor to collect all grid vertices
-    this->append([&](const E& entity) {
-      for (size_t cc = 0; cc < entity.subEntities(d); ++cc)
-        subdomain_vertices_.emplace_back(entity.template subEntity<d>(cc).geometry().center());
-    });
-
-    // the functor to compute the min eigenvalue of the diffusion
-    this->append([&](const E& entity) {
-      const auto local_lambda = lambda_.local_function(entity);
-      const auto local_kappa = kappa_.local_function(entity);
-      // To find the minimum of a function we evaluate it
-      // * in all quadrature points of a quadrature which would integrate such a function exactly
-      for (const auto& quadrature_point :
-           QuadratureRules<D, d>::rule(entity.type(), local_lambda->order() + local_kappa->order() + over_integrate_)) {
-        const auto xx = quadrature_point.position();
-        auto diffusion = local_kappa->evaluate(xx);
-        diffusion *= local_lambda->evaluate(xx);
-        min_diffusion_ev_ = std::min(
-            min_diffusion_ev_,
-            XT::LA::make_eigen_solver(diffusion, XT::Common::Configuration{{"assert_positive_eigenvalues"}, {1e-15}})
-                .min_eigenvalues(1)
-                .at(0));
-      }
-      // * and in the corners of the gigen entity.
-      const auto& reference_element = ReferenceElements<D, d>::general(entity.type());
-      for (int ii = 0; ii < reference_element.size(d); ++ii) {
-        const auto xx = reference_element.position(ii, d);
-        auto diffusion = local_kappa->evaluate(xx);
-        diffusion *= local_lambda->evaluate(xx);
-        min_diffusion_ev_ = std::min(
-            min_diffusion_ev_,
-            XT::LA::make_eigen_solver(diffusion, XT::Common::Configuration{{"assert_positive_eigenvalues"}, {1e-15}})
-                .min_eigenvalues(1)
-                .at(0));
-      }
-    });
-  } // ResidualProduct(...)
-
-  R apply2()
-  {
-    if (!finalized_) {
-      this->walk();
-      R subdomain_h = std::numeric_limits<R>::min();
-      for (size_t ii = 0; ii < subdomain_vertices_.size(); ++ii)
-        for (size_t jj = ii + 1; jj < subdomain_vertices_.size(); ++jj)
-          subdomain_h = std::max(subdomain_h, (subdomain_vertices_[ii] - subdomain_vertices_[jj]).two_norm());
-      this->result_ *= (poincare_constant_ / min_diffusion_ev_) * subdomain_h * subdomain_h;
-    }
-    return this->result_;
-  }
-
-private:
-  const ScalarFunctionType& lambda_;
-  const TensorFunctionType& kappa_;
-  const double poincare_constant_;
-  const size_t over_integrate_;
-  const LocalProductType local_product_;
-  R min_diffusion_ev_;
-  std::vector<FieldVector<D, d>> subdomain_vertices_;
-  bool finalized_;
-}; // class ResidualProduct
-
-
-template <class ProductGridLayer, class ReconstructionGridLayer>
-class DiffusiveFluxProduct
-    : public LocalizableProductBase<ProductGridLayer,
-                                    XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<ProductGridLayer>,
-                                                                         typename ProductGridLayer::ctype,
-                                                                         ProductGridLayer::dimension,
-                                                                         double,
-                                                                         1>>
-{
-  static_assert(XT::Grid::is_layer<ProductGridLayer>::value, "");
-  static_assert(XT::Grid::is_layer<ReconstructionGridLayer>::value, "");
-  typedef XT::Grid::extract_entity_t<ProductGridLayer> E;
-  static_assert(std::is_same<XT::Grid::extract_entity_t<ReconstructionGridLayer>, E>::value, "");
-  typedef typename ProductGridLayer::ctype D;
-  static const constexpr size_t d = ProductGridLayer::dimension;
-  typedef double R;
-  typedef DiffusiveFluxProduct<ProductGridLayer, ReconstructionGridLayer> ThisType;
-
-public:
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, 1> ScalarFunctionType;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, d, d> TensorFunctionType;
-
-private:
-  typedef RaviartThomasSpace<ReconstructionGridLayer, 0, R> RtSpaceType;
-  typedef DiscreteFunction<RtSpaceType> FluxReconstructionType;
-  typedef LocalizableProductBase<ProductGridLayer, XT::Functions::GridFunctionInterface<E, D, d, R, 1>> BaseType;
-  typedef LocalVolumeIntegralOperator<LocalLambdaBinaryVolumeIntegrand<E>,
-                                      typename ScalarFunctionType::LocalfunctionType>
-      LocalProductType;
-
-public:
-  DiffusiveFluxProduct(ProductGridLayer product_grid_layer,
-                       ReconstructionGridLayer reconstruction_grid_layer,
-                       const ScalarFunctionType& lambda,
-                       const ScalarFunctionType& lambda_hat,
-                       const TensorFunctionType& kappa,
-                       const ScalarFunctionType& u,
-                       const ScalarFunctionType& v,
-                       const size_t over_integrate = 2)
-    : BaseType(product_grid_layer, u, v)
-    , lambda_(lambda)
-    , lambda_hat_(lambda_hat)
-    , kappa_(kappa)
-    , rt_space_(reconstruction_grid_layer)
-    , reconstructed_u_(rt_space_)
-    , reconstructed_v_(rt_space_)
-    , over_integrate_(over_integrate)
-    , local_product_(
-          // the order lambda
-          [&](const auto& local_u, const auto& local_v) {
-            const auto& entity = local_u.entity();
-            const size_t diffusion_order =
-                lambda_.local_function(entity)->order() + kappa_.local_function(entity)->order();
-            return 3 * diffusion_order + size_t(std::max(ssize_t(local_u.order()) - 1, ssize_t(0)))
-                   + size_t(std::max(ssize_t(local_v.order()) - 1, ssize_t(0))) + over_integrate_;
-          },
-          // the evaluate lambda
-          [&](const auto& local_u, const auto& local_v, const auto& local_point, auto& ret) {
-            const auto& entity = local_u.entity();
-            XT::Common::FieldMatrix<R, d, d> diffusion = kappa_.local_function(entity)->evaluate(local_point);
-            XT::Common::FieldMatrix<R, d, d> one_over_diffusion_hat = diffusion;
-            diffusion *= lambda_.local_function(entity)->evaluate(local_point);
-            one_over_diffusion_hat *= lambda_hat_.local_function(entity)->evaluate(local_point);
-            one_over_diffusion_hat.invert(); // there is no documented way to assert that the inversion was successfull
-            const auto grad_u = local_u.jacobian(local_point).at(0)[0];
-            const auto grad_v = local_v.jacobian(local_point).at(0)[0];
-            const auto val_reconstructed_u = reconstructed_u_.local_function(entity)->evaluate(local_point);
-            const auto val_reconstructed_v = reconstructed_v_.local_function(entity)->evaluate(local_point);
-            ret[0][0] = (one_over_diffusion_hat * ((diffusion * grad_u) + val_reconstructed_u)) // clang-format off
-                                                * ((diffusion * grad_v) + val_reconstructed_v); // clang-format on
-          })
-  {
-    DiffusiveFluxReconstructionOperator<ReconstructionGridLayer,
-                                        ScalarFunctionType,
-                                        TensorFunctionType,
-                                        LocalEllipticIpdgIntegrands::Method::swipdg_affine_factor>
-        flux_reconstruction(reconstruction_grid_layer, lambda, kappa);
-    flux_reconstruction.apply(u, reconstructed_u_);
-    flux_reconstruction.apply(v, reconstructed_v_);
-    this->append(local_product_);
-  }
-
-  DiffusiveFluxProduct(const ThisType&) = delete;
-  DiffusiveFluxProduct(ThisType&&) = delete;
-
-private:
-  const ScalarFunctionType& lambda_;
-  const ScalarFunctionType& lambda_hat_;
-  const TensorFunctionType& kappa_;
-  const RtSpaceType rt_space_;
-  FluxReconstructionType reconstructed_u_;
-  FluxReconstructionType reconstructed_v_;
-  const size_t over_integrate_;
-  const LocalProductType local_product_;
-}; // class DiffusiveFluxProduct
-
-
-} // namespace OS2015
-} // namespace GDT
-} // namespace Dune
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // DUNE_GDT_PLAYGROUND_OPERATORS_OS2015_HH
diff --git a/python/dune/gdt/playground/operators/RS2017.cc b/python/dune/gdt/playground/operators/RS2017.cc
deleted file mode 100644
index 64074df779c3c94401309505e2c4d913b77469e7..0000000000000000000000000000000000000000
--- a/python/dune/gdt/playground/operators/RS2017.cc
+++ /dev/null
@@ -1,204 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_ALUGRID && HAVE_DUNE_PYBINDXI
-
-#  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/gdt/shared.hh>
-
-#  include <dune/xt/la/eigen-solver.hh>
-
-#  include <dune/gdt/operators/elliptic.hh>
-
-#  include <python/dune/gdt/playground/operators/RS2017.hh>
-
-using namespace Dune;
-using namespace Dune::GDT::RS2017;
-using XT::Grid::Backends;
-using XT::Grid::Layers;
-namespace py = pybind11;
-
-
-PYBIND11_MODULE(__operators_RS2017, m)
-{
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-
-  SwipdgPenaltySubdomainProduct<ALU_2D_SIMPLEX_CONFORMING>::bind(m);
-  HdivSemiProduct<ALU_2D_SIMPLEX_CONFORMING>::bind(m);
-  DiffusiveFluxAaProduct<ALU_2D_SIMPLEX_CONFORMING>::bind(m);
-  DiffusiveFluxAbProduct<ALU_2D_SIMPLEX_CONFORMING>::bind(m);
-  DiffusiveFluxBbProduct<ALU_2D_SIMPLEX_CONFORMING>::bind(m);
-  ResidualPartFunctional<ALU_2D_SIMPLEX_CONFORMING>::bind(m);
-
-  bind_neighborhood_reconstruction<ALU_2D_SIMPLEX_CONFORMING>(m);
-  bind_neighborhood_discretization<ALU_2D_SIMPLEX_CONFORMING>(m);
-
-  typedef typename ALU_2D_SIMPLEX_CONFORMING::template Codim<0>::Entity E;
-  typedef double D;
-  static const constexpr size_t d = 2;
-  typedef double R;
-
-  typedef XT::LA::IstlDenseVector<R> V;
-  typedef XT::LA::IstlRowMajorSparseMatrix<R> M;
-
-  m.def("RS2017_residual_indicator_min_diffusion_eigenvalue",
-        [](XT::Grid::GridProvider<ALU_2D_SIMPLEX_CONFORMING, XT::Grid::DD::SubdomainGrid<ALU_2D_SIMPLEX_CONFORMING>>&
-               dd_grid_provider,
-           const ssize_t subdomain,
-           const XT::Functions::GridFunctionInterface<E, D, d, R, 1>& lambda,
-           const XT::Functions::GridFunctionInterface<E, D, d, R, d, d>& kappa,
-           const ssize_t over_int) {
-          py::gil_scoped_release DUNE_UNUSED(release);
-          const auto over_integrate = XT::Common::numeric_cast<size_t>(over_int);
-          auto subdomain_layer = dd_grid_provider.template layer<Layers::dd_subdomain, Backends::view>(
-              XT::Common::numeric_cast<size_t>(subdomain));
-          typedef decltype(subdomain_layer) GL;
-          XT::Grid::Walker<GL> walker(subdomain_layer);
-          double min_ev = std::numeric_limits<double>::max();
-          walker.append([&](const E& entity) {
-            const auto local_lambda = lambda.local_function(entity);
-            const auto local_kappa = kappa.local_function(entity);
-            // To find the minimum of a function we evaluate it
-            // * in all quadrature points of a quadrature which would integrate such a function exactly
-            for (const auto& quadrature_point : QuadratureRules<D, d>::rule(
-                     entity.type(), local_lambda->order() + local_kappa->order() + over_integrate)) {
-              const auto xx = quadrature_point.position();
-              auto diffusion = local_kappa->evaluate(xx);
-              diffusion *= local_lambda->evaluate(xx);
-              min_ev = std::min(min_ev,
-                                XT::LA::make_eigen_solver(
-                                    diffusion, XT::Common::Configuration{{"assert_positive_eigenvalues"}, {1e-15}})
-                                    .min_eigenvalues(1)
-                                    .at(0));
-            }
-            // * and in the corners of the gigen entity.
-            const auto& reference_element = ReferenceElements<D, d>::general(entity.type());
-            for (int ii = 0; ii < reference_element.size(d); ++ii) {
-              const auto xx = reference_element.position(ii, d);
-              auto diffusion = local_kappa->evaluate(xx);
-              diffusion *= local_lambda->evaluate(xx);
-              min_ev = std::min(min_ev,
-                                XT::LA::make_eigen_solver(
-                                    diffusion, XT::Common::Configuration{{"assert_positive_eigenvalues"}, {1e-15}})
-                                    .min_eigenvalues(1)
-                                    .at(0));
-            }
-          });
-          walker.walk();
-          return min_ev;
-        },
-        "dd_grid_provider"_a,
-        "subdomain"_a,
-        "lambda"_a,
-        "kappa"_a,
-        "over_integrate"_a = 2);
-  m.def("RS2017_residual_indicator_subdomain_diameter",
-        [](XT::Grid::GridProvider<ALU_2D_SIMPLEX_CONFORMING, XT::Grid::DD::SubdomainGrid<ALU_2D_SIMPLEX_CONFORMING>>&
-               dd_grid_provider,
-           const ssize_t subdomain) {
-          py::gil_scoped_release DUNE_UNUSED(release);
-          auto subdomain_layer = dd_grid_provider.template layer<Layers::dd_subdomain, Backends::view>(
-              XT::Common::numeric_cast<size_t>(subdomain));
-          typedef decltype(subdomain_layer) GL;
-          XT::Grid::Walker<GL> walker(subdomain_layer);
-          std::vector<FieldVector<D, d>> subdomain_vertices;
-          walker.append([&](const E& entity) {
-            for (size_t cc = 0; cc < entity.subEntities(d); ++cc)
-              subdomain_vertices.emplace_back(entity.template subEntity<d>(cc).geometry().center());
-          });
-          walker.walk();
-          R subdomain_h = std::numeric_limits<R>::min();
-          for (size_t ii = 0; ii < subdomain_vertices.size(); ++ii)
-            for (size_t jj = ii + 1; jj < subdomain_vertices.size(); ++jj)
-              subdomain_h = std::max(subdomain_h, (subdomain_vertices[ii] - subdomain_vertices[jj]).two_norm());
-          return subdomain_h;
-        },
-        "dd_grid_provider"_a,
-        "subdomain"_a);
-  m.def("RS2017_apply_l2_product",
-        [](XT::Grid::GridProvider<ALU_2D_SIMPLEX_CONFORMING, XT::Grid::DD::SubdomainGrid<ALU_2D_SIMPLEX_CONFORMING>>&
-               dd_grid_provider,
-           const ssize_t subdomain,
-           const XT::Functions::GridFunctionInterface<E, D, d, R, 1>& u,
-           const XT::Functions::GridFunctionInterface<E, D, d, R, 1>& v,
-           const ssize_t over_integrate) {
-          py::gil_scoped_release DUNE_UNUSED(release);
-          return GDT::make_l2_operator(dd_grid_provider.template layer<Layers::dd_subdomain, Backends::view>(
-                                           XT::Common::numeric_cast<size_t>(subdomain)),
-                                       XT::Common::numeric_cast<size_t>(over_integrate))
-              ->apply2(u, v);
-        },
-        "dd_grid_provider"_a,
-        "subdomain"_a,
-        "u"_a,
-        "v"_a,
-        "over_integrate"_a = 2);
-
-  typedef GDT::EllipticMatrixOperator<
-      XT::Functions::GridFunctionInterface<E, D, d, R, 1>,
-      XT::Functions::GridFunctionInterface<E, D, d, R, d, d>,
-      typename GDT::SpaceProvider<ALU_2D_SIMPLEX_CONFORMING,
-                                  Layers::dd_subdomain,
-                                  GDT::SpaceType::dg,
-                                  GDT::Backends::gdt,
-                                  1,
-                                  double,
-                                  1>::type,
-      XT::LA::IstlRowMajorSparseMatrix<double>,
-      typename XT::Grid::Layer<ALU_2D_SIMPLEX_CONFORMING, Layers::dd_subdomain, Backends::view>::type>
-      EllipticMatrixOperatorType;
-  try { // we might not be the first to add this
-    py::class_<EllipticMatrixOperatorType,
-               GDT::SystemAssembler<typename EllipticMatrixOperatorType::SourceSpaceType,
-                                    typename EllipticMatrixOperatorType::GridLayerType>>
-        elliptic_matrix_operator(m, "EllipticMatrixOperatorNeighborhood");
-    elliptic_matrix_operator.def("matrix", [](EllipticMatrixOperatorType& self) { return self.matrix(); });
-  } catch (std::runtime_error&) {
-  }
-  m.def("RS2017_make_elliptic_matrix_operator_on_subdomain",
-        [](XT::Grid::GridProvider<ALU_2D_SIMPLEX_CONFORMING, XT::Grid::DD::SubdomainGrid<ALU_2D_SIMPLEX_CONFORMING>>&
-               dd_grid_provider,
-           const ssize_t subdomain,
-           const typename EllipticMatrixOperatorType::SourceSpaceType& space,
-           const XT::Functions::GridFunctionInterface<E, D, d, R, 1>& lambda,
-           const XT::Functions::GridFunctionInterface<E, D, d, R, d, d>& kappa,
-           const ssize_t over_integrate) {
-          return new EllipticMatrixOperatorType(XT::Common::numeric_cast<ssize_t>(over_integrate),
-                                                lambda,
-                                                kappa,
-                                                space,
-                                                dd_grid_provider.template layer<Layers::dd_subdomain, Backends::view>(
-                                                    XT::Common::numeric_cast<int>(subdomain)));
-        },
-        "dd_grid_provider"_a,
-        "subdomain"_a,
-        "space"_a,
-        "lambda"_a,
-        "kappa"_a,
-        "over_integrate"_a = 2);
-
-  add_initialization(m, "dune.gdt.operators.elliptic");
-}
-
-#endif // HAVE_DUNE_ALUGRID && HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/playground/operators/RS2017.hh b/python/dune/gdt/playground/operators/RS2017.hh
deleted file mode 100644
index 3992a5e2bd7c7d3ef46212f113d19295869becf2..0000000000000000000000000000000000000000
--- a/python/dune/gdt/playground/operators/RS2017.hh
+++ /dev/null
@@ -1,1311 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef DUNE_GDT_PLAYGROUND_OPERATORS_RS2017_HH
-#define DUNE_GDT_PLAYGROUND_OPERATORS_RS2017_HH
-
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-#  include <type_traits>
-
-#  include <dune/common/typetraits.hh>
-
-#  include <dune/pybindxi/pybind11.h>
-#  include <dune/pybindxi/stl.h>
-
-#  include <dune/xt/common/numeric_cast.hh>
-#  include <dune/xt/la/container/istl.hh>
-#  include <dune/xt/grid/dd/subdomains/grid.hh>
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <dune/xt/grid/layers.hh>
-#  include <dune/xt/grid/type_traits.hh>
-#  include <dune/xt/functions/interfaces/grid-function.hh>
-
-#  include <python/dune/gdt/assembler/system.hh>
-#  include <dune/gdt/functionals/elliptic-ipdg.hh>
-#  include <dune/gdt/functionals/l2.hh>
-#  include <dune/gdt/local/integrands/elliptic.hh>
-#  include <dune/gdt/local/integrands/elliptic-ipdg.hh>
-#  include <dune/gdt/local/integrands/lambda.hh>
-#  include <dune/gdt/local/operators/integrals.hh>
-#  include <dune/gdt/operators/base.hh>
-#  include <python/dune/gdt/operators/base.hh>
-#  include <dune/gdt/operators/elliptic-ipdg.hh>
-#  include <dune/gdt/operators/fluxreconstruction.hh>
-#  include <dune/gdt/operators/l2.hh>
-#  include <dune/gdt/spaces.hh>
-
-namespace Dune {
-namespace GDT {
-namespace RS2017 {
-
-#  if HAVE_DUNE_ISTL
-
-
-template <class G>
-class DiffusiveFluxAaProduct
-    : public GDT::MatrixOperatorBase<XT::LA::IstlRowMajorSparseMatrix<double>,
-                                     typename GDT::SpaceProvider<G,
-                                                                 XT::Grid::Layers::dd_subdomain,
-                                                                 GDT::SpaceType::dg,
-                                                                 GDT::Backends::gdt,
-                                                                 1,
-                                                                 double,
-                                                                 1>::type,
-                                     typename XT::Grid::Layer<G,
-                                                              XT::Grid::Layers::dd_subdomain,
-                                                              XT::Grid::Backends::view,
-                                                              XT::Grid::DD::SubdomainGrid<G>>::type>
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef typename GDT::
-      SpaceProvider<G, XT::Grid::Layers::dd_subdomain, GDT::SpaceType::dg, GDT::Backends::gdt, 1, double, 1>
-          SP;
-  typedef GDT::MatrixOperatorBase<XT::LA::IstlRowMajorSparseMatrix<double>,
-                                  typename SP::type,
-                                  typename XT::Grid::Layer<G,
-                                                           XT::Grid::Layers::dd_subdomain,
-                                                           XT::Grid::Backends::view,
-                                                           XT::Grid::DD::SubdomainGrid<G>>::type>
-      BaseType;
-  typedef DiffusiveFluxAaProduct<G> ThisType;
-
-public:
-  using typename BaseType::GridLayerType;
-  using typename BaseType::RangeSpaceType;
-
-  typedef XT::Grid::extract_entity_t<GridLayerType> E;
-  typedef XT::Grid::extract_intersection_t<GridLayerType> I;
-  typedef typename G::ctype D;
-  static const constexpr size_t d = G::dimension;
-  typedef double R;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, 1> ScalarFunctionType;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, d, d> TensorFunctionType;
-  typedef typename RangeSpaceType::BaseFunctionSetType BasisType;
-
-  static void bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    GDT::bindings::MatrixOperatorBase<ThisType>::bind(
-        m,
-        XT::Common::to_camel_case("RS2017_diffusive_flux_aa_product_matrix_operator_subdomain_"
-                                  + XT::Grid::bindings::grid_name<G>::value())
-            .c_str());
-
-    m.def("RS2017_make_diffusive_flux_aa_product_matrix_operator_on_subdomain",
-          [](const XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-             const ssize_t subdomain,
-             const RangeSpaceType& space,
-             const ScalarFunctionType& lambda_hat,
-             const ScalarFunctionType& lambda_u,
-             const ScalarFunctionType& lambda_v,
-             const TensorFunctionType& kappa,
-             const size_t over_integrate) {
-            return new ThisType(
-                space,
-                dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain, XT::Grid::Backends::view>(
-                    XT::Common::numeric_cast<size_t>(subdomain)),
-                lambda_hat,
-                lambda_u,
-                lambda_v,
-                kappa,
-                over_integrate);
-          },
-          "dd_grid_provider"_a,
-          "subdomain"_a,
-          "space"_a,
-          "lambda_hat"_a,
-          "lambda_u"_a,
-          "lambda_v"_a,
-          "kappa"_a,
-          "over_integrate"_a = 2);
-  } // ... bind(...)
-
-  DiffusiveFluxAaProduct(RangeSpaceType space,
-                         GridLayerType grd_lyr,
-                         const ScalarFunctionType& lambda_hat,
-                         const ScalarFunctionType& lambda_u,
-                         const ScalarFunctionType& lambda_v,
-                         const TensorFunctionType& kappa,
-                         const size_t over_integrate = 2)
-    : BaseType(space, grd_lyr)
-    , unit_matrix_(XT::Common::from_string<XT::Common::FieldMatrix<D, d, d>>("[1 0 0; 0 1 0; 0 0 1]"))
-    , over_integrate_(over_integrate)
-    , local_operator_(
-          [&](const auto& test_base, const auto& ansatz_base) {
-            const auto& entity = test_base.entity();
-            const auto local_lambda_hat = lambda_hat.local_function(entity);
-            const auto local_lambda_u = lambda_u.local_function(entity);
-            const auto local_lambda_v = lambda_v.local_function(entity);
-            const auto local_kappa = kappa.local_function(entity);
-            const auto integrand_order = local_lambda_hat->order() + local_lambda_u->order() + local_lambda_v->order()
-                                         + 3 * local_kappa->order()
-                                         + size_t(std::max(ssize_t(test_base.order()) - 1, ssize_t(0)))
-                                         + size_t(std::max(ssize_t(ansatz_base.order()) - 1, ssize_t(0)));
-            return integrand_order + over_integrate_;
-          },
-          [&](const auto& test_base, const auto& ansatz_base, const auto& local_point, auto& ret) {
-            const auto& entity = test_base.entity();
-            const auto local_lambda_hat = lambda_hat.local_function(entity);
-            const auto local_lambda_u = lambda_u.local_function(entity);
-            const auto local_lambda_v = lambda_v.local_function(entity);
-            const auto local_kappa = kappa.local_function(entity);
-            XT::Common::FieldMatrix<D, d, d> diffusion_hat_inverse = local_kappa->evaluate(local_point);
-            XT::Common::FieldMatrix<D, d, d> diffusion_u = diffusion_hat_inverse;
-            XT::Common::FieldMatrix<D, d, d> diffusion_v = diffusion_hat_inverse;
-            diffusion_hat_inverse *= local_lambda_hat->evaluate(local_point);
-#    ifndef NDEBUG
-            const auto diffusion_hat = diffusion_hat_inverse;
-#    endif
-            diffusion_hat_inverse.invert();
-#    ifndef NDEBUG
-            // there is no documented way to tell if the inversion was successfull
-            if (XT::Common::FloatCmp::ne(diffusion_hat_inverse * diffusion_hat, unit_matrix_))
-              DUNE_THROW(XT::Common::Exceptions::internal_error,
-                         "Local inversion of lambda_hat*kappa failed!\n\nx = "
-                             << local_point
-                             << "\nlocal_lambda_hat(x) = "
-                             << local_lambda_hat->evaluate(local_point)
-                             << "\nlocal_kappa(x) = "
-                             << local_kappa->evaluate(local_point)
-                             << "\ninverse = "
-                             << diffusion_hat_inverse
-                             << "\ninverse * (local_lambda_hat(x)*local_kappa(x))) = "
-                             << diffusion_hat_inverse * diffusion_hat);
-#    endif
-            diffusion_u *= local_lambda_u->evaluate(local_point);
-            diffusion_v *= local_lambda_v->evaluate(local_point);
-            const auto test_grads = test_base.jacobian(local_point);
-            const auto ansatz_grads = ansatz_base.jacobian(local_point);
-            ret *= 0.;
-            for (size_t ii = 0; ii < test_base.size(); ++ii)
-              for (size_t jj = 0; jj < ansatz_base.size(); ++jj)
-                ret[ii][jj] =
-                    ((diffusion_hat_inverse * (diffusion_u * ansatz_grads[jj][0])) * (diffusion_v * test_grads[ii][0]));
-          })
-  {
-    this->append(local_operator_);
-  }
-
-  DiffusiveFluxAaProduct(const ThisType&) = delete;
-  DiffusiveFluxAaProduct(ThisType&&) = delete;
-
-private:
-  const XT::Common::FieldMatrix<D, d, d> unit_matrix_;
-  const size_t over_integrate_;
-  const GDT::LocalVolumeIntegralOperator<GDT::LocalLambdaBinaryVolumeIntegrand<E, R, 1>, BasisType> local_operator_;
-}; // class DiffusiveFluxAaProduct
-
-
-template <class G>
-class DiffusiveFluxAbProduct
-    : public GDT::
-          MatrixOperatorBase<XT::LA::IstlRowMajorSparseMatrix<double>,
-                             typename GDT::SpaceProvider<G,
-                                                         XT::Grid::Layers::dd_subdomain,
-                                                         GDT::SpaceType::dg,
-                                                         GDT::Backends::gdt,
-                                                         1,
-                                                         double,
-                                                         1>::type,
-                             typename XT::Grid::Layer<G,
-                                                      XT::Grid::Layers::dd_subdomain,
-                                                      XT::Grid::Backends::view,
-                                                      XT::Grid::DD::SubdomainGrid<G>>::type,
-                             GDT::RestrictedSpace<typename GDT::SpaceProvider<G,
-                                                                              XT::Grid::Layers::leaf,
-                                                                              GDT::SpaceType::rt,
-                                                                              GDT::Backends::gdt,
-                                                                              0,
-                                                                              double,
-                                                                              G::dimension>::type,
-                                                  typename XT::Grid::Layer<G,
-                                                                           XT::Grid::Layers::dd_subdomain,
-                                                                           XT::Grid::Backends::view,
-                                                                           XT::Grid::DD::SubdomainGrid<G>>::type>>
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef GDT::MatrixOperatorBase<XT::LA::IstlRowMajorSparseMatrix<double>,
-                                  typename GDT::SpaceProvider<G,
-                                                              XT::Grid::Layers::dd_subdomain,
-                                                              GDT::SpaceType::dg,
-                                                              GDT::Backends::gdt,
-                                                              1,
-                                                              double,
-                                                              1>::type,
-                                  typename XT::Grid::Layer<G,
-                                                           XT::Grid::Layers::dd_subdomain,
-                                                           XT::Grid::Backends::view,
-                                                           XT::Grid::DD::SubdomainGrid<G>>::type,
-                                  GDT::RestrictedSpace<typename GDT::SpaceProvider<G,
-                                                                                   XT::Grid::Layers::leaf,
-                                                                                   GDT::SpaceType::rt,
-                                                                                   GDT::Backends::gdt,
-                                                                                   0,
-                                                                                   double,
-                                                                                   G::dimension>::type,
-                                                       typename XT::Grid::Layer<G,
-                                                                                XT::Grid::Layers::dd_subdomain,
-                                                                                XT::Grid::Backends::view,
-                                                                                XT::Grid::DD::SubdomainGrid<G>>::type>>
-      BaseType;
-  typedef DiffusiveFluxAbProduct<G> ThisType;
-
-public:
-  using typename BaseType::GridLayerType;
-  using typename BaseType::RangeSpaceType;
-  using typename BaseType::RangeBaseType;
-  using typename BaseType::SourceSpaceType;
-  using typename BaseType::SourceBaseType;
-
-  typedef XT::Grid::extract_entity_t<GridLayerType> E;
-  typedef XT::Grid::extract_intersection_t<GridLayerType> I;
-  typedef typename G::ctype D;
-  static const constexpr size_t d = G::dimension;
-  typedef double R;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, 1> ScalarFunctionType;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, d, d> TensorFunctionType;
-
-  static void bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    py::class_<ThisType, XT::Grid::Walker<GridLayerType>> c(
-        m,
-        XT::Common::to_camel_case("RS2017_diffusive_flux_ab_product_matrix_operator_subdomain_"
-                                  + XT::Grid::bindings::grid_name<G>::value())
-            .c_str());
-    c.def("assemble", [](ThisType& self) { self.assemble(); });
-    c.def("matrix", [](ThisType& self) { return self.matrix(); });
-
-    m.def("RS2017_make_diffusive_flux_ab_product_matrix_operator_on_subdomain",
-          [](const XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-             const ssize_t subdomain,
-             const RangeSpaceType& range_space,
-             const SourceSpaceType& source_space,
-             const ScalarFunctionType& lambda_hat,
-             const ScalarFunctionType& lambda_range,
-             const TensorFunctionType& kappa,
-             const size_t over_integrate) {
-            return new ThisType(
-                range_space,
-                source_space,
-                dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain, XT::Grid::Backends::view>(
-                    XT::Common::numeric_cast<size_t>(subdomain)),
-                lambda_hat,
-                lambda_range,
-                kappa,
-                over_integrate);
-          },
-          "dd_grid_provider"_a,
-          "subdomain"_a,
-          "range_space"_a,
-          "source_space"_a,
-          "lambda_hat"_a,
-          "lambda_range"_a,
-          "kappa"_a,
-          "over_integrate"_a = 2);
-  } // ... bind(...)
-
-  DiffusiveFluxAbProduct(RangeSpaceType range_space,
-                         SourceSpaceType source_space,
-                         GridLayerType grd_lyr,
-                         const ScalarFunctionType& lambda_hat,
-                         const ScalarFunctionType& lambda_range,
-                         const TensorFunctionType& kappa,
-                         const size_t over_integrate = 2)
-    : BaseType(range_space, source_space, grd_lyr)
-    , unit_matrix_(XT::Common::from_string<XT::Common::FieldMatrix<D, d, d>>("[1 0 0; 0 1 0; 0 0 1]"))
-    , over_integrate_(over_integrate)
-    , local_operator_(
-          [&](const auto& test_base, const auto& ansatz_base) {
-            const auto& entity = test_base.entity();
-            const auto local_lambda_hat = lambda_hat.local_function(entity);
-            const auto local_lambda_range = lambda_range.local_function(entity);
-            const auto local_kappa = kappa.local_function(entity);
-            const auto integrand_order =
-                local_lambda_hat->order() + local_lambda_range->order() + 2 * local_kappa->order()
-                + size_t(std::max(ssize_t(test_base.order()) - 1, ssize_t(0))) + ansatz_base.order();
-            return integrand_order + over_integrate_;
-          },
-          [&](const auto& test_base, const auto& ansatz_base, const auto& local_point, auto& ret) {
-            const auto& entity = test_base.entity();
-            const auto local_lambda_hat = lambda_hat.local_function(entity);
-            const auto local_lambda_range = lambda_range.local_function(entity);
-            const auto local_kappa = kappa.local_function(entity);
-            XT::Common::FieldMatrix<D, d, d> diffusion_hat_inverse = local_kappa->evaluate(local_point);
-            XT::Common::FieldMatrix<D, d, d> diffusion_range = diffusion_hat_inverse;
-            diffusion_hat_inverse *= local_lambda_hat->evaluate(local_point);
-#    ifndef NDEBUG
-            const auto diffusion_hat = diffusion_hat_inverse;
-#    endif
-            diffusion_hat_inverse.invert();
-#    ifndef NDEBUG
-            // there is no documented way to tell if the inversion was successfull
-            if (XT::Common::FloatCmp::ne(diffusion_hat_inverse * diffusion_hat, unit_matrix_))
-              DUNE_THROW(XT::Common::Exceptions::internal_error,
-                         "Local inversion of lambda_hat*kappa failed!\n\nx = "
-                             << local_point
-                             << "\nlocal_lambda_hat(x) = "
-                             << local_lambda_hat->evaluate(local_point)
-                             << "\nlocal_kappa(x) = "
-                             << local_kappa->evaluate(local_point)
-                             << "\ninverse = "
-                             << diffusion_hat_inverse
-                             << "\ninverse * (local_lambda_hat(x)*local_kappa(x))) = "
-                             << diffusion_hat_inverse * diffusion_hat);
-#    endif
-            diffusion_range *= local_lambda_range->evaluate(local_point);
-            const auto test_grads = test_base.jacobian(local_point);
-            const auto ansatz_values = ansatz_base.evaluate(local_point);
-            ret *= 0.;
-            for (size_t ii = 0; ii < test_base.size(); ++ii)
-              for (size_t jj = 0; jj < ansatz_base.size(); ++jj)
-                ret[ii][jj] = ((diffusion_hat_inverse * (diffusion_range * test_grads[ii][0])) * (ansatz_values[jj]));
-          })
-  {
-    this->append(local_operator_);
-  }
-
-  DiffusiveFluxAbProduct(const ThisType&) = delete;
-  DiffusiveFluxAbProduct(ThisType&&) = delete;
-
-private:
-  const XT::Common::FieldMatrix<D, d, d> unit_matrix_;
-  const size_t over_integrate_;
-  const GDT::LocalVolumeIntegralOperator<GDT::LocalLambdaBinaryVolumeIntegrand<E, R, 1, 1, d, 1>,
-                                         RangeBaseType,
-                                         SourceBaseType>
-      local_operator_;
-}; // class DiffusiveFluxAbProduct
-
-
-template <class G>
-class DiffusiveFluxBbProduct
-    : public GDT::
-          MatrixOperatorBase<XT::LA::IstlRowMajorSparseMatrix<double>,
-                             GDT::RestrictedSpace<typename GDT::SpaceProvider<G,
-                                                                              XT::Grid::Layers::leaf,
-                                                                              GDT::SpaceType::rt,
-                                                                              GDT::Backends::gdt,
-                                                                              0,
-                                                                              double,
-                                                                              G::dimension>::type,
-                                                  typename XT::Grid::Layer<G,
-                                                                           XT::Grid::Layers::dd_subdomain,
-                                                                           XT::Grid::Backends::view,
-                                                                           XT::Grid::DD::SubdomainGrid<G>>::type>,
-                             typename XT::Grid::Layer<G,
-                                                      XT::Grid::Layers::dd_subdomain,
-                                                      XT::Grid::Backends::view,
-                                                      XT::Grid::DD::SubdomainGrid<G>>::type>
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef GDT::MatrixOperatorBase<XT::LA::IstlRowMajorSparseMatrix<double>,
-                                  GDT::RestrictedSpace<typename GDT::SpaceProvider<G,
-                                                                                   XT::Grid::Layers::leaf,
-                                                                                   GDT::SpaceType::rt,
-                                                                                   GDT::Backends::gdt,
-                                                                                   0,
-                                                                                   double,
-                                                                                   G::dimension>::type,
-                                                       typename XT::Grid::Layer<G,
-                                                                                XT::Grid::Layers::dd_subdomain,
-                                                                                XT::Grid::Backends::view,
-                                                                                XT::Grid::DD::SubdomainGrid<G>>::type>,
-                                  typename XT::Grid::Layer<G,
-                                                           XT::Grid::Layers::dd_subdomain,
-                                                           XT::Grid::Backends::view,
-                                                           XT::Grid::DD::SubdomainGrid<G>>::type>
-      BaseType;
-  typedef DiffusiveFluxBbProduct<G> ThisType;
-
-public:
-  using typename BaseType::GridLayerType;
-  using typename BaseType::RangeSpaceType;
-  using typename BaseType::RangeBaseType;
-
-  typedef XT::Grid::extract_entity_t<GridLayerType> E;
-  typedef XT::Grid::extract_intersection_t<GridLayerType> I;
-  typedef typename G::ctype D;
-  static const constexpr size_t d = G::dimension;
-  typedef double R;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, 1> ScalarFunctionType;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, d, d> TensorFunctionType;
-
-  static void bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    py::class_<ThisType, XT::Grid::Walker<GridLayerType>> c(
-        m,
-        XT::Common::to_camel_case("RS2017_diffusive_flux_bb_product_matrix_operator_subdomain_"
-                                  + XT::Grid::bindings::grid_name<G>::value())
-            .c_str());
-    c.def("assemble", [](ThisType& self) { self.assemble(); });
-    c.def("matrix", [](ThisType& self) { return self.matrix(); });
-
-    m.def("RS2017_make_diffusive_flux_bb_product_matrix_operator_on_subdomain",
-          [](const XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-             const ssize_t subdomain,
-             const RangeSpaceType& space,
-             const ScalarFunctionType& lambda_hat,
-             const TensorFunctionType& kappa,
-             const size_t over_integrate) {
-            return new ThisType(
-                space,
-                dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain, XT::Grid::Backends::view>(
-                    XT::Common::numeric_cast<size_t>(subdomain)),
-                lambda_hat,
-                kappa,
-                over_integrate);
-          },
-          "dd_grid_provider"_a,
-          "subdomain"_a,
-          "space"_a,
-          "lambda_hat"_a,
-          "kappa"_a,
-          "over_integrate"_a = 2);
-  } // ... bind(...)
-
-  DiffusiveFluxBbProduct(RangeSpaceType space,
-                         GridLayerType grd_lyr,
-                         const ScalarFunctionType& lambda_hat,
-                         const TensorFunctionType& kappa,
-                         const size_t over_integrate = 2)
-    : BaseType(space, grd_lyr)
-    , unit_matrix_(XT::Common::from_string<XT::Common::FieldMatrix<D, d, d>>("[1 0 0; 0 1 0; 0 0 1]"))
-    , over_integrate_(over_integrate)
-    , local_operator_(
-          [&](const auto& test_base, const auto& ansatz_base) {
-            const auto& entity = test_base.entity();
-            const auto local_lambda_hat = lambda_hat.local_function(entity);
-            const auto local_kappa = kappa.local_function(entity);
-            const auto integrand_order =
-                local_lambda_hat->order() + local_kappa->order() + test_base.order() + ansatz_base.order();
-            return integrand_order + over_integrate_;
-          },
-          [&](const auto& test_base, const auto& ansatz_base, const auto& local_point, auto& ret) {
-            const auto& entity = test_base.entity();
-            const auto local_lambda_hat = lambda_hat.local_function(entity);
-            const auto local_kappa = kappa.local_function(entity);
-            XT::Common::FieldMatrix<D, d, d> diffusion_hat_inverse = local_kappa->evaluate(local_point);
-            diffusion_hat_inverse *= local_lambda_hat->evaluate(local_point);
-#    ifndef NDEBUG
-            const auto diffusion_hat = diffusion_hat_inverse;
-#    endif
-            diffusion_hat_inverse.invert();
-#    ifndef NDEBUG
-            // there is no documented way to tell if the inversion was successfull
-            if (XT::Common::FloatCmp::ne(diffusion_hat_inverse * diffusion_hat, unit_matrix_))
-              DUNE_THROW(XT::Common::Exceptions::internal_error,
-                         "Local inversion of lambda_hat*kappa failed!\n\nx = "
-                             << local_point
-                             << "\nlocal_lambda_hat(x) = "
-                             << local_lambda_hat->evaluate(local_point)
-                             << "\nlocal_kappa(x) = "
-                             << local_kappa->evaluate(local_point)
-                             << "\ninverse = "
-                             << diffusion_hat_inverse
-                             << "\ninverse * (local_lambda_hat(x)*local_kappa(x))) = "
-                             << diffusion_hat_inverse * diffusion_hat);
-#    endif
-            const auto test_values = test_base.evaluate(local_point);
-            const auto ansatz_values = ansatz_base.evaluate(local_point);
-            ret *= 0.;
-            for (size_t ii = 0; ii < test_base.size(); ++ii)
-              for (size_t jj = 0; jj < ansatz_base.size(); ++jj)
-                ret[ii][jj] = ((diffusion_hat_inverse * test_values[ii]) * ansatz_values[jj]);
-          })
-  {
-    this->append(local_operator_);
-  }
-
-  DiffusiveFluxBbProduct(const ThisType&) = delete;
-  DiffusiveFluxBbProduct(ThisType&&) = delete;
-
-private:
-  const XT::Common::FieldMatrix<D, d, d> unit_matrix_;
-  const size_t over_integrate_;
-  const GDT::LocalVolumeIntegralOperator<GDT::LocalLambdaBinaryVolumeIntegrand<E, R, d, 1>, RangeBaseType>
-      local_operator_;
-}; // class DiffusiveFluxBbProduct
-
-
-template <class G>
-class SwipdgPenaltySubdomainProduct
-    : public GDT::MatrixOperatorBase<XT::LA::IstlRowMajorSparseMatrix<double>,
-                                     typename GDT::SpaceProvider<G,
-                                                                 XT::Grid::Layers::dd_subdomain,
-                                                                 GDT::SpaceType::dg,
-                                                                 GDT::Backends::gdt,
-                                                                 1,
-                                                                 double,
-                                                                 1>::type,
-                                     typename XT::Grid::Layer<G,
-                                                              XT::Grid::Layers::dd_subdomain,
-                                                              XT::Grid::Backends::view,
-                                                              XT::Grid::DD::SubdomainGrid<G>>::type>
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef GDT::SpaceProvider<G, XT::Grid::Layers::dd_subdomain, GDT::SpaceType::dg, GDT::Backends::gdt, 1, double, 1>
-      SP;
-  typedef GDT::MatrixOperatorBase<XT::LA::IstlRowMajorSparseMatrix<double>,
-                                  typename SP::type,
-                                  typename XT::Grid::Layer<G,
-                                                           XT::Grid::Layers::dd_subdomain,
-                                                           XT::Grid::Backends::view,
-                                                           XT::Grid::DD::SubdomainGrid<G>>::type>
-      BaseType;
-  typedef SwipdgPenaltySubdomainProduct<G> ThisType;
-
-public:
-  using typename BaseType::GridLayerType;
-  using typename BaseType::RangeSpaceType;
-
-  typedef XT::Grid::extract_entity_t<GridLayerType> E;
-  typedef XT::Grid::extract_intersection_t<GridLayerType> I;
-  typedef typename G::ctype D;
-  static const constexpr size_t d = G::dimension;
-  typedef double R;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, 1> ScalarFunctionType;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, d, d> TensorFunctionType;
-  typedef typename RangeSpaceType::BaseFunctionSetType BasisType;
-
-  static void bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    GDT::bindings::MatrixOperatorBase<ThisType>::bind(
-        m,
-        XT::Common::to_camel_case("RS2017_penalty_product_matrix_operator_subdomain_"
-                                  + XT::Grid::bindings::grid_name<G>::value())
-            .c_str());
-
-    m.def("RS2017_make_penalty_product_matrix_operator_on_subdomain",
-          [](const XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-             const ssize_t subdomain,
-             const XT::Grid::BoundaryInfo<I>& boundary_info,
-             const RangeSpaceType& space,
-             const ScalarFunctionType& lambda,
-             const TensorFunctionType& kappa,
-             const size_t over_integrate) {
-            return new ThisType(
-                space,
-                dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain, XT::Grid::Backends::view>(
-                    XT::Common::numeric_cast<size_t>(subdomain)),
-                boundary_info,
-                lambda,
-                kappa,
-                over_integrate);
-          },
-          "dd_grid_provider"_a,
-          "subdomain"_a,
-          "boundary_info"_a,
-          "space"_a,
-          "lambda_bar"_a,
-          "kappa"_a,
-          "over_integrate"_a = 2);
-  } // ... bind(...)
-
-  SwipdgPenaltySubdomainProduct(RangeSpaceType space,
-                                GridLayerType grd_lyr,
-                                const XT::Grid::BoundaryInfo<I>& boundary_info,
-                                const ScalarFunctionType& lambda,
-                                const TensorFunctionType& kappa,
-                                const size_t over_integrate = 2)
-    : BaseType(space, grd_lyr)
-    , lambda_(lambda)
-    , kappa_(kappa)
-    , over_integrate_(over_integrate)
-    , local_coupling_operator_(
-          // the order lambda
-          [&](const auto& test_base_en,
-              const auto& ansatz_base_en,
-              const auto& test_base_ne,
-              const auto& ansatz_base_ne) {
-            const auto& entity = test_base_en.entity();
-            const auto& neighbor = test_base_ne.entity();
-            const auto local_lambda_en = lambda_.local_function(entity);
-            const auto local_lambda_ne = lambda_.local_function(neighbor);
-            const auto local_kappa_en = kappa_.local_function(entity);
-            const auto local_kappa_ne = kappa_.local_function(neighbor);
-            const auto integrand_order = std::max(local_lambda_en->order(), local_lambda_ne->order())
-                                         + std::max(local_kappa_en->order(), local_kappa_ne->order())
-                                         + std::max(test_base_en.order(), test_base_ne.order())
-                                         + std::max(ansatz_base_en.order(), ansatz_base_ne.order());
-            return integrand_order + over_integrate_;
-          },
-          // The evaluate lambda, this is the penalty part of LocalEllipticIpdgIntegrands::Inner from
-          // dune/gdt/local/integrands/elliptic-ipdg.hh, swipdg_affine_factor variant.
-          [&](const auto& test_base_en,
-              const auto& ansatz_base_en,
-              const auto& test_base_ne,
-              const auto& ansatz_base_ne,
-              const auto& intersection,
-              const auto& local_point,
-              auto& ret_en_en,
-              auto& ret_ne_ne,
-              auto& ret_en_ne,
-              auto& ret_ne_en) {
-            // clear ret
-            ret_en_en *= 0.0;
-            ret_ne_ne *= 0.0;
-            ret_en_ne *= 0.0;
-            ret_ne_en *= 0.0;
-            // convert local point (which is in intersection coordinates) to entity/neighbor coordinates
-            const auto local_point_en = intersection.geometryInInside().global(local_point);
-            const auto local_point_ne = intersection.geometryInOutside().global(local_point);
-            const auto normal = intersection.unitOuterNormal(local_point);
-            // evaluate local function
-            const auto& entity = test_base_en.entity();
-            const auto& neighbor = test_base_ne.entity();
-            const auto lambda_val_en = lambda_.local_function(entity)->evaluate(local_point_en);
-            const auto lambda_val_ne = lambda_.local_function(neighbor)->evaluate(local_point_ne);
-            const XT::Common::FieldMatrix<D, d, d> kappa_val_en =
-                kappa_.local_function(entity)->evaluate(local_point_en);
-            const XT::Common::FieldMatrix<D, d, d> kappa_val_ne =
-                kappa_.local_function(neighbor)->evaluate(local_point_ne);
-            // compute penalty
-            const size_t max_polorder =
-                std::max(test_base_en.order(),
-                         std::max(ansatz_base_en.order(), std::max(test_base_ne.order(), ansatz_base_ne.order())));
-            const R sigma = GDT::LocalEllipticIpdgIntegrands::internal::inner_sigma(max_polorder);
-            const R delta_plus = normal * (kappa_val_ne * normal);
-            const R delta_minus = normal * (kappa_val_en * normal);
-            const R gamma = (delta_plus * delta_minus) / (delta_plus + delta_minus);
-            const auto h = intersection.geometry().volume();
-            const auto beta = GDT::LocalEllipticIpdgIntegrands::internal::default_beta(d);
-            const R penalty = (0.5 * (lambda_val_en + lambda_val_ne) * sigma * gamma) / std::pow(h, beta);
-            // evaluate bases
-            const size_t rows_en = test_base_en.size();
-            const size_t cols_en = ansatz_base_en.size();
-            const size_t rows_ne = test_base_ne.size();
-            const size_t cols_ne = ansatz_base_ne.size();
-            const auto test_values_en = test_base_en.evaluate(local_point_en);
-            const auto ansatz_values_en = ansatz_base_en.evaluate(local_point_en);
-            const auto test_values_ne = test_base_ne.evaluate(local_point_ne);
-            const auto ansatz_values_ne = ansatz_base_ne.evaluate(local_point_ne);
-            // compute integrals, loop over all combinations of basis functions
-            assert(ret_en_en.rows() >= rows_en && ret_en_en.cols() >= cols_en);
-            assert(ret_en_ne.rows() >= rows_en && ret_en_ne.cols() >= cols_ne);
-            assert(ret_ne_en.rows() >= rows_ne && ret_ne_en.cols() >= cols_en);
-            assert(ret_ne_ne.rows() >= rows_ne && ret_ne_ne.cols() >= cols_ne);
-            for (size_t ii = 0; ii < rows_en; ++ii) {
-              for (size_t jj = 0; jj < cols_en; ++jj)
-                ret_en_en[ii][jj] += penalty * ansatz_values_en[jj] * test_values_en[ii];
-              for (size_t jj = 0; jj < cols_ne; ++jj)
-                ret_en_ne[ii][jj] += -1.0 * penalty * ansatz_values_ne[jj] * test_values_en[ii];
-            }
-            for (size_t ii = 0; ii < rows_ne; ++ii) {
-              for (size_t jj = 0; jj < cols_en; ++jj)
-                ret_ne_en[ii][jj] += -1.0 * penalty * ansatz_values_en[jj] * test_values_ne[ii];
-              for (size_t jj = 0; jj < cols_ne; ++jj)
-                ret_ne_ne[ii][jj] += penalty * ansatz_values_ne[jj] * test_values_ne[ii];
-            }
-          })
-    , local_boundary_operator_(
-          // the order lambda
-          [&](const auto& test_base, const auto& ansatz_base) {
-            const auto& entity = test_base.entity();
-            const auto local_lambda = lambda_.local_function(entity);
-            const auto local_kappa = kappa_.local_function(entity);
-            const auto integrand_order =
-                local_lambda->order() + local_kappa->order() + test_base.order() + ansatz_base.order();
-            return integrand_order + over_integrate_;
-          },
-          // The evaluate lambda, this is the penalty part of LocalEllipticIpdgIntegrands::BoundaryLHS from
-          // dune/gdt/local/integrands/elliptic-ipdg.hh, swipdg_affine_factor variant.
-          [&](const auto& test_base,
-              const auto& ansatz_base,
-              const auto& intersection,
-              const auto& local_point,
-              auto& ret) {
-            // clear ret
-            ret *= 0.0;
-            // get local point (which is in intersection coordinates) in entity coordinates
-            const auto local_point_entity = intersection.geometryInInside().global(local_point);
-            const auto normal = intersection.unitOuterNormal(local_point);
-            // evaluate local functions
-            const auto& entity = test_base.entity();
-            XT::Common::FieldMatrix<D, d, d> diffusion = kappa_.local_function(entity)->evaluate(local_point_entity);
-            diffusion *= lambda_.local_function(entity)->evaluate(local_point_entity);
-            // compute penalty
-            const size_t max_polorder = std::max(test_base.order(), ansatz_base.order());
-            const R sigma = GDT::LocalEllipticIpdgIntegrands::internal::boundary_sigma(max_polorder);
-            const R gamma = normal * (diffusion * normal);
-            const auto h = intersection.geometry().volume();
-            const auto beta = GDT::LocalEllipticIpdgIntegrands::internal::default_beta(d);
-            const R penalty = (sigma * gamma) / std::pow(h, beta);
-            // evaluate bases
-            const size_t rows = test_base.size();
-            const size_t cols = ansatz_base.size();
-            const auto test_values = test_base.evaluate(local_point_entity);
-            const auto ansatz_values = ansatz_base.evaluate(local_point_entity);
-            // compute integrals, loop over all combinations of basis functions
-            assert(ret.rows() >= rows && ret.cols() >= cols);
-            for (size_t ii = 0; ii < rows; ++ii)
-              for (size_t jj = 0; jj < cols; ++jj)
-                ret[ii][jj] += penalty * ansatz_values[jj] * test_values[ii];
-          })
-  {
-    this->append(local_coupling_operator_, new XT::Grid::ApplyOn::InnerIntersectionsPrimally<GridLayerType>());
-    this->append(local_boundary_operator_, new XT::Grid::ApplyOn::DirichletIntersections<GridLayerType>(boundary_info));
-  }
-
-  SwipdgPenaltySubdomainProduct(const ThisType&) = delete;
-  SwipdgPenaltySubdomainProduct(ThisType&&) = delete;
-
-private:
-  const ScalarFunctionType& lambda_;
-  const TensorFunctionType& kappa_;
-  const size_t over_integrate_;
-  const GDT::LocalCouplingIntegralOperator<GDT::LocalLambdaQuaternaryFaceIntegrand<E, I>, BasisType, I>
-      local_coupling_operator_;
-  const GDT::LocalBoundaryIntegralOperator<GDT::LocalLambdaBinaryFaceIntegrand<E, I>, BasisType, I>
-      local_boundary_operator_;
-}; // class SwipdgPenaltySubdomainProduct
-
-
-template <class G>
-void bind_neighborhood_discretization(pybind11::module& m)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  typedef typename G::template Codim<0>::Entity E;
-  typedef double D;
-  static const constexpr size_t d = 2;
-  typedef double R;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, 1> DF;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, d, d> DT;
-
-  typedef typename XT::Grid::Layer<G,
-                                   XT::Grid::Layers::dd_subdomain_oversampled,
-                                   XT::Grid::Backends::view,
-                                   XT::Grid::DD::SubdomainGrid<G>>::type NGL;
-  typedef GDT::
-      SpaceProvider<G, XT::Grid::Layers::dd_subdomain, GDT::SpaceType::block_dg, GDT::Backends::gdt, 1, double, 1>
-          SP;
-  typedef typename SP::type S;
-  typedef XT::LA::IstlDenseVector<R> V;
-  typedef XT::LA::IstlRowMajorSparseMatrix<R> M;
-
-  try { // we might not be the first to add this SystemAssembler
-    GDT::bindings::SystemAssembler<SP, XT::Grid::Layers::dd_subdomain_oversampled, XT::Grid::Backends::view>::bind(m);
-  } catch (std::runtime_error&) {
-  }
-
-  m.def("RS2017_make_neighborhood_system_assembler",
-        [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-           const ssize_t subdomain,
-           const S& neighborhood_space) {
-          return new GDT::SystemAssembler<S, NGL>(
-              neighborhood_space,
-              dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain_oversampled, XT::Grid::Backends::view>(
-                  XT::Common::numeric_cast<size_t>(subdomain)));
-        });
-
-  typedef GDT::
-      EllipticIpdgMatrixOperator<DF, DT, S, GDT::LocalEllipticIpdgIntegrands::Method::swipdg_affine_factor, M, NGL>
-          DgMatrixOperator;
-  py::class_<DgMatrixOperator, GDT::SystemAssembler<S, NGL>> dg_matrix_operator(
-      m, "EllipticIpdgMatrixOperatorNeighborhood");
-  dg_matrix_operator.def("matrix", [](DgMatrixOperator& self) { return self.matrix(); });
-
-  m.def("RS2017_make_elliptic_swipdg_matrix_operator_on_neighborhood",
-        [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-           const ssize_t subdomain,
-           XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<NGL>>& boundary_info,
-           const S& neighborhood_space,
-           const DF& lambda,
-           const DT& kappa,
-           const ssize_t over_integrate) {
-          return new DgMatrixOperator(
-              over_integrate,
-              boundary_info,
-              lambda,
-              kappa,
-              neighborhood_space,
-              dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain_oversampled, XT::Grid::Backends::view>(
-                  XT::Common::numeric_cast<size_t>(subdomain)));
-        },
-        "dd_grid_provider"_a,
-        "subdomain"_a,
-        "boundary_info"_a,
-        "neighborhood_space"_a,
-        "lambda"_a,
-        "kappa"_a,
-        "over_integrate"_a = 2);
-
-  typedef GDT::EllipticIpdgDirichletVectorFunctional<DF,
-                                                     DF,
-                                                     DT,
-                                                     S,
-                                                     GDT::LocalEllipticIpdgIntegrands::Method::swipdg_affine_factor,
-                                                     V,
-                                                     NGL>
-      DgVectorFunctional;
-  py::class_<DgVectorFunctional, GDT::SystemAssembler<S, NGL>> dg_vector_functional(
-      m, "EllipticSwipdgVectorFunctionalNeighborhood");
-  dg_vector_functional.def("vector", [](DgVectorFunctional& self) { return self.vector(); });
-
-  m.def("RS2017_make_elliptic_swipdg_vector_functional_on_neighborhood",
-        [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-           const ssize_t subdomain,
-           XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<NGL>>& boundary_info,
-           const S& neighborhood_space,
-           const DF& g_D,
-           const DF& lambda,
-           const DT& kappa,
-           const ssize_t over_integrate) {
-          return new DgVectorFunctional(
-              over_integrate,
-              boundary_info,
-              g_D,
-              lambda,
-              kappa,
-              neighborhood_space,
-              dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain_oversampled, XT::Grid::Backends::view>(
-                  XT::Common::numeric_cast<size_t>(subdomain)));
-        },
-        "dd_grid_provider"_a,
-        "subdomain"_a,
-        "boundary_info"_a,
-        "neighborhood_space"_a,
-        "g_D"_a,
-        "lambda"_a,
-        "kappa"_a,
-        "over_integrate"_a = 2);
-
-  typedef GDT::L2VolumeVectorFunctional<DF, S, V, NGL> L2VolumeFunctional;
-  py::class_<L2VolumeFunctional, GDT::SystemAssembler<S, NGL>> l2_volume_vector_functional(
-      m, "L2VolumeVectorFunctionalNeighborhood");
-  l2_volume_vector_functional.def("vector", [](L2VolumeFunctional& self) { return self.vector(); });
-
-  m.def("RS2017_make_l2_vector_functional_on_neighborhood",
-        [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-           const ssize_t subdomain,
-           const S& neighborhood_space,
-           const DF& f,
-           const ssize_t over_integrate) {
-          return new L2VolumeFunctional(
-              over_integrate,
-              f,
-              neighborhood_space,
-              dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain_oversampled, XT::Grid::Backends::view>(
-                  XT::Common::numeric_cast<size_t>(subdomain)));
-        },
-        "dd_grid_provider"_a,
-        "subdomain"_a,
-        "neighborhood_space"_a,
-        "f"_a,
-        "over_integrate"_a = 2);
-} // ... bind_neighborhood_discretization(...)
-
-
-template <class G>
-class HdivSemiProduct
-    : public GDT::
-          MatrixOperatorBase<XT::LA::IstlRowMajorSparseMatrix<double>,
-                             GDT::RestrictedSpace<typename GDT::SpaceProvider<G,
-                                                                              XT::Grid::Layers::leaf,
-                                                                              GDT::SpaceType::rt,
-                                                                              GDT::Backends::gdt,
-                                                                              0,
-                                                                              double,
-                                                                              G::dimension>::type,
-                                                  typename XT::Grid::Layer<G,
-                                                                           XT::Grid::Layers::dd_subdomain,
-                                                                           XT::Grid::Backends::view,
-                                                                           XT::Grid::DD::SubdomainGrid<G>>::type>,
-                             typename XT::Grid::Layer<G,
-                                                      XT::Grid::Layers::dd_subdomain,
-                                                      XT::Grid::Backends::view,
-                                                      XT::Grid::DD::SubdomainGrid<G>>::type>
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef GDT::MatrixOperatorBase<XT::LA::IstlRowMajorSparseMatrix<double>,
-                                  GDT::RestrictedSpace<typename GDT::SpaceProvider<G,
-                                                                                   XT::Grid::Layers::leaf,
-                                                                                   GDT::SpaceType::rt,
-                                                                                   GDT::Backends::gdt,
-                                                                                   0,
-                                                                                   double,
-                                                                                   G::dimension>::type,
-                                                       typename XT::Grid::Layer<G,
-                                                                                XT::Grid::Layers::dd_subdomain,
-                                                                                XT::Grid::Backends::view,
-                                                                                XT::Grid::DD::SubdomainGrid<G>>::type>,
-                                  typename XT::Grid::Layer<G,
-                                                           XT::Grid::Layers::dd_subdomain,
-                                                           XT::Grid::Backends::view,
-                                                           XT::Grid::DD::SubdomainGrid<G>>::type>
-      BaseType;
-  typedef HdivSemiProduct<G> ThisType;
-
-public:
-  using typename BaseType::GridLayerType;
-  using typename BaseType::RangeSpaceType;
-
-  typedef XT::Grid::extract_entity_t<GridLayerType> E;
-  typedef XT::Grid::extract_intersection_t<GridLayerType> I;
-  typedef typename G::ctype D;
-  static const constexpr size_t d = G::dimension;
-  typedef double R;
-  typedef typename RangeSpaceType::BaseFunctionSetType BasisType;
-
-  static void bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    py::class_<ThisType, XT::Grid::Walker<GridLayerType>> c(
-        m,
-        XT::Common::to_camel_case("RS2017_Hdiv_semi_product_matrix_operator_subdomain_"
-                                  + XT::Grid::bindings::grid_name<G>::value())
-            .c_str());
-    c.def("assemble", [](ThisType& self) { self.assemble(); });
-    c.def("matrix", [](ThisType& self) { return self.matrix(); });
-
-    m.def("RS2017_make_Hdiv_semi_product_matrix_operator_on_subdomain",
-          [](const XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-             const ssize_t subdomain,
-             const RangeSpaceType& space,
-             const size_t over_integrate) {
-            return new ThisType(
-                space,
-                dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain, XT::Grid::Backends::view>(
-                    XT::Common::numeric_cast<size_t>(subdomain)),
-                over_integrate);
-          },
-          "dd_grid_provider"_a,
-          "subdomain"_a,
-          "space"_a,
-          "over_integrate"_a = 2);
-  } // ... bind(...)
-
-  HdivSemiProduct(RangeSpaceType space, GridLayerType grd_lyr, const size_t over_integrate = 2)
-    : BaseType(space, grd_lyr)
-    , over_integrate_(over_integrate)
-    , local_operator_(
-          [&](const auto& test_base, const auto& ansatz_base) {
-            const auto integrand_order = std::max(ssize_t(test_base.order()) - 1, ssize_t(0))
-                                         + std::max(ssize_t(ansatz_base.order()) - 1, ssize_t(0));
-            return size_t(integrand_order) + over_integrate_;
-          },
-          [&](const auto& test_base, const auto& ansatz_base, const auto& local_point, auto& ret) {
-            const auto test_gradient = test_base.jacobian(local_point);
-            const auto ansatz_gradient = ansatz_base.jacobian(local_point);
-            for (size_t ii = 0; ii < test_base.size(); ++ii)
-              for (size_t jj = 0; jj < ansatz_base.size(); ++jj) {
-                R test_divergence = 0.;
-                R ansatz_divergence = 0.;
-                for (size_t dd = 0; dd < d; ++dd) {
-                  test_divergence += test_gradient[ii][dd][dd];
-                  ansatz_divergence += ansatz_gradient[jj][dd][dd];
-                }
-                ret[ii][jj] = test_divergence * ansatz_divergence;
-              }
-          })
-  {
-    this->append(local_operator_);
-  }
-
-  HdivSemiProduct(const ThisType&) = delete;
-  HdivSemiProduct(ThisType&&) = delete;
-
-private:
-  const size_t over_integrate_;
-  const GDT::LocalVolumeIntegralOperator<GDT::LocalLambdaBinaryVolumeIntegrand<E, R, d>, BasisType> local_operator_;
-}; // class HdivSemiProduct
-
-
-template <class G>
-class ResidualPartFunctional
-    : public GDT::
-          VectorFunctionalBase<XT::LA::IstlDenseVector<double>,
-                               GDT::RestrictedSpace<typename GDT::SpaceProvider<G,
-                                                                                XT::Grid::Layers::leaf,
-                                                                                GDT::SpaceType::rt,
-                                                                                GDT::Backends::gdt,
-                                                                                0,
-                                                                                double,
-                                                                                G::dimension>::type,
-                                                    typename XT::Grid::Layer<G,
-                                                                             XT::Grid::Layers::dd_subdomain,
-                                                                             XT::Grid::Backends::view,
-                                                                             XT::Grid::DD::SubdomainGrid<G>>::type>,
-                               typename XT::Grid::Layer<G,
-                                                        XT::Grid::Layers::dd_subdomain,
-                                                        XT::Grid::Backends::view,
-                                                        XT::Grid::DD::SubdomainGrid<G>>::type>
-{
-  static_assert(XT::Grid::is_grid<G>::value, "");
-  typedef GDT::RestrictedSpace<
-      typename GDT::
-          SpaceProvider<G, XT::Grid::Layers::leaf, GDT::SpaceType::rt, GDT::Backends::gdt, 0, double, G::dimension>::
-              type,
-      typename XT::Grid::
-          Layer<G, XT::Grid::Layers::dd_subdomain, XT::Grid::Backends::view, XT::Grid::DD::SubdomainGrid<G>>::type>
-      RtSpaceType;
-  typedef GDT::VectorFunctionalBase<XT::LA::IstlDenseVector<double>,
-                                    RtSpaceType,
-                                    typename XT::Grid::Layer<G,
-                                                             XT::Grid::Layers::dd_subdomain,
-                                                             XT::Grid::Backends::view,
-                                                             XT::Grid::DD::SubdomainGrid<G>>::type>
-      BaseType;
-  typedef ResidualPartFunctional<G> ThisType;
-
-public:
-  using typename BaseType::GridLayerType;
-  using typename BaseType::SpaceType;
-
-  typedef XT::Grid::extract_entity_t<GridLayerType> E;
-  typedef XT::Grid::extract_intersection_t<GridLayerType> I;
-  typedef typename G::ctype D;
-  static const constexpr size_t d = G::dimension;
-  typedef double R;
-  typedef XT::Functions::GridFunctionInterface<E, D, d, R, 1> ScalarFunctionType;
-  typedef typename SpaceType::BaseFunctionSetType BasisType;
-
-  static void bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    py::class_<ThisType, XT::Grid::Walker<GridLayerType>> c(
-        m,
-        XT::Common::to_camel_case("RS2017_residual_part_vector_functional_subdomain_"
-                                  + XT::Grid::bindings::grid_name<G>::value())
-            .c_str());
-    c.def("assemble", [](ThisType& self) { self.assemble(); });
-    c.def("vector", [](ThisType& self) { return self.vector(); });
-
-    m.def("RS2017_make_residual_part_vector_functional_on_subdomain",
-          [](const XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-             const ssize_t subdomain,
-             const SpaceType& space,
-             const ScalarFunctionType& f,
-             const size_t over_integrate) {
-            return new ThisType(
-                space,
-                dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain, XT::Grid::Backends::view>(
-                    XT::Common::numeric_cast<size_t>(subdomain)),
-                f,
-                over_integrate);
-          },
-          "dd_grid_provider"_a,
-          "subdomain"_a,
-          "space"_a,
-          "f"_a,
-          "over_integrate"_a = 2);
-  } // ... bind(...)
-
-  ResidualPartFunctional(SpaceType space,
-                         GridLayerType grd_lyr,
-                         const ScalarFunctionType& f,
-                         const size_t over_integrate = 0)
-    : BaseType(space, grd_lyr)
-    , f_(f)
-    , over_integrate_(over_integrate)
-    , local_functional_(
-          [&](const auto& test_base) {
-            const auto integrand_order = ssize_t(f_.local_function(test_base.entity())->order())
-                                         + std::max(ssize_t(test_base.order()) - 1, ssize_t(0));
-            return size_t(integrand_order) + over_integrate_;
-          },
-          [&](const auto& test_base, const auto& local_point, auto& ret) {
-            const auto local_f = f_.local_function(test_base.entity());
-            ret *= 0.;
-            // f \times \divergence test
-            const auto test_jacobians = test_base.jacobian(local_point);
-            for (size_t ii = 0; ii < test_base.size(); ++ii)
-              for (size_t dd = 0; dd < d; ++dd)
-                ret[ii] += test_jacobians[ii][dd][dd];
-            ret *= local_f->evaluate(local_point);
-          })
-  {
-    this->append(local_functional_);
-  }
-
-  ResidualPartFunctional(const ThisType&) = delete;
-  ResidualPartFunctional(ThisType&&) = delete;
-
-private:
-  const ScalarFunctionType& f_;
-  const size_t over_integrate_;
-  const GDT::LocalVolumeIntegralFunctional<GDT::LocalLambdaUnaryVolumeIntegrand<E, R, d, 1>, BasisType>
-      local_functional_;
-}; // class ResidualPartFunctional
-
-
-template <class G>
-void bind_neighborhood_reconstruction(pybind11::module& m)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  typedef typename G::template Codim<0>::Entity E;
-  typedef double D;
-  static const constexpr size_t d = 2;
-  typedef double R;
-  typedef typename XT::Grid::Layer<G, XT::Grid::Layers::leaf, XT::Grid::Backends::view>::type LeafViewType;
-  typedef GDT::RaviartThomasSpace<LeafViewType, 0> RtSpaceType;
-  typedef typename XT::Grid::Layer<G,
-                                   XT::Grid::Layers::dd_subdomain_oversampled,
-                                   XT::Grid::Backends::view,
-                                   XT::Grid::DD::SubdomainGrid<G>>::type NeighborHoodGridLayer;
-  typedef GDT::RestrictedSpace<RtSpaceType, NeighborHoodGridLayer> NeighborhoodRtSpaceType;
-  typedef XT::LA::IstlDenseVector<R> VectorType;
-  typedef GDT::
-      LocalizableDiffusiveFluxReconstructionOperator<NeighborHoodGridLayer,
-                                                     XT::Functions::GridFunctionInterface<E, D, d, R, 1>,
-                                                     GDT::DiscreteFunction<NeighborhoodRtSpaceType, VectorType>,
-                                                     GDT::LocalEllipticIpdgIntegrands::Method::swipdg_affine_factor>
-          LocalizableDiffusiveFluxReconstructionOperatorForRestrictedSpaceType;
-
-  m.def("RS2017_apply_diffusive_flux_reconstruction_in_neighborhood",
-        [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-           const ssize_t subdomain,
-           const XT::Functions::GridFunctionInterface<E, D, d, R, 1>& lambda,
-           const XT::Functions::GridFunctionInterface<E, D, d, R, d, d>& kappa,
-           const XT::Functions::GridFunctionInterface<E, D, d, R, 1>& u,
-           typename LocalizableDiffusiveFluxReconstructionOperatorForRestrictedSpaceType::RangeType& reconstructed_u,
-           const ssize_t over_integrate) {
-          py::gil_scoped_release DUNE_UNUSED(release);
-          LocalizableDiffusiveFluxReconstructionOperatorForRestrictedSpaceType(
-              dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain_oversampled, XT::Grid::Backends::view>(
-                  XT::Common::numeric_cast<size_t>(subdomain)),
-              lambda,
-              kappa,
-              u,
-              reconstructed_u,
-              over_integrate)
-              .apply();
-        },
-        "dd_grid_provider"_a,
-        "subdomain"_a,
-        "lambda_hat"_a,
-        "kappa"_a,
-        "u"_a,
-        "reconstructed_u"_a,
-        "over_integrate"_a = 2);
-
-  typedef GDT::
-      LocalizableDiffusiveFluxReconstructionOperator<NeighborHoodGridLayer,
-                                                     XT::Functions::GridFunctionInterface<E, D, d, R, 1>,
-                                                     GDT::DiscreteFunction<RtSpaceType, VectorType>,
-                                                     GDT::LocalEllipticIpdgIntegrands::Method::swipdg_affine_factor>
-          LocalizableDiffusiveFluxReconstructionOperatorForLeafSpaceType;
-
-  m.def("RS2017_apply_diffusive_flux_reconstruction_in_neighborhood",
-        [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider,
-           const ssize_t subdomain,
-           const XT::Functions::GridFunctionInterface<E, D, d, R, 1>& lambda,
-           const XT::Functions::GridFunctionInterface<E, D, d, R, d, d>& kappa,
-           const XT::Functions::GridFunctionInterface<E, D, d, R, 1>& u,
-           typename LocalizableDiffusiveFluxReconstructionOperatorForLeafSpaceType::RangeType& reconstructed_u,
-           const ssize_t over_integrate) {
-          py::gil_scoped_release DUNE_UNUSED(release);
-          LocalizableDiffusiveFluxReconstructionOperatorForLeafSpaceType(
-              dd_grid_provider.template layer<XT::Grid::Layers::dd_subdomain_oversampled, XT::Grid::Backends::view>(
-                  XT::Common::numeric_cast<size_t>(subdomain)),
-              lambda,
-              kappa,
-              u,
-              reconstructed_u,
-              over_integrate)
-              .apply();
-        },
-        "dd_grid_provider"_a,
-        "subdomain"_a,
-        "lambda_hat"_a,
-        "kappa"_a,
-        "u"_a,
-        "reconstructed_u"_a,
-        "over_integrate"_a = 2);
-} // ... bind_neighborhood_reconstruction(...)
-
-
-#  else // HAVE_DUNE_ISTL
-
-
-template <class G>
-class DiffusiveFluxAaProduct
-{
-  static_assert(AlwaysFalse<G>::value, "You are missing dune-istl!");
-};
-
-
-template <class G>
-class DiffusiveFluxAbProduct
-{
-  static_assert(AlwaysFalse<G>::value, "You are missing dune-istl!");
-};
-
-
-template <class G>
-class DiffusiveFluxBbProduct
-{
-  static_assert(AlwaysFalse<G>::value, "You are missing dune-istl!");
-};
-
-
-template <class G>
-class SwipdgPenaltySubdomainProduct
-{
-  static_assert(AlwaysFalse<G>::value, "You are missing dune-istl!");
-};
-
-
-template <class G>
-void bind_neighborhood_discretization(pybind11::module& /*m*/)
-{
-  static_assert(AlwaysFalse<G>::value, "You are missing dune-istl!");
-}
-
-
-template <class G>
-class HdivSemiProduct
-{
-  static_assert(AlwaysFalse<G>::value, "You are missing dune-istl!");
-};
-
-
-template <class G>
-class ResidualPartFunctional
-{
-  static_assert(AlwaysFalse<G>::value, "You are missing dune-istl!");
-};
-
-
-template <class G>
-void bind_neighborhood_reconstruction(pybind11::module& /*m*/)
-{
-  static_assert(AlwaysFalse<G>::value, "You are missing dune-istl!");
-}
-
-
-#  endif // HAVE_DUNE_ISTL
-
-} // namespace RS2017
-} // namespace GDT
-} // namespace Dune
-
-#endif // 0
-#endif // DUNE_GDT_PLAYGROUND_OPERATORS_RS2017_HH
diff --git a/python/dune/gdt/playground/spaces/block.hh b/python/dune/gdt/playground/spaces/block.hh
deleted file mode 100644
index 7cbadcd95ff233850b9ee8a0c96614200a18abd0..0000000000000000000000000000000000000000
--- a/python/dune/gdt/playground/spaces/block.hh
+++ /dev/null
@@ -1,453 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_PLAYGROUND_SPACES_BLOCK_BINDINGS_HH
-#define PYTHON_DUNE_GDT_PLAYGROUND_SPACES_BLOCK_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <dune/grid/common/rangegenerators.hh>
-
-#  include <dune/xt/common/numeric_cast.hh>
-#  include <dune/xt/common/string.hh>
-#  include <dune/xt/la/container.hh>
-#  include <dune/xt/grid/gridprovider/provider.hh>
-#  include <dune/xt/grid/dd/subdomains/grid.hh>
-#  include <python/dune/xt/grid/grids.bindings.hh>
-
-#  include <dune/gdt/assembler/system.hh>
-#  include <dune/gdt/discretefunction/default.hh>
-#  include <dune/gdt/projections.hh>
-#  include <dune/gdt/spaces/bindings.hh>
-#  include <dune/gdt/type_traits.hh>
-
-#  include <dune/gdt/playground/spaces/block.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class SP>
-class BlockMapper
-{
-  typedef typename SP::type S;
-  static_assert(is_space<S>::value, "");
-  using G = XT::Grid::extract_grid_t<typename S::GridLayerType>;
-
-public:
-  typedef GDT::BlockMapper<S> type;
-  typedef pybind11::class_<type> bound_type;
-
-  template <XT::LA::Backends la>
-  static void addbind_matrix(bound_type& c)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    typedef typename XT::LA::Container<double, la>::MatrixType M;
-
-    c.def("copy_local_to_global",
-          [](const type& self,
-             const M& local_matrix,
-             const XT::LA::SparsityPatternDefault& local_pattern,
-             const ssize_t block,
-             M& global_matrix) {
-            auto bb = XT::Common::numeric_cast<size_t>(block);
-            for (size_t local_ii = 0; local_ii < local_pattern.size(); ++local_ii) {
-              const size_t global_ii = self.mapToGlobal(bb, local_ii);
-              for (const size_t& local_jj : local_pattern.inner(local_ii)) {
-                const size_t global_jj = self.mapToGlobal(bb, local_jj);
-                global_matrix.add_to_entry(global_ii, global_jj, local_matrix.get_entry(local_ii, local_jj));
-              }
-            }
-          },
-          "local_matrix"_a,
-          "local_sparsity_pattern"_a,
-          "block"_a,
-          "global_matrix"_a);
-    c.def("copy_local_to_global",
-          [](const type& self,
-             const M& local_matrix,
-             const XT::LA::SparsityPatternDefault& local_pattern,
-             const ssize_t test_block,
-             const ssize_t ansatz_block,
-             M& global_matrix) {
-            auto tt = XT::Common::numeric_cast<size_t>(test_block);
-            auto aa = XT::Common::numeric_cast<size_t>(ansatz_block);
-            for (size_t local_ii = 0; local_ii < local_pattern.size(); ++local_ii) {
-              const size_t global_ii = self.mapToGlobal(tt, local_ii);
-              for (const size_t& local_jj : local_pattern.inner(local_ii)) {
-                const size_t global_jj = self.mapToGlobal(aa, local_jj);
-                global_matrix.add_to_entry(global_ii, global_jj, local_matrix.get_entry(local_ii, local_jj));
-              }
-            }
-          },
-          "local_matrix"_a,
-          "local_sparsity_pattern"_a,
-          "test_block"_a,
-          "ansatz_block"_a,
-          "global_matrix"_a);
-  } // ... addbind_matrix(...)
-
-  template <XT::LA::Backends la>
-  static void addbind_vector(bound_type& c)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    typedef typename XT::LA::Container<double, la>::VectorType V;
-
-    c.def("copy_local_to_global",
-          [](const type& self, const V& local_vector, const ssize_t block, V& global_vector) {
-            auto bb = XT::Common::numeric_cast<size_t>(block);
-            for (size_t local_ii = 0; local_ii < local_vector.size(); ++local_ii) {
-              const size_t global_ii = self.mapToGlobal(bb, local_ii);
-              global_vector.add_to_entry(global_ii, local_vector.get_entry(local_ii));
-            }
-          },
-          "local_vector"_a,
-          "block"_a,
-          "global_vector"_a);
-  } // ... addbind_vector(...)
-
-  static bound_type bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    const auto ClassName = XT::Common::to_camel_case("block_" + space_name<SP>::value() + "_mappe");
-
-    bound_type c(m, ClassName.c_str());
-
-    c.def_property_readonly("size", [](const type& self) { return self.size(); });
-
-    // sparsity patterns
-    c.def("copy_local_to_global",
-          [](const type& self,
-             const XT::LA::SparsityPatternDefault& local_pattern,
-             const ssize_t block,
-             XT::LA::SparsityPatternDefault& global_pattern) {
-            auto bb = XT::Common::numeric_cast<size_t>(block);
-            for (size_t local_ii = 0; local_ii < local_pattern.size(); ++local_ii) {
-              const size_t global_ii = self.mapToGlobal(bb, local_ii);
-              const auto& local_rows = local_pattern.inner(local_ii);
-              for (const auto& local_jj : local_rows) {
-                const size_t global_jj = self.mapToGlobal(bb, local_jj);
-                global_pattern.insert(global_ii, global_jj);
-              }
-            }
-          },
-          "local_sparsity_pattern"_a,
-          "block"_a,
-          "global_sparsity_pattern"_a);
-    c.def("copy_local_to_global",
-          [](const type& self,
-             const XT::LA::SparsityPatternDefault& local_pattern,
-             const ssize_t test_block,
-             const ssize_t ansatz_block,
-             XT::LA::SparsityPatternDefault& global_pattern) {
-            auto tt = XT::Common::numeric_cast<size_t>(test_block);
-            auto aa = XT::Common::numeric_cast<size_t>(ansatz_block);
-            for (size_t local_ii = 0; local_ii < local_pattern.size(); ++local_ii) {
-              const size_t global_ii = self.mapToGlobal(tt, local_ii);
-              const auto& local_rows = local_pattern.inner(local_ii);
-              for (const auto& local_jj : local_rows) {
-                const size_t global_jj = self.mapToGlobal(aa, local_jj);
-                global_pattern.insert(global_ii, global_jj);
-              }
-            }
-          },
-          "local_sparsity_pattern"_a,
-          "test_block"_a,
-          "ansatz_block"_a,
-          "global_sparsity_pattern"_a);
-
-    // matrices
-    addbind_matrix<XT::LA::Backends::common_dense>(c);
-    addbind_matrix<XT::LA::Backends::common_sparse>(c);
-#  if HAVE_EIGEN
-    addbind_matrix<XT::LA::Backends::eigen_dense>(c);
-    addbind_matrix<XT::LA::Backends::eigen_sparse>(c);
-#  endif
-#  if HAVE_DUNE_ISTL
-    addbind_matrix<XT::LA::Backends::istl_sparse>(c);
-#  endif
-
-    // vectors
-    addbind_vector<XT::LA::Backends::common_dense>(c);
-#  if HAVE_EIGEN
-    addbind_vector<XT::LA::Backends::eigen_dense>(c);
-#  endif
-#  if HAVE_DUNE_ISTL
-    addbind_vector<XT::LA::Backends::istl_dense>(c);
-#  endif
-
-    return c;
-  } // ... bind(...)
-}; // class BlockMapper
-
-
-template <class SP>
-class BlockSpace
-{
-  typedef typename SP::type S;
-  static_assert(is_space<S>::value, "");
-  using G = XT::Grid::extract_grid_t<typename S::GridLayerType>;
-
-public:
-  typedef GDT::BlockSpace<S> type;
-  typedef pybind11::class_<type> bound_type;
-
-private:
-  template <bool is_dg = (SP::space_type == SpaceType::dg), bool anything = false>
-  struct projector
-  {
-    template <class V>
-    static V project(const type& self, const std::vector<V>& local_vectors, const std::vector<size_t>& subdomains)
-    {
-      V neighborhood_vector(self.mapper().size(), 0.);
-      assert(local_vectors.size() == subdomains.size() && "This should not happen, blame the caller!");
-      for (size_t ii = 0; ii < local_vectors.size(); ++ii) {
-        const auto subdomain = subdomains[ii];
-        const auto& local_vector = local_vectors[ii];
-        for (size_t jj = 0; jj < local_vector.size(); ++jj)
-          neighborhood_vector[self.mapper().mapToGlobal(subdomain, jj)] = local_vector[jj];
-      }
-      return neighborhood_vector;
-    }
-  }; // struct projector<true, ...>
-
-  template <bool anything>
-  struct projector<false, anything>
-  {
-    template <class V>
-    static V project(const type&, const std::vector<V>&, const std::vector<size_t>&)
-    {
-      static_assert(AlwaysFalse<V>::value, "Not implemented for non DG spaces");
-    }
-  };
-
-  template <XT::LA::Backends la>
-  static void addbind_vector(bound_type& c)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    typedef typename XT::LA::Container<double, la>::VectorType V;
-
-    c.def("project_onto_neighborhood",
-          [](const type& self, const std::vector<V>& local_vectors, const std::vector<ssize_t>& neighborhood) {
-            if (local_vectors.size() != neighborhood.size())
-              DUNE_THROW(XT::Common::Exceptions::shapes_do_not_match,
-                         "local_vectors.size(): " << local_vectors.size() << "\n   neighborhood.size(): "
-                                                  << neighborhood.size());
-            std::vector<size_t> subdomains(neighborhood.size());
-            for (size_t ii = 0; ii < neighborhood.size(); ++ii)
-              subdomains[ii] = XT::Common::numeric_cast<size_t>(neighborhood[ii]);
-            std::vector<std::shared_ptr<const S>> neighborhood_spaces(self.dd_grid().size(), nullptr);
-            for (const auto& subdomain : subdomains) {
-              if (self.backend()[subdomain] == nullptr)
-                DUNE_THROW(XT::Common::Exceptions::you_are_using_this_wrong,
-                           "This BlockSpace (restricted to a neighborhood) does not have a local space for subdomain "
-                               << subdomain
-                               << "!");
-              neighborhood_spaces[subdomain] = self.backend()[subdomain];
-            }
-            const type neighborhood_space(self.dd_grid(), neighborhood_spaces);
-            return projector<>::project(neighborhood_space, local_vectors, subdomains);
-          },
-          "local_vectors"_a,
-          "neighborhood"_a);
-  } // ... addbind_vector(...)
-
-public:
-  static bound_type bind(pybind11::module& m)
-  {
-    BlockMapper<SP>::bind(m);
-
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    const auto ClassName = XT::Common::to_camel_case("block_" + space_name<SP>::value());
-
-    bound_type c(m, ClassName.c_str(), ClassName.c_str()); // metaclass required for static properties
-
-    c.def("__init__",
-          [](type& self, XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider) {
-            const auto& dd_grid = dd_grid_provider.dd_grid();
-            std::vector<std::shared_ptr<const S>> local_spaces(dd_grid.size());
-            for (size_t ss = 0; ss < dd_grid.size(); ++ss)
-              local_spaces[ss] = std::make_shared<S>(SP::create(dd_grid_provider, boost::numeric_cast<int>(ss)));
-            try {
-              new (&self) type(dd_grid, local_spaces);
-            } catch (...) {
-              self.~type();
-              throw;
-            }
-          },
-          "dd_grid"_a,
-          py::keep_alive<1, 2>());
-    c.def_property_readonly("dimDomain", [](const type& /*self*/) { return S::dimDomain; });
-    c.def_property_readonly("dimRange", [](const type& /*self*/) { return S::dimRange; });
-    c.def_property_readonly("dimRangeCols", [](const type& /*self*/) { return S::dimRangeCols; });
-    c.def_property_readonly("polOrder", [](const type& /*self*/) { return S::polOrder; });
-    c.def_property_readonly("num_blocks", [](const type& self) { return self.num_blocks(); });
-    c.def_property_readonly("mapper",
-                            [](const type& self) {
-                              // we get a segfault without the explicit copy
-                              return std::decay_t<decltype(self.mapper())>(self.mapper());
-                            },
-                            py::keep_alive<0, 1>());
-    // these need to be defined *after* their non static counterparts
-    c.def_property_readonly_static("dimDomain", [](const type& /*self*/) { return S::dimDomain; });
-    c.def_property_readonly_static("dimRange", [](const type& /*self*/) { return S::dimRange; });
-    c.def_property_readonly_static("dimRangeCols", [](const type& /*self*/) { return S::dimRangeCols; });
-    c.def_property_readonly_static("polOrder", [](const type& /*self*/) { return S::polOrder; });
-    c.def("local_space",
-          [](const type& self, ssize_t block) { return self.local_space(XT::Common::numeric_cast<size_t>(block)); },
-          "block"_a);
-    c.def("compute_boundary_pattern",
-          [](const type& self, const ssize_t block, const std::string tp) {
-            auto bb = XT::Common::numeric_cast<size_t>(block);
-            if (tp == "volume")
-              return self.local_space(bb).compute_volume_pattern(self.dd_grid().boundary_grid_view(bb));
-            else if (tp == "face")
-              return self.local_space(bb).compute_face_pattern(self.dd_grid().boundary_grid_view(bb));
-            else if (tp == "face_and_volume")
-              return self.local_space(bb).compute_face_and_volume_pattern(self.dd_grid().boundary_grid_view(bb));
-            else
-              DUNE_THROW(XT::Common::Exceptions::wrong_input_given,
-                         "  type has to be one of ('volume', 'face', 'face_and_volume'), is '" << tp << "'!");
-            // we will never get here
-            return XT::LA::SparsityPatternDefault();
-          },
-          "block"_a,
-          "type"_a);
-    c.def("compute_coupling_pattern",
-          [](const type& self, const ssize_t subdomain, const ssize_t neighbor, const std::string tp) {
-            auto ss = XT::Common::numeric_cast<size_t>(subdomain);
-            auto nn = XT::Common::numeric_cast<size_t>(neighbor);
-            if (tp == "volume")
-              return self.local_space(ss).compute_volume_pattern(self.dd_grid().coupling_grid_view(ss, nn),
-                                                                 self.local_space(nn));
-            else if (tp == "face")
-              return self.local_space(ss).compute_face_pattern(self.dd_grid().coupling_grid_view(ss, nn),
-                                                               self.local_space(nn));
-            else if (tp == "face_and_volume")
-              return self.local_space(ss).compute_face_and_volume_pattern(self.dd_grid().coupling_grid_view(ss, nn),
-                                                                          self.local_space(nn));
-            else
-              DUNE_THROW(XT::Common::Exceptions::wrong_input_given,
-                         "  type has to be one of ('volume', 'face', 'face_and_volume'), is '" << tp << "'!");
-            // we will never get here
-            return XT::LA::SparsityPatternDefault();
-          },
-          "block_subdomain"_a,
-          "neighboring_subdomain"_a,
-          "type"_a);
-    c.def("boundary_assembler",
-          [](const type& self, const ssize_t subdomain) {
-            auto ss = XT::Common::numeric_cast<size_t>(subdomain);
-            auto boundary_grid_part = self.dd_grid().boundary_grid_view(ss);
-            return new GDT::SystemAssembler<S, decltype(boundary_grid_part), S>(self.local_space(ss), // see below for
-                                                                                boundary_grid_part); //  the 'new'
-          },
-          "subdomain"_a);
-    c.def("coupling_assembler",
-          [](const type& self, const ssize_t subdomain, const ssize_t neighbor) {
-            auto ss = XT::Common::numeric_cast<size_t>(subdomain);
-            auto nn = XT::Common::numeric_cast<size_t>(neighbor);
-            auto coupling_grid_part = self.dd_grid().coupling_grid_view(ss, nn);
-            return new GDT::SystemAssembler<S, decltype(coupling_grid_part), S>(coupling_grid_part, //   SystemAssembler
-                                                                                self.local_space(ss), // is not copyable
-                                                                                self.local_space(ss), // or movable,
-                                                                                self.local_space(nn), // thus the raw
-                                                                                self.local_space(nn)); // pointer
-          },
-          "subdomain"_a,
-          "neighbor"_a);
-    c.def("restricted_to_neighborhood",
-          [](const type& self, const std::vector<ssize_t>& neighborhood) {
-            std::vector<std::shared_ptr<const S>> neighborhood_spaces(self.dd_grid().size(), nullptr);
-            for (const auto& subdomain : neighborhood)
-              neighborhood_spaces[subdomain] = self.backend()[XT::Common::numeric_cast<size_t>(subdomain)];
-            return type(self.dd_grid(), neighborhood_spaces);
-          },
-          "neighborhood"_a);
-
-    addbind_vector<XT::LA::Backends::common_dense>(c);
-#  if HAVE_EIGEN
-    addbind_vector<XT::LA::Backends::eigen_dense>(c);
-#  endif
-#  if HAVE_DUNE_ISTL
-    addbind_vector<XT::LA::Backends::istl_dense>(c);
-#  endif
-
-    const std::string factory_method_name = "make_block_" + space_name<SP>::value_wo_grid();
-    m.def(factory_method_name.c_str(),
-          [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& dd_grid_provider) {
-            const auto& dd_grid = dd_grid_provider.dd_grid();
-            std::vector<std::shared_ptr<const S>> local_spaces(dd_grid.size());
-            for (size_t ss = 0; ss < dd_grid.size(); ++ss)
-              local_spaces[ss] = std::make_shared<S>(SP::create(dd_grid_provider, boost::numeric_cast<int>(ss)));
-            return type(dd_grid, local_spaces);
-          },
-          "dd_grid_provider"_a);
-
-    return c;
-  } // ... bind(...)
-}; // class BlockSpace
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-
-// begin: this is what we need for the .so
-
-#  define _DUNE_GDT_SPACES_BLOCK_BIND(_m, _GRID, _s_type, _s_backend, _p)                                              \
-    Dune::GDT::bindings::BlockSpace<Dune::GDT::SpaceProvider<_GRID,                                                    \
-                                                             Dune::XT::Grid::Layers::dd_subdomain,                     \
-                                                             Dune::GDT::SpaceType::_s_type,                            \
-                                                             Dune::GDT::Backends::_s_backend,                          \
-                                                             _p,                                                       \
-                                                             double,                                                   \
-                                                             1,                                                        \
-                                                             1>>::bind(_m)
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_SPACES_BLOCK_BIND_ALU(_m, _s_type, _s_backend, _p)                                               \
-      _DUNE_GDT_SPACES_BLOCK_BIND(_m, ALU_2D_SIMPLEX_CONFORMING, _s_type, _s_backend, _p)
-#  else
-#    define _DUNE_GDT_SPACES_BLOCK_BIND_ALU(_m, _s_type, _s_backend, _p)
-#  endif
-
-#  define _DUNE_GDT_SPACES_BLOCK_BIND_YASP(_m, _s_type, _s_backend, _p)                                                \
-    _DUNE_GDT_SPACES_BLOCK_BIND(_m, YASP_1D_EQUIDISTANT_OFFSET, _s_type, _s_backend, _p);                              \
-    _DUNE_GDT_SPACES_BLOCK_BIND(_m, YASP_2D_EQUIDISTANT_OFFSET, _s_type, _s_backend, _p)
-
-#  define _DUNE_GDT_SPACES_BLOCK_BIND_ALL_GRIDS(_m, _s_type, _s_backend, _p)                                           \
-    _DUNE_GDT_SPACES_BLOCK_BIND_ALU(_m, _s_type, _s_backend, _p);                                                      \
-    _DUNE_GDT_SPACES_BLOCK_BIND_YASP(_m, _s_type, _s_backend, _p)
-
-
-#  define DUNE_GDT_SPACES_BLOCK_BIND(_m) _DUNE_GDT_SPACES_BLOCK_BIND_ALL_GRIDS(_m, dg, gdt, 1)
-#  define _DUNE_GDT_SPACES_BLOCK_BIND_FEM(_m) _DUNE_GDT_SPACES_BLOCK_BIND_ALL_GRIDS(_m, dg, gdt, 1)
-
-// end: this is what we need for the .so
-
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_PLAYGROUND_SPACES_BLOCK_BINDINGS_HH
diff --git a/python/dune/gdt/projections/bindings.cc b/python/dune/gdt/projections/bindings.cc
deleted file mode 100644
index 93fcf43079acac3c27f171673526cfeda60ca0ba..0000000000000000000000000000000000000000
--- a/python/dune/gdt/projections/bindings.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017)
-//   René Fritze     (2018)
-
-#include "config.h"
-
-#if HAVE_DUNE_PYBINDXI
-
-#  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/gdt/shared.hh>
-
-#  include <dune/gdt/projections/bindings.hh>
-#  include <python/dune/gdt/projections/dirichlet.hh>
-
-
-PYBIND11_MODULE(__projections, m)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-
-  py::module::import("dune.xt.common");
-  py::module::import("dune.xt.grid");
-  py::module::import("dune.xt.functions");
-  py::module::import("dune.xt.la");
-  py::module::import("dune.gdt.__spaces");
-
-  DUNE_GDT_PROJECTIONS_BIND(m);
-  DUNE_GDT_PROJECTIONS_DIRICHLET_BIND(m);
-
-  add_initialization(m, "dune.gdt.projections");
-}
-
-#endif // HAVE_DUNE_PYBINDXI
diff --git a/python/dune/gdt/projections/bindings.hh b/python/dune/gdt/projections/bindings.hh
deleted file mode 100644
index 90f8401e30ed493ba3f66e13d9ea595efd44ba26..0000000000000000000000000000000000000000
--- a/python/dune/gdt/projections/bindings.hh
+++ /dev/null
@@ -1,150 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_PROJECTIONS_BINDINGS_HH
-#define PYTHON_DUNE_GDT_PROJECTIONS_BINDINGS_HH
-
-// Todo: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <python/dune/xt/la/container.bindings.hh>
-
-#  include <dune/gdt/spaces/bindings.hh>
-#  include <dune/gdt/type_traits.hh>
-
-#  include <dune/gdt/projections.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class SP, class V>
-class project
-{
-  typedef typename SP::type SpaceType;
-  static_assert(is_space<SpaceType>::value, "");
-  static_assert(XT::LA::is_vector<V>::value, "");
-  typedef typename XT::Functions::GridFunctionInterface<typename SpaceType::EntityType,
-                                                        typename SpaceType::DomainFieldType,
-                                                        SpaceType::dimDomain,
-                                                        typename SpaceType::RangeFieldType,
-                                                        SpaceType::dimRange,
-                                                        SpaceType::dimRangeCols>
-      SourceType;
-  typedef DiscreteFunction<SpaceType, V> RangeType;
-
-public:
-  static void bind(pybind11::module& m)
-  {
-    using namespace pybind11::literals;
-
-    m.def("project",
-          [](const SourceType& source, RangeType& range, const size_t over_integrate) {
-            GDT::project(source, range, over_integrate);
-          },
-          "source"_a,
-          "range"_a,
-          "over_integrate"_a = 0);
-  } // ... bind(...)
-}; // class project
-
-
-// begin: this is what we need for the .so
-
-#  define _DUNE_GDT_PROJECTIONS_BIND(_m, _GRID, _g_layer, _s_backend, _s_type, _p, _la)                                \
-    Dune::GDT::bindings::project<                                                                                      \
-        Dune::GDT::SpaceProvider<_GRID,                                                                                \
-                                 Dune::XT::Grid::Layers::_g_layer,                                                     \
-                                 Dune::GDT::SpaceType::_s_type,                                                        \
-                                 Dune::GDT::Backends::_s_backend,                                                      \
-                                 _p,                                                                                   \
-                                 double,                                                                               \
-                                 1,                                                                                    \
-                                 1>,                                                                                   \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>::bind(_m)
-
-// for each grid
-
-//#if HAVE_ALBERTA
-//  ...
-//#else
-#  define _DUNE_GDT_PROJECTIONS_BIND_ALBERTA(_m, _g_layer, _s_backend, _s_type, _p, _la)
-//#endif
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_PROJECTIONS_BIND_ALU(_m, _g_layer, _s_backend, _s_type, _p, _la)                                 \
-      _DUNE_GDT_PROJECTIONS_BIND(_m, ALU_2D_SIMPLEX_CONFORMING, _g_layer, _s_backend, _s_type, _p, _la)
-#  else
-#    define _DUNE_GDT_PROJECTIONS_BIND_ALU(_m, _g_layer, _s_backend, _s_type, _p, _la)
-#  endif
-
-//#if HAVE_DUNE_UGGRID || HAVE_UG
-//  ...
-//#else
-#  define _DUNE_GDT_PROJECTIONS_BIND_UG(_m, _g_layer, _s_backend, _s_type, _p, _la)
-//#endif
-
-#  define _DUNE_GDT_PROJECTIONS_BIND_YASP(_m, _g_layer, _s_backend, _s_type, _p, _la)                                  \
-    _DUNE_GDT_PROJECTIONS_BIND(_m, YASP_1D_EQUIDISTANT_OFFSET, _g_layer, _s_backend, _s_type, _p, _la);                \
-    _DUNE_GDT_PROJECTIONS_BIND(_m, YASP_2D_EQUIDISTANT_OFFSET, _g_layer, _s_backend, _s_type, _p, _la)
-
-#  define _DUNE_GDT_PROJECTIONS_BIND_ALL_GRIDS(_m, _g_layer, _s_backend, _s_type, _p, _la)                             \
-    _DUNE_GDT_PROJECTIONS_BIND_ALBERTA(_m, _g_layer, _s_backend, _s_type, _p, _la);                                    \
-    _DUNE_GDT_PROJECTIONS_BIND_ALU(_m, _g_layer, _s_backend, _s_type, _p, _la);                                        \
-    _DUNE_GDT_PROJECTIONS_BIND_UG(_m, _g_layer, _s_backend, _s_type, _p, _la);                                         \
-    _DUNE_GDT_PROJECTIONS_BIND_YASP(_m, _g_layer, _s_backend, _s_type, _p, _la)
-
-// for each space backend
-
-#  define _DUNE_GDT_PROJECTIONS_BIND_DEFAULT(_m, _la)                                                                  \
-    _DUNE_GDT_PROJECTIONS_BIND_ALL_GRIDS(_m, leaf, gdt, fv, 0, _la);                                                   \
-    _DUNE_GDT_PROJECTIONS_BIND_ALL_GRIDS(_m, dd_subdomain, gdt, cg, 1, _la);                                           \
-    _DUNE_GDT_PROJECTIONS_BIND_ALL_GRIDS(_m, level, gdt, fv, 0, _la);                                                  \
-    _DUNE_GDT_PROJECTIONS_BIND_ALL_GRIDS(_m, dd_subdomain, gdt, dg, 1, _la)
-
-
-#  define _DUNE_GDT_PROJECTIONS_BIND_ALL_SPACES(_m, _la) _DUNE_GDT_PROJECTIONS_BIND_DEFAULT(_m, _la);
-
-// for each la backend
-
-//#define _DUNE_GDT_PROJECTIONS_BIND_COMMON(_m) _DUNE_GDT_PROJECTIONS_BIND_ALL_SPACES(_m, common_dense)
-#  define _DUNE_GDT_PROJECTIONS_BIND_COMMON(_m)
-
-//#if HAVE_EIGEN
-//#define _DUNE_GDT_PROJECTIONS_BIND_EIGEN(_m) _DUNE_GDT_PROJECTIONS_BIND_ALL_SPACES(_m, eigen_dense)
-//#else
-#  define _DUNE_GDT_PROJECTIONS_BIND_EIGEN(_m)
-//#endif
-
-#  if HAVE_DUNE_ISTL
-#    define _DUNE_GDT_PROJECTIONS_BIND_ISTL(_m) _DUNE_GDT_PROJECTIONS_BIND_ALL_SPACES(_m, istl_dense)
-#  else
-#    define _DUNE_GDT_PROJECTIONS_BIND_ISTL(_m)
-#  endif
-
-#  define DUNE_GDT_PROJECTIONS_BIND(_m)                                                                                \
-    _DUNE_GDT_PROJECTIONS_BIND_COMMON(_m);                                                                             \
-    _DUNE_GDT_PROJECTIONS_BIND_EIGEN(_m);                                                                              \
-    _DUNE_GDT_PROJECTIONS_BIND_ISTL(_m)
-
-// end: this is what we need for the .so
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_PROJECTIONS_BINDINGS_HH
diff --git a/python/dune/gdt/projections/dirichlet.hh b/python/dune/gdt/projections/dirichlet.hh
deleted file mode 100644
index 50038a1b68ec8134db849d5f4af8e6302bfbcece..0000000000000000000000000000000000000000
--- a/python/dune/gdt/projections/dirichlet.hh
+++ /dev/null
@@ -1,160 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_PROJECTIONS_DIRICHLET_BINDINGS_HH
-#define PYTHON_DUNE_GDT_PROJECTIONS_DIRICHLET_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <python/dune/xt/la/container.bindings.hh>
-
-#  include <dune/gdt/spaces/bindings.hh>
-#  include <dune/gdt/type_traits.hh>
-
-#  include <dune/gdt/projections/dirichlet.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class SP, class V>
-class DirichletProjectionLocalizableOperator
-{
-  typedef typename SP::type SpaceType;
-  static_assert(is_space<SpaceType>::value, "");
-  static_assert(XT::LA::is_vector<V>::value, "");
-  typedef typename SpaceType::GridLayerType GridLayerType;
-  typedef typename XT::Functions::GridFunctionInterface<typename SpaceType::EntityType,
-                                                        typename SpaceType::DomainFieldType,
-                                                        SpaceType::dimDomain,
-                                                        typename SpaceType::RangeFieldType,
-                                                        SpaceType::dimRange,
-                                                        SpaceType::dimRangeCols>
-      SourceType;
-  typedef DiscreteFunction<SpaceType, V> RangeType;
-  typedef XT::Grid::Walker<GridLayerType> BaseType;
-
-public:
-  typedef GDT::DirichletProjectionLocalizableOperator<GridLayerType, SourceType, RangeType, double> type;
-  typedef pybind11::class_<type, BaseType> bound_type;
-
-  static bound_type bind(pybind11::module& m)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    const auto ClassName =
-        XT::Common::to_camel_case("dirichlet_projection_localizable_operator_" + space_name<SP>::value() + "_"
-                                  + XT::LA::bindings::container_name<V>::value());
-
-    bound_type c(m, ClassName.c_str());
-    c.def("apply", [](type& self) { self.apply(); });
-
-    m.def(std::string("make_localizable_dirichlet_projection_operator").c_str(),
-          [](const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<GridLayerType>>& boundary_info,
-             const SourceType& source,
-             RangeType& range) {
-            return make_localizable_dirichlet_projection_operator(
-                       range.space().grid_layer(), boundary_info, source, range)
-                .release();
-          },
-          "boundary_info"_a,
-          "source"_a,
-          "range"_a,
-          py::keep_alive<0, 1>(),
-          py::keep_alive<0, 2>(),
-          py::keep_alive<0, 3>());
-
-    return c;
-  } // ... bind(...)
-}; // class DirichletProjectionLocalizableOperator
-
-
-// begin: this is what we need for the .so
-
-#  define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND(_m, _GRID, _layer, _backend, _r, _rC, _la)                              \
-    Dune::GDT::bindings::DirichletProjectionLocalizableOperator<                                                       \
-        Dune::GDT::                                                                                                    \
-            CgSpaceProvider<_GRID, Dune::XT::Grid::Layers::_layer, Dune::GDT::Backends::_backend, 1, double, _r, _rC>, \
-        typename Dune::XT::LA::Container<double, Dune::XT::LA::Backends::_la>::VectorType>::bind(_m)
-
-/*
-#if HAVE_ALBERTA
-#define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_ALBERTA(_m, _layer, _backend, _la)                                      \
-  _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND(_m, ALBERTA_2D, _layer, _backend, 1, 1, _la)
-#else
-*/
-#  define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_ALBERTA(_m, _layer, _backend, _la)
-//#endif
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_ALU(_m, _layer, _backend, _la)                                        \
-      _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND(_m, ALU_2D_SIMPLEX_CONFORMING, _layer, _backend, 1, 1, _la)
-#  else
-#    define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_ALU(_m, _layer, _backend, _la)
-#  endif
-
-/*
-#if HAVE_DUNE_UGGRID || HAVE_UG
-#define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_UG(_m, _layer, _backend, _la)                                           \
-  _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND(_m, UG_2D, _layer, _backend, 1, 1, _la)
-#else
-*/
-#  define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_UG(_m, _layer, _backend, _la)
-//#endif
-
-#  define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_YASP(_m, _layer, _backend, _la)                                         \
-    _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND(_m, YASP_1D_EQUIDISTANT_OFFSET, _layer, _backend, 1, 1, _la);                 \
-    _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND(_m, YASP_2D_EQUIDISTANT_OFFSET, _layer, _backend, 1, 1, _la)
-
-#  define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_GDT(_m, _la)                                                            \
-    _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_ALBERTA(_m, leaf, gdt, _la);                                                  \
-    _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_ALU(_m, leaf, gdt, _la);                                                      \
-    _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_UG(_m, leaf, gdt, _la);                                                       \
-    _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_YASP(_m, leaf, gdt, _la)
-
-
-#  define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_BACKENDS(_m, _la) _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_GDT(_m, _la);
-
-#  define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_COMMON(_m)
-//_DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_BACKENDS(_m, common_dense)
-
-//#if HAVE_EIGEN
-//#define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_EIGEN(_m) _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_BACKENDS(_m, eigen_dense)
-//#else
-#  define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_EIGEN(_m)
-//#endif
-
-#  if HAVE_DUNE_ISTL
-#    define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_ISTL(_m) _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_BACKENDS(_m, istl_dense)
-#  else
-#    define _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_ISTL(_m)
-#  endif
-
-
-#  define DUNE_GDT_PROJECTIONS_DIRICHLET_BIND(_m)                                                                      \
-    _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_COMMON(_m);                                                                   \
-    _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_EIGEN(_m);                                                                    \
-    _DUNE_GDT_PROJECTIONS_DIRICHLET_BIND_ISTL(_m)
-
-// end: this is what we need for the .so
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_PROJECTIONS_DIRICHLET_BINDINGS_HH
diff --git a/python/dune/gdt/shared.hh b/python/dune/gdt/shared.hh
deleted file mode 100644
index bd622d28ab1f7918c5afb5b8a3eda5279aebed75..0000000000000000000000000000000000000000
--- a/python/dune/gdt/shared.hh
+++ /dev/null
@@ -1,76 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017)
-//   René Fritze     (2018)
-//
-// Created by r_milk01 on 2/7/18.
-
-#ifndef PYTHON_DUNE_GDT_SHARED_HH
-#define PYTHON_DUNE_GDT_SHARED_HH
-
-#if HAVE_DUNE_PYBINDXI
-
-#  include <dune/common/parallel/mpihelper.hh>
-
-#  include <dune/pybindxi/pybind11.h>
-#  include <dune/pybindxi/stl.h>
-
-#  include <python/dune/xt/common/bindings.hh>
-#  include <dune/xt/common/numeric_cast.hh>
-#  include <dune/xt/common/timedlogging.hh>
-
-void add_initialization(pybind11::module& m, std::string logger_name)
-{
-  namespace py = pybind11;
-  using namespace pybind11::literals;
-
-  m.def("_init_mpi",
-        [](const std::vector<std::string>& args) {
-          int argc = Dune::XT::Common::numeric_cast<int>(args.size());
-          char** argv = Dune::XT::Common::vector_to_main_args(args);
-          Dune::MPIHelper::instance(argc, argv);
-        },
-        "args"_a = std::vector<std::string>());
-
-  m.def("_init_logger",
-        [](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) {
-          Dune::XT::Common::TimedLogger().create(
-              max_info_level, max_debug_level, enable_warnings, enable_colors, info_color, debug_color, warning_color);
-        },
-        "max_info_level"_a = std::numeric_limits<ssize_t>::max(),
-        "max_debug_level"_a = std::numeric_limits<ssize_t>::max(),
-        "enable_warnings"_a = true,
-        "enable_colors"_a = true,
-        "info_color"_a = "blue",
-        "debug_color"_a = "darkgray",
-        "warning_color"_a = "red");
-
-  m.def("_test_logger",
-        [=](const bool info, const bool debug, const bool warning) {
-          auto logger = Dune::XT::Common::TimedLogger().get(logger_name);
-          if (info)
-            logger.info() << "info logging works!" << std::endl;
-          if (debug)
-            logger.debug() << "debug logging works!" << std::endl;
-          if (warning)
-            logger.warn() << "warning logging works!" << std::endl;
-        },
-        "info"_a = true,
-        "debug"_a = true,
-        "warning"_a = true);
-}
-
-#endif // HAVE_DUNE_PYBINDXI
-
-#endif // PYTHON_DUNE_GDT_SHARED_HH
diff --git a/python/dune/gdt/spaces/bindings.hh b/python/dune/gdt/spaces/bindings.hh
deleted file mode 100644
index fed318daa80532403034da6168c748afe2be4e35..0000000000000000000000000000000000000000
--- a/python/dune/gdt/spaces/bindings.hh
+++ /dev/null
@@ -1,26 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_SPACES_BINDINGS_HH
-#define PYTHON_DUNE_GDT_SPACES_BINDINGS_HH
-
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/gdt/spaces.hh>
-#  include <python/dune/gdt/spaces/interface.hh>
-#  include <python/dune/gdt/spaces/cg.hh>
-#  include <python/dune/gdt/spaces/dg.hh>
-#  include <python/dune/gdt/spaces/fv.hh>
-#  include <python/dune/gdt/spaces/rt.hh>
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_SPACES_BINDINGS_HH
diff --git a/python/dune/gdt/spaces/cg.hh b/python/dune/gdt/spaces/cg.hh
deleted file mode 100644
index 56104c9d5c3f35c97ab59f743e08abd028c1def9..0000000000000000000000000000000000000000
--- a/python/dune/gdt/spaces/cg.hh
+++ /dev/null
@@ -1,86 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_SPACES_CG_BINDINGS_HH
-#define PYTHON_DUNE_GDT_SPACES_CG_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <python/dune/xt/grid/grids.bindings.hh>
-
-#  include <dune/gdt/spaces/cg.hh>
-#  include <python/dune/gdt/spaces/interface.hh>
-
-
-// begin: this is what we need for the .so
-
-#  define _DUNE_GDT_SPACES_CG_BIND(_m, _GRID, _layer, _r, _rC)                                                         \
-    Dune::GDT::bindings::SpaceInterface<                                                                               \
-        Dune::GDT::                                                                                                    \
-            CgSpaceProvider<_GRID, Dune::XT::Grid::Layers::_layer, Dune::GDT::Backends::gdt, 1, double, _r, _rC>>::    \
-        bind(_m)
-
-/*
-#if HAVE_ALBERTA
-#define _DUNE_GDT_SPACES_CG_BIND_ALBERTA_LAYER(_m, _layer)                                                       \
-  _DUNE_GDT_SPACES_CG_BIND(_m, ALBERTA_2D, _layer, 1, 1)
-#define _DUNE_GDT_SPACES_CG_BIND_ALBERTA(_m)                                                                     \
-  _DUNE_GDT_SPACES_CG_BIND_ALBERTA_LAYER(_m, dd_subdomain);                                                      \
-  _DUNE_GDT_SPACES_CG_BIND_ALBERTA_LAYER(_m, leaf);                                                              \
-  _DUNE_GDT_SPACES_CG_BIND_ALBERTA_LAYER(_m, level)
-#else
-*/
-#  define _DUNE_GDT_SPACES_CG_BIND_ALBERTA(_m)
-//#endif
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_SPACES_CG_BIND_ALU_LAYER(_m, _layer)                                                             \
-      _DUNE_GDT_SPACES_CG_BIND(_m, ALU_2D_SIMPLEX_CONFORMING, _layer, 1, 1)
-#    define _DUNE_GDT_SPACES_CG_BIND_ALU(_m)                                                                           \
-      _DUNE_GDT_SPACES_CG_BIND_ALU_LAYER(_m, dd_subdomain);                                                            \
-      _DUNE_GDT_SPACES_CG_BIND_ALU_LAYER(_m, leaf);                                                                    \
-      _DUNE_GDT_SPACES_CG_BIND_ALU_LAYER(_m, level)
-#  else
-#    define _DUNE_GDT_SPACES_CG_BIND_ALU(_m)
-#  endif
-
-/*
-#if HAVE_DUNE_UGGRID || HAVE_UG
-#define _DUNE_GDT_SPACES_CG_BIND_UG_LAYER(_m, _layer) _DUNE_GDT_SPACES_CG_BIND(_m, UG_2D, _layer, 1, 1)
-#define _DUNE_GDT_SPACES_CG_BIND_UG(_m)                                                                          \
-  _DUNE_GDT_SPACES_CG_BIND_UG_LAYER(_m, dd_subdomain);                                                           \
-  _DUNE_GDT_SPACES_CG_BIND_UG_LAYER(_m, leaf);                                                                   \
-  _DUNE_GDT_SPACES_CG_BIND_UG_LAYER(_m, level)
-#else
-*/
-#  define _DUNE_GDT_SPACES_CG_BIND_UG(_m)
-//#endif
-
-#  define _DUNE_GDT_SPACES_CG_BIND_YASP_LAYER(_m, _layer)                                                              \
-    _DUNE_GDT_SPACES_CG_BIND(_m, YASP_1D_EQUIDISTANT_OFFSET, _layer, 1, 1);                                            \
-    _DUNE_GDT_SPACES_CG_BIND(_m, YASP_2D_EQUIDISTANT_OFFSET, _layer, 1, 1)
-#  define _DUNE_GDT_SPACES_CG_BIND_YASP(_m)                                                                            \
-    _DUNE_GDT_SPACES_CG_BIND_YASP_LAYER(_m, dd_subdomain);                                                             \
-    _DUNE_GDT_SPACES_CG_BIND_YASP_LAYER(_m, leaf);                                                                     \
-    _DUNE_GDT_SPACES_CG_BIND_YASP_LAYER(_m, level)
-
-#  define DUNE_GDT_SPACES_CG_BIND(_m)                                                                                  \
-    _DUNE_GDT_SPACES_CG_BIND_ALBERTA(_m);                                                                              \
-    _DUNE_GDT_SPACES_CG_BIND_ALU(_m);                                                                                  \
-    _DUNE_GDT_SPACES_CG_BIND_UG(_m);                                                                                   \
-    _DUNE_GDT_SPACES_CG_BIND_YASP(_m)
-
-
-// end: this is what we need for the .so
-
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_SPACES_CG_BINDINGS_HH
diff --git a/python/dune/gdt/spaces/constraints.hh b/python/dune/gdt/spaces/constraints.hh
deleted file mode 100644
index 99bff702b6c3faca69c15f184659770c4232cafe..0000000000000000000000000000000000000000
--- a/python/dune/gdt/spaces/constraints.hh
+++ /dev/null
@@ -1,245 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_SPACES_CONSTRAINTS_BINDINGS_HH
-#define PYTHON_DUNE_GDT_SPACES_CONSTRAINTS_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <boost/numeric/conversion/cast.hpp>
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <dune/xt/common/exceptions.hh>
-#  include <dune/xt/common/type_traits.hh>
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <dune/xt/grid/type_traits.hh>
-#  include <dune/xt/la/container.hh>
-#  include <python/dune/xt/la/container.bindings.hh>
-
-#  include <dune/gdt/assembler/system.hh>
-#  include <dune/gdt/spaces/cg/interface.hh>
-#  include <dune/gdt/spaces/cg.hh>
-
-#  include <dune/gdt/spaces/constraints.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <class I, class G>
-class DirichletConstraints
-{
-public:
-  typedef GDT::DirichletConstraints<I> type;
-  typedef pybind11::class_<type> bound_type;
-
-  static bound_type bind(pybind11::module& m, const std::string& layer_name)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    const auto grid_name = XT::Grid::bindings::grid_name<G>::value();
-    const auto ClassName = XT::Common::to_camel_case("DirichletConstraints_" + layer_name + "_" + grid_name);
-
-    bound_type c(m, ClassName.c_str(), ClassName.c_str());
-    c.def("__init__",
-          [](type& self, const XT::Grid::BoundaryInfo<I>& boundary_info, const ssize_t size, const bool set) {
-            try {
-              new (&self) type(boundary_info, boost::numeric_cast<size_t>(size), set);
-            } catch (boost::bad_numeric_cast& ee) {
-              DUNE_THROW(XT::Common::Exceptions::wrong_input_given,
-                         "Given size has to be positive!\n\n The error in boost while converting '"
-                             << size
-                             << "' to '"
-                             << XT::Common::Typename<size_t>::value()
-                             << "' was: "
-                             << ee.what());
-            }
-          },
-          "boundary_info"_a,
-          "size"_a,
-          "set_diagonal_entries"_a = true,
-          py::keep_alive<1, 2>());
-    c.def("boundary_info", &type::boundary_info);
-    c.def("size", &type::size);
-
-    m.def(std::string("make_dirichlet_constraints").c_str(),
-          [](const XT::Grid::BoundaryInfo<I>& boundary_info, const ssize_t size, const bool set) {
-            size_t size__as_size_t = 0;
-            try {
-              size__as_size_t = boost::numeric_cast<size_t>(size);
-            } catch (boost::bad_numeric_cast& ee) {
-              DUNE_THROW(XT::Common::Exceptions::wrong_input_given,
-                         "Given size has to be positive!\n\n The error in boost while converting '"
-                             << size
-                             << "' to '"
-                             << XT::Common::Typename<size_t>::value()
-                             << "' was: "
-                             << ee.what());
-            }
-            return type(boundary_info, size__as_size_t, set);
-          },
-          "boundary_info"_a,
-          "size"_a,
-          "set_diagonal_entries"_a,
-          py::keep_alive<0, 1>());
-
-    return c;
-  } // ... bind(...)
-
-  template <XT::LA::Backends la_backend, class R = double>
-  static void addbind(bound_type& c)
-  {
-    typedef typename XT::LA::Container<R, la_backend>::MatrixType M;
-    typedef typename XT::LA::Container<R, la_backend>::VectorType V;
-    using namespace pybind11::literals;
-
-    c.def("apply", [](const type& self, M& matrix) { self.apply(matrix); }, "matrix"_a);
-    c.def("apply", [](const type& self, V& vector) { self.apply(vector); }, "vector"_a);
-    c.def("apply", [](const type& self, M& matrix, V& vector) { self.apply(matrix, vector); }, "matrix"_a, "vector"_a);
-  } // ... addbind(...)
-
-private:
-  template <class T, bool is_cg = is_cg_space<T>::value>
-  struct addbind_assembler
-  {
-    template <class GL, class A>
-    void operator()(pybind11::class_<GDT::SystemAssembler<T, GL, A>, XT::Grid::Walker<GL>>& bound_system_assembler)
-    {
-      using namespace pybind11::literals;
-
-      bound_system_assembler.def("append",
-                                 [](GDT::SystemAssembler<T, GL, A>& self,
-                                    GDT::DirichletConstraints<XT::Grid::extract_intersection_t<GL>>& constraints) {
-                                   self.append(constraints);
-                                 },
-                                 "dirichlet_constraints"_a);
-    } // ... addbind(...)
-  }; // struct addbind_assembler
-
-  template <class T>
-  struct addbind_assembler<T, false>
-  {
-    template <class GL, class A>
-    void operator()(pybind11::class_<GDT::SystemAssembler<T, GL, A>, XT::Grid::Walker<GL>>& /*bound_system_assembler*/)
-    {
-    }
-  };
-
-public:
-  template <class T, class GL, class A>
-  static void addbind(pybind11::class_<GDT::SystemAssembler<T, GL, A>, XT::Grid::Walker<GL>>& bound_system_assembler)
-  {
-    addbind_assembler<T>()(bound_system_assembler);
-  }
-}; // class DirichletConstraints
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-
-// begin: this is what we need for the .so
-
-#  define _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA(_c, _GRID, _layer, _backend, _la)                                    \
-    Dune::GDT::bindings::DirichletConstraints<Dune::XT::Grid::extract_intersection_t<typename Dune::XT::Grid::Layer<   \
-                                                  _GRID,                                                               \
-                                                  Dune::XT::Grid::Layers::_layer,                                      \
-                                                  Dune::XT::Grid::Backends::_backend,                                  \
-                                                  Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,                    \
-                                              _GRID>::addbind<Dune::XT::LA::Backends::_la>(_c)
-
-#  define _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA_COMMON(_c, _GRID, _layer, _backend)
-//  _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA(_c, _GRID, _layer, _backend, common_dense)
-
-/*
-#if HAVE_EIGEN
-#define _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA_EIGEN(_c, _GRID, _layer, _backend)                                   \
-  _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA(_c, _GRID, _layer, _backend, eigen_sparse)
-#else
-*/
-#  define _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA_EIGEN(_c, _GRID, _layer, _backend)
-//#endif
-
-#  if HAVE_DUNE_ISTL
-#    define _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA_ISTL(_c, _GRID, _layer, _backend)                                  \
-      _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA(_c, _GRID, _layer, _backend, istl_sparse)
-#  else
-#    define _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA_ISTL(_c, _GRID, _layer, _backend)
-#  endif
-
-#  define _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA_ALL(_c, _GRID, _layer, _backend)                                     \
-    _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA_COMMON(_c, _GRID, _layer, _backend);                                       \
-    _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA_EIGEN(_c, _GRID, _layer, _backend);                                        \
-    _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA_ISTL(_c, _GRID, _layer, _backend)
-
-#  define _DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m, _GRID, _layer, _backend, _layer_name)                                  \
-    auto dirichlet_constraints_##_GRID##_##_layer##_##_backend = Dune::GDT::bindings::DirichletConstraints<            \
-        Dune::XT::Grid::extract_intersection_t<                                                                        \
-            typename Dune::XT::Grid::Layer<_GRID,                                                                      \
-                                           Dune::XT::Grid::Layers::_layer,                                             \
-                                           Dune::XT::Grid::Backends::_backend,                                         \
-                                           Dune::XT::Grid::DD::SubdomainGrid<_GRID>>::type>,                           \
-        _GRID>::bind(_m, _layer_name);                                                                                 \
-    _DUNE_GDT_SPACES_CONSTRAINTS_ADDBIND_LA_ALL(                                                                       \
-        dirichlet_constraints_##_GRID##_##_layer##_##_backend, _GRID, _layer, _backend)
-
-
-/*
-#if HAVE_ALBERTA
-#define _DUNE_GDT_SPACES_CONSTRAINTS_BIND_ALBERTA(_m)                                                                \
-  _DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m, ALBERTA_2D, leaf, view, "");                                                 \
-  _DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m, ALBERTA_2D, dd_subdomain, part, "dd_subdomain")
-#else
-*/
-#  define _DUNE_GDT_SPACES_CONSTRAINTS_BIND_ALBERTA(_m)
-//#endif
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_SPACES_CONSTRAINTS_BIND_ALU(_m)                                                                  \
-      _DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m, ALU_2D_SIMPLEX_CONFORMING, leaf, view, "leaf");                            \
-      _DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m, ALU_2D_SIMPLEX_CONFORMING, level, view, "level");                          \
-      _DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m, ALU_2D_SIMPLEX_CONFORMING, dd_subdomain, view, "dd_subdomain")
-#  else
-#    define _DUNE_GDT_SPACES_CONSTRAINTS_BIND_ALU(_m)
-#  endif
-
-/*
-#if HAVE_DUNE_UGGRID
-#define _DUNE_GDT_SPACES_CONSTRAINTS_BIND_UG(_m)                                                                     \
-  _DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m, UG_2D, leaf, view, "leaf");                                                  \
-  _DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m, UG_2D, level, view, "level");                                                \
-  _DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m, UG_2D, dd_subdomain, part, "dd_subdomain")
-#else
-*/
-#  define _DUNE_GDT_SPACES_CONSTRAINTS_BIND_UG(_m)
-//#endif
-
-#  define _DUNE_GDT_SPACES_CONSTRAINTS_BIND_YASP(_m)                                                                   \
-    _DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m, YASP_1D_EQUIDISTANT_OFFSET, leaf, view, "");                                 \
-    _DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m, YASP_1D_EQUIDISTANT_OFFSET, dd_subdomain, view, "dd_subdomain");             \
-    _DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m, YASP_2D_EQUIDISTANT_OFFSET, leaf, view, "");                                 \
-    _DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m, YASP_2D_EQUIDISTANT_OFFSET, dd_subdomain, view, "dd_subdomain")
-
-#  define DUNE_GDT_SPACES_CONSTRAINTS_BIND(_m)                                                                         \
-    _DUNE_GDT_SPACES_CONSTRAINTS_BIND_ALBERTA(_m);                                                                     \
-    _DUNE_GDT_SPACES_CONSTRAINTS_BIND_ALU(_m);                                                                         \
-    _DUNE_GDT_SPACES_CONSTRAINTS_BIND_UG(_m);                                                                          \
-    _DUNE_GDT_SPACES_CONSTRAINTS_BIND_YASP(_m)
-
-// end: this is what we need for the .so
-
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_SPACES_CONSTRAINTS_BINDINGS_HH
diff --git a/python/dune/gdt/spaces/dg.hh b/python/dune/gdt/spaces/dg.hh
deleted file mode 100644
index 6db34ad25b577243861de4f0ca3c58a54935c711..0000000000000000000000000000000000000000
--- a/python/dune/gdt/spaces/dg.hh
+++ /dev/null
@@ -1,83 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_SPACES_DG_BINDINGS_HH
-#define PYTHON_DUNE_GDT_SPACES_DG_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <python/dune/xt/grid/grids.bindings.hh>
-
-#  include <dune/gdt/spaces/dg.hh>
-#  include <python/dune/gdt/spaces/interface.hh>
-
-
-// begin: this is what we need for the .so
-
-#  define _DUNE_GDT_SPACES_DG_BIND(_m, _GRID, _layer, _r, _rC)                                                         \
-    Dune::GDT::bindings::SpaceInterface<                                                                               \
-        Dune::GDT::                                                                                                    \
-            DgSpaceProvider<_GRID, Dune::XT::Grid::Layers::_layer, Dune::GDT::Backends::gdt, 1, double, _r, _rC>>::    \
-        bind(_m)
-
-/*
-#if HAVE_ALBERTA
-#define _DUNE_GDT_SPACES_DG_BIND_ALBERTA_LAYER(_m, _layer)                                                       \
-  _DUNE_GDT_SPACES_DG_BIND(_m, ALBERTA_2D, _layer, 1, 1)
-#define _DUNE_GDT_SPACES_DG_BIND_ALBERTA(_m)                                                                     \
-  _DUNE_GDT_SPACES_DG_BIND_ALBERTA_LAYER(_m, dd_subdomain);                                                      \
-  _DUNE_GDT_SPACES_DG_BIND_ALBERTA_LAYER(_m, leaf);                                                              \
-  _DUNE_GDT_SPACES_DG_BIND_ALBERTA_LAYER(_m, level)
-#else
-*/
-#  define _DUNE_GDT_SPACES_DG_BIND_ALBERTA(_m)
-//#endif
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_SPACES_DG_BIND_ALU_LAYER(_m, _layer)                                                             \
-      _DUNE_GDT_SPACES_DG_BIND(_m, ALU_2D_SIMPLEX_CONFORMING, _layer, 1, 1)
-#    define _DUNE_GDT_SPACES_DG_BIND_ALU(_m)                                                                           \
-      _DUNE_GDT_SPACES_DG_BIND_ALU_LAYER(_m, dd_subdomain);                                                            \
-      _DUNE_GDT_SPACES_DG_BIND_ALU_LAYER(_m, leaf);                                                                    \
-      _DUNE_GDT_SPACES_DG_BIND_ALU_LAYER(_m, level)
-#  else
-#    define _DUNE_GDT_SPACES_DG_BIND_ALU(_m)
-#  endif
-
-//#if HAVE_DUNE_UGGRID || HAVE_UG // <- does not work
-//#define _DUNE_GDT_SPACES_DG_BIND_UG_LAYER(_m, _layer) _DUNE_GDT_SPACES_DG_BIND(_m, UG_2D, _layer, 1, 1)
-//#define _DUNE_GDT_SPACES_DG_BIND_UG(_m)
-//  _DUNE_GDT_SPACES_DG_BIND_UG_LAYER(_m, dd_subdomain);
-//  _DUNE_GDT_SPACES_DG_BIND_UG_LAYER(_m, leaf);
-//  _DUNE_GDT_SPACES_DG_BIND_UG_LAYER(_m, level)
-//#else
-//#define _DUNE_GDT_SPACES_DG_BIND_UG(_m)
-//#endif
-
-#  define _DUNE_GDT_SPACES_DG_BIND_YASP_LAYER(_m, _layer)                                                              \
-    _DUNE_GDT_SPACES_DG_BIND(_m, YASP_1D_EQUIDISTANT_OFFSET, _layer, 1, 1);                                            \
-    _DUNE_GDT_SPACES_DG_BIND(_m, YASP_2D_EQUIDISTANT_OFFSET, _layer, 1, 1)
-#  define _DUNE_GDT_SPACES_DG_BIND_YASP(_m)                                                                            \
-    _DUNE_GDT_SPACES_DG_BIND_YASP_LAYER(_m, dd_subdomain);                                                             \
-    _DUNE_GDT_SPACES_DG_BIND_YASP_LAYER(_m, leaf);                                                                     \
-    _DUNE_GDT_SPACES_DG_BIND_YASP_LAYER(_m, level)
-
-#  define DUNE_GDT_SPACES_DG_BIND(_m)                                                                                  \
-    _DUNE_GDT_SPACES_DG_BIND_ALBERTA(_m);                                                                              \
-    _DUNE_GDT_SPACES_DG_BIND_ALU(_m);                                                                                  \
-    _DUNE_GDT_SPACES_DG_BIND_YASP(_m)
-//_DUNE_GDT_SPACES_DG_BIND_UG(_m); // <- does not work
-
-// end: this is what we need for the .so
-
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_SPACES_DG_BINDINGS_HH
diff --git a/python/dune/gdt/spaces/fv.hh b/python/dune/gdt/spaces/fv.hh
deleted file mode 100644
index 2b31c30d37c5be0b298fdb5f2750e7484989bf12..0000000000000000000000000000000000000000
--- a/python/dune/gdt/spaces/fv.hh
+++ /dev/null
@@ -1,80 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_SPACES_FV_BINDINGS_HH
-#define PYTHON_DUNE_GDT_SPACES_FV_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <python/dune/xt/grid/grids.bindings.hh>
-
-#  include <dune/gdt/spaces/fv.hh>
-#  include <python/dune/gdt/spaces/interface.hh>
-
-
-// begin: this is what we need for the .so
-
-#  define _DUNE_GDT_SPACES_FV_BIND_GDT(_m, _GRID, _layer, _r, _rC)                                                     \
-    Dune::GDT::bindings::SpaceInterface<                                                                               \
-        Dune::GDT::                                                                                                    \
-            FvSpaceProvider<_GRID, Dune::XT::Grid::Layers::_layer, Dune::GDT::Backends::gdt, double, _r, _rC>>::       \
-        bind(_m)
-
-/*
-#if HAVE_ALBERTA
-#define _DUNE_GDT_SPACES_FV_BIND_GDT_ALBERTA_LAYER(_m, _layer)                                                       \
-  _DUNE_GDT_SPACES_FV_BIND_GDT(_m, ALBERTA_2D, _layer, 1, 1)
-#define _DUNE_GDT_SPACES_FV_BIND_GDT_ALBERTA(_m)                                                                     \
-  _DUNE_GDT_SPACES_FV_BIND_GDT_ALBERTA_LAYER(_m, leaf);                                                              \
-  _DUNE_GDT_SPACES_FV_BIND_GDT_ALBERTA_LAYER(_m, level)
-#else
-*/
-#  define _DUNE_GDT_SPACES_FV_BIND_GDT_ALBERTA(_m)
-//#endif
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_SPACES_FV_BIND_GDT_ALU_LAYER(_m, _layer)                                                         \
-      _DUNE_GDT_SPACES_FV_BIND_GDT(_m, ALU_2D_SIMPLEX_CONFORMING, _layer, 1, 1)
-#    define _DUNE_GDT_SPACES_FV_BIND_GDT_ALU(_m)                                                                       \
-      _DUNE_GDT_SPACES_FV_BIND_GDT_ALU_LAYER(_m, leaf);                                                                \
-      _DUNE_GDT_SPACES_FV_BIND_GDT_ALU_LAYER(_m, level)
-#  else
-#    define _DUNE_GDT_SPACES_FV_BIND_GDT_ALU(_m)
-#  endif
-
-/*
-#if HAVE_DUNE_UGGRID || HAVE_UG
-#define _DUNE_GDT_SPACES_FV_BIND_GDT_UG_LAYER(_m, _layer) _DUNE_GDT_SPACES_FV_BIND_GDT(_m, UG_2D, _layer, 1, 1)
-#define _DUNE_GDT_SPACES_FV_BIND_GDT_UG(_m)                                                                          \
-  _DUNE_GDT_SPACES_FV_BIND_GDT_UG_LAYER(_m, leaf);                                                                   \
-  _DUNE_GDT_SPACES_FV_BIND_GDT_UG_LAYER(_m, level)
-#else
-*/
-#  define _DUNE_GDT_SPACES_FV_BIND_GDT_UG(_m)
-//#endif
-
-#  define _DUNE_GDT_SPACES_FV_BIND_GDT_YASP_LAYER(_m, _layer)                                                          \
-    _DUNE_GDT_SPACES_FV_BIND_GDT(_m, YASP_2D_EQUIDISTANT_OFFSET, _layer, 1, 1)
-#  define _DUNE_GDT_SPACES_FV_BIND_GDT_YASP(_m)                                                                        \
-    _DUNE_GDT_SPACES_FV_BIND_GDT_YASP_LAYER(_m, leaf);                                                                 \
-    _DUNE_GDT_SPACES_FV_BIND_GDT_YASP_LAYER(_m, level)
-
-#  define DUNE_GDT_SPACES_FV_BIND(_m)                                                                                  \
-    _DUNE_GDT_SPACES_FV_BIND_GDT_ALBERTA(_m);                                                                          \
-    _DUNE_GDT_SPACES_FV_BIND_GDT_ALU(_m);                                                                              \
-    _DUNE_GDT_SPACES_FV_BIND_GDT_UG(_m);                                                                               \
-    _DUNE_GDT_SPACES_FV_BIND_GDT_YASP(_m)
-
-// end: this is what we need for the .so
-
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_SPACES_FV_BINDINGS_HH
diff --git a/python/dune/gdt/spaces/interface.hh b/python/dune/gdt/spaces/interface.hh
deleted file mode 100644
index 9871cae7aa06b7cc0bf3922ef037374584524632..0000000000000000000000000000000000000000
--- a/python/dune/gdt/spaces/interface.hh
+++ /dev/null
@@ -1,557 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_SPACES_INTERFACE_BINDINGS_HH
-#define PYTHON_DUNE_GDT_SPACES_INTERFACE_BINDINGS_HH
-// TODO: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <dune/pybindxi/pybind11.h>
-
-#  include <dune/xt/common/string.hh>
-#  include <python/dune/xt/grid/grids.bindings.hh>
-#  include <python/dune/xt/grid/layers.bindings.hh>
-#  include <dune/xt/grid/dd/subdomains/grid.hh>
-#  include <dune/xt/grid/gridprovider/provider.hh>
-#  include <dune/xt/grid/type_traits.hh>
-
-#  include <dune/gdt/playground/spaces/restricted.hh>
-#  include <dune/gdt/spaces.hh>
-#  include <dune/gdt/type_traits.hh>
-
-#  include <dune/gdt/spaces/interface.hh>
-#  include <dune/gdt/spaces/cg.hh>
-#  include <dune/gdt/spaces/dg.hh>
-#  include <dune/gdt/spaces/fv.hh>
-
-namespace Dune {
-namespace GDT {
-namespace bindings {
-
-
-template <Backends backend>
-struct backend_name
-{
-  static_assert(AlwaysFalse<typename internal::backend_dependent_typename<backend>::type>::value,
-                "Please add a specialization for this backend!");
-
-  static std::string value()
-  {
-    return "";
-  }
-};
-
-template <>
-struct backend_name<Backends::gdt>
-{
-  static std::string value()
-  {
-    return "gdt";
-  }
-};
-
-
-template <SpaceType tp>
-struct space_type_name
-{
-  static_assert(AlwaysFalse<typename internal::space_type_dependent_typename<tp>::type>::value,
-                "Please add a specialization for this space type!");
-
-  static std::string value()
-  {
-    return "";
-  }
-};
-
-template <>
-struct space_type_name<SpaceType::cg>
-{
-  static std::string value()
-  {
-    return "cg";
-  }
-};
-
-template <>
-struct space_type_name<SpaceType::block_cg>
-{
-  static std::string value()
-  {
-    return "block_cg";
-  }
-};
-
-template <>
-struct space_type_name<SpaceType::dg>
-{
-  static std::string value()
-  {
-    return "dg";
-  }
-};
-
-template <>
-struct space_type_name<SpaceType::block_dg>
-{
-  static std::string value()
-  {
-    return "block_dg";
-  }
-};
-
-template <>
-struct space_type_name<SpaceType::fv>
-{
-  static std::string value()
-  {
-    return "fv";
-  }
-};
-
-template <>
-struct space_type_name<SpaceType::block_fv>
-{
-  static std::string value()
-  {
-    return "block_fv";
-  }
-};
-
-template <>
-struct space_type_name<SpaceType::rt>
-{
-  static std::string value()
-  {
-    return "rt";
-  }
-};
-
-template <>
-struct space_type_name<SpaceType::block_rt>
-{
-  static std::string value()
-  {
-    return "block_rt";
-  }
-};
-
-
-namespace internal {
-
-
-template <class G, XT::Grid::Layers layer, Backends backend, size_t r, size_t rC, XT::Grid::Backends g>
-struct space_name_base
-{
-  static std::string value_wo_grid()
-  {
-    using XT::Common::to_string;
-    return XT::Grid::bindings::layer_name<layer>::value() + "_" + XT::Grid::bindings::backend_name<g>::value() + "_to_"
-           + to_string(r) + "x" + to_string(rC) + "_" + backend_name<backend>::value();
-  }
-
-  static std::string value()
-  {
-    return XT::Grid::bindings::grid_name<G>::value() + "_" + value_wo_grid();
-  }
-};
-
-
-} // namespace internal
-
-
-template <class P>
-struct space_name
-{
-  static_assert(AlwaysFalse<P>::value, "Please add a specialization for this space provider!");
-
-  static std::string value()
-  {
-    return "";
-  }
-};
-
-template <class G, XT::Grid::Layers layer, Backends backend, int p, size_t r, size_t rC, XT::Grid::Backends g>
-struct space_name<CgSpaceProvider<G, layer, backend, p, double, r, rC, g>>
-{
-  static std::string value()
-  {
-    return space_type_name<SpaceType::cg>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value() + "_p" + XT::Common::to_string(p)
-           + "_space";
-  }
-
-  static std::string value_wo_grid()
-  {
-    return space_type_name<SpaceType::cg>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value_wo_grid() + "_p" + XT::Common::to_string(p)
-           + "_space";
-  }
-};
-
-template <class G, XT::Grid::Layers layer, Backends backend, int p, size_t r, size_t rC, XT::Grid::Backends g>
-struct space_name<BlockCgSpaceProvider<G, layer, backend, p, double, r, rC, g>>
-{
-  static std::string value()
-  {
-    return space_type_name<SpaceType::block_cg>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value() + "_p" + XT::Common::to_string(p)
-           + "_space";
-  }
-
-  static std::string value_wo_grid()
-  {
-    return space_type_name<SpaceType::block_cg>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value_wo_grid() + "_p" + XT::Common::to_string(p)
-           + "_space";
-  }
-};
-
-template <class G, XT::Grid::Layers layer, Backends backend, int p, size_t r, size_t rC, XT::Grid::Backends g>
-struct space_name<DgSpaceProvider<G, layer, backend, p, double, r, rC, g>>
-{
-  static std::string value()
-  {
-    return space_type_name<SpaceType::dg>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value() + "_p" + XT::Common::to_string(p)
-           + "_space";
-  }
-
-  static std::string value_wo_grid()
-  {
-    return space_type_name<SpaceType::dg>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value_wo_grid() + "_p" + XT::Common::to_string(p)
-           + "_space";
-  }
-};
-
-template <class G, XT::Grid::Layers layer, Backends backend, int p, size_t r, size_t rC, XT::Grid::Backends g>
-struct space_name<BlockDgSpaceProvider<G, layer, backend, p, double, r, rC, g>>
-{
-  static std::string value()
-  {
-    return space_type_name<SpaceType::block_dg>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value() + "_p" + XT::Common::to_string(p)
-           + "_space";
-  }
-
-  static std::string value_wo_grid()
-  {
-    return space_type_name<SpaceType::block_dg>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value_wo_grid() + "_p" + XT::Common::to_string(p)
-           + "_space";
-  }
-};
-
-template <class G, XT::Grid::Layers layer, Backends backend, size_t r, size_t rC, XT::Grid::Backends g>
-struct space_name<FvSpaceProvider<G, layer, backend, double, r, rC, g>>
-{
-  static std::string value()
-  {
-    return space_type_name<SpaceType::fv>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value() + "_space";
-  }
-
-  static std::string value_wo_grid()
-  {
-    return space_type_name<SpaceType::fv>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value_wo_grid() + "_space";
-  }
-};
-
-template <class G, XT::Grid::Layers layer, Backends backend, size_t r, size_t rC, XT::Grid::Backends g>
-struct space_name<BlockFvSpaceProvider<G, layer, backend, double, r, rC, g>>
-{
-  static std::string value()
-  {
-    return space_type_name<SpaceType::block_fv>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value() + "_space";
-  }
-
-  static std::string value_wo_grid()
-  {
-    return space_type_name<SpaceType::block_fv>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value_wo_grid() + "_space";
-  }
-};
-
-template <class G, XT::Grid::Layers layer, Backends backend, int p, size_t r, size_t rC, XT::Grid::Backends g>
-struct space_name<RtSpaceProvider<G, layer, backend, p, double, r, rC, g>>
-{
-  static std::string value()
-  {
-    return space_type_name<SpaceType::rt>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value() + "_p" + XT::Common::to_string(p)
-           + "_space";
-  }
-
-  static std::string value_wo_grid()
-  {
-    return space_type_name<SpaceType::rt>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value_wo_grid() + "_p" + XT::Common::to_string(p)
-           + "_space";
-  }
-};
-
-template <class G, XT::Grid::Layers layer, Backends backend, int p, size_t r, size_t rC, XT::Grid::Backends g>
-struct space_name<BlockRtSpaceProvider<G, layer, backend, p, double, r, rC, g>>
-{
-  static std::string value()
-  {
-    return space_type_name<SpaceType::block_rt>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value() + "_p" + XT::Common::to_string(p)
-           + "_space";
-  }
-
-  static std::string value_wo_grid()
-  {
-    return space_type_name<SpaceType::block_rt>::value() + "_"
-           + internal::space_name_base<G, layer, backend, r, rC, g>::value_wo_grid() + "_p" + XT::Common::to_string(p)
-           + "_space";
-  }
-};
-
-template <class G,
-          XT::Grid::Layers l,
-          SpaceType tp,
-          Backends b,
-          int p,
-          class R,
-          size_t r,
-          size_t rC,
-          XT::Grid::Backends g>
-struct space_name<SpaceProvider<G, l, tp, b, p, R, r, rC, g>>
-{
-  static std::string value()
-  {
-    return space_type_name<tp>::value() + "_" + internal::space_name_base<G, l, b, r, rC, g>::value() + "_p"
-           + XT::Common::to_string(p) + "_space";
-  }
-
-  static std::string value_wo_grid()
-  {
-    return space_type_name<tp>::value() + "_" + internal::space_name_base<G, l, b, r, rC, g>::value_wo_grid() + "_p"
-           + XT::Common::to_string(p) + "_space";
-  }
-};
-
-
-template <class S>
-class SpaceInterfaceWoFactory
-{
-  static_assert(is_space<S>::value, "");
-
-public:
-  typedef S type;
-  typedef pybind11::class_<type> bound_type;
-
-  static bound_type bind(pybind11::module& m, const std::string& space_name)
-  {
-    namespace py = pybind11;
-    using namespace pybind11::literals;
-
-    const auto ClassName = XT::Common::to_camel_case(space_name /*space_name<SP>::value()*/);
-
-    bound_type c(m, ClassName.c_str(), ClassName.c_str());
-
-    c.def_property_readonly("dimDomain", [](const type& /*self*/) { return S::dimDomain; });
-    c.def_property_readonly("dimRange", [](const type& /*self*/) { return S::dimRange; });
-    c.def_property_readonly("dimRangeCols", [](const type& /*self*/) { return S::dimRangeCols; });
-    c.def_property_readonly("polOrder", [](const type& /*self*/) { return S::polOrder; });
-    c.def_property_readonly_static("dimDomain", [](const type& /*self*/) { return S::dimDomain; });
-    c.def_property_readonly_static("dimRange", [](const type& /*self*/) { return S::dimRange; });
-    c.def_property_readonly_static("dimRangeCols", [](const type& /*self*/) { return S::dimRangeCols; });
-    c.def_property_readonly_static("polOrder", [](const type& /*self*/) { return S::polOrder; });
-
-    c.def("size", [](const type& self) { return self.mapper().size(); });
-    c.def("visualize",
-          [](const type& self, const std::string& filename) { self.visualize(filename); },
-          "filename"_a = "");
-    c.def("compute_pattern",
-          [](const type& self, const std::string tp) {
-            if (tp == "default")
-              return self.compute_pattern();
-            else if (tp == "volume")
-              return self.compute_volume_pattern();
-            else if (tp == "face")
-              return self.compute_face_pattern();
-            else if (tp == "face_and_volume")
-              return self.compute_face_and_volume_pattern();
-            else
-              DUNE_THROW(XT::Common::Exceptions::wrong_input_given,
-                         "  type has to be one of ('default', volume', 'face', 'face_and_volume'), is '" << tp << "'!");
-            // we will never get here
-            return XT::LA::SparsityPatternDefault();
-          },
-          "type"_a = "default");
-
-    return c;
-  } // ... bind(...)
-}; // class SpaceInterfaceWoFactory
-
-
-template <class SP>
-class SpaceInterface
-{
-  typedef typename SP::type S;
-  static_assert(is_space<S>::value, "");
-  using G = XT::Grid::extract_grid_t<typename S::GridLayerType>;
-
-  template <bool is_dd_subdomain_layer = (SP::grid_layer == XT::Grid::Layers::dd_subdomain), bool anything = true>
-  struct factory_methods
-  {
-    static void addbind(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-      const std::string factory_method_name = "make_" + space_name<SP>::value_wo_grid();
-
-      m.def(factory_method_name.c_str(),
-            [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& grid_provider, int level) {
-              return SP::create(grid_provider, level);
-            },
-            "grid_provider"_a,
-            "level"_a = 0,
-            py::keep_alive<0, 1>());
-    }
-  };
-
-  template <bool anything>
-  struct factory_methods<false, anything>
-  {
-    static void addbind(pybind11::module& m)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-      const std::string factory_method_name = "make_" + space_name<SP>::value_wo_grid();
-
-      m.def(factory_method_name.c_str(),
-            [](XT::Grid::GridProvider<G>& grid_provider, int level) { return SP::create(grid_provider, level); },
-            "grid_provider"_a,
-            "level"_a = 0,
-            py::keep_alive<0, 1>());
-      m.def(factory_method_name.c_str(),
-            [](XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& grid_provider, int level) {
-              return SP::create(grid_provider, level);
-            },
-            "grid_provider"_a,
-            "level"_a = 0,
-            py::keep_alive<0, 1>());
-    }
-  };
-
-public:
-  typedef S type;
-  typedef pybind11::class_<type> bound_type;
-
-private:
-  template <XT::Grid::Backends backend,
-            XT::Grid::Layers layer,
-            bool is_dd = (layer == XT::Grid::Layers::dd_subdomain || layer == XT::Grid::Layers::dd_subdomain_boundary
-                          || layer == XT::Grid::Layers::dd_subdomain_coupling
-                          || layer == XT::Grid::Layers::dd_subdomain_oversampled)>
-  struct restriction_methods // true
-  {
-    static void addbind(bound_type& c)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      typedef GDT::RestrictedSpace<S, typename XT::Grid::Layer<G, layer, backend>::type> RestrictedSpaceType;
-
-      c.def(std::string("restrict_to_" + XT::Grid::bindings::layer_name<layer>::value() + "_"
-                        + XT::Grid::bindings::backend_name<backend>::value())
-                .c_str(),
-            [](type& self,
-               XT::Grid::GridProvider<G, XT::Grid::DD::SubdomainGrid<G>>& grid_provider,
-               const int level_or_subdomain = -1) {
-              return RestrictedSpaceType(self, grid_provider.template layer<layer, backend>(level_or_subdomain));
-            },
-            "grid_provider"_a,
-            "level_or_subdomain"_a,
-            py::keep_alive<1, 0>());
-    }
-  }; // struct restriction_methods
-
-  template <XT::Grid::Backends backend, XT::Grid::Layers layer>
-  struct restriction_methods<backend, layer, false>
-  {
-    static void addbind(bound_type& c)
-    {
-      namespace py = pybind11;
-      using namespace pybind11::literals;
-
-      typedef GDT::RestrictedSpace<S, typename XT::Grid::Layer<G, layer, backend>::type> RestrictedSpaceType;
-
-      c.def(std::string("restrict_to_" + XT::Grid::bindings::layer_name<layer>::value() + "_"
-                        + XT::Grid::bindings::backend_name<backend>::value())
-                .c_str(),
-            [](type& self, XT::Grid::GridProvider<G>& grid_provider, const int level = -1) {
-              return RestrictedSpaceType(self, grid_provider.template layer<layer, backend>(level));
-            },
-            "grid_provider"_a,
-            "level"_a,
-            py::keep_alive<1, 0>());
-      restriction_methods<backend, layer, true>::addbind(c);
-    }
-  }; // struct restriction_methods<..., false>
-
-  template <XT::Grid::Backends backend, XT::Grid::Layers layer>
-  static void addbind_restricted(pybind11::module& m, bound_type& c, const std::string sp_name)
-  {
-    using namespace pybind11::literals;
-
-    typedef GDT::RestrictedSpace<S, typename XT::Grid::Layer<G, layer, backend>::type> RestrictedSpaceType;
-
-    try { // we might not be the first to add this RestrictedSpace
-      const auto restricted_space_name = sp_name + "_restricted_to_" + XT::Grid::bindings::layer_name<layer>::value()
-                                         + "_" + XT::Grid::bindings::backend_name<backend>::value();
-      auto restricted_space = SpaceInterfaceWoFactory<RestrictedSpaceType>::bind(m, restricted_space_name);
-      restricted_space.def("restrict",
-                           [](RestrictedSpaceType& self, const XT::LA::IstlDenseVector<double>& unrestricted_vector) {
-                             return self.mapper().restrict(unrestricted_vector);
-                           },
-                           "unrestricted_vector"_a);
-      restricted_space.def("extend",
-                           [](RestrictedSpaceType& self, const XT::LA::IstlDenseVector<double>& restricted_vector) {
-                             return self.mapper().extend(restricted_vector);
-                           },
-                           "restricted_vector"_a);
-    } catch (std::runtime_error&) {
-    }
-
-    restriction_methods<backend, layer>::addbind(c);
-  } // ... addbind_restricted(...)
-
-public:
-  static bound_type bind(pybind11::module& m)
-  {
-    const auto sp_name = space_name<SP>::value();
-    auto c = SpaceInterfaceWoFactory<S>::bind(m, sp_name);
-
-    addbind_restricted<XT::Grid::Backends::view, XT::Grid::Layers::dd_subdomain>(m, c, sp_name);
-    addbind_restricted<XT::Grid::Backends::view, XT::Grid::Layers::dd_subdomain_boundary>(m, c, sp_name);
-    addbind_restricted<XT::Grid::Backends::view, XT::Grid::Layers::dd_subdomain_coupling>(m, c, sp_name);
-    addbind_restricted<XT::Grid::Backends::view, XT::Grid::Layers::dd_subdomain_oversampled>(m, c, sp_name);
-    addbind_restricted<XT::Grid::Backends::view, XT::Grid::Layers::leaf>(m, c, sp_name);
-    addbind_restricted<XT::Grid::Backends::view, XT::Grid::Layers::level>(m, c, sp_name);
-
-    factory_methods<>::addbind(m);
-    return c;
-  } // ... bind(...)
-}; // class SpaceInterface
-
-
-} // namespace bindings
-} // namespace GDT
-} // namespace Dune
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_SPACES_INTERFACE_BINDINGS_HH
diff --git a/python/dune/gdt/spaces/rt.hh b/python/dune/gdt/spaces/rt.hh
deleted file mode 100644
index 1fc867fe474da70d8697757cf06625bbc106c95f..0000000000000000000000000000000000000000
--- a/python/dune/gdt/spaces/rt.hh
+++ /dev/null
@@ -1,50 +0,0 @@
-// This file is part of the dune-gdt project:
-//   https://github.com/dune-community/dune-gdt
-// Copyright 2010-2018 dune-gdt 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 (2017 - 2018)
-//   René Fritze     (2018)
-//   Tobias Leibner  (2018)
-
-#ifndef PYTHON_DUNE_GDT_SPACES_RT_BINDINGS_HH
-#define PYTHON_DUNE_GDT_SPACES_RT_BINDINGS_HH
-// Todo: python bindings need to be updated to the new-master
-#if 0 // HAVE_DUNE_PYBINDXI
-
-#  include <python/dune/xt/grid/grids.bindings.hh>
-
-#  include <python/dune/gdt/spaces/interface.hh>
-#  include <dune/gdt/spaces/hdiv/raviart-thomas.hh>
-
-
-// begin: this is what we need for the .so
-
-#  define _DUNE_GDT_SPACES_RT_BIND(_m, _GRID, _layer)                                                                  \
-    Dune::GDT::bindings::SpaceInterface<Dune::GDT::RtSpaceProvider<_GRID,                                              \
-                                                                   Dune::XT::Grid::Layers::_layer,                     \
-                                                                   Dune::GDT::Backends::gdt,                           \
-                                                                   0,                                                  \
-                                                                   double,                                             \
-                                                                   _GRID::dimension,                                   \
-                                                                   1>>::bind(_m)
-
-#  if HAVE_DUNE_ALUGRID
-#    define _DUNE_GDT_SPACES_RT_BIND_ALU_LAYER(_m, _layer)                                                             \
-      _DUNE_GDT_SPACES_RT_BIND(_m, ALU_2D_SIMPLEX_CONFORMING, _layer)
-#    define _DUNE_GDT_SPACES_RT_BIND_ALU(_m)                                                                           \
-      _DUNE_GDT_SPACES_RT_BIND_ALU_LAYER(_m, leaf);                                                                    \
-      _DUNE_GDT_SPACES_RT_BIND_ALU_LAYER(_m, level)
-#  else
-#    define _DUNE_GDT_SPACES_RT_BIND_ALU(_m)
-#  endif
-
-#  define DUNE_GDT_SPACES_RT_BIND(_m) _DUNE_GDT_SPACES_RT_BIND_ALU(_m)
-
-// end: this is what we need for the .so
-
-
-#endif // HAVE_DUNE_PYBINDXI
-#endif // PYTHON_DUNE_GDT_SPACES_RT_BINDINGS_HH
diff --git a/python/dune/gdt/usercode.cc b/python/dune/gdt/usercode.cc
index b54d6753eac0ef0f17b45ffbf365c044ab2bee32..989cf947ffaedb4e412319882f50ad894e4fd053 100644
--- a/python/dune/gdt/usercode.cc
+++ b/python/dune/gdt/usercode.cc
@@ -25,9 +25,7 @@ PYBIND11_MODULE(usercode, m)
   py::module::import("dune.xt.la");
   py::module::import("dune.xt.grid");
   py::module::import("dune.xt.functions");
-
-  Dune::XT::Common::bindings::addbind_exceptions(m);
-  Dune::XT::Common::bindings::add_initialization(m, "dune.gdt");
+  py::module::import("dune.gdt.discretefunction");
 
   // put your bindings below
 } // PYBIND11_MODULE(usercode, ...)
diff --git a/python/test/base.py b/python/test/base.py
index c3080c308f4ed1887b58d42e800f71edd156c46f..77bae28bcb14eda1fb9c70d4a79ca4020047753f 100644
--- a/python/test/base.py
+++ b/python/test/base.py
@@ -1,4 +1,3 @@
-
 import pytest
 from dune.xt.common.test import load_all_submodule
 
@@ -6,5 +5,3 @@ from dune.xt.common.test import load_all_submodule
 def test_load_all():
     import dune.gdt as gdt
     load_all_submodule(gdt)
-
-