From 4a3d668c59eefa871c668e58afd98a099affdd43 Mon Sep 17 00:00:00 2001
From: Tobias Leibner <tobias.leibner@googlemail.com>
Date: Wed, 26 Jun 2019 09:09:54 +0200
Subject: [PATCH] [test] Fix tests for kinetic scheme

---
 dune/gdt/momentmodels/density_evaluations.hh  | 172 +++++
 .../entropyflux_implementations.hh            | 627 +++++++++++++-----
 .../momentmodels/entropyflux_kineticcoords.hh |  84 ++-
 dune/gdt/momentmodels/hessianinverter.hh      |  49 +-
 .../operators/advection-fv-entropybased.hh    |  11 +-
 dune/gdt/operators/reconstruction/internal.hh |   2 -
 .../reconstruction/linear_kinetic.hh          |  31 +-
 dune/gdt/operators/reconstruction/slopes.hh   |  26 +-
 .../test/entropic-coords-mn-discretization.hh |  78 +--
 ...bolic__momentmodels__entropic_coords_mn.cc |  58 +-
 .../momentmodels/kinetictransport/base.hh     |  30 +
 .../kinetictransport/sourcebeam.hh            | 128 +++-
 .../kinetictransport/testcases.hh             | 159 ++++-
 13 files changed, 1081 insertions(+), 374 deletions(-)
 create mode 100644 dune/gdt/momentmodels/density_evaluations.hh

diff --git a/dune/gdt/momentmodels/density_evaluations.hh b/dune/gdt/momentmodels/density_evaluations.hh
new file mode 100644
index 000000000..2e6a212d6
--- /dev/null
+++ b/dune/gdt/momentmodels/density_evaluations.hh
@@ -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 (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/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, SlopeType 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())
+  {}
+
+  virtual 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_density_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());
+  } // 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,
+          SlopeType 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)
+  {}
+
+  virtual bool linear() const override final
+  {
+    return false;
+  }
+
+  virtual const SourceSpaceType& source_space() const override final
+  {
+    return space_;
+  }
+
+  virtual 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_.density_evaluations().resize(space_.grid_view().size(0));
+    analytical_flux_.boundary_density_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/momentmodels/entropyflux_implementations.hh b/dune/gdt/momentmodels/entropyflux_implementations.hh
index 5d3fdbc30..b3f3e78eb 100644
--- a/dune/gdt/momentmodels/entropyflux_implementations.hh
+++ b/dune/gdt/momentmodels/entropyflux_implementations.hh
@@ -50,6 +50,14 @@ 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
@@ -72,6 +80,15 @@ namespace GDT {
 #endif
 
 
+enum class SlopeType
+{
+  minmod,
+  superbee,
+  mc,
+  no_slope
+};
+
+
 template <class MomentBasisImp>
 class EntropyBasedFluxImplementationUnspecializedBase
   : public XT::Functions::FunctionInterface<MomentBasisImp::dimRange,
@@ -105,7 +122,8 @@ public:
   using DynamicRangeType = DynamicVector<RangeFieldType>;
   using BasisValuesMatrixType = XT::LA::CommonDenseMatrix<RangeFieldType>;
   using AlphaReturnType = std::pair<VectorType, std::pair<DomainType, RangeFieldType>>;
-  using QuadraturePointsValuesType = std::vector<RangeFieldType>;
+  using QuadraturePointsType = std::vector<BasisDomainType, boost::alignment::aligned_allocator<BasisDomainType, 64>>;
+  using QuadratureWeightsType = std::vector<RangeFieldType, boost::alignment::aligned_allocator<BasisDomainType, 64>>;
 
   explicit EntropyBasedFluxImplementationUnspecializedBase(const MomentBasis& basis_functions,
                                                            const RangeFieldType tau,
@@ -230,7 +248,7 @@ public:
                                                const size_t dd) const
 
   {
-    thread_local FieldVector<std::vector<RangeFieldType>, 2> work_vecs;
+    thread_local FieldVector<QuadratureWeightsType, 2> work_vecs;
     work_vecs[0].resize(quad_points_.size());
     work_vecs[1].resize(quad_points_.size());
     calculate_scalar_products(alpha_i, M_, work_vecs[0]);
@@ -260,8 +278,8 @@ public:
   // 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 <typename T, typename Compare>
-  static std::vector<std::size_t> get_sort_permutation(const std::vector<T>& vec, const Compare& compare)
+  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);
@@ -269,8 +287,8 @@ public:
     return p;
   }
 
-  template <typename T>
-  static void apply_permutation_in_place(std::vector<T>& vec, const std::vector<std::size_t>& 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) {
@@ -290,8 +308,7 @@ public:
   }
 
   // Joins duplicate quadpoints, vectors have to be sorted!
-  static void join_duplicate_quadpoints(std::vector<BasisDomainType>& quad_points,
-                                        std::vector<RangeFieldType>& quad_weights)
+  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;
@@ -319,7 +336,7 @@ public:
     static_assert(std::is_same<BasisFuncImp, MomentBasis>::value, "BasisFuncImp has to be MomentBasis!");
 
     RealizabilityHelper(const MomentBasis& basis_functions,
-                        const std::vector<BasisDomainType>& quad_points,
+                        const QuadraturePointsType& quad_points,
                         XT::Common::PerThreadValue<std::unique_ptr<ClpSimplex>>& lp)
       : basis_functions_(basis_functions)
       , quad_points_(quad_points)
@@ -386,7 +403,7 @@ public:
 
   private:
     const MomentBasis& basis_functions_;
-    const std::vector<BasisDomainType>& quad_points_;
+    const QuadraturePointsType& quad_points_;
     XT::Common::PerThreadValue<std::unique_ptr<ClpSimplex>>& lp_;
   }; // struct RealizabilityHelper<...>
 #else // HAVE_CLP
@@ -432,16 +449,16 @@ public:
   }; // struct RealizabilityHelper<Hatfunctions, ...>
 
   // temporary vectors to store inner products and exponentials
-  std::vector<RangeFieldType>& working_storage() const
+  QuadratureWeightsType& working_storage() const
   {
-    thread_local std::vector<RangeFieldType> work_vec;
+    thread_local QuadratureWeightsType work_vec;
     work_vec.resize(quad_points_.size());
     return work_vec;
   }
 
   void calculate_scalar_products(const DomainType& beta_in,
                                  const BasisValuesMatrixType& M,
-                                 std::vector<RangeFieldType>& scalar_products) const
+                                 QuadratureWeightsType& scalar_products) const
   {
 #if HAVE_MKL || HAVE_CBLAS
     XT::Common::Blas::dgemv(XT::Common::Blas::row_major(),
@@ -465,7 +482,7 @@ public:
 #endif
   }
 
-  void apply_exponential(std::vector<RangeFieldType>& values) const
+  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());
@@ -571,10 +588,11 @@ public:
     } // ii
   } // void calculate_A_Binv(...)
 
-  void apply_inverse_hessian(const DomainType& alpha, const DomainType& u, DomainType& Hinv_u) const
+  void
+  apply_inverse_hessian(const QuadratureWeightsType& density_evaluations, const DomainType& u, DomainType& Hinv_u) const
   {
     thread_local auto H = XT::Common::make_unique<MatrixType>();
-    calculate_hessian(alpha, M_, *H);
+    calculate_hessian(density_evaluations, M_, *H);
     XT::LA::cholesky(*H);
     thread_local DomainType tmp_vec;
     XT::LA::solve_lower_triangular(*H, tmp_vec, u);
@@ -600,11 +618,33 @@ public:
       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;
-        if (!XT::Common::is_zero(factor_ll_ii)) {
-          for (size_t kk = 0; kk <= ii; ++kk) {
-            H_row[kk] += basis_ll[kk] * factor_ll_ii;
-          } // kk
-        }
+        //        if (!XT::Common::is_zero(factor_ll_ii)) {
+        for (size_t kk = 0; kk <= ii; ++kk) {
+          H_row[kk] += basis_ll[kk] * factor_ll_ii;
+        } // kk
+        //        }
+      } // ii
+    } // ll
+  } // void calculate_hessian(...)
+
+  void calculate_hessian(const QuadratureWeightsType& density_evaluations,
+                         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 = density_evaluations[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;
+        //        if (!XT::Common::is_zero(factor_ll_ii)) {
+        for (size_t kk = 0; kk <= ii; ++kk) {
+          H_row[kk] += basis_ll[kk] * factor_ll_ii;
+        } // kk
+        //        }
       } // ii
     } // ll
   } // void calculate_hessian(...)
@@ -663,8 +703,8 @@ public:
   } // void change_basis(...)
 
   const MomentBasis& basis_functions_;
-  std::vector<BasisDomainType> quad_points_;
-  std::vector<RangeFieldType> quad_weights_;
+  QuadraturePointsType quad_points_;
+  QuadratureWeightsType quad_weights_;
   BasisValuesMatrixType M_;
   const RangeFieldType tau_;
   const RangeFieldType epsilon_gamma_;
@@ -699,9 +739,10 @@ public:
   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::QuadraturePointsValuesType;
+  using typename BaseType::QuadratureWeightsType;
   using typename BaseType::RangeFieldType;
   using typename BaseType::VectorType;
 
@@ -732,43 +773,46 @@ public:
     return ret;
   }
 
-  static RangeFieldType minmod(const RangeFieldType& first_slope, const RangeFieldType& second_slope)
+  DomainType get_u(const QuadratureWeightsType& density_evaluations) const
   {
-    RangeFieldType ret = 0.;
-    if (std::signbit(first_slope) == std::signbit(second_slope)) // check for equal sign
-      ret = std::abs(first_slope) < std::abs(second_slope) ? first_slope : second_slope;
+    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 = density_evaluations[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;
   }
 
-  static RangeFieldType maxmod(const RangeFieldType first_slope, const RangeFieldType second_slope)
+  void store_density_evaluations(QuadratureWeightsType& density_evaluations, const DomainType& alpha) const
   {
-    RangeFieldType ret(0.);
-    if (std::signbit(first_slope) == std::signbit(second_slope)) // check for equal sign
-      ret = (std::abs(first_slope) < std::abs(second_slope)) ? second_slope : first_slope;
-    return ret;
+    density_evaluations.resize(quad_points_.size());
+    this->calculate_scalar_products(alpha, M_, density_evaluations);
+    this->apply_exponential(density_evaluations);
   }
 
-  static RangeFieldType superbee(const RangeFieldType first_slope, const RangeFieldType second_slope)
+  void store_evaluations(QuadratureWeightsType& density_evaluations,
+                         const std::function<RangeFieldType(const FluxDomainType&)>& boundary_density) const
   {
-    const RangeFieldType first_minmod = minmod(first_slope, second_slope * 2.);
-    const RangeFieldType second_minmod = minmod(first_slope * 2., second_slope);
-    return maxmod(first_minmod, second_minmod);
+    density_evaluations.resize(quad_points_.size());
+    for (size_t ll = 0; ll < quad_points_.size(); ++ll)
+      density_evaluations[ll] = boundary_density(quad_points_[ll]);
   }
 
   template <class IntersectionType>
-  DomainType calculate_boundary_flux(const DomainType& alpha, const IntersectionType& intersection)
+  DomainType calculate_boundary_flux(const QuadratureWeightsType& density_evaluations,
+                                     const IntersectionType& intersection)
   {
     // evaluate exp(alpha^T b(v_i)) at all quadratures points v_i
-    auto& work_vecs = this->working_storage();
-    this->calculate_scalar_products(alpha, M_, work_vecs);
-    this->apply_exponential(work_vecs);
     const auto dd = intersection.indexInInside() / 2;
     const auto& n_ij = intersection.centerUnitOuterNormal();
     DomainType ret(0.);
     for (size_t ll = 0; ll < quad_points_.size(); ++ll) {
       const auto position = quad_points_[ll][dd];
       if (position * n_ij[dd] < 0.) {
-        RangeFieldType factor = work_vecs[ll] * quad_weights_[ll] * position;
+        RangeFieldType factor = density_evaluations[ll] * 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;
@@ -777,38 +821,29 @@ public:
     return ret;
   }
 
-  // TODO: Calculates exp(alpha^T b) 2*dim times for each alpha (as it is contained in 2*dim stencils) at the moment.
-  template <class FluxesMapType>
-  void calculate_minmod_density_reconstruction(const FieldVector<DomainType, 3>& alphas,
-                                               FluxesMapType& flux_values,
-                                               const size_t dd) const
+  template <SlopeType slope_type, class FluxesMapType>
+  void calculate_reconstructed_fluxes(const FieldVector<const QuadratureWeightsType*, 3>& density_evaluations,
+                                      FluxesMapType& flux_values,
+                                      const size_t dd) const
   {
-    // evaluate exp(alpha^T b(v_i)) at all quadratures points v_i for all three alphas
-    thread_local XT::Common::FieldVector<QuadraturePointsValuesType, 3> density_evaluations(
-        QuadraturePointsValuesType(quad_points_.size()));
-    thread_local XT::Common::FieldVector<QuadraturePointsValuesType, 2> reconstructed_values(
-        QuadraturePointsValuesType(quad_points_.size()));
-    for (size_t ii = 0; ii < 3; ++ii) {
-      this->calculate_scalar_products(alphas[ii], M_, density_evaluations[ii]);
-      this->apply_exponential(density_evaluations[ii]);
-      for (size_t ll = 0; ll < quad_points_.size(); ++ll) {
-        if (std::isnan(density_evaluations[ii][ll]) || std::isnan(density_evaluations[ii][ll])) {
-          std::cout << "Wrong val: " << density_evaluations[ii][ll] << " for alpha " << alphas[ii] << std::endl;
-          DUNE_THROW(Dune::MathError, "");
-        }
-      }
-    }
     // 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];
-    for (size_t ll = 0; ll < quad_points_.size(); ++ll) {
-      const auto slope = minmod(density_evaluations[1][ll] - density_evaluations[0][ll],
-                                density_evaluations[2][ll] - density_evaluations[1][ll]);
-      //      const auto slope = superbee(density_evaluations[1][ll] - density_evaluations[0][ll],
-      //                                  density_evaluations[2][ll] - density_evaluations[1][ll]);
-      vals_left[ll] = density_evaluations[1][ll] - 0.5 * slope;
-      vals_right[ll] = density_evaluations[1][ll] + 0.5 * slope;
-    } // ll
+    if (slope_type == SlopeType::no_slope) {
+      for (size_t ll = 0; ll < quad_points_.size(); ++ll)
+        vals_left[ll] = vals_right[ll] = (*density_evaluations[1])[ll];
+    } else {
+      const auto slope_func =
+          (slope_type == SlopeType::minmod) ? XT::Common::minmod<RangeFieldType> : superbee<RangeFieldType>;
+      for (size_t ll = 0; ll < quad_points_.size(); ++ll) {
+        const auto slope = slope_func((*density_evaluations[1])[ll] - (*density_evaluations[0])[ll],
+                                      (*density_evaluations[2])[ll] - (*density_evaluations[1])[ll]);
+        vals_left[ll] = (*density_evaluations[1])[ll] - 0.5 * slope;
+        vals_right[ll] = (*density_evaluations[1])[ll] + 0.5 * slope;
+      } // ll
+    }
 
     BasisDomainType coord(0.5);
     coord[dd] = 0;
@@ -826,9 +861,7 @@ public:
       for (size_t ii = 0; ii < basis_dimRange; ++ii)
         val[ii] += basis_ll[ii] * factor;
     } // ll
-    //    std::cout << "right_flux_value, left_flux_value = " << right_flux_value << ", " << left_flux_value <<
-    //    std::endl;
-  } // void calculate_minmod_density_reconstruction(...)
+  } // void calculate_reconstructed_fluxes(...)
 
   // returns (alpha, (actual_u, r)), where r is the regularization parameter and actual_u the regularized u
   virtual std::unique_ptr<AlphaReturnType>
@@ -1221,10 +1254,9 @@ public:
   using BasisValuesMatrixType = FieldVector<XT::LA::CommonDenseMatrix<RangeFieldType>, num_blocks>;
   using QuadraturePointsType =
       FieldVector<std::vector<BasisDomainType, boost::alignment::aligned_allocator<BasisDomainType, 64>>, num_blocks>;
-  using QuadratureWeightsType =
-      FieldVector<std::vector<RangeFieldType, boost::alignment::aligned_allocator<RangeFieldType, 64>>, num_blocks>;
-  using TemporaryVectorType = std::vector<RangeFieldType, boost::alignment::aligned_allocator<RangeFieldType, 64>>;
-  using TemporaryVectorsType = FieldVector<TemporaryVectorType, num_blocks>;
+  using BlockQuadratureWeightsType =
+      std::vector<RangeFieldType, boost::alignment::aligned_allocator<RangeFieldType, 64>>;
+  using QuadratureWeightsType = FieldVector<BlockQuadratureWeightsType, num_blocks>;
   using AlphaReturnType = std::pair<BlockVectorType, std::pair<DomainType, RangeFieldType>>;
 
   explicit EntropyBasedFluxImplementation(const MomentBasis& basis_functions,
@@ -1258,10 +1290,6 @@ public:
       }
     } // jj
     for (size_t jj = 0; jj < num_blocks; ++jj) {
-      while (quad_weights_[jj].size() % 8) { // align to 64 byte boundary
-        quad_points_[jj].push_back(quad_points_[jj].back());
-        quad_weights_[jj].push_back(0.);
-      }
       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(quad_points_[jj][ll], jj);
@@ -1338,7 +1366,7 @@ public:
                                                const size_t dd) const
   {
     // calculate \sum_{i=1}^d < \omega_i m G_\alpha(u) > n_i
-    thread_local FieldVector<TemporaryVectorsType, 2> work_vecs;
+    thread_local FieldVector<QuadratureWeightsType, 2> work_vecs;
     for (size_t jj = 0; jj < num_blocks; ++jj) {
       work_vecs[0][jj].resize(quad_points_[jj].size());
       work_vecs[1][jj].resize(quad_points_[jj].size());
@@ -1378,6 +1406,108 @@ public:
     return u_block;
   }
 
+  DomainType get_u(const QuadratureWeightsType& density_evaluations) 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 = density_evaluations[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;
+  }
+
+  void store_density_evaluations(QuadratureWeightsType& density_evaluations, const DomainType& alpha) const
+  {
+    this->calculate_scalar_products(alpha, M_, density_evaluations);
+    this->apply_exponential(density_evaluations);
+  }
+
+  void store_evaluations(QuadratureWeightsType& density_evaluations,
+                         const std::function<RangeFieldType(const FluxDomainType&)>& boundary_density) const
+  {
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      density_evaluations[jj].resize(quad_points_[jj].size());
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll)
+        density_evaluations[jj][ll] = boundary_density(quad_points_[jj][ll]);
+    } // jj
+  }
+
+  template <class IntersectionType>
+  DomainType calculate_boundary_flux(const QuadratureWeightsType& density_evaluations,
+                                     const IntersectionType& intersection)
+  {
+    // evaluate exp(alpha^T b(v_i)) at all quadratures points v_i
+    const auto dd = intersection.indexInInside() / 2;
+    const auto& n_ij = intersection.centerUnitOuterNormal();
+    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];
+        if (position * n_ij[dd] < 0.) {
+          RangeFieldType factor = density_evaluations[jj][ll] * 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
+    return ret;
+  }
+
+  template <SlopeType slope_type, class FluxesMapType>
+  void calculate_reconstructed_fluxes(const FieldVector<const QuadratureWeightsType*, 3>& density_evaluations,
+                                      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 == SlopeType::minmod) ? XT::Common::minmod<RangeFieldType> : superbee<RangeFieldType>;
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      // reconstruct densities
+      if (slope_type == SlopeType::no_slope) {
+        for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll)
+          vals_left[ll] = vals_right[ll] = (*density_evaluations[1])[jj][ll];
+      } else {
+        for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll) {
+          const auto slope = slope_func((*density_evaluations[1])[jj][ll] - (*density_evaluations[0])[jj][ll],
+                                        (*density_evaluations[2])[jj][ll] - (*density_evaluations[1])[jj][ll]);
+          vals_left[ll] = (*density_evaluations[1])[jj][ll] - 0.5 * slope;
+          vals_right[ll] = (*density_evaluations[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(...)
+
   std::unique_ptr<AlphaReturnType>
   get_alpha(const DomainType& u, const VectorType& alpha_in, const bool regularize) const
   {
@@ -1518,9 +1648,9 @@ public:
   }
 
   // temporary vectors to store inner products and exponentials
-  TemporaryVectorsType& working_storage() const
+  QuadratureWeightsType& working_storage() const
   {
-    thread_local TemporaryVectorsType work_vecs;
+    thread_local QuadratureWeightsType work_vecs;
     for (size_t jj = 0; jj < num_blocks; ++jj)
       work_vecs[jj].resize(quad_points_[jj].size());
     return work_vecs;
@@ -1542,7 +1672,7 @@ public:
   void calculate_scalar_products_block(const size_t jj,
                                        const LocalVectorType& beta_in,
                                        const XT::LA::CommonDenseMatrix<RangeFieldType>& M,
-                                       TemporaryVectorType& scalar_products) const
+                                       BlockQuadratureWeightsType& scalar_products) const
   {
     const size_t num_quad_points = quad_points_[jj].size();
     for (size_t ll = 0; ll < num_quad_points; ++ll) {
@@ -1553,19 +1683,21 @@ public:
 
   void calculate_scalar_products(const BlockVectorType& beta_in,
                                  const BasisValuesMatrixType& M,
-                                 TemporaryVectorsType& scalar_products) const
+                                 QuadratureWeightsType& scalar_products) const
   {
-    for (size_t jj = 0; jj < num_blocks; ++jj)
+    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(TemporaryVectorType& values) const
+  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(TemporaryVectorsType& values) const
+  void apply_exponential(QuadratureWeightsType& values) const
   {
     for (size_t jj = 0; jj < num_blocks; ++jj)
       apply_exponential(values[jj]);
@@ -1772,10 +1904,11 @@ public:
     } // jj
   } // void calculate_A_Binv(...)
 
-  void apply_inverse_hessian(const DomainType& alpha, const DomainType& u, DomainType& Hinv_u) const
+  void
+  apply_inverse_hessian(const QuadratureWeightsType& density_evaluations, const DomainType& u, DomainType& Hinv_u) const
   {
     thread_local auto H = XT::Common::make_unique<BlockMatrixType>();
-    calculate_hessian(alpha, M_, *H);
+    calculate_hessian(density_evaluations, M_, *H);
     for (size_t jj = 0; jj < num_blocks; ++jj)
       XT::LA::cholesky(H->block(jj));
     FieldVector<RangeFieldType, block_size> tmp_vec;
@@ -1792,17 +1925,16 @@ public:
     } // jj
   }
 
-  void calculate_hessian(const BlockVectorType& alpha, const BasisValuesMatrixType& M, BlockMatrixType& H) const
+  void calculate_hessian(const QuadratureWeightsType& density_evaluations,
+                         const BasisValuesMatrixType& M,
+                         BlockMatrixType& H) const
   {
-    auto& work_vec = working_storage();
-    calculate_scalar_products(alpha, M, work_vec);
-    apply_exponential(work_vec);
     // 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 = work_vec[jj][ll] * quad_weights_[jj][ll];
+        auto factor_ll = density_evaluations[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]);
@@ -1814,6 +1946,14 @@ public:
     } // jj
   } // void calculate_hessian(...)
 
+  void calculate_hessian(const BlockVectorType& alpha, const BasisValuesMatrixType& M, BlockMatrixType& H) const
+  {
+    auto& work_vec = working_storage();
+    calculate_scalar_products(alpha, M, work_vec);
+    apply_exponential(work_vec);
+    calculate_hessian(work_vec, M, H);
+  } // 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
@@ -2156,6 +2296,135 @@ public:
     return u;
   }
 
+  DomainType get_u(const QuadratureWeightsType& density_evaluations) const
+  {
+    DomainType u(0.);
+    LocalVectorType local_u;
+    const auto& triangulation = basis_functions_.triangulation();
+    const auto& faces = triangulation.faces();
+    for (size_t jj = 0; jj < faces.size(); ++jj) {
+      const auto& face = faces[jj];
+      const auto& vertices = face->vertices();
+      local_u *= 0.;
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto& basis_ll = M_[jj][ll];
+        for (size_t ii = 0; ii < 3; ++ii)
+          local_u[ii] += basis_ll[ii] * density_evaluations[jj][ll];
+      } // ll (quad points)
+      for (size_t ii = 0; ii < 3; ++ii)
+        u[vertices[ii]->index()] = local_u[ii];
+    } // jj (faces)
+    return u;
+  }
+
+  void store_density_evaluations(QuadratureWeightsType& density_evaluations, const DomainType& alpha) const
+  {
+    const auto& triangulation = basis_functions_.triangulation();
+    const auto& faces = triangulation.faces();
+    density_evaluations.resize(faces.size());
+    XT::Common::FieldVector<RangeFieldType, 3> local_alpha;
+    for (size_t jj = 0; jj < faces.size(); ++jj) {
+      density_evaluations[jj].resize(quad_weights_[jj].size());
+      const auto& face = faces[jj];
+      const auto& vertices = face->vertices();
+      for (size_t ii = 0; ii < 3; ++ii)
+        local_alpha[ii] = alpha[vertices[ii]->index()];
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll)
+        density_evaluations[jj][ll] = std::exp(local_alpha * M_[jj][ll]);
+    } // jj (faces)
+  }
+
+  void store_evaluations(QuadratureWeightsType& density_evaluations,
+                         const std::function<RangeFieldType(const FluxDomainType&)>& boundary_density) const
+  {
+    const auto& triangulation = basis_functions_.triangulation();
+    const auto& faces = triangulation.faces();
+    density_evaluations.resize(faces.size());
+    for (size_t jj = 0; jj < faces.size(); ++jj) {
+      density_evaluations[jj].resize(quad_points_[jj].size());
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll)
+        density_evaluations[jj][ll] = boundary_density(quad_points_[jj][ll]);
+    } // jj
+  }
+
+  template <class IntersectionType>
+  DomainType calculate_boundary_flux(const QuadratureWeightsType& density_evaluations,
+                                     const IntersectionType& intersection)
+  {
+    // evaluate exp(alpha^T b(v_i)) at all quadratures points v_i
+    const auto dd = intersection.indexInInside() / 2;
+    const auto& n_ij = intersection.centerUnitOuterNormal();
+    const auto& triangulation = basis_functions_.triangulation();
+    const auto& faces = triangulation.faces();
+    DomainType ret(0.);
+    LocalVectorType local_ret;
+    for (size_t jj = 0; jj < faces.size(); ++jj) {
+      local_ret *= 0.;
+      const auto& face = faces[jj];
+      const auto& vertices = face->vertices();
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto position = quad_points_[jj][ll][dd];
+        if (position * n_ij[dd] < 0.) {
+          const auto& basis_ll = M_[jj][ll];
+          RangeFieldType factor = density_evaluations[jj][ll] * quad_weights_[jj][ll] * position;
+          for (size_t ii = 0; ii < 3; ++ii)
+            local_ret[ii] += basis_ll[ii] * factor;
+        }
+      } // ll (quad points)
+      for (size_t ii = 0; ii < 3; ++ii)
+        ret[vertices[ii]->index()] += local_ret[ii];
+    } // jj
+    return ret;
+  }
+
+  template <SlopeType slope_type, class FluxesMapType>
+  void calculate_reconstructed_fluxes(const FieldVector<const QuadratureWeightsType*, 3>& density_evaluations,
+                                      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& triangulation = basis_functions_.triangulation();
+    const auto& faces = triangulation.faces();
+    const auto slope_func =
+        (slope_type == SlopeType::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 < faces.size(); ++jj) {
+      const auto& face = faces[jj];
+      const auto& vertices = face->vertices();
+      // reconstruct densities
+      if (slope_type == SlopeType::no_slope) {
+        for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll)
+          vals_left[ll] = vals_right[ll] = (*density_evaluations[1])[jj][ll];
+      } else {
+        for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+          const auto slope = slope_func((*density_evaluations[1])[jj][ll] - (*density_evaluations[0])[jj][ll],
+                                        (*density_evaluations[2])[jj][ll] - (*density_evaluations[1])[jj][ll]);
+          vals_left[ll] = (*density_evaluations[1])[jj][ll] - 0.5 * slope;
+          vals_right[ll] = (*density_evaluations[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(...)
+
   std::unique_ptr<AlphaReturnType>
   get_alpha(const DomainType& u, const VectorType& alpha_in, const bool regularize) const
   {
@@ -2301,7 +2570,7 @@ public:
   }
 
   // temporary vectors to store inner products and exponentials
-  std::vector<std::vector<RangeFieldType>>& get_work_vecs() const
+  std::vector<std::vector<RangeFieldType>>& working_storage() const
   {
     thread_local std::vector<std::vector<RangeFieldType>> work_vecs;
     const auto& triangulation = basis_functions_.triangulation();
@@ -2343,15 +2612,14 @@ public:
     } // ii
   } // void calculate_J_Hinv(...)
 
-  void apply_inverse_hessian(const DomainType& alpha, const DomainType& u, DomainType& Hinv_u) const
+  void
+  apply_inverse_hessian(const QuadratureWeightsType& density_evaluations, const DomainType& u, DomainType& Hinv_u) const
   {
     thread_local SparseMatrixType H(basis_dimRange, basis_dimRange, pattern_, 0);
-    thread_local VectorType alpha_vec(basis_dimRange, 0., 0), u_vec(basis_dimRange, 0., 0),
-        Hinv_u_vec(basis_dimRange, 0., 0);
-    std::copy(alpha.begin(), alpha.end(), alpha_vec.begin());
-    std::copy(u.begin(), u.end(), u_vec.begin());
-    calculate_hessian(alpha_vec, M_, H);
+    calculate_hessian(density_evaluations, 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;
@@ -2390,7 +2658,7 @@ public:
     LocalVectorType local_alpha, local_u;
     const auto& triangulation = basis_functions_.triangulation();
     const auto& faces = triangulation.faces();
-    auto& work_vecs = get_work_vecs();
+    auto& work_vecs = working_storage();
     for (size_t jj = 0; jj < faces.size(); ++jj) {
       const auto& face = faces[jj];
       const auto& vertices = face->vertices();
@@ -2414,6 +2682,30 @@ public:
     g_k -= v;
   }
 
+  void calculate_hessian(const QuadratureWeightsType& density_evaluations,
+                         const BasisValuesMatrixType& M,
+                         SparseMatrixType& H) const
+  {
+    H *= 0.;
+    LocalMatrixType H_local(0.);
+    const auto& triangulation = basis_functions_.triangulation();
+    const auto& faces = triangulation.faces();
+    for (size_t jj = 0; jj < faces.size(); ++jj) {
+      H_local *= 0.;
+      const auto& face = faces[jj];
+      const auto& vertices = face->vertices();
+      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+        const auto& basis_ll = M[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] * density_evaluations[jj][ll] * quad_weights_[jj][ll];
+      } // 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,
@@ -2424,7 +2716,7 @@ public:
     LocalMatrixType H_local(0.);
     const auto& triangulation = basis_functions_.triangulation();
     const auto& faces = triangulation.faces();
-    auto& work_vecs = get_work_vecs();
+    auto& work_vecs = working_storage();
     for (size_t jj = 0; jj < faces.size(); ++jj) {
       H_local *= 0.;
       const auto& face = faces[jj];
@@ -2457,7 +2749,7 @@ public:
     assert(dd < dimFlux);
     J_dd *= 0.;
     LocalMatrixType J_local(0.);
-    auto& work_vecs = get_work_vecs();
+    auto& work_vecs = working_storage();
     const auto& triangulation = basis_functions_.triangulation();
     const auto& faces = triangulation.faces();
     for (size_t jj = 0; jj < faces.size(); ++jj) {
@@ -3270,7 +3562,7 @@ public:
   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 = FieldVector<std::vector<RangeFieldType>, num_intervals>;
+  using QuadratureWeightsType = QuadraturePointsType;
 
   explicit EntropyBasedFluxImplementation(const MomentBasis& basis_functions,
                                           const RangeFieldType tau,
@@ -3414,31 +3706,42 @@ public:
     return ret;
   }
 
-  static RangeFieldType minmod(const RangeFieldType& first_slope, const RangeFieldType& second_slope)
+  DomainType get_u(const QuadratureWeightsType& density_evaluations) const
   {
-    RangeFieldType ret = 0.;
-    if (std::signbit(first_slope) == std::signbit(second_slope)) // check for equal sign
-      ret = std::abs(first_slope) < std::abs(second_slope) ? first_slope : second_slope;
+    DomainType ret;
+    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 = density_evaluations[jj][ll] * quad_weights_[jj][ll];
+        for (size_t ii = 0; ii < 2; ++ii)
+          ret[jj + ii] += basis_ll[ii] * factor_ll;
+      } // ll (quad points)
+    } // jj (intervals)
     return ret;
   }
 
-  static RangeFieldType maxmod(const RangeFieldType first_slope, const RangeFieldType second_slope)
+  void store_density_evaluations(QuadratureWeightsType& density_evaluations, const DomainType& alpha) const
   {
-    RangeFieldType ret(0.);
-    if (std::signbit(first_slope) == std::signbit(second_slope)) // check for equal sign
-      ret = (std::abs(first_slope) < std::abs(second_slope)) ? second_slope : first_slope;
-    return ret;
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      density_evaluations[jj].resize(quad_points_[jj].size());
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll)
+        density_evaluations[jj][ll] = std::exp(alpha[jj] * M_[jj][ll][0] + alpha[jj + 1] * M_[jj][ll][1]);
+    } // jj
   }
 
-  static RangeFieldType superbee(const RangeFieldType first_slope, const RangeFieldType second_slope)
+  void store_evaluations(QuadratureWeightsType& density_evaluations,
+                         const std::function<RangeFieldType(const FluxDomainType&)>& boundary_density) const
   {
-    const RangeFieldType first_minmod = minmod(first_slope, second_slope * 2.);
-    const RangeFieldType second_minmod = minmod(first_slope * 2., second_slope);
-    return maxmod(first_minmod, second_minmod);
+    for (size_t jj = 0; jj < num_intervals; ++jj) {
+      density_evaluations[jj].resize(quad_points_[jj].size());
+      for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll)
+        density_evaluations[jj][ll] = boundary_density(quad_points_[jj][ll]);
+    } // jj
   }
 
   template <class IntersectionType>
-  DomainType calculate_boundary_flux(const DomainType& alpha, const IntersectionType& intersection)
+  DomainType calculate_boundary_flux(const QuadratureWeightsType& density_evaluations,
+                                     const IntersectionType& intersection)
   {
     // evaluate exp(alpha^T b(v_i)) at all quadratures points v_i
     const auto dd = intersection.indexInInside() / 2;
@@ -3448,8 +3751,7 @@ public:
       for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll) {
         const auto position = quad_points_[jj][ll];
         if (position * n_ij[dd] < 0.) {
-          RangeFieldType factor =
-              std::exp(alpha[jj] * M_[jj][ll][0] + alpha[jj + 1] * M_[jj][ll][1]) * quad_weights_[jj][ll] * position;
+          RangeFieldType factor = density_evaluations[jj][ll] * quad_weights_[jj][ll] * position;
           const auto& basis_ll = M_[jj][ll];
           for (size_t mm = 0; mm < 2; ++mm)
             ret[jj + mm] += basis_ll[mm] * factor;
@@ -3459,18 +3761,14 @@ public:
     return ret;
   }
 
-  // TODO: Calculates exp(alpha^T b) 2*dim times for each alpha (as it is contained in 2*dim stencils) at the moment.
-  template <class FluxesMapType>
-  void calculate_minmod_density_reconstruction(const FieldVector<DomainType, 3>& alphas,
-                                               FluxesMapType& flux_values,
-                                               const size_t dd) const
+  template <SlopeType slope_type, class FluxesMapType>
+  void calculate_reconstructed_fluxes(const FieldVector<const QuadratureWeightsType*, 3>& density_evaluations,
+                                      FluxesMapType& flux_values,
+                                      const size_t dd) const
   {
     // evaluate exp(alpha^T b(v_i)) at all quadratures points v_i for all three alphas
-    thread_local XT::Common::FieldVector<std::vector<RangeFieldType>, 3> density_evaluations(
-        std::vector<RangeFieldType>(quad_points_[0].size()));
     thread_local XT::Common::FieldVector<std::vector<RangeFieldType>, 2> reconstructed_values(
         std::vector<RangeFieldType>(quad_points_[0].size()));
-    thread_local XT::Common::FieldVector<LocalVectorType, 3> local_alphas;
 
     // get flux storage
     BasisDomainType coord(0.5);
@@ -3480,29 +3778,23 @@ public:
     auto& right_flux_value = flux_values[coord];
     right_flux_value = left_flux_value = DomainType(0.);
 
+    auto& vals_left = reconstructed_values[0];
+    auto& vals_right = reconstructed_values[1];
+    const auto slope_func =
+        (slope_type == SlopeType::minmod) ? XT::Common::minmod<RangeFieldType> : superbee<RangeFieldType>;
     for (size_t jj = 0; jj < num_intervals; ++jj) {
-      // get local alphas
-      for (size_t ii = 0; ii < 3; ++ii)
-        for (size_t mm = 0; mm < 2; ++mm)
-          local_alphas[ii][mm] = alphas[ii][jj + mm];
-
-      // evaluate densitys
-      for (size_t ii = 0; ii < 3; ++ii)
-        for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll)
-          density_evaluations[ii][ll] = std::exp(local_alphas[ii] * M_[jj][ll]);
-
       // reconstruct densities
-      auto& vals_left = reconstructed_values[0];
-      auto& vals_right = reconstructed_values[1];
-      for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
-        const auto slope = minmod(density_evaluations[1][ll] - density_evaluations[0][ll],
-                                  density_evaluations[2][ll] - density_evaluations[1][ll]);
-        //              const auto slope = superbee(density_evaluations[1][ll] - density_evaluations[0][ll],
-        //                                          density_evaluations[2][ll] - density_evaluations[1][ll]);
-        vals_left[ll] = density_evaluations[1][ll] - 0.5 * slope;
-        vals_right[ll] = density_evaluations[1][ll] + 0.5 * slope;
-      } // ll (quad points)
-
+      if (slope_type == SlopeType::no_slope) {
+        for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll)
+          vals_left[ll] = vals_right[ll] = (*density_evaluations[1])[jj][ll];
+      } else {
+        for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) {
+          const auto slope = slope_func((*density_evaluations[1])[jj][ll] - (*density_evaluations[0])[jj][ll],
+                                        (*density_evaluations[2])[jj][ll] - (*density_evaluations[1])[jj][ll]);
+          vals_left[ll] = (*density_evaluations[1])[jj][ll] - 0.5 * slope;
+          vals_right[ll] = (*density_evaluations[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];
@@ -3514,7 +3806,7 @@ public:
           val[jj + mm] += basis_ll[mm] * factor;
       } // ll (quad points)
     } // jj (intervals)
-  } // void calculate_minmod_density_reconstruction(...)
+  } // void calculate_reconstructed_fluxes(...)
 
   // returns (alpha, (actual_u, r)), where r is the regularization parameter and actual_u the regularized u
   std::unique_ptr<AlphaReturnType>
@@ -3634,15 +3926,14 @@ public:
     return true;
   }
 
-  FieldVector<std::vector<RangeFieldType>, num_intervals>& working_storage() const
+  QuadratureWeightsType& working_storage() const
   {
-    thread_local FieldVector<std::vector<RangeFieldType>, num_intervals> work_vec;
+    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;
   }
 
-
   RangeFieldType calculate_f(const VectorType& alpha, const VectorType& v) const
   {
     RangeFieldType ret(0.);
@@ -3679,6 +3970,24 @@ public:
     g_k -= v;
   }
 
+  void calculate_hessian(const QuadratureWeightsType& density_evalutations,
+                         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 = density_evalutations[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,
@@ -3725,12 +4034,12 @@ public:
     } // jj (intervals)
   } // void calculate_J(...)
 
-  void apply_inverse_hessian(const DomainType& alpha, const DomainType& u, DomainType& Hinv_u) const
+  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(alpha, M_, H_diag, H_subdiag);
-    //    std::cout << "H_diag: " << H_diag << ", H_subdiag: " << H_subdiag << std::endl;
+    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
diff --git a/dune/gdt/momentmodels/entropyflux_kineticcoords.hh b/dune/gdt/momentmodels/entropyflux_kineticcoords.hh
index bb65eab97..889b05e6d 100644
--- a/dune/gdt/momentmodels/entropyflux_kineticcoords.hh
+++ b/dune/gdt/momentmodels/entropyflux_kineticcoords.hh
@@ -26,7 +26,7 @@ namespace Dune {
 namespace GDT {
 
 
-template <class GridViewImp, class MomentBasisImp, bool fluxes_precomputed>
+template <class GridViewImp, class MomentBasisImp, SlopeType slope>
 class EntropyBasedFluxEntropyCoordsFunction
   : public XT::Functions::FluxFunctionInterface<XT::Grid::extract_entity_t<GridViewImp>,
                                                 MomentBasisImp::dimRange,
@@ -56,6 +56,9 @@ public:
   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>>;
 
   explicit EntropyBasedFluxEntropyCoordsFunction(
       const MomentBasis& basis_functions,
@@ -75,7 +78,6 @@ public:
     : implementation_(other.implementation_)
   {}
 
-
   static const constexpr bool available = true;
 
   class Localfunction : public LocalFunctionType
@@ -132,17 +134,12 @@ public:
 
   StateType evaluate_kinetic_flux(const E& /*inside_entity*/,
                                   const E& /*outside_entity*/,
-                                  //                                  const StateType& alpha_i,
-                                  //                                  const StateType& alpha_j,
-                                  const StateType& flux_or_alpha_i,
-                                  const StateType& flux_or_alpha_j,
+                                  const StateType& flux_i,
+                                  const StateType& flux_j,
                                   const DomainType& n_ij,
                                   const size_t dd) const
   {
-    if (fluxes_precomputed)
-      return (flux_or_alpha_i + flux_or_alpha_j) * n_ij[dd];
-    else
-      return implementation_->evaluate_kinetic_flux_with_alphas(flux_or_alpha_i, flux_or_alpha_j, n_ij, dd);
+    return (flux_i + flux_j) * n_ij[dd];
   } // StateType evaluate_kinetic_flux(...)
 
   const MomentBasis& basis_functions() const
@@ -155,6 +152,11 @@ public:
     return implementation_->get_u(alpha);
   }
 
+  StateType get_u(const size_t entity_index) const
+  {
+    return implementation_->get_u(density_evaluations_[entity_index]);
+  }
+
   StateType get_alpha(const StateType& u) const
   {
     const auto alpha = implementation_->get_alpha(u)->first;
@@ -164,25 +166,71 @@ public:
   }
 
   template <class FluxesMapType>
-  void calculate_minmod_density_reconstruction(const FieldVector<StateType, 3>& alphas,
-                                               FluxesMapType& precomputed_fluxes,
-                                               const size_t dd) const
+  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_density_evaluations_[entity_indices[1]][dd][boundary_direction[ii]];
+      else
+        densities_stencil[ii] = &density_evaluations_[entity_indices[ii]];
+    implementation_->template calculate_reconstructed_fluxes<slope, FluxesMapType>(
+        densities_stencil, precomputed_fluxes, dd);
+  }
+
+  StateType calculate_boundary_flux(const size_t entity_index, const I& intersection)
+  {
+    const size_t dd = intersection.indexInInside() / 2;
+    const size_t dir = intersection.indexInInside() % 2;
+    return implementation_->calculate_boundary_flux(boundary_density_evaluations_[entity_index][dd][dir], intersection);
+  }
+
+  void apply_inverse_hessian(const size_t entity_index, const StateType& u, StateType& Hinv_u) const
   {
-    implementation_->calculate_minmod_density_reconstruction(alphas, precomputed_fluxes, dd);
+    implementation_->apply_inverse_hessian(density_evaluations_[entity_index], u, Hinv_u);
   }
 
-  StateType calculate_boundary_flux(const StateType& alpha, const I& intersection)
+  void store_density_evaluations(const size_t entity_index, const StateType& alpha)
   {
-    return implementation_->calculate_boundary_flux(alpha, intersection);
+    implementation_->store_density_evaluations(density_evaluations_[entity_index], alpha);
   }
 
-  void apply_inverse_hessian(const StateType& alpha, const StateType& u, StateType& Hinv_u) const
+  void store_boundary_evaluations(const std::function<RangeFieldType(const DomainType&)>& boundary_density,
+                                  const size_t entity_index,
+                                  const size_t intersection_index)
   {
-    implementation_->apply_inverse_hessian(alpha, u, Hinv_u);
+    implementation_->store_evaluations(
+        boundary_density_evaluations_[entity_index][intersection_index / 2][intersection_index % 2], boundary_density);
   }
 
+  std::vector<QuadratureWeightsType>& density_evaluations()
+  {
+    return density_evaluations_;
+  }
+
+  const std::vector<QuadratureWeightsType>& density_evaluations() const
+  {
+    return density_evaluations_;
+  }
+
+  BoundaryQuadratureWeightsType& boundary_density_evaluations()
+  {
+    return boundary_density_evaluations_;
+  }
+
+  const BoundaryQuadratureWeightsType& boundary_density_evaluations() const
+  {
+    return boundary_density_evaluations_;
+  }
+
+
 private:
   std::shared_ptr<ImplementationType> implementation_;
+  std::vector<QuadratureWeightsType> density_evaluations_;
+  BoundaryQuadratureWeightsType boundary_density_evaluations_;
 };
 
 
diff --git a/dune/gdt/momentmodels/hessianinverter.hh b/dune/gdt/momentmodels/hessianinverter.hh
index 3f0320516..b3c525939 100644
--- a/dune/gdt/momentmodels/hessianinverter.hh
+++ b/dune/gdt/momentmodels/hessianinverter.hh
@@ -24,16 +24,15 @@ namespace Dune {
 namespace GDT {
 
 
-template <class SpaceType, class VectorType, class MomentBasis, bool reconstruction>
+template <class SpaceType, class VectorType, class MomentBasis, SlopeType 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, reconstruction>;
+  using EntropyFluxType = EntropyBasedFluxEntropyCoordsFunction<GridViewType, MomentBasis, slope>;
   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>;
@@ -45,9 +44,7 @@ public:
                                         const VectorType& u_update_dofs,
                                         VectorType& alpha_range_dofs,
                                         const EntropyFluxType& analytical_flux,
-                                        const RangeFieldType min_acceptable_density,
-                                        const XT::Common::Parameter& param,
-                                        const std::string filename = "")
+                                        const XT::Common::Parameter& param)
     : space_(space)
     , alpha_(space_, alpha_dofs, "alpha")
     , u_update_(space_, u_update_dofs, "u_update")
@@ -56,11 +53,7 @@ public:
     , range_(space_, alpha_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 LocalEntropicHessianInverter(LocalEntropicHessianInverter& other)
@@ -73,11 +66,7 @@ public:
     , 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())
   {}
 
   virtual XT::Grid::ElementFunctor<GridViewType>* copy() override final
@@ -87,15 +76,12 @@ public:
 
   void apply_local(const EntityType& entity) override final
   {
-    local_alpha_->bind(entity);
     local_u_update_->bind(entity);
     local_range_->bind(entity);
-    XT::Common::FieldVector<RangeFieldType, dimRange> u, Hinv_u, alpha;
-    for (size_t ii = 0; ii < dimRange; ++ii) {
+    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);
-      alpha[ii] = local_alpha_->dofs().get_entry(ii);
-    }
-    analytical_flux_.apply_inverse_hessian(alpha, u, Hinv_u);
+    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: "
@@ -116,16 +102,12 @@ private:
   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 LocalEntropicHessianInverter<...>
 
 template <class MomentBasisImp,
           class SpaceImp,
-          bool reconstruction,
+          SlopeType slope,
           class MatrixType = typename XT::LA::Container<typename MomentBasisImp::RangeFieldType>::MatrixType>
 class EntropicHessianInverter
   : public OperatorInterface<MatrixType, typename SpaceImp::GridViewType, MomentBasisImp::dimRange, 1>
@@ -138,19 +120,12 @@ public:
   using SpaceType = SpaceImp;
   using SourceSpaceType = SpaceImp;
   using RangeSpaceType = SpaceImp;
-  using EntropyFluxType =
-      EntropyBasedFluxEntropyCoordsFunction<typename SpaceType::GridViewType, MomentBasis, reconstruction>;
+  using EntropyFluxType = EntropyBasedFluxEntropyCoordsFunction<typename SpaceType::GridViewType, MomentBasis, slope>;
   using RangeFieldType = typename MomentBasis::RangeFieldType;
-  using LocalVectorType = typename EntropyFluxType::VectorType;
 
-  EntropicHessianInverter(const EntropyFluxType& analytical_flux,
-                          const SpaceType& space,
-                          const RangeFieldType min_acceptable_density,
-                          const std::string filename = "")
+  EntropicHessianInverter(const EntropyFluxType& analytical_flux, const SpaceType& space)
     : analytical_flux_(analytical_flux)
     , space_(space)
-    , min_acceptable_density_(min_acceptable_density)
-    , filename_(filename)
   {}
 
   virtual bool linear() const override final
@@ -180,8 +155,8 @@ public:
                              VectorType& alpha_update,
                              const XT::Common::Parameter& param) const
   {
-    LocalEntropicHessianInverter<SpaceType, VectorType, MomentBasis, reconstruction> local_hessian_inverter(
-        space_, alpha, u_update, alpha_update, analytical_flux_, min_acceptable_density_, param, filename_);
+    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);
@@ -190,8 +165,6 @@ public:
 private:
   const EntropyFluxType& analytical_flux_;
   const SpaceType& space_;
-  const RangeFieldType min_acceptable_density_;
-  const std::string filename_;
 }; // class EntropicHessianInverter<...>
 
 
diff --git a/dune/gdt/operators/advection-fv-entropybased.hh b/dune/gdt/operators/advection-fv-entropybased.hh
index e9a5b6fe1..06f37a1bb 100644
--- a/dune/gdt/operators/advection-fv-entropybased.hh
+++ b/dune/gdt/operators/advection-fv-entropybased.hh
@@ -64,7 +64,7 @@ public:
   const InverseHessianOperatorType& inverse_hessian_operator_;
 }; // class EntropicCoordinatesOperator<...>
 
-template <class AdvectionOperatorImp, class RhsOperatorImp, class InverseHessianOperatorImp>
+template <class DensityOperatorImp, class AdvectionOperatorImp, class RhsOperatorImp, class InverseHessianOperatorImp>
 class EntropicCoordinatesCombinedOperator
   : public OperatorInterface<typename AdvectionOperatorImp::MatrixType,
                              typename AdvectionOperatorImp::SGV,
@@ -79,14 +79,17 @@ public:
   using typename BaseType::SourceSpaceType;
   using typename BaseType::VectorType;
 
+  using DensityOperatorType = DensityOperatorImp;
   using AdvectionOperatorType = AdvectionOperatorImp;
   using RhsOperatorType = RhsOperatorImp;
   using InverseHessianOperatorType = InverseHessianOperatorImp;
 
-  EntropicCoordinatesCombinedOperator(const AdvectionOperatorType& advection_op,
+  EntropicCoordinatesCombinedOperator(const DensityOperatorType& density_op,
+                                      const AdvectionOperatorType& advection_op,
                                       const RhsOperatorType& rhs_op,
                                       const InverseHessianOperatorType& inverse_hessian_operator)
-    : advection_op_(advection_op)
+    : density_op_(density_op)
+    , advection_op_(advection_op)
     , rhs_op_(rhs_op)
     , inverse_hessian_operator_(inverse_hessian_operator)
   {}
@@ -108,6 +111,7 @@ public:
 
   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);
@@ -116,6 +120,7 @@ public:
     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_;
diff --git a/dune/gdt/operators/reconstruction/internal.hh b/dune/gdt/operators/reconstruction/internal.hh
index 850095c7c..787c2e16a 100644
--- a/dune/gdt/operators/reconstruction/internal.hh
+++ b/dune/gdt/operators/reconstruction/internal.hh
@@ -22,8 +22,6 @@
 
 #include <dune/gdt/operators/interfaces.hh>
 
-#include "slopes.hh"
-
 namespace Dune {
 namespace GDT {
 namespace internal {
diff --git a/dune/gdt/operators/reconstruction/linear_kinetic.hh b/dune/gdt/operators/reconstruction/linear_kinetic.hh
index 2fa385508..a2f55c91e 100644
--- a/dune/gdt/operators/reconstruction/linear_kinetic.hh
+++ b/dune/gdt/operators/reconstruction/linear_kinetic.hh
@@ -37,27 +37,22 @@ class LocalPointwiseLinearKineticReconstructionOperator : public XT::Grid::Eleme
   static constexpr size_t dimRange = BoundaryValueType::r;
   using EntityType = typename GV::template Codim<0>::Entity;
   using RangeFieldType = typename BoundaryValueType::RangeFieldType;
-  using StencilType = DynamicVector<LocalVectorType>;
-  using StencilsType = std::vector<StencilType>;
+  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;
-  static constexpr size_t stencil_size = 3;
   using ReconstructedFunctionType = DiscreteValuedGridFunction<GV, dimRange, 1, RangeFieldType>;
 
 public:
   explicit LocalPointwiseLinearKineticReconstructionOperator(ReconstructedFunctionType& reconstructed_function,
                                                              const GV& grid_view,
                                                              const AnalyticalFluxType& analytical_flux,
-                                                             const std::vector<LocalVectorType>& source_values,
-                                                             const BoundaryValueType& boundary_values,
                                                              const XT::Common::Parameter& param)
     : reconstructed_function_(reconstructed_function)
     , grid_view_(grid_view)
     , analytical_flux_(analytical_flux)
-    , source_values_(source_values)
-    , boundary_values_(boundary_values)
     , param_(param)
-    , stencils_(dimDomain, StencilType(stencil_size))
   {}
 
   LocalPointwiseLinearKineticReconstructionOperator(const ThisType& other)
@@ -65,10 +60,7 @@ public:
     , reconstructed_function_(other.reconstructed_function_)
     , grid_view_(other.grid_view_)
     , analytical_flux_(other.analytical_flux_)
-    , source_values_(other.source_values_)
-    , boundary_values_(other.boundary_values_)
     , param_(other.param_)
-    , stencils_(dimDomain, StencilType(stencil_size))
   {}
 
   virtual XT::Grid::ElementFunctor<GV>* copy() override final
@@ -84,22 +76,23 @@ public:
 
     auto& local_reconstructed_values = reconstructed_function_.local_values(entity);
     for (size_t dd = 0; dd < dimDomain; ++dd)
-      analytical_flux_.calculate_minmod_density_reconstruction(stencils_[dd], local_reconstructed_values, 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)
   {
-    const auto entity_index = grid_view_.indexSet().index(entity);
     for (size_t dd = 0; dd < dimDomain; ++dd)
-      stencils_[dd][1] = source_values_[entity_index];
+      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] = boundary_values_.evaluate(intersection.geometry().center());
+        stencils_[dd][index] = size_t(-1);
+        boundary_dirs_[dd][index] = intersection.indexInInside() % 2;
       } else if (intersection.neighbor()) { // inner and periodic intersections {
-        stencils_[dd][index] = source_values_[grid_view_.indexSet().index(intersection.outside())];
+        stencils_[dd][index] = grid_view_.indexSet().index(intersection.outside());
       } else if (!intersection.neighbor() && !intersection.boundary()) { // processor boundary
         return false;
       } else {
@@ -112,10 +105,10 @@ private:
   ReconstructedFunctionType& reconstructed_function_;
   const GV& grid_view_;
   const AnalyticalFluxType& analytical_flux_;
-  const std::vector<LocalVectorType>& source_values_;
-  const BoundaryValueType& boundary_values_;
   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
@@ -170,7 +163,7 @@ public:
     // do reconstruction
     auto local_reconstruction_operator =
         LocalPointwiseLinearKineticReconstructionOperator<GV, AnalyticalFluxType, BoundaryValueType, LocalVectorType>(
-            range, grid_view, analytical_flux_, source_values, boundary_values_, param);
+            range, grid_view, analytical_flux_, param);
     auto walker = XT::Grid::Walker<GV>(grid_view);
     walker.append(local_reconstruction_operator);
     walker.walk(true);
diff --git a/dune/gdt/operators/reconstruction/slopes.hh b/dune/gdt/operators/reconstruction/slopes.hh
index a840a2aa7..4a3772eaa 100644
--- a/dune/gdt/operators/reconstruction/slopes.hh
+++ b/dune/gdt/operators/reconstruction/slopes.hh
@@ -31,8 +31,6 @@
 #include <dune/gdt/momentmodels/basisfunctions/partial_moments.hh>
 #include <dune/gdt/momentmodels/entropyflux.hh>
 
-#include "internal.hh"
-
 namespace Dune {
 namespace GDT {
 
@@ -213,22 +211,11 @@ public:
     return minmod(slope_left_char, slope_right_char);
   }
 
-  virtual VectorType get(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];
-    return minmod(slope_left, slope_right);
-  }
-
   static VectorType minmod(const VectorType& first_slope, const VectorType& second_slope)
   {
-    VectorType ret(0.);
+    VectorType ret;
     for (size_t ii = 0; ii < first_slope.size(); ++ii)
-      if (std::signbit(first_slope[ii]) == std::signbit(second_slope[ii])) // check for equal sign
-        ret[ii] = (std::abs(first_slope[ii]) < std::abs(second_slope[ii])) ? first_slope[ii] : second_slope[ii];
+      ret[ii] = XT::Common::minmod(first_slope[ii], second_slope[ii]);
     return ret;
   }
 };
@@ -297,17 +284,14 @@ public:
     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);
-    const VectorType first_slope = MinmodType::minmod(slope_left_char, slope_right_char * 2.);
-    const VectorType second_slope = MinmodType::minmod(slope_left_char * 2., slope_right_char);
-    return maxmod(first_slope, second_slope);
+    return superbee(slope_left_char, slope_right_char);
   }
 
-  static VectorType maxmod(const VectorType& first_slope, const VectorType& second_slope)
+  static VectorType superbee(const VectorType& first_slope, const VectorType& second_slope)
   {
     VectorType ret(0.);
     for (size_t ii = 0; ii < first_slope.size(); ++ii)
-      if (std::signbit(first_slope[ii]) == std::signbit(second_slope[ii])) // check for equal sign
-        ret[ii] = (std::abs(first_slope[ii]) < std::abs(second_slope[ii])) ? second_slope[ii] : first_slope[ii];
+      ret[ii] = superbee(first_slope[ii], second_slope[ii]);
     return ret;
   }
 }; // class SuperbeeSlope
diff --git a/dune/gdt/test/entropic-coords-mn-discretization.hh b/dune/gdt/test/entropic-coords-mn-discretization.hh
index 220125d78..71e162969 100644
--- a/dune/gdt/test/entropic-coords-mn-discretization.hh
+++ b/dune/gdt/test/entropic-coords-mn-discretization.hh
@@ -28,6 +28,7 @@
 #include <dune/gdt/momentmodels/hessianinverter.hh>
 #include <dune/gdt/momentmodels/entropyflux_kineticcoords.hh>
 #include <dune/gdt/momentmodels/entropyflux.hh>
+#include <dune/gdt/momentmodels/density_evaluations.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>
@@ -99,9 +100,11 @@ struct HyperbolicEntropicCoordsMnDiscretization
     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;
-    using EntropyFluxType = EntropyBasedFluxEntropyCoordsFunction<GV, MomentBasis, TestCaseType::reconstruction>;
+    constexpr SlopeType slope = TestCaseType::reconstruction ? SlopeType::minmod : SlopeType::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());
@@ -123,18 +126,6 @@ struct HyperbolicEntropicCoordsMnDiscretization
     GenericFunctionType boundary_values_alpha(
         1, [&](const DomainType& x, const XT::Common::Parameter&) { return alpha_boundary_vals[x]; });
 
-    // calculate boundary kinetic 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 boundary_flux = analytical_flux->calculate_boundary_flux(alpha_boundary_vals[x], intersection);
-          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]; });
-
 
     // ***************** project initial values to discrete function *********************
     // create a discrete function for the solution
@@ -153,12 +144,6 @@ struct HyperbolicEntropicCoordsMnDiscretization
       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 (auto&& entry : alpha_local)
-        if (entry > 1) {
-          std::cout << XT::Common::to_string(element.geometry().center()) << ", " << XT::Common::to_string(u_local)
-                    << ", " << XT::Common::to_string(alpha_local) << std::endl;
-          break;
-        }
       for (size_t ii = 0; ii < dimRange; ++ii)
         alpha_local_func->dofs().set_entry(ii, alpha_local[ii]);
     }
@@ -166,7 +151,7 @@ struct HyperbolicEntropicCoordsMnDiscretization
     // ******************** choose flux and rhs operator and timestepper ******************************************
 
     using AdvectionOperatorType = AdvectionFvOperator<MatrixType, GV, dimRange>;
-    using HessianInverterType = EntropicHessianInverter<MomentBasis, SpaceType, TestCaseType::reconstruction>;
+    using HessianInverterType = EntropicHessianInverter<MomentBasis, SpaceType, slope>;
 #if 0
     using ReconstructionOperatorType = PointwiseLinearReconstructionNoCharOperator<
                                                                              GV,
@@ -180,14 +165,15 @@ struct HyperbolicEntropicCoordsMnDiscretization
 #endif
     using ReconstructionAdvectionOperatorType =
         AdvectionWithPointwiseReconstructionOperator<AdvectionOperatorType, ReconstructionOperatorType>;
-    using NonEntropicAdvectionOperatorType =
-        std::conditional_t<TestCaseType::reconstruction, ReconstructionAdvectionOperatorType, AdvectionOperatorType>;
+    using NonEntropicAdvectionOperatorType = ReconstructionAdvectionOperatorType;
     //    using FvOperatorType = EntropicCoordinatesOperator<
     //        NonEntropicAdvectionOperatorType,
     //        HessianInverterType>;
     using NonEntropicRhsOperatorType = GenericOperator<MatrixType, GV, dimRange>;
     //    using RhsOperatorType = EntropicCoordinatesOperator<NonEntropicRhsOperatorType, HessianInverterType>;
-    using CombinedOperatorType = EntropicCoordinatesCombinedOperator<NonEntropicAdvectionOperatorType,
+    using DensityOperatorType = DensityEvaluator<MomentBasis, SpaceType, slope>;
+    using CombinedOperatorType = EntropicCoordinatesCombinedOperator<DensityOperatorType,
+                                                                     NonEntropicAdvectionOperatorType,
                                                                      NonEntropicRhsOperatorType,
                                                                      HessianInverterType>;
 
@@ -231,24 +217,38 @@ struct HyperbolicEntropicCoordsMnDiscretization
     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 TestCaseType::reconstruction
-                     ? boundary_kinetic_fluxes.evaluate(
-                           intersection.geometry().global(xx_in_reference_intersection_coordinates))
-                     : boundary_values_alpha.evaluate(
-                           intersection.geometry().global(xx_in_reference_intersection_coordinates));
+          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);
 
-    //    constexpr double epsilon = 1e-11;
-    //    MinmodSlope<E, DummyEigenVectorWrapper<RangeType>> slope;
-    //    ReconstructionOperatorType reconstruction_operator(boundary_values_alpha, fv_space, *analytical_flux, slope);
     ReconstructionOperatorType reconstruction_operator(boundary_values_alpha, fv_space, *analytical_flux);
     ReconstructionAdvectionOperatorType reconstruction_advection_operator(advection_operator, reconstruction_operator);
 
@@ -265,12 +265,8 @@ struct HyperbolicEntropicCoordsMnDiscretization
     filename += TestCaseType::reconstruction ? "_ord2" : "_ord1";
     filename += "_" + basis_functions->mn_name();
 
-    HessianInverterType hessian_inverter(
-        *analytical_flux, fv_space, problem.psi_vac() * basis_functions->unit_ball_volume() / 10, filename);
-    auto& non_entropic_advection_operator =
-        FvOperatorChooser<TestCaseType::reconstruction>::choose(advection_operator, reconstruction_advection_operator);
-    //    FvOperatorType fv_operator(non_entropic_advection_operator, hessian_inverter);
-
+    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();
@@ -278,14 +274,12 @@ struct HyperbolicEntropicCoordsMnDiscretization
     const auto sigma_s = problem.sigma_s();
     const auto Q = problem.Q();
     auto rhs_func = [&](const auto& /*source*/,
-                        const auto& local_source,
+                        const auto& /*local_source*/,
                         auto& local_range,
                         const Dune::XT::Common::Parameter& /*param*/) {
       const auto& element = local_range.element();
-      local_source->bind(element);
       const auto center = element.geometry().center();
-      const auto alpha = local_source->evaluate(center);
-      const auto u = analytical_flux->get_u(alpha);
+      const auto u = 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;
@@ -300,7 +294,7 @@ struct HyperbolicEntropicCoordsMnDiscretization
         fv_space, fv_space, std::vector<typename NonEntropicRhsOperatorType::GenericElementFunctionType>(1, rhs_func));
     //    RhsOperatorType rhs_operator(non_entropic_rhs_operator, hessian_inverter);
     CombinedOperatorType combined_operator(
-        non_entropic_advection_operator, non_entropic_rhs_operator, hessian_inverter);
+        density_operator, non_entropic_advection_operator, non_entropic_rhs_operator, hessian_inverter);
 
     // ******************************** do the time steps ***********************************************************
     //    OperatorTimeStepperType timestepper_op(fv_operator, alpha, -1.0);
diff --git a/dune/gdt/test/hyperbolic__momentmodels__entropic_coords_mn.cc b/dune/gdt/test/hyperbolic__momentmodels__entropic_coords_mn.cc
index 0318d5c42..217629bbe 100644
--- a/dune/gdt/test/hyperbolic__momentmodels__entropic_coords_mn.cc
+++ b/dune/gdt/test/hyperbolic__momentmodels__entropic_coords_mn.cc
@@ -18,42 +18,32 @@ using Yasp2 = Dune::YaspGrid<2, Dune::EquidistantOffsetCoordinates<double, 2>>;
 using Yasp3 = Dune::YaspGrid<3, Dune::EquidistantOffsetCoordinates<double, 3>>;
 
 using YaspGridTestCasesAll = 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, 20>, true>
-//    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 20>, true>
-//    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 21>, true>
-#endif
+    // 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, 20, 1, 1>, false>,
-//    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 100, 1, 1>, true>
-//    Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, false>
-//    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>, false>
-//    Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, 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
-//    ,
-#  if HAVE_CLP
-//    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>,
-//    false>, Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2,
-//    3>, true> Dune::GDT::CheckerboardMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3>,
-//    true> Dune::GDT::CheckerboardMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>,
-//    false>, Dune::GDT::ShadowMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>,
-//    false>, Dune::GDT::ShadowMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 1, 1, 3>, true>
-#  endif
-//    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, false>
-// 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>, false>,
-//    Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::PartialMomentBasis<double, 3, double, 0, 1, 3>, true>
-#  endif
-#endif
+    ,
+    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);
diff --git a/dune/gdt/test/momentmodels/kinetictransport/base.hh b/dune/gdt/test/momentmodels/kinetictransport/base.hh
index c092f43a9..e5f4165a8 100644
--- a/dune/gdt/test/momentmodels/kinetictransport/base.hh
+++ b/dune/gdt/test/momentmodels/kinetictransport/base.hh
@@ -57,6 +57,7 @@ public:
   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 DomainType&)>(const DomainType&)>;
 
   using BaseType::default_boundary_cfg;
   using BaseType::default_grid_cfg;
@@ -173,6 +174,35 @@ public:
         [=](const DomainType&, const XT::Common::Parameter&) { return value; });
   } // ... boundary_values()
 
+  virtual BoundaryDistributionType boundary_distribution() const
+  {
+    return [this](const DomainType&) { return [this](const DomainType&) { 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);
+  }
+
   virtual RangeFieldType CFL() const override
   {
     return 0.49;
diff --git a/dune/gdt/test/momentmodels/kinetictransport/sourcebeam.hh b/dune/gdt/test/momentmodels/kinetictransport/sourcebeam.hh
index adbeb01c9..93a22fd00 100644
--- a/dune/gdt/test/momentmodels/kinetictransport/sourcebeam.hh
+++ b/dune/gdt/test/momentmodels/kinetictransport/sourcebeam.hh
@@ -32,10 +32,12 @@ 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;
@@ -92,6 +94,25 @@ public:
     });
   } // ... boundary_values()
 
+  virtual 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_);
@@ -133,11 +154,16 @@ protected:
   struct helper_base
   {
     // returns the numerator g of the left boundary value (see create_boundary_values)
-    static RangeFieldType numerator(const RangeFieldType v)
+    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()
     {
@@ -155,7 +181,13 @@ protected:
     // 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 * (numerator(v_u) - numerator(v_l));
+      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);
     }
   };
 
@@ -164,7 +196,7 @@ protected:
   struct helper : public helper_base
   {
     using helper_base::denominator;
-    using helper_base::numerator;
+    using helper_base::g;
 
     static DynamicRangeType get_left_boundary_values(const MomentBasisImp& basis_functions,
                                                      const RangeFieldType& psi_vac,
@@ -182,7 +214,7 @@ protected:
         for (const auto& quad_point : quadrature) {
           const auto& v = quad_point.position()[0];
           auto summand = basis_functions.evaluate(v, ii);
-          summand *= numerator(v) * quad_point.weight();
+          summand *= g(v) * quad_point.weight();
           ret += summand;
         }
       }
@@ -191,6 +223,17 @@ protected:
       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>
@@ -198,8 +241,10 @@ protected:
     : public helper_base
   {
     using helper_base::denominator;
+    using helper_base::g;
     using helper_base::integral_1;
-    using helper_base::numerator;
+    using helper_base::integral_2;
+    using helper_base::integral_3;
 
     static DynamicRangeType get_left_boundary_values(const MomentBasisImp& basis_functions,
                                                      const RangeFieldType psi_vac,
@@ -211,19 +256,55 @@ protected:
         const auto vn = triangulation[nn];
         if (nn < dimRange - 1) {
           const auto vnp = triangulation[nn + 1];
-          ret[nn] += 1. / ((vn - vnp) * denominator())
-                     * ((1 - vnp) * integral_1(vn, vnp) - 1. / 2e5 * (numerator(vnp) - numerator(vn)));
+          ret[nn] += 1. / ((vn - vnp) * denominator()) * (integral_2(vn, vnp) - vnp * integral_1(vn, vnp));
         }
         if (nn > 0) {
           const auto vnm = triangulation[nn - 1];
-          ret[nn] += 1. / ((vn - vnm) * denominator())
-                     * ((1 - vnm) * integral_1(vnm, vn) - 1. / 2e5 * (numerator(vn) - numerator(vnm)));
+          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& triangulation = basis_functions.triangulation();
+        for (size_t nn = 0; nn < dimRange; ++nn) {
+          const auto vn = triangulation[nn];
+          if (nn < dimRange - 1) {
+            const auto vnp = triangulation[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 = triangulation[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>
@@ -232,6 +313,7 @@ protected:
     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,
@@ -247,6 +329,32 @@ protected:
       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& triangulation = basis_functions.triangulation();
+        DynamicRangeType ret(dimRange, 0.);
+        for (size_t ii = 0; ii < dimRange / 2; ++ii) {
+          if (triangulation[ii + 1] > 0.) {
+            const auto left_limit = triangulation[ii] > 0. ? triangulation[ii] : 0.;
+            ret[2 * ii] = integral_2(left_limit, triangulation[ii + 1]) / denominator();
+            ret[2 * ii + 1] = integral_3(left_limit, triangulation[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_;
diff --git a/dune/gdt/test/momentmodels/kinetictransport/testcases.hh b/dune/gdt/test/momentmodels/kinetictransport/testcases.hh
index bc5b7bd3f..11b99cfb2 100644
--- a/dune/gdt/test/momentmodels/kinetictransport/testcases.hh
+++ b/dune/gdt/test/momentmodels/kinetictransport/testcases.hh
@@ -238,7 +238,7 @@ struct SourceBeamPnTestCase
 
 
 // SourceBeam Mn
-template <class MomentBasisImp, bool reconstruct, bool kinetic_scheme>
+template <class MomentBasisImp, bool reconstruct, bool kinetic_scheme = false>
 struct SourceBeamMnExpectedResults;
 
 template <bool reconstruct, bool kinetic_scheme>
@@ -262,14 +262,14 @@ struct SourceBeamMnExpectedResults<HatFunctionMomentBasis<double, 1, double, 8,
 template <bool reconstruct>
 struct SourceBeamMnExpectedResults<HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, reconstruct, true>
 {
-  static constexpr double l1norm = reconstruct ? 370.93823722076462 : 367.56820428572496;
-  static constexpr double l2norm = reconstruct ? 235.97872826934559 : 235.22990290508343;
-  static constexpr double linfnorm = reconstruct ? 210.82343000666447 : 209.02263326452186;
+  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-9;
 };
 
-template <bool reconstruct, bool kinetic_scheme>
-struct SourceBeamMnExpectedResults<PartialMomentBasis<double, 1, double, 8, 1, 1>, reconstruct, kinetic_scheme>
+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;
@@ -277,6 +277,15 @@ struct SourceBeamMnExpectedResults<PartialMomentBasis<double, 1, double, 8, 1, 1
   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-9;
+};
+
 template <class GridImp, class MomentBasisImp, bool reconstruct, bool kinetic_scheme = false>
 struct SourceBeamMnTestCase : public SourceBeamPnTestCase<GridImp, MomentBasisImp, reconstruct>
 {
@@ -334,11 +343,17 @@ struct PlaneSourcePnTestCase : SourceBeamPnTestCase<GridImp, MomentBasisImp, rec
 
 
 // PlaneSource Mn
-template <class MomentBasisImp, bool reconstruct>
-struct PlaneSourceMnExpectedResults;
+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>
+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;
@@ -347,7 +362,16 @@ struct PlaneSourceMnExpectedResults<LegendreMomentBasis<double, double, 7>, reco
 };
 
 template <bool reconstruct>
-struct PlaneSourceMnExpectedResults<HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, 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-7;
+};
+
+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;
@@ -356,7 +380,16 @@ struct PlaneSourceMnExpectedResults<HatFunctionMomentBasis<double, 1, double, 8,
 };
 
 template <bool reconstruct>
-struct PlaneSourceMnExpectedResults<PartialMomentBasis<double, 1, double, 8, 1, 1>, reconstruct>
+struct PlaneSourceMnExpectedResults<HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, reconstruct, true>
+{
+  static constexpr double l1norm = reconstruct ? 268.42786311776274 : 246.7429359648828;
+  static constexpr double l2norm = reconstruct ? 197.1506642519208 : 186.09403264481648;
+  static constexpr double linfnorm = reconstruct ? 152.91062339609854 : 152.91062339609854;
+  static constexpr double tol = 1e-9;
+};
+
+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;
@@ -364,7 +397,17 @@ struct PlaneSourceMnExpectedResults<PartialMomentBasis<double, 1, double, 8, 1,
   static constexpr double tol = 1e-9;
 };
 
-template <class GridImp, class MomentBasisImp, bool reconstruct>
+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-9;
+};
+
+
+template <class GridImp, class MomentBasisImp, bool reconstruct, bool kinetic_scheme = false>
 struct PlaneSourceMnTestCase : SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct>
 {
   using BaseType = SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct>;
@@ -374,7 +417,7 @@ struct PlaneSourceMnTestCase : SourceBeamMnTestCase<GridImp, MomentBasisImp, rec
   using ProblemType = PlaneSourceMn<GridViewType, MomentBasisImp>;
   static constexpr RangeFieldType t_end = 0.25;
   static constexpr bool reconstruction = reconstruct;
-  using ExpectedResultsType = PlaneSourceMnExpectedResults<MomentBasisImp, reconstruction>;
+  using ExpectedResultsType = PlaneSourceMnExpectedResults<MomentBasisImp, reconstruction, kinetic_scheme>;
   using RealizabilityLimiterChooserType =
       RealizabilityLimiterChooser<GridViewType, MomentBasisImp, typename ProblemType::FluxType, DiscreteFunctionType>;
 };
@@ -522,11 +565,17 @@ struct ShadowPnTestCase : SourceBeamPnTestCase<GridImp, MomentBasisImp, reconstr
 
 
 // PointSourceMn
-template <class MomentBasisImp, bool reconstruct>
-struct PointSourceMnExpectedResults;
+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>
+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;
@@ -535,7 +584,16 @@ struct PointSourceMnExpectedResults<RealSphericalHarmonicsMomentBasis<double, do
 };
 
 template <bool reconstruct>
-struct PointSourceMnExpectedResults<HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, 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-9;
+};
+
+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;
@@ -551,7 +609,16 @@ struct PointSourceMnExpectedResults<HatFunctionMomentBasis<double, 3, double, 0,
 };
 
 template <bool reconstruct>
-struct PointSourceMnExpectedResults<PartialMomentBasis<double, 3, double, 0, 1, 3, 1>, reconstruct>
+struct PointSourceMnExpectedResults<HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, reconstruct, true>
+{
+  static constexpr double l1norm = reconstruct ? 743.43592672927934 : 683.47651349520368;
+  static constexpr double l2norm = reconstruct ? 279.11700895978396 : 258.45009663177717;
+  static constexpr double linfnorm = reconstruct ? 125.7102296804655 : 125.71014355518233;
+  static constexpr double tol = 1e-9;
+};
+
+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;
@@ -559,16 +626,25 @@ struct PointSourceMnExpectedResults<PartialMomentBasis<double, 3, double, 0, 1,
   static constexpr double tol = 1e-9;
 };
 
-template <class GridImp, class MomentBasisImp, bool reconstruct>
-struct PointSourceMnTestCase : SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct>
+template <bool reconstruct>
+struct PointSourceMnExpectedResults<PartialMomentBasis<double, 3, double, 0, 1, 3, 1>, reconstruct, true>
 {
-  using BaseType = SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct>;
+  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-9;
+};
+
+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>;
+  using ExpectedResultsType = PointSourceMnExpectedResults<MomentBasisImp, reconstruction, kinetic_scheme>;
   using RealizabilityLimiterChooserType = RealizabilityLimiterChooser<GridViewType,
                                                                       MomentBasisImp,
                                                                       typename ProblemType::FluxType,
@@ -577,11 +653,11 @@ struct PointSourceMnTestCase : SourceBeamMnTestCase<GridImp, MomentBasisImp, rec
 
 
 // CheckerboardMn
-template <class MomentBasisImp, bool reconstruct>
+template <class MomentBasisImp, bool reconstruct, bool kinetic_scheme = false>
 struct CheckerboardMnExpectedResults;
 
 template <bool reconstruct>
-struct CheckerboardMnExpectedResults<RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, 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;
@@ -589,16 +665,43 @@ struct CheckerboardMnExpectedResults<RealSphericalHarmonicsMomentBasis<double, d
   static constexpr double tol = 1e-9;
 };
 
-template <class GridImp, class MomentBasisImp, bool reconstruct>
-struct CheckerboardMnTestCase : SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct>
+template <bool reconstruct>
+struct CheckerboardMnExpectedResults<RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, reconstruct, true>
 {
-  using BaseType = SourceBeamMnTestCase<GridImp, MomentBasisImp, reconstruct>;
+  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, 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 ? 44760.517221844952 : 0.;
+  static constexpr double l2norm = reconstruct ? 2491.7035345029099 : 0.;
+  static constexpr double linfnorm = reconstruct ? 542.02861997383934 : 0.;
+  static constexpr double tol = 1e-9;
+};
+
+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>;
+  using ExpectedResultsType = CheckerboardMnExpectedResults<MomentBasisImp, reconstruction, kinetic_scheme>;
   using RealizabilityLimiterChooserType = RealizabilityLimiterChooser<GridViewType,
                                                                       MomentBasisImp,
                                                                       typename ProblemType::FluxType,
-- 
GitLab