diff --git a/dune/gdt/test/momentmodels/basisfunctions/hatfunctions.hh b/dune/gdt/test/momentmodels/basisfunctions/hatfunctions.hh
index c5929c639cfb08838616a6516f62b6eeeb35f104..64082860486db731a1e26c990f7402067280c298 100644
--- a/dune/gdt/test/momentmodels/basisfunctions/hatfunctions.hh
+++ b/dune/gdt/test/momentmodels/basisfunctions/hatfunctions.hh
@@ -11,10 +11,11 @@
 #ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_HATFUNCTIONS_HH
 #define DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_HATFUNCTIONS_HH
 
-#include <vector>
 #include <string>
+#include <vector>
 
 #include <dune/xt/common/fmatrix.hh>
+#include <dune/xt/common/numeric.hh>
 
 #include <dune/gdt/test/momentmodels/triangulation.hh>
 
@@ -24,6 +25,129 @@ namespace Dune {
 namespace GDT {
 
 
+template <class DomainFieldType,
+          size_t domainDim,
+          class RangeFieldType,
+          size_t rangeDim,
+          size_t fluxDim,
+          EntropyType entropy>
+class HatFunctionMomentBasisBase
+  : public MomentBasisInterface<DomainFieldType, domainDim, RangeFieldType, rangeDim, 1, fluxDim, entropy>
+{
+  using BaseType = MomentBasisInterface<DomainFieldType, domainDim, RangeFieldType, rangeDim, 1, fluxDim, entropy>;
+
+public:
+  using BaseType::dimDomain;
+  using BaseType::dimRange;
+
+  using typename BaseType::DynamicRangeType;
+  using typename BaseType::RangeType;
+  using typename BaseType::StringifierType;
+
+  template <class... Args>
+  HatFunctionMomentBasisBase(Args&&... args)
+    : BaseType(std::forward<Args>(args)...)
+  {}
+
+  static StringifierType stringifier()
+  {
+    return [](const RangeType& val) {
+      RangeFieldType psi(0);
+      for (const auto& entry : val)
+        psi += entry;
+      return XT::Common::to_string(psi, 15);
+    };
+  } // ... stringifier()
+
+  DynamicRangeType alpha_one() const override final
+  {
+    return DynamicRangeType(dimRange, 1.);
+  }
+
+  RangeFieldType density(const DynamicRangeType& u) const override final
+  {
+    return XT::Common::reduce(&(u[0]), &(u[0]) + dimRange, RangeFieldType(0));
+  }
+
+  RangeFieldType density(const RangeType& u) const override final
+  {
+    return XT::Common::reduce(&(u[0]), &(u[0]) + dimRange, RangeFieldType(0));
+  }
+
+  template <class Vec>
+  std::enable_if_t<XT::Common::is_vector<Vec>::value, void> alpha_one(Vec& ret) const
+  {
+    for (size_t ii = 0; ii < ret.size(); ++ii)
+      XT::Common::VectorAbstraction<Vec>::set_entry(ret, ii, 1.);
+  }
+
+  template <class Vec>
+  std::enable_if_t<XT::Common::is_vector<Vec>::value && !std::is_same<Vec, DynamicRangeType>::value, RangeFieldType>
+  density(const Vec& u) const
+  {
+    RangeFieldType ret(0.);
+    for (size_t ii = 0; ii < u.size(); ++ii)
+      ret += XT::Common::VectorAbstraction<Vec>::get_entry(u, ii);
+    return ret;
+  }
+
+  using BaseType::u_iso;
+
+  template <class Vec>
+  std::enable_if_t<XT::Common::is_vector<Vec>::value, void> u_iso(Vec& ret) const
+  {
+    const auto& ret_range = u_iso();
+    using V = XT::Common::VectorAbstraction<Vec>;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      V::set_entry(ret, ii, ret_range[ii]);
+  }
+
+  void ensure_min_density(DynamicRangeType& u, const RangeFieldType psi_min) const override final
+  {
+    const auto u_iso_min = u_iso() * psi_min;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      if (u[ii] < u_iso_min[ii])
+        u[ii] = u_iso_min[ii];
+  }
+
+  void ensure_min_density(RangeType& u, const RangeFieldType psi_min) const override final
+  {
+    const auto u_iso_min = u_iso() * psi_min;
+    for (size_t ii = 0; ii < dimRange; ++ii)
+      if (u[ii] < u_iso_min[ii])
+        u[ii] = u_iso_min[ii];
+  }
+
+  bool adjust_alpha_to_ensure_min_density(RangeType& alpha, const RangeFieldType psi_min) const override final
+  {
+    bool changed = false;
+    const auto alpha_min = std::log(psi_min);
+    for (size_t ii = 0; ii < dimRange; ++ii) {
+      if (alpha[ii] < alpha_min) {
+        alpha[ii] = alpha_min;
+        changed = true;
+      }
+    }
+    return changed;
+  }
+
+  std::string short_id() const override final
+  {
+    return "hf";
+  }
+
+  std::string mn_name() const override final
+  {
+    return "hfm" + XT::Common::to_string(dimRange);
+  }
+
+  std::string pn_name() const override final
+  {
+    return "hfp" + XT::Common::to_string(dimRange);
+  }
+}; // class HatFunctionMomentBasisBase
+
+
 template <class DomainFieldType,
           size_t dimDomain,
           class RangeFieldType,
@@ -43,7 +167,7 @@ template <class DomainFieldType,
           size_t fluxDim,
           EntropyType entropy>
 class HatFunctionMomentBasis<DomainFieldType, 1, RangeFieldType, rangeDim, rangeDimCols, fluxDim, entropy>
-  : public MomentBasisInterface<DomainFieldType, 1, RangeFieldType, rangeDim, rangeDimCols, fluxDim, entropy>
+  : public HatFunctionMomentBasisBase<DomainFieldType, 1, RangeFieldType, rangeDim, fluxDim, entropy>
 {
 public:
   static const size_t dimDomain = 1;
@@ -52,8 +176,7 @@ public:
   static const size_t num_intervals = dimRange - 1;
 
 private:
-  using BaseType =
-      MomentBasisInterface<DomainFieldType, dimDomain, RangeFieldType, dimRange, dimRangeCols, fluxDim, entropy>;
+  using BaseType = HatFunctionMomentBasisBase<DomainFieldType, 1, RangeFieldType, rangeDim, fluxDim, entropy>;
 
 public:
   using typename BaseType::DomainType;
@@ -63,14 +186,7 @@ public:
   using typename BaseType::RangeType;
   using typename BaseType::SphericalTriangulationType;
   using typename BaseType::StringifierType;
-  using typename BaseType::VisualizerType;
   using PartitioningType = typename BaseType::Partitioning1dType;
-  template <class DiscreteFunctionType>
-
-  static std::string static_id()
-  {
-    return "hatfunctions";
-  }
 
   static size_t default_quad_order()
   {
@@ -130,18 +246,6 @@ public:
     return ret;
   } // ... evaluate(...)
 
-  DynamicRangeType integrated() const override final
-  {
-    DynamicRangeType ret(dimRange, 0);
-    const auto& mu = partitioning_;
-    ret[0] = mu[1] - mu[0];
-    for (size_t ii = 1; ii < num_intervals; ++ii)
-      ret[ii] = mu[ii + 1] - mu[ii - 1];
-    ret[num_intervals] = mu[num_intervals] - mu[num_intervals - 1];
-    ret *= 0.5;
-    return ret;
-  }
-
   // returns matrix with entries <h_i h_j>
   MatrixType mass_matrix() const override final
   {
@@ -259,76 +363,58 @@ public:
     return ret;
   }
 
-  static StringifierType stringifier()
-  {
-    return [](const RangeType& val) {
-      RangeFieldType psi(0);
-      for (const auto& entry : val)
-        psi += entry;
-      return XT::Common::to_string(psi, 15);
-    };
-  } // ... stringifier()
-
-  const PartitioningType& partitioning() const
-  {
-    return partitioning_;
-  }
-
-  DynamicRangeType alpha_one() const override final
-  {
-    return DynamicRangeType(dimRange, 1.);
-  }
-
-  RangeFieldType density(const DynamicRangeType& u) const override final
-  {
-    return std::accumulate(u.begin(), u.end(), RangeFieldType(0));
-  }
-
-  using BaseType::u_iso;
-
-  void ensure_min_density(DynamicRangeType& u, const RangeFieldType min_density) const override final
-  {
-    const auto u_iso_min = u_iso() * min_density;
-    for (size_t ii = 0; ii < dimRange; ++ii)
-      if (u[ii] < u_iso_min[ii])
-        u[ii] = u_iso_min[ii];
-  }
-
-  void ensure_min_density(RangeType& u, const RangeFieldType min_density) const override final
+  // get indices of all faces that contain point v
+  std::vector<size_t> get_face_indices(const DomainType& v) const
   {
-    const auto u_iso_min = u_iso() * min_density;
-    for (size_t ii = 0; ii < dimRange; ++ii)
-      if (u[ii] < u_iso_min[ii])
-        u[ii] = u_iso_min[ii];
+    std::vector<size_t> face_indices;
+    for (size_t jj = 0; jj < partitioning_.size() - 1; ++jj)
+      if (XT::Common::FloatCmp::ge(v[0], partitioning_[jj]) && XT::Common::FloatCmp::le(v[0], partitioning_[jj + 1]))
+        face_indices.push_back(jj);
+    assert(face_indices.size());
+    return face_indices;
   }
 
-  std::string short_id() const override final
+  DynamicRangeType integrated_initializer(const QuadraturesType& /*quadratures*/) const override final
   {
-    return "hf";
+    DynamicRangeType ret(dimRange, 0);
+    const auto& mu = partitioning_;
+    ret[0] = mu[1] - mu[0];
+    for (size_t ii = 1; ii < num_intervals; ++ii)
+      ret[ii] = mu[ii + 1] - mu[ii - 1];
+    ret[num_intervals] = mu[num_intervals] - mu[num_intervals - 1];
+    ret *= 0.5;
+    return ret;
   }
 
-  std::string mn_name() const override final
+  const PartitioningType& partitioning() const
   {
-    return "hfm" + XT::Common::to_string(dimRange);
+    return partitioning_;
   }
 
-  std::string pn_name() const override final
+  DynamicRangeType
+  get_u(const std::function<RangeFieldType(DomainType, std::array<int, dimDomain>)>& psi) const override final
   {
-    return "hfp" + XT::Common::to_string(dimRange);
+    DynamicRangeType ret(dimRange, 0.);
+    const auto merged_quads = XT::Data::merged_quadrature(quadratures_);
+    for (auto it = merged_quads.begin(); it != merged_quads.end(); ++it) {
+      const auto& quad_point = *it;
+      const size_t interval_index = it.first_index();
+      const auto& v = quad_point.position();
+      const auto val = evaluate_on_interval(v, interval_index);
+      const auto factor = psi(v, has_fixed_sign(interval_index)) * quad_point.weight();
+      for (size_t ii = 0; ii < 2; ++ii)
+        ret[interval_index + ii] += val[ii] * factor;
+    }
+    return ret;
   }
 
-  // get indices of all faces that contain point v
-  std::vector<size_t> get_face_indices(const DomainType& v) const
+  std::array<int, dimDomain> has_fixed_sign(const size_t index) const override final
   {
-    std::vector<size_t> face_indices;
-    for (size_t jj = 0; jj < partitioning_.size() - 1; ++jj)
-      if (XT::Common::FloatCmp::ge(v[0], partitioning_[jj]) && XT::Common::FloatCmp::le(v[0], partitioning_[jj + 1]))
-        face_indices.push_back(jj);
-    assert(face_indices.size());
-    return face_indices;
+    return BaseType::interval_has_fixed_sign(index, num_intervals);
   }
 
 private:
+  using BaseType::quadratures_;
   const PartitioningType partitioning_;
 }; // class HatFunctionMomentBasis<DomainFieldType, 1, ...>
 
@@ -343,13 +429,12 @@ constexpr size_t
 
 template <class DomainFieldType, class RangeFieldType, size_t refinements, size_t fluxDim, EntropyType entropy>
 class HatFunctionMomentBasis<DomainFieldType, 3, RangeFieldType, refinements, 1, fluxDim, entropy>
-  : public MomentBasisInterface<DomainFieldType,
-                                3,
-                                RangeFieldType,
-                                OctaederStatistics<refinements>::num_vertices(),
-                                1,
-                                fluxDim,
-                                entropy>
+  : public HatFunctionMomentBasisBase<DomainFieldType,
+                                      3,
+                                      RangeFieldType,
+                                      OctaederStatistics<refinements>::num_vertices(),
+                                      fluxDim,
+                                      entropy>
 {
 public:
   static constexpr size_t dimDomain = 3;
@@ -359,8 +444,12 @@ public:
   static constexpr size_t num_refinements = refinements;
 
 private:
-  using BaseType =
-      MomentBasisInterface<DomainFieldType, dimDomain, RangeFieldType, dimRange, dimRangeCols, dimFlux, entropy>;
+  using BaseType = HatFunctionMomentBasisBase<DomainFieldType,
+                                              3,
+                                              RangeFieldType,
+                                              OctaederStatistics<refinements>::num_vertices(),
+                                              fluxDim,
+                                              entropy>;
   using ThisType = HatFunctionMomentBasis;
 
 public:
@@ -372,11 +461,9 @@ public:
   using typename BaseType::QuadraturesType;
   using typename BaseType::RangeType;
   using typename BaseType::StringifierType;
-  using typename BaseType::VisualizerType;
   using LocalMatrixType = XT::Common::FieldMatrix<RangeFieldType, 3, 3>;
 
   using BaseType::barycentre_rule;
-  using BaseType::is_negative;
 
   static size_t default_quad_order()
   {
@@ -461,16 +548,6 @@ public:
     return ret;
   } // ... evaluate(...)
 
-  static StringifierType stringifier()
-  {
-    return [](const RangeType& val) {
-      RangeFieldType psi(0);
-      for (const auto& entry : val)
-        psi += entry;
-      return XT::Common::to_string(psi, 15);
-    };
-  } // ... stringifier()
-
   const TriangulationType& triangulation() const
   {
     return triangulation_;
@@ -481,48 +558,6 @@ public:
     return BaseType::unit_ball_volume_quad();
   }
 
-  DynamicRangeType alpha_one() const override final
-  {
-    return DynamicRangeType(dimRange, 1.);
-  }
-
-  template <class Vec>
-  std::enable_if_t<XT::Common::is_vector<Vec>::value, void> alpha_one(Vec& ret) const
-  {
-    for (size_t ii = 0; ii < ret.size(); ++ii)
-      XT::Common::VectorAbstraction<Vec>::set_entry(ret, ii, 1.);
-  }
-
-  RangeFieldType density(const DynamicRangeType& u) const override final
-  {
-    return std::accumulate(u.begin(), u.end(), 0.);
-  }
-
-  template <class Vec>
-  std::enable_if_t<XT::Common::is_vector<Vec>::value && !std::is_same<Vec, DynamicRangeType>::value, RangeFieldType>
-  density(const Vec& u) const
-  {
-    RangeFieldType ret(0.);
-    for (size_t ii = 0; ii < u.size(); ++ii)
-      ret += XT::Common::VectorAbstraction<Vec>::get_entry(u, ii);
-    return ret;
-  }
-
-  std::string short_id() const override final
-  {
-    return "hf";
-  }
-
-  std::string mn_name() const override final
-  {
-    return "hfm" + XT::Common::to_string(dimRange);
-  }
-
-  std::string pn_name() const override final
-  {
-    return "hfp" + XT::Common::to_string(dimRange);
-  }
-
   // get indices of all faces that contain point v
   std::vector<size_t> get_face_indices(const DomainType& v) const
   {
@@ -537,33 +572,8 @@ public:
 
   using BaseType::u_iso;
 
-  template <class Vec>
-  std::enable_if_t<XT::Common::is_vector<Vec>::value, void> u_iso(Vec& ret) const
-  {
-    auto ret_range = u_iso();
-    using V = XT::Common::VectorAbstraction<Vec>;
-    for (size_t ii = 0; ii < dimRange; ++ii)
-      V::set_entry(ret, ii, ret_range[ii]);
-  }
-
-  void ensure_min_density(DynamicRangeType& u, const RangeFieldType min_density) const override final
-  {
-    const auto u_iso_min = u_iso() * min_density;
-    for (size_t ii = 0; ii < dimRange; ++ii)
-      if (u[ii] < u_iso_min[ii])
-        u[ii] = u_iso_min[ii];
-  }
-
-  void ensure_min_density(RangeType& u, const RangeFieldType min_density) const override final
-  {
-    const auto u_iso_min = u_iso() * min_density;
-    for (size_t ii = 0; ii < dimRange; ++ii)
-      if (u[ii] < u_iso_min[ii])
-        u[ii] = u_iso_min[ii];
-  }
-
-  virtual DynamicRangeType
-  get_moment_vector(const std::function<RangeFieldType(DomainType, bool)>& psi) const override final
+  DynamicRangeType
+  get_u(const std::function<RangeFieldType(DomainType, std::array<int, dimDomain>)>& psi) const override final
   {
     DynamicRangeType ret(dimRange, 0.);
     const auto merged_quads = XT::Data::merged_quadrature(quadratures_);
@@ -573,13 +583,18 @@ public:
       const auto& quad_point = *it;
       const auto& v = quad_point.position();
       const auto val = evaluate_on_face(v, face_index);
-      const auto factor = psi(v, is_negative(it)) * quad_point.weight();
+      const auto factor = psi(v, has_fixed_sign(it)) * quad_point.weight();
       for (size_t ii = 0; ii < 3; ++ii)
         ret[vertices[ii]->index()] += val[ii] * factor;
     }
     return ret;
   }
 
+  std::array<int, dimDomain> has_fixed_sign(const size_t index) const override final
+  {
+    return BaseType::triangle_has_fixed_sign(index);
+  }
+
 protected:
   template <class VertexVectorType>
   bool calculate_barycentric_coordinates(const DomainType& v, const VertexVectorType& vertices, DomainType& ret) const
@@ -653,10 +668,10 @@ protected:
     return ret;
   }
 
-  virtual void parallel_quadrature(const QuadraturesType& quadratures,
-                                   MatrixType& matrix,
-                                   const size_t v_index,
-                                   const bool reflecting = false) const override final
+  void parallel_quadrature(const QuadraturesType& quadratures,
+                           MatrixType& matrix,
+                           const size_t v_index,
+                           const bool reflecting = false) const override final
   {
     const auto& faces = triangulation_.faces();
     size_t num_threads = std::min(XT::Common::threadManager().max_threads(), faces.size());
@@ -689,12 +704,12 @@ protected:
     } // threads
   } // void parallel_quadrature(...)
 
-  virtual void calculate_in_thread_hat(std::vector<LocalMatrixType>& local_matrices,
-                                       const QuadraturesType& quadratures,
-                                       const size_t v_index,
-                                       const std::vector<size_t>& decomposition,
-                                       const size_t ii,
-                                       const bool reflecting) const
+  void calculate_in_thread_hat(std::vector<LocalMatrixType>& local_matrices,
+                               const QuadraturesType& quadratures,
+                               const size_t v_index,
+                               const std::vector<size_t>& decomposition,
+                               const size_t ii,
+                               const bool reflecting) const
   {
     const auto& reflected_indices = triangulation_.reflected_face_indices();
     for (size_t face_index = decomposition[ii]; face_index < decomposition[ii + 1]; ++face_index) {
@@ -718,9 +733,9 @@ protected:
     } // faces
   } // void calculate_in_thread(...)
 
-  virtual void integrated_initializer_thread(DynamicRangeType& local_range,
-                                             const std::vector<MergedQuadratureIterator>& decomposition,
-                                             const size_t ii) const override final
+  void integrated_initializer_thread(DynamicRangeType& local_range,
+                                     const std::vector<MergedQuadratureIterator>& decomposition,
+                                     const size_t ii) const override final
   {
     const auto& faces = triangulation_.faces();
     for (auto it = decomposition[ii]; it != decomposition[ii + 1]; ++it) {
diff --git a/dune/gdt/test/momentmodels/basisfunctions/interface.hh b/dune/gdt/test/momentmodels/basisfunctions/interface.hh
index 61ee3c8b1d2b8fad9ce751d2c849f712aac69a5d..587de6be185f332b721d423187cf227297665334 100644
--- a/dune/gdt/test/momentmodels/basisfunctions/interface.hh
+++ b/dune/gdt/test/momentmodels/basisfunctions/interface.hh
@@ -214,7 +214,7 @@ public:
   }
 
   // returns <b>, where b is the basis functions vector
-  virtual DynamicRangeType integrated() const
+  virtual const DynamicRangeType& integrated() const
   {
     return integrated_;
   }
@@ -233,21 +233,30 @@ public:
     return ret;
   }
 
-  virtual DynamicRangeType get_moment_vector(const std::function<RangeFieldType(DomainType, bool)>& psi) const
+  // Get moment vector from distribution function psi. psi also takes an array of bools as argument to decide which
+  // value to use at the discontinuities. Is currently ignored in all testcases except the Heaviside test in one
+  // dimension, so we only have to test if we are on the positive or negative interval touching 0. For more general
+  // testcases, this would have to be adapted.
+  virtual DynamicRangeType get_u(const std::function<RangeFieldType(DomainType, std::array<int, dimDomain>)>& psi) const
   {
     DynamicRangeType ret(dimRange, 0.);
     const auto merged_quads = XT::Data::merged_quadrature(quadratures());
     for (auto it = merged_quads.begin(); it != merged_quads.end(); ++it) {
       const auto& quad_point = *it;
       const auto& v = quad_point.position();
-      ret += evaluate(v, it.first_index()) * psi(v, is_negative(it)) * quad_point.weight();
+      ret += evaluate(v, it.first_index()) * psi(v, has_fixed_sign(it.first_index())) * quad_point.weight();
     }
     return ret;
   }
 
-  virtual bool is_negative(const MergedQuadratureIterator& /*it*/) const
+  // Tests (per coordinate direction) whether the positions in the quadrature with index index are all negative or
+  // positive. Returns -1 if the entries are all non-positive, 1 if the entries are all non-negative, and 0 else.
+  virtual std::array<int, dimDomain> has_fixed_sign(const size_t /*index*/) const
   {
-    return false;
+    std::array<int, dimDomain> ret;
+    for (size_t dd = 0; dd < dimDomain; ++dd)
+      ret[dd] = 0;
+    return ret;
   }
 
   virtual FieldVector<MatrixType, dimFlux> flux_matrix() const
@@ -347,6 +356,11 @@ public:
     }
   }
 
+  virtual bool adjust_alpha_to_ensure_min_density(RangeType& /*alpha*/, const RangeFieldType /*min_density*/) const
+  {
+    return false;
+  }
+
   // Volume of integration domain. For the Mn models it is important that u_iso has density 1. If the basis is exactly
   // integrated, we thus use the exact unit ball volume. If the basis is only integrated by quadrature, we have to use
   // <1> as volume to get a density of 1.
@@ -355,7 +369,7 @@ public:
     return unit_ball_volume_exact();
   }
 
-  virtual DynamicRangeType u_iso() const
+  virtual const DynamicRangeType& u_iso() const
   {
     return u_iso_;
   }
diff --git a/dune/gdt/test/momentmodels/basisfunctions/partial_moments.hh b/dune/gdt/test/momentmodels/basisfunctions/partial_moments.hh
index 2c5b7ff8c530e65ad6532ce8c8dc74dc7ab37e8b..20a69bc3a8a6769ee9a12b2d5d9ba55d1d53ee23 100644
--- a/dune/gdt/test/momentmodels/basisfunctions/partial_moments.hh
+++ b/dune/gdt/test/momentmodels/basisfunctions/partial_moments.hh
@@ -13,6 +13,7 @@
 
 #include <boost/iostreams/stream.hpp>
 #include <boost/iostreams/device/null.hpp>
+#include <boost/math/special_functions/lambert_w.hpp>
 
 #if HAVE_QHULL
 #  include <dune/xt/common/disable_warnings.hh>
@@ -29,6 +30,200 @@ namespace Dune {
 namespace GDT {
 
 
+template <class DomainFieldType,
+          size_t domainDim,
+          class RangeFieldType,
+          size_t rangeDim,
+          size_t dimFlux,
+          EntropyType entropy,
+          size_t sizeBlock>
+class PartialMomentBasisBase
+  : public MomentBasisInterface<DomainFieldType, domainDim, RangeFieldType, rangeDim, 1, dimFlux, entropy>
+{
+  using BaseType = MomentBasisInterface<DomainFieldType, domainDim, RangeFieldType, rangeDim, 1, dimFlux, entropy>;
+  using ThisType = PartialMomentBasisBase;
+
+public:
+  using BaseType::dimDomain;
+  using BaseType::dimRange;
+  static constexpr size_t block_size = sizeBlock;
+  static constexpr size_t num_blocks = dimRange / block_size;
+
+  using typename BaseType::DomainType;
+  using typename BaseType::DynamicRangeType;
+  using typename BaseType::RangeType;
+  using typename BaseType::StringifierType;
+  using LocalVectorType = XT::Common::FieldVector<RangeFieldType, block_size>;
+
+  template <class... Args>
+  PartialMomentBasisBase(Args&&... args)
+    : BaseType(std::forward<Args>(args)...)
+  {}
+
+  // evaluate on spherical triangle face_index
+  DynamicRangeType evaluate(const DomainType& v, const size_t face_index) const override final
+  {
+    DynamicRangeType ret(dimRange, 0);
+    const auto local_eval = evaluate_on_face(v, face_index);
+    for (size_t ii = 0; ii < block_size; ++ii)
+      ret[block_size * face_index + ii] = local_eval[ii];
+    return ret;
+  } // ... evaluate(...)
+
+  // evaluate on spherical triangle face_index
+  LocalVectorType evaluate_on_face(const DomainType& v, const size_t /*face_index*/) const
+  {
+    LocalVectorType ret;
+    ret[0] = 1.;
+    for (size_t ii = 1; ii < block_size; ++ii)
+      ret[ii] = v[ii - 1];
+    return ret;
+  } // ... evaluate(...)
+
+  static StringifierType stringifier()
+  {
+    return [](const DynamicRangeType& val) {
+      RangeFieldType psi(0);
+      for (size_t ii = 0; ii < dimRange; ii += block_size)
+        psi += val[ii];
+      return XT::Common::to_string(psi, 15);
+    };
+  } // ... stringifier()
+
+  DynamicRangeType alpha_one() const override final
+  {
+    DynamicRangeType ret(dimRange, 0.);
+    for (size_t ii = 0; ii < dimRange; ii += block_size)
+      ret[ii] = 1.;
+    return ret;
+  }
+
+  RangeFieldType density(const RangeType& u) const override final
+  {
+    RangeFieldType ret(0.);
+    for (size_t ii = 0; ii < dimRange; ii += block_size)
+      ret += u[ii];
+    return ret;
+  }
+
+  RangeFieldType density(const DynamicRangeType& u) const override final
+  {
+    RangeFieldType ret(0.);
+    for (size_t ii = 0; ii < dimRange; ii += block_size)
+      ret += u[ii];
+    return ret;
+  }
+
+  RangeFieldType density(const XT::Common::BlockedFieldVector<RangeFieldType, num_blocks, block_size>& u) const
+  {
+    RangeFieldType ret(0.);
+    for (size_t jj = 0; jj < num_blocks; ++jj)
+      ret += u.block(jj)[0];
+    return ret;
+  }
+
+  RangeFieldType min_density(const XT::Common::BlockedFieldVector<RangeFieldType, num_blocks, block_size>& u) const
+  {
+    RangeFieldType ret(u.block(0)[0]);
+    for (size_t jj = 1; jj < num_blocks; ++jj)
+      ret = std::min(ret, u.block(jj)[0]);
+    return ret;
+  }
+
+  using BaseType::u_iso;
+
+  // For the partial moments, we might not be able to solve the optimization problem for some moments where the density
+  // on one interval/spherical triangle is very low. The overall density might be much higher than the density on that
+  // triangle, so we specialize this function.
+  void ensure_min_density(DynamicRangeType& u, const RangeFieldType min_density) const override final
+  {
+    const auto u_iso_min = u_iso() * min_density;
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      const auto block_density = u[block_size * jj];
+      if (block_density < u_iso_min[block_size * jj]) {
+        for (size_t ii = 0; ii < block_size; ++ii)
+          u[block_size * jj + ii] = u_iso_min[block_size * jj + ii];
+      }
+    }
+  }
+
+  // For the partial moments, we might not be able to solve the optimization problem for some moments where the density
+  // on one interval/spherical triangle is very low. The overall density might be much higher than the density on that
+  // triangle, so we specialize this function.
+  void ensure_min_density(RangeType& u, const RangeFieldType min_density) const override final
+  {
+    const auto u_iso_min = u_iso() * min_density;
+    for (size_t jj = 0; jj < num_blocks; ++jj) {
+      const auto block_density = u[block_size * jj];
+      if (block_density < u_iso_min[block_size * jj]) {
+        for (size_t ii = 0; ii < block_size; ++ii)
+          u[block_size * jj + ii] = u_iso_min[block_size * jj + ii];
+      }
+    }
+  }
+
+  using BaseType::has_fixed_sign;
+
+  DynamicRangeType
+  get_u(const std::function<RangeFieldType(DomainType, std::array<int, dimDomain>)>& psi) const override final
+  {
+    DynamicRangeType ret(dimRange, 0);
+    for (size_t jj = 0; jj < quadratures_.size(); ++jj) {
+      const size_t offset = jj * block_size;
+      for (const auto& quad_point : quadratures_[jj]) {
+        const auto& v = quad_point.position();
+        const auto basis_val = evaluate_on_face(v, jj);
+        const auto psi_val = psi(v, has_fixed_sign(jj));
+        const auto factor = psi_val * quad_point.weight();
+        for (size_t ii = 0; ii < block_size; ++ii)
+          ret[offset + ii] += basis_val[ii] * factor;
+      }
+    }
+    return ret;
+  }
+
+  std::string short_id() const override final
+  {
+    return "pm";
+  }
+
+  std::string mn_name() const override final
+  {
+    return "pmm" + XT::Common::to_string(dimRange);
+  }
+
+  std::string pn_name() const override final
+  {
+    return "pmp" + XT::Common::to_string(dimRange);
+  }
+
+protected:
+  using BaseType::quadratures_;
+}; // class PartialMomentBasisBase<...>
+
+template <class DomainFieldType,
+          size_t domainDim,
+          class RangeFieldType,
+          size_t rangeDim,
+          size_t dimFlux,
+          EntropyType entropy,
+          size_t sizeBlock>
+constexpr size_t
+    PartialMomentBasisBase<DomainFieldType, domainDim, RangeFieldType, rangeDim, dimFlux, entropy, sizeBlock>::
+        num_blocks;
+
+template <class DomainFieldType,
+          size_t domainDim,
+          class RangeFieldType,
+          size_t rangeDim,
+          size_t dimFlux,
+          EntropyType entropy,
+          size_t sizeBlock>
+constexpr size_t
+    PartialMomentBasisBase<DomainFieldType, domainDim, RangeFieldType, rangeDim, dimFlux, entropy, sizeBlock>::
+        block_size;
+
+
 template <class DomainFieldType,
           size_t dimDomain,
           class RangeFieldType,
@@ -49,20 +244,16 @@ template <class DomainFieldType,
           size_t dimFlux,
           EntropyType entropy>
 class PartialMomentBasis<DomainFieldType, 1, RangeFieldType, rangeDim, rangeDimCols, dimFlux, 1, entropy>
-  : public MomentBasisInterface<DomainFieldType, 1, RangeFieldType, rangeDim, rangeDimCols, dimFlux, entropy>
+  : public PartialMomentBasisBase<DomainFieldType, 1, RangeFieldType, rangeDim, dimFlux, entropy, 2>
 {
+  using BaseType = PartialMomentBasisBase<DomainFieldType, 1, RangeFieldType, rangeDim, dimFlux, entropy, 2>;
+
 public:
-  static constexpr size_t dimDomain = 1;
-  static constexpr size_t dimRange = rangeDim;
-  static constexpr size_t dimRangeCols = rangeDimCols;
+  using BaseType::dimDomain;
+  using BaseType::dimRange;
   static_assert(!(dimRange % 2), "dimRange has to be even!");
-  static constexpr size_t num_intervals = dimRange / 2;
-
-private:
-  using BaseType =
-      MomentBasisInterface<DomainFieldType, dimDomain, RangeFieldType, dimRange, dimRangeCols, dimFlux, entropy>;
+  static constexpr size_t num_intervals = BaseType::num_blocks;
 
-public:
   using typename BaseType::DomainType;
   using typename BaseType::DynamicRangeType;
   using typename BaseType::MatrixType;
@@ -70,15 +261,9 @@ public:
   using typename BaseType::RangeType;
   using typename BaseType::SphericalTriangulationType;
   using typename BaseType::StringifierType;
-  using typename BaseType::VisualizerType;
   using LocalVectorType = FieldVector<RangeFieldType, 2>;
   using PartitioningType = typename BaseType::Partitioning1dType;
 
-  static std::string static_id()
-  {
-    return "pcw";
-  }
-
   static size_t default_quad_order()
   {
     return 15;
@@ -105,6 +290,8 @@ public:
     BaseType::initialize_base_values();
   }
 
+  using BaseType::evaluate;
+
   DynamicRangeType evaluate(const DomainType& v) const override final
   {
     for (size_t ii = 0; ii < num_intervals; ++ii)
@@ -114,41 +301,54 @@ public:
     return DynamicRangeType();
   } // ... evaluate(...)
 
-  // evaluate on interval ii
-  DynamicRangeType evaluate(const DomainType& v, const size_t ii) const override final
-  {
-    DynamicRangeType ret(dimRange, 0);
-    const auto local_ret = evaluate_on_interval(v, ii);
-    ret[2 * ii] = local_ret[0];
-    ret[2 * ii + 1] = local_ret[1];
-    return ret;
-  } // ... evaluate(...)
-
-  LocalVectorType evaluate_on_interval(const DomainType& v, const size_t /*ii*/) const
+  bool adjust_alpha_to_ensure_min_density(RangeType& alpha, const RangeFieldType psi_min) const override final
   {
-    return LocalVectorType{{1, v[0]}};
-  } // ... evaluate(...)
-
-  LocalVectorType evaluate_on_face(const DomainType& v, const size_t ii) const
-  {
-    return evaluate_on_interval(v, ii);
-  } // ... evaluate(...)
-
-  DynamicRangeType integrated() const override final
-  {
-    DynamicRangeType ret(dimRange, 0);
+    bool changed = false;
+    const auto alpha_min = std::log(psi_min);
     for (size_t ii = 0; ii < num_intervals; ++ii) {
-      ret[2 * ii] = partitioning_[ii + 1] - partitioning_[ii];
-      ret[2 * ii + 1] = (std::pow(partitioning_[ii + 1], 2) - std::pow(partitioning_[ii], 2)) / 2.;
-    }
-    return ret;
-  }
-
-  virtual bool
-  is_negative(const typename XT::Data::MergedQuadrature<RangeFieldType, dimDomain>::MergedQuadratureIterator& it)
-      const override final
-  {
-    return it.first_index() < num_intervals / 2;
+      auto& alpha_0 = alpha[2 * ii];
+      auto& alpha_1 = alpha[2 * ii + 1];
+      const auto mu_i = partitioning_[ii];
+      const auto mu_ip1 = partitioning_[ii + 1];
+      const auto alpha_left = alpha_0 + mu_i * alpha_1;
+      const auto alpha_right = alpha_0 + mu_ip1 * alpha_1;
+      const bool min_is_left = alpha_left < alpha_right;
+      const auto alpha_min_ii = min_is_left ? alpha_left : alpha_right;
+      const auto alpha_max_ii = min_is_left ? alpha_right : alpha_left;
+      if (alpha_min_ii < alpha_min) {
+        if (XT::Common::FloatCmp::le(alpha_max_ii, alpha_min)) {
+          alpha_0 = alpha_min;
+          alpha_1 = 0.;
+          changed = true;
+        } else {
+          // We know that alpha_1 != 0 because alpha_max_ii > alpha_min and alpha_min_ii < alpha_min
+          const auto rho_ii = -(std::exp(alpha_0 + mu_i * alpha_1) - std::exp(alpha_0 + mu_ip1 * alpha_1)) / alpha_1;
+          if (std::isnan(rho_ii) || std::isinf(rho_ii))
+            DUNE_THROW(Dune::MathError, "Inf or nan in rho!");
+          const auto h = (mu_ip1 - mu_i);
+          if (XT::Common::FloatCmp::le(rho_ii, psi_min * h)) {
+            alpha_0 = alpha_min;
+            alpha_1 = 0.;
+            changed = true;
+            continue;
+          }
+          // get positive slope
+#if 0
+        // Set minimum to alpha_min, leave max alpha unchanged
+        alpha_1 = (alpha_max_ii - alpha_min_ii) / h;
+#else
+          // Set minimum to alpha_min, leave rho unchanged
+          alpha_1 = -1. / h * boost::math::lambert_wm1(-h * psi_min / rho_ii * std::exp(-h * psi_min / rho_ii))
+                    - psi_min / rho_ii;
+#endif
+          if (!min_is_left)
+            alpha_1 *= -1.; // slope has to be negative in this case
+          alpha_0 = alpha_min - (min_is_left ? mu_i : mu_ip1) * alpha_1;
+          changed = true;
+        }
+      }
+    } // ii
+    return changed;
   }
 
   // returns matrix with entries <h_i h_j>
@@ -250,105 +450,11 @@ public:
     return ret;
   }
 
-  static StringifierType stringifier()
-  {
-    return [](const RangeType& val) {
-      RangeFieldType psi(0);
-      for (size_t ii = 0; ii < dimRange; ii += 2)
-        psi += val[ii];
-      return XT::Common::to_string(psi, 15);
-    };
-  } // ... stringifier()
-
   const PartitioningType& partitioning() const
   {
     return partitioning_;
   }
 
-  DynamicRangeType alpha_one() const override final
-  {
-    DynamicRangeType ret(dimRange, 0);
-    for (size_t ii = 0; ii < dimRange; ii += 2)
-      ret[ii] = 1.;
-    return ret;
-  }
-
-  RangeFieldType density(const DynamicRangeType& u) const override final
-  {
-    RangeFieldType ret(0.);
-    for (size_t ii = 0; ii < dimRange; ii += 2) {
-      ret += u[ii];
-    }
-    return ret;
-  }
-
-  RangeFieldType density(const RangeType& u) const
-  {
-    RangeFieldType ret(0.);
-    for (size_t ii = 0; ii < dimRange; ii += 2) {
-      ret += u[ii];
-    }
-    return ret;
-  }
-
-  RangeFieldType density(const XT::Common::BlockedFieldVector<RangeFieldType, num_intervals, 2>& u) const
-  {
-    RangeFieldType ret(0.);
-    for (size_t jj = 0; jj < num_intervals; ++jj)
-      ret += u.block(jj)[0];
-    return ret;
-  }
-
-  RangeFieldType min_density(const XT::Common::BlockedFieldVector<RangeFieldType, num_intervals, 2>& u) const
-  {
-    RangeFieldType ret(u.block(0)[0]);
-    for (size_t jj = 1; jj < num_intervals; ++jj)
-      ret = std::min(ret, u.block(jj)[0]);
-    return ret;
-  }
-
-  using BaseType::u_iso;
-
-  // For the partial moments, we might not be able to solve the optimization problem for some moments where the density
-  // on one interval/spherical triangle is very low. The overall density might be much higher than the density on that
-  // triangle, so we specialize this function.
-  void ensure_min_density(DynamicRangeType& u, const RangeFieldType min_density) const override final
-  {
-    const auto u_iso_min = u_iso() * min_density;
-    for (size_t jj = 0; jj < num_intervals; ++jj) {
-      if (u[2 * jj] < u_iso_min[2 * jj]) {
-        u[2 * jj] = u_iso_min[2 * jj];
-        u[2 * jj + 1] = u_iso_min[2 * jj + 1];
-      }
-    }
-  }
-
-  void ensure_min_density(RangeType& u, const RangeFieldType min_density) const override final
-  {
-    const auto u_iso_min = u_iso() * min_density;
-    for (size_t jj = 0; jj < num_intervals; ++jj) {
-      if (u[2 * jj] < u_iso_min[2 * jj]) {
-        u[2 * jj] = u_iso_min[2 * jj];
-        u[2 * jj + 1] = u_iso_min[2 * jj + 1];
-      }
-    }
-  }
-
-  std::string short_id() const override final
-  {
-    return "pm";
-  }
-
-  std::string mn_name() const override final
-  {
-    return "pmm" + XT::Common::to_string(dimRange);
-  }
-
-  std::string pn_name() const override final
-  {
-    return "pmp" + XT::Common::to_string(dimRange);
-  }
-
   // get indices of all faces that contain point
   std::vector<size_t> get_face_indices(const DomainType& v) const
   {
@@ -360,6 +466,21 @@ public:
     return face_indices;
   }
 
+  DynamicRangeType integrated_initializer(const QuadraturesType& /*quadratures*/) const override final
+  {
+    DynamicRangeType ret(dimRange, 0);
+    for (size_t ii = 0; ii < num_intervals; ++ii) {
+      ret[2 * ii] = partitioning_[ii + 1] - partitioning_[ii];
+      ret[2 * ii + 1] = (std::pow(partitioning_[ii + 1], 2) - std::pow(partitioning_[ii], 2)) / 2.;
+    }
+    return ret;
+  }
+
+  std::array<int, dimDomain> has_fixed_sign(const size_t index) const override final
+  {
+    return BaseType::interval_has_fixed_sign(index, num_intervals);
+  }
+
 private:
   const PartitioningType partitioning_;
   using BaseType::quadratures_;
@@ -372,32 +493,34 @@ template <class DomainFieldType,
           size_t dimFlux,
           EntropyType entropy>
 constexpr size_t
-    PartialMomentBasis<DomainFieldType, 1, RangeFieldType, rangeDim, rangeDimCols, dimFlux, 1, entropy>::dimRange;
+    PartialMomentBasis<DomainFieldType, 1, RangeFieldType, rangeDim, rangeDimCols, dimFlux, 1, entropy>::num_intervals;
 
 template <class DomainFieldType, class RangeFieldType, size_t refinements, size_t dimFlux, EntropyType entropy>
 class PartialMomentBasis<DomainFieldType, 3, RangeFieldType, refinements, 1, dimFlux, 1, entropy>
-  : public MomentBasisInterface<DomainFieldType,
-                                3,
-                                RangeFieldType,
-                                OctaederStatistics<refinements>::num_faces() * 4,
-                                1,
-                                dimFlux,
-                                entropy>
+  : public PartialMomentBasisBase<DomainFieldType,
+                                  3,
+                                  RangeFieldType,
+                                  OctaederStatistics<refinements>::num_faces() * 4,
+                                  dimFlux,
+                                  entropy,
+                                  4>
 {
-public:
-  static const size_t dimDomain = 3;
-  static const size_t dimRange = OctaederStatistics<refinements>::num_faces() * 4;
-  static const size_t dimRangeCols = 1;
-  static constexpr size_t block_size = 4;
-  static constexpr size_t num_blocks = dimRange / block_size;
-  static constexpr size_t num_refinements = refinements;
-
-private:
-  using BaseType =
-      MomentBasisInterface<DomainFieldType, dimDomain, RangeFieldType, dimRange, dimRangeCols, dimFlux, entropy>;
+  using BaseType = PartialMomentBasisBase<DomainFieldType,
+                                          3,
+                                          RangeFieldType,
+                                          OctaederStatistics<refinements>::num_faces() * 4,
+                                          dimFlux,
+                                          entropy,
+                                          4>;
   using ThisType = PartialMomentBasis;
 
 public:
+  using BaseType::block_size;
+  using BaseType::dimDomain;
+  using BaseType::dimRange;
+  using BaseType::num_blocks;
+  static constexpr size_t num_refinements = refinements;
+
   using TriangulationType = typename BaseType::SphericalTriangulationType;
   using typename BaseType::DomainType;
   using typename BaseType::DynamicRangeType;
@@ -406,7 +529,6 @@ public:
   using typename BaseType::QuadratureType;
   using typename BaseType::RangeType;
   using typename BaseType::StringifierType;
-  using typename BaseType::VisualizerType;
   using BlockRangeType = XT::Common::FieldVector<RangeFieldType, block_size>;
   using BlockPlaneCoefficientsType = typename std::vector<std::pair<BlockRangeType, RangeFieldType>>;
   using PlaneCoefficientsType = XT::Common::FieldVector<BlockPlaneCoefficientsType, num_blocks>;
@@ -456,6 +578,8 @@ public:
     BaseType::initialize_base_values();
   }
 
+  using BaseType::evaluate;
+
   DynamicRangeType evaluate(const DomainType& v) const override final
   {
     DynamicRangeType ret(dimRange, 0);
@@ -467,129 +591,11 @@ public:
     return evaluate(v, face_indices[0]);
   } // ... evaluate(...)
 
-  // evaluate on spherical triangle face_index
-  DynamicRangeType evaluate(const DomainType& v, const size_t face_index) const override final
-  {
-    DynamicRangeType ret(dimRange, 0);
-    const auto local_eval = evaluate_on_face(v, face_index);
-    assert(4 * face_index + 3 < ret.size());
-    for (size_t ii = 0; ii < 4; ++ii)
-      ret[4 * face_index + ii] = local_eval[ii];
-    return ret;
-  } // ... evaluate(...)
-
-  // evaluate on spherical triangle face_index
-  LocalVectorType evaluate_on_face(const DomainType& v, const size_t /*face_index*/) const
-  {
-    LocalVectorType ret;
-    ret[0] = 1.;
-    for (size_t ii = 1; ii < 4; ++ii)
-      ret[ii] = v[ii - 1];
-    return ret;
-  } // ... evaluate(...)
-
-  static StringifierType stringifier()
-  {
-    return [](const DynamicRangeType& val) {
-      RangeFieldType psi(0);
-      for (size_t ii = 0; ii < dimRange; ii += 4)
-        psi += val[ii];
-      return XT::Common::to_string(psi, 15);
-    };
-  } // ... stringifier()
-
   RangeFieldType unit_ball_volume() const override final
   {
     return BaseType::unit_ball_volume_quad();
   }
 
-  DynamicRangeType alpha_one() const override final
-  {
-    DynamicRangeType ret(dimRange, 0.);
-    for (size_t ii = 0; ii < dimRange; ii += 4)
-      ret[ii] = 1.;
-    return ret;
-  }
-
-  virtual RangeFieldType density(const RangeType& u) const
-  {
-    RangeFieldType ret(0.);
-    for (size_t ii = 0; ii < dimRange; ii += 4)
-      ret += u[ii];
-    return ret;
-  }
-
-  RangeFieldType density(const DynamicRangeType& u) const override final
-  {
-    RangeFieldType ret(0.);
-    for (size_t ii = 0; ii < dimRange; ii += 4)
-      ret += u[ii];
-    return ret;
-  }
-
-  RangeFieldType density(const XT::Common::BlockedFieldVector<RangeFieldType, dimRange / 4, 4>& u) const
-  {
-    RangeFieldType ret(0.);
-    for (size_t jj = 0; jj < dimRange / 4; ++jj)
-      ret += u.block(jj)[0];
-    return ret;
-  }
-
-  RangeFieldType min_density(const XT::Common::BlockedFieldVector<RangeFieldType, dimRange / 4, 4>& u) const
-  {
-    RangeFieldType ret(u.block(0)[0]);
-    for (size_t jj = 1; jj < dimRange / 4; ++jj)
-      ret = std::min(ret, u.block(jj)[0]);
-    return ret;
-  }
-
-  using BaseType::u_iso;
-
-  // For the partial moments, we might not be able to solve the optimization problem for some moments where the density
-  // on one interval/spherical triangle is very low. The overall density might be much higher than the density on that
-  // triangle, so we specialize this function.
-  void ensure_min_density(DynamicRangeType& u, const RangeFieldType min_density) const override final
-  {
-    const auto u_iso_min = u_iso() * min_density;
-    for (size_t jj = 0; jj < num_blocks; ++jj) {
-      const auto block_density = u[4 * jj];
-      if (block_density < u_iso_min[4 * jj]) {
-        for (size_t ii = 0; ii < block_size; ++ii)
-          u[4 * jj + ii] = u_iso_min[4 * jj + ii];
-      }
-    }
-  }
-
-  // For the partial moments, we might not be able to solve the optimization problem for some moments where the density
-  // on one interval/spherical triangle is very low. The overall density might be much higher than the density on that
-  // triangle, so we specialize this function.
-  void ensure_min_density(RangeType& u, const RangeFieldType min_density) const override final
-  {
-    const auto u_iso_min = u_iso() * min_density;
-    for (size_t jj = 0; jj < num_blocks; ++jj) {
-      const auto block_density = u[4 * jj];
-      if (block_density < u_iso_min[4 * jj]) {
-        for (size_t ii = 0; ii < block_size; ++ii)
-          u[4 * jj + ii] = u_iso_min[4 * jj + ii];
-      }
-    }
-  }
-
-  std::string short_id() const override final
-  {
-    return "pm";
-  }
-
-  std::string mn_name() const override final
-  {
-    return "pmm" + XT::Common::to_string(dimRange);
-  }
-
-  std::string pn_name() const override final
-  {
-    return "pmp" + XT::Common::to_string(dimRange);
-  }
-
   std::vector<size_t> get_face_indices(const DomainType& v) const
   {
     return triangulation_.get_face_indices(v);
@@ -637,24 +643,6 @@ public:
       threads[jj].join();
   }
 
-  virtual DynamicRangeType
-  get_moment_vector(const std::function<RangeFieldType(DomainType, bool)>& psi) const override final
-  {
-    DynamicRangeType ret(dimRange, 0);
-    for (size_t jj = 0; jj < quadratures_.size(); ++jj) {
-      const size_t offset = jj * block_size;
-      for (const auto& quad_point : quadratures_[jj]) {
-        const auto& v = quad_point.position();
-        const auto basis_val = evaluate_on_face(v, jj);
-        const auto psi_val = psi(v, false);
-        const auto factor = psi_val * quad_point.weight();
-        for (size_t ii = 0; ii < block_size; ++ii)
-          ret[offset + ii] += basis_val[ii] * factor;
-      }
-    }
-    return ret;
-  }
-
   std::unique_ptr<BlockMatrixType> block_mass_matrix() const
   {
     auto block_matrix = std::make_unique<BlockMatrixType>();
@@ -697,6 +685,10 @@ public:
     return B_kinetic;
   } // ... kinetic_flux_matrices()
 
+  std::array<int, dimDomain> has_fixed_sign(const size_t index) const override final
+  {
+    return BaseType::triangle_has_fixed_sign(index);
+  }
 
 private:
   void calculate_plane_coefficients_block(std::vector<XT::Common::FieldVector<RangeFieldType, block_size>>& points,
@@ -821,13 +813,6 @@ private:
   mutable PlaneCoefficientsType plane_coefficients_;
 }; // class PartialMomentBasis<DomainFieldType, 3, ...>
 
-template <class DomainFieldType, class RangeFieldType, size_t refinements, size_t dimFlux, EntropyType entropy>
-constexpr size_t PartialMomentBasis<DomainFieldType, 3, RangeFieldType, refinements, 1, dimFlux, 1, entropy>::dimRange;
-
-template <class DomainFieldType, class RangeFieldType, size_t refinements, size_t dimFlux, EntropyType entropy>
-constexpr size_t
-    PartialMomentBasis<DomainFieldType, 3, RangeFieldType, refinements, 1, dimFlux, 1, entropy>::num_blocks;
-
 template <class DomainFieldType, class RangeFieldType, size_t refinements, size_t dimFlux, EntropyType entropy>
 constexpr size_t
     PartialMomentBasis<DomainFieldType, 3, RangeFieldType, refinements, 1, dimFlux, 1, entropy>::num_refinements;
diff --git a/dune/gdt/test/momentmodels/basisfunctions/spherical_harmonics.hh b/dune/gdt/test/momentmodels/basisfunctions/spherical_harmonics.hh
index bdd0afa436fecd26403386ce6962e7bfb8b0a458..4a4f5e9cf5d0e4741a94864a8ac36c1e1bb5a19d 100644
--- a/dune/gdt/test/momentmodels/basisfunctions/spherical_harmonics.hh
+++ b/dune/gdt/test/momentmodels/basisfunctions/spherical_harmonics.hh
@@ -105,13 +105,6 @@ public:
     return ret;
   } // ... evaluate(...)
 
-  DynamicRangeType integrated() const override final
-  {
-    DynamicRangeType ret(dimRange, 0.);
-    ret[0] = std::sqrt(4. * M_PI);
-    return ret;
-  }
-
   MatrixType mass_matrix() const override
   {
     MatrixType M(dimRange, dimRange, 0);
@@ -167,6 +160,14 @@ public:
     return "p" + XT::Common::to_string(order);
   }
 
+
+  DynamicRangeType integrated_initializer() const override final
+  {
+    DynamicRangeType ret(dimRange, 0.);
+    ret[0] = std::sqrt(4. * M_PI);
+    return ret;
+  }
+
 private:
   static RangeFieldType A_lm(const int l, const int m)
   {
diff --git a/dune/gdt/test/momentmodels/kinetictransport/base.hh b/dune/gdt/test/momentmodels/kinetictransport/base.hh
index c9705f94278d971d8877fdb332d2d2d35afe3073..5fa1e676b1ffb1e12946eb3b4de44fdc0a9aac8e 100644
--- a/dune/gdt/test/momentmodels/kinetictransport/base.hh
+++ b/dune/gdt/test/momentmodels/kinetictransport/base.hh
@@ -41,6 +41,7 @@ public:
   using typename BaseType::BasisDomainType;
   using typename BaseType::DomainFieldType;
   using typename BaseType::DomainType;
+  using typename BaseType::DynamicRangeType;
   using typename BaseType::GenericFluxFunctionType;
   using typename BaseType::GenericFunctionType;
   using typename BaseType::MatrixType;
@@ -162,19 +163,19 @@ public:
   // Thus, the initial value of the n-th moment is basis_integrated * psi_vac.
   std::unique_ptr<InitialValueType> initial_values() const override
   {
-    RangeReturnType value = basis_functions_.integrated() * psi_vac_;
+    const auto value = basis_functions_.integrated() * psi_vac_;
     return std::make_unique<GenericFunctionType>(
         [](const XT::Common::Parameter&) { return 0; },
-        [value](const DomainType&, const XT::Common::Parameter&) { return value; });
+        [value](const DomainType&, DynamicRangeType& ret, const XT::Common::Parameter&) { ret = value; });
   } // ... initial_values()
 
   // Use a constant vacuum concentration basis_integrated * psi_vac as default boundary value
   std::unique_ptr<BoundaryValueType> boundary_values() const override
   {
-    RangeReturnType value = basis_functions_.integrated() * psi_vac_;
+    const auto value = basis_functions_.integrated() * psi_vac_;
     return std::make_unique<GenericFunctionType>(
         [](const XT::Common::Parameter&) { return 0; },
-        [=](const DomainType&, const XT::Common::Parameter&) { return value; });
+        [=](const DomainType&, DynamicRangeType& ret, const XT::Common::Parameter&) { ret = value; });
   } // ... boundary_values()
 
   virtual BoundaryDistributionType boundary_distribution() const
diff --git a/dune/gdt/test/momentmodels/kinetictransport/planesource.hh b/dune/gdt/test/momentmodels/kinetictransport/planesource.hh
index 01906d8e43f53de2a98da736734bd35cc72f4377..715cbfbfbe977b0d13ea66f3091c43a6979bdcbf 100644
--- a/dune/gdt/test/momentmodels/kinetictransport/planesource.hh
+++ b/dune/gdt/test/momentmodels/kinetictransport/planesource.hh
@@ -28,6 +28,7 @@ public:
   using BaseType::dimRange;
   using typename BaseType::ConstantScalarFunctionType;
   using typename BaseType::DomainType;
+  using typename BaseType::DynamicRangeType;
   using typename BaseType::GenericFunctionType;
   using typename BaseType::InitialValueType;
   using typename BaseType::MomentBasis;
@@ -66,18 +67,17 @@ public:
     const size_t num_elements = XT::Common::from_string<std::vector<size_t>>(grid_cfg_["num_elements"])[0];
     const RangeFieldType len_domain = upper_right[0] - lower_left[0];
     const RangeFieldType vol_entity = len_domain / num_elements;
-    RangeReturnType basis_integrated = basis_functions_.integrated();
+    const auto basis_integrated = basis_functions_.integrated();
     const RangeFieldType domain_center = lower_left[0] + len_domain / 2;
 
     // approximate delta function by constant value of 1/(2*vol_entity) on cells on both side of 0.
-    const auto eval_func = [=](const DomainType& x, const XT::Common::Parameter&) {
-      auto ret = basis_integrated;
+    const auto eval_func = [=](const DomainType& x, DynamicRangeType& ret, const XT::Common::Parameter&) {
+      ret = basis_integrated;
       if (XT::Common::FloatCmp::ge(x[0], domain_center - vol_entity)
           && XT::Common::FloatCmp::le(x[0], domain_center + vol_entity))
         ret *= psi_vac_ + 1. / (2. * vol_entity);
       else
         ret *= psi_vac_;
-      return ret;
     };
     return std::make_unique<GenericFunctionType>(0, eval_func);
   } // ... initial_values()
diff --git a/dune/gdt/test/momentmodels/kinetictransport/sourcebeam.hh b/dune/gdt/test/momentmodels/kinetictransport/sourcebeam.hh
index 92e6da5972060d74592b08eba6187e622bdd3e23..a264c51b0125562666bf156c033a244cb0413500 100644
--- a/dune/gdt/test/momentmodels/kinetictransport/sourcebeam.hh
+++ b/dune/gdt/test/momentmodels/kinetictransport/sourcebeam.hh
@@ -82,16 +82,15 @@ public:
   // at x = 3.
   std::unique_ptr<BoundaryValueType> boundary_values() const override final
   {
-    return std::make_unique<GenericFunctionType>(1, [&](const DomainType& x, const XT::Common::Parameter&) {
-      if (x[0] < 1.5) {
-        static auto ret = helper<MomentBasis>::get_left_boundary_values(basis_functions_, psi_vac_, is_mn_model_);
-        return ret;
-      } else {
-        auto ret = basis_functions_.integrated();
-        ret *= psi_vac_;
-        return ret;
-      }
-    });
+    return std::make_unique<GenericFunctionType>(
+        1, [&](const DomainType& x, DynamicRangeType& ret, const XT::Common::Parameter&) {
+          if (x[0] < 1.5) {
+            helper<MomentBasis>::get_left_boundary_values(basis_functions_, psi_vac_, is_mn_model_, ret);
+          } else {
+            ret = basis_functions_.integrated();
+            ret *= psi_vac_;
+          }
+        });
   } // ... boundary_values()
 
   BoundaryDistributionType boundary_distribution() const override final
@@ -115,7 +114,9 @@ public:
 
   RangeReturnType left_boundary_value() const
   {
-    return helper<MomentBasis>::get_left_boundary_values(basis_functions_, psi_vac_, is_mn_model_);
+    DynamicRangeType ret;
+    helper<MomentBasis>::get_left_boundary_values(basis_functions_, psi_vac_, is_mn_model_, ret);
+    return ret;
   }
 
   RangeFieldType t_end() const override
@@ -198,15 +199,16 @@ protected:
     using helper_base::denominator;
     using helper_base::g;
 
-    static DynamicRangeType get_left_boundary_values(const MomentBasisImp& basis_functions,
-                                                     const RangeFieldType& psi_vac,
-                                                     const bool is_mn_model)
+    static void get_left_boundary_values(const MomentBasisImp& basis_functions,
+                                         const RangeFieldType& psi_vac,
+                                         const bool is_mn_model,
+                                         DynamicRangeType& ret)
     {
-      DynamicRangeType ret(dimRange, 0);
       // For the MN-Models, we have to use the quadrature also used in the optimization problem to guarantee
       // realizability of the boundary_values.
       // For the PN-Models, we do not have these issues and just use a very fine quadrature (which is not a performance
       // problem as the integration is only done once).
+      std::fill(ret.begin(), ret.end(), 0.);
       const auto& quadratures =
           is_mn_model ? basis_functions.quadratures() : MomentBasisImp::gauss_lobatto_quadratures(100, 31);
       for (size_t ii = 0; ii < quadratures.size(); ++ii) {
@@ -221,7 +223,6 @@ protected:
       ret /= denominator();
       // add small vacuum concentration to move away from realizable boundary
       ret += basis_functions.integrated() * psi_vac;
-      return ret;
     }
 
     static DynamicRangeType get_kinetic_boundary_flux(const MomentBasisImp& /*basis_functions*/,
@@ -246,11 +247,12 @@ protected:
     using helper_base::integral_2;
     using helper_base::integral_3;
 
-    static DynamicRangeType get_left_boundary_values(const MomentBasisImp& basis_functions,
-                                                     const RangeFieldType psi_vac,
-                                                     const bool /*is_mn_model*/)
+    static void get_left_boundary_values(const MomentBasisImp& basis_functions,
+                                         const RangeFieldType psi_vac,
+                                         const bool /*is_mn_model*/,
+                                         DynamicRangeType& ret)
     {
-      DynamicRangeType ret(dimRange, 0);
+      std::fill(ret.begin(), ret.end(), 0.);
       for (size_t nn = 0; nn < dimRange; ++nn) {
         const auto& partitioning = basis_functions.partitioning();
         const auto vn = partitioning[nn];
@@ -265,7 +267,6 @@ protected:
       }
       // 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,
@@ -316,19 +317,19 @@ protected:
     using helper_base::integral_2;
     using helper_base::integral_3;
 
-    static DynamicRangeType get_left_boundary_values(const MomentBasisImp& basis_functions,
-                                                     const RangeFieldType psi_vac,
-                                                     const bool /*is_mn_model*/)
+    static void get_left_boundary_values(const MomentBasisImp& basis_functions,
+                                         const RangeFieldType psi_vac,
+                                         const bool /*is_mn_model*/,
+                                         DynamicRangeType& ret)
     {
       const auto& partitioning = basis_functions.partitioning();
-      DynamicRangeType ret(dimRange, 0);
+      std::fill(ret.begin(), ret.end(), 0.);
       for (size_t ii = 0; ii < dimRange / 2; ++ii) {
         ret[2 * ii] = integral_1(partitioning[ii], partitioning[ii + 1]) / denominator();
         ret[2 * ii + 1] = integral_2(partitioning[ii], partitioning[ii + 1]) / denominator();
       }
       // add small vacuum concentration to move away from realizable boundary
       ret += basis_functions.integrated() * psi_vac;
-      return ret;
     }
 
     static DynamicRangeType get_kinetic_boundary_flux(const MomentBasisImp& basis_functions,