From 7465e2c22e7eb369948423ecd2bd9eebbe20ec62 Mon Sep 17 00:00:00 2001
From: "Praetorius, Simon" <simon.praetorius@tu-dresden.de>
Date: Mon, 4 Feb 2019 13:33:19 +0100
Subject: [PATCH] Local basis cache depending on node type

---
 src/amdis/LocalBasisCache.hpp                 | 234 +++++++++++-------
 src/amdis/LocalBasisEvaluatorCache.hpp        |  96 -------
 .../assembler/ConvectionDiffusionOperator.hpp |  15 +-
 src/amdis/assembler/FirstOrderDivTestvec.hpp  |   8 +-
 src/amdis/assembler/FirstOrderGradTest.hpp    |   8 +-
 src/amdis/assembler/FirstOrderPartialTest.hpp |   8 +-
 .../assembler/FirstOrderTestDivTrialvec.hpp   |  13 +-
 .../assembler/FirstOrderTestGradTrial.hpp     |  13 +-
 .../assembler/FirstOrderTestPartialTrial.hpp  |  13 +-
 .../assembler/FirstOrderTestvecGradTrial.hpp  |  13 +-
 .../SecondOrderDivTestvecDivTrialvec.hpp      |  23 +-
 .../SecondOrderGradTestGradTrial.hpp          |  21 +-
 .../SecondOrderPartialTestPartialTrial.hpp    |  15 +-
 src/amdis/assembler/StokesOperator.hpp        |  14 +-
 src/amdis/assembler/ZeroOrderTest.hpp         |   5 +-
 src/amdis/assembler/ZeroOrderTestTrial.hpp    |  15 +-
 src/amdis/assembler/ZeroOrderTestTrialvec.hpp |  10 +-
 src/amdis/assembler/ZeroOrderTestvec.hpp      |   5 +-
 .../assembler/ZeroOrderTestvecTrialvec.hpp    |  25 +-
 .../gridfunctions/DiscreteFunction.inc.hpp    |  12 +-
 20 files changed, 243 insertions(+), 323 deletions(-)
 delete mode 100644 src/amdis/LocalBasisEvaluatorCache.hpp

diff --git a/src/amdis/LocalBasisCache.hpp b/src/amdis/LocalBasisCache.hpp
index cab8d27c..cf4b1c45 100644
--- a/src/amdis/LocalBasisCache.hpp
+++ b/src/amdis/LocalBasisCache.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <tuple>
 #include <vector>
 
 #include <dune/common/hash.hh>
@@ -7,99 +8,188 @@
 #include <dune/geometry/type.hh>
 
 #include <amdis/utility/ConcurrentCache.hpp>
+#include <amdis/utility/FiniteElementType.hpp>
 
 namespace AMDiS
 {
-  template <class LocalBasisType>
-  class LocalBasisCache
+  /// \brief Cache of LocalBasis evaluations and jacobians at local coordinates
+  /**
+   * Caching is done using the ConcurrentCache data structure with a key that
+   * depends on the element type and local coordinates inside the element.
+   * Two methods are provided for evaluation of local basis functions and local
+   * basis jacobians at the local coordinate.
+   *
+   * \tparam Node  A leaf basis node in a basis tree
+   **/
+  template <class Node>
+  class NodeCache
   {
+    static_assert(Node::isLeaf, "");
+    using LocalBasisType = typename Node::FiniteElement::Traits::LocalBasisType;
+    using DomainType = typename LocalBasisType::Traits::DomainType;
+
   public:
     using Traits = typename LocalBasisType::Traits;
 
-    using DomainType = typename Traits::DomainType;
-    using RangeType = typename Traits::RangeType;
-    using RangeFieldType = typename Traits::RangeFieldType;
-    using JacobianType = typename Traits::JacobianType;
-
-    using QuadratureRule = Dune::QuadratureRule<RangeFieldType,Traits::dimDomain>;
-    using QuadratureRules = Dune::QuadratureRules<RangeFieldType,Traits::dimDomain>;
+    using ShapeValues = std::vector<typename Traits::RangeType>;
+    using ShapeGradients = std::vector<typename Traits::JacobianType>;
 
   private:
-
-    /// Pair of GeometryType and quadrature order and size
-    template <class Tag>
-    struct QuadKey
+    // Pair of GeometryType and local coordinates
+    struct Key
     {
       unsigned int id;    // topologyId
-      int p;              // quadrature order
-      std::size_t size;   // number of quadrature points
+      DomainType local;   // local coordinate
 
       struct hasher
       {
-        std::size_t operator()(QuadKey const& t) const
+        std::size_t operator()(Key const& t) const
         {
           std::size_t seed = 0;
           Dune::hash_combine(seed, t.id);
-          Dune::hash_combine(seed, t.p);
-          Dune::hash_combine(seed, t.size);
+          Dune::hash_range(seed, t.local.begin(), t.local.end());
           return seed;
         }
       };
 
-      friend bool operator==(QuadKey const& lhs, QuadKey const& rhs)
+      friend bool operator==(Key const& lhs, Key const& rhs)
       {
-        return std::tie(lhs.id,lhs.p,lhs.size) == std::tie(rhs.id,rhs.p,rhs.size);
+        return std::tie(lhs.id,lhs.local) == std::tie(rhs.id,rhs.local);
       }
     };
 
-    /// Pair of GeometryType and local coordinates
-    template <class Tag>
-    struct CoordsKey
+  public:
+    /// Create a cache not associated to a local basis.
+    /// Requires a call to \ref bind() before the first evaluation of the cache.
+    NodeCache() = default;
+
+    /// Associate the cache to a localBasis.
+    NodeCache(LocalBasisType const& localBasis)
+      : localBasis_(&localBasis)
+    {}
+
+    /// Bind the cache to a local basis for later evaluations.
+    // NOTE: Should be called before the first evaluation of the local basis function or
+    //       jacobian.
+    // NOTE: Binding is only necessary, if the default constructor is used and
+    //       the cache is not associated with a localBasis in the constructor.
+    void bind(LocalBasisType const& localBasis)
+    {
+      localBasis_ = &localBasis;
+    }
+
+    /// Evaluate local basis functions at local coordinates
+    ShapeValues const& evaluateFunction(Dune::GeometryType const& type, DomainType const& local) const
+    {
+      Key key{type.id(), local};
+      return shapeValues_.get(key, [&](Key const&)
+      {
+        ShapeValues data;
+        localBasis_->evaluateFunction(local, data);
+        return data;
+      });
+    }
+
+    /// Evaluate local basis jacobians at local coordinates
+    ShapeGradients const& evaluateJacobian(Dune::GeometryType const& type, DomainType const& local) const
+    {
+      Key key{type.id(), local};
+      return shapeGradients_.get(key, [&](Key const&)
+      {
+        ShapeGradients data;
+        localBasis_->evaluateJacobian(local, data);
+        return data;
+      });
+    }
+
+  private:
+    template <class Value>
+    using Cache = ConcurrentCache<Key, Value, ThreadLocalPolicy,
+      std::unordered_map<Key, Value, typename Key::hasher>>;
+
+    Cache<ShapeValues> shapeValues_;
+    Cache<ShapeGradients> shapeGradients_;
+
+    LocalBasisType const* localBasis_ = nullptr;
+  };
+
+
+  /// \brief Cache of LocalBasis evaluations and jacobians at quadrature points
+  /**
+   * Caching is done using the ConcurrentCache data structure with a key that
+   * depends on the element type and order and size of a quadrature rule.
+   * Two methods are provided for evaluation of local basis functions and local
+   * basis jacobians at all quadrature points. A vector of values is returned.
+   *
+   * \tparam Node  A leaf basis node in a basis tree
+   **/
+  template <class Node>
+  class NodeQuadCache
+  {
+    static_assert(Node::isLeaf, "");
+    using LocalBasisType = typename Node::FiniteElement::Traits::LocalBasisType;
+
+  public:
+    using Traits = typename LocalBasisType::Traits;
+
+    using ShapeValues = std::vector<typename Traits::RangeType>;
+    using ShapeGradients = std::vector<typename Traits::JacobianType>;
+
+    using ShapeValuesContainer = std::vector<ShapeValues>;
+    using ShapeGradientsContainer = std::vector<ShapeGradients>;
+
+  private:
+    // Pair of GeometryType and quadrature order and size
+    struct Key
     {
       unsigned int id;    // topologyId
-      DomainType local;   // local coordinate
+      int p;              // quadrature order
+      std::size_t size;   // number of quadrature points
 
       struct hasher
       {
-        std::size_t operator()(CoordsKey const& t) const
+        std::size_t operator()(Key const& t) const
         {
           std::size_t seed = 0;
           Dune::hash_combine(seed, t.id);
-          Dune::hash_range(seed, t.local.begin(), t.local.end());
+          Dune::hash_combine(seed, t.p);
+          Dune::hash_combine(seed, t.size);
           return seed;
         }
       };
 
-      friend bool operator==(CoordsKey const& lhs, CoordsKey const& rhs)
+      friend bool operator==(Key const& lhs, Key const& rhs)
       {
-        return std::tie(lhs.id,lhs.local) == std::tie(rhs.id,rhs.local);
+        return std::tie(lhs.id,lhs.p,lhs.size) == std::tie(rhs.id,rhs.p,rhs.size);
       }
     };
 
-    using LocalValuesKey = CoordsKey<struct ValuesTag>;
-    using LocalGradientsKey = CoordsKey<struct GradientsTag>;
-
-    using ValuesKey = QuadKey<struct ValuesTag>;
-    using GradientsKey = QuadKey<struct GradientsTag>;
-
   public:
-    using ShapeValues = std::vector<RangeType>;
-    using ShapeGradients = std::vector<JacobianType>;
-
-    using ShapeValuesContainer = std::vector<ShapeValues>;
-    using ShapeGradientsContainer = std::vector<ShapeGradients>;
+    /// Create a cache not associated to a local basis.
+    /// Requires a call to \ref bind() before the first evaluation of the cache.
+    NodeQuadCache() = default;
 
-
-    LocalBasisCache(LocalBasisType const& localBasis)
+    /// Associate the cache to a localBasis.
+    NodeQuadCache(LocalBasisType const& localBasis)
       : localBasis_(&localBasis)
     {}
 
-    // evaluate local basis-function at all quadrature points
-    template <class LocalContext, int d>
-    ShapeValuesContainer const& evaluateFunctionAtQp(LocalContext const& context, Dune::QuadratureRule<RangeFieldType,d> const& quad) const
+    /// Bind the cache to a local basis for later evaluations.
+    // NOTE: Should be called before the first evaluation of the local basis function or
+    //       jacobian.
+    // NOTE: Binding is only necessary, if the default constructor is used and
+    //       the cache is not associated with a localBasis in the constructor.
+    void bind(LocalBasisType const& localBasis)
+    {
+      localBasis_ = &localBasis;
+    }
+
+    /// Evaluate local basis functions at all quadrature points of a quadrature rule.
+    template <class LocalContext, class QuadratureRule>
+    ShapeValuesContainer const& evaluateFunctionAtQP(LocalContext const& context, QuadratureRule const& quad) const
     {
-      ValuesKey key{context.type().id(),quad.order(),quad.size()};
-      return ShapeValuesContainerCache::get(key, [&](ValuesKey const&)
+      Key key{context.type().id(), quad.order(), quad.size()};
+      return shapeValuesContainer_.get(key, [&](Key const&)
       {
         ShapeValuesContainer data(quad.size());
         for (std::size_t iq = 0; iq < quad.size(); ++iq)
@@ -108,12 +198,12 @@ namespace AMDiS
       });
     }
 
-    // evaluate local basis-gradients at all quadrature points
-    template <class LocalContext, int d>
-    ShapeGradientsContainer const& evaluateJacobianAtQp(LocalContext const& context, Dune::QuadratureRule<RangeFieldType,d> const& quad) const
+    /// Evaluate local basis jacobians at all quadrature points of a quadrature rule.
+    template <class LocalContext, class QuadratureRule>
+    ShapeGradientsContainer const& evaluateJacobianAtQP(LocalContext const& context, QuadratureRule const& quad) const
     {
-      GradientsKey key{context.type().id(),quad.order(),quad.size()};
-      return ShapeGradientsContainerCache::get(key, [&](GradientsKey const&)
+      Key key{context.type().id(), quad.order(), quad.size()};
+      return shapeGradientsContainer_.get(key, [&](Key const&)
       {
         ShapeGradientsContainer data(quad.size());
         for (std::size_t iq = 0; iq < quad.size(); ++iq)
@@ -122,45 +212,15 @@ namespace AMDiS
       });
     }
 
-
-    // evaluate local basis-function at point
-    template <class Element>
-    ShapeValues const& evaluateFunction(Element const& element, DomainType const& local) const
-    {
-      LocalValuesKey key{element.type().id(),local};
-      return ShapeValuesCache::get(key, [&](LocalValuesKey const&)
-      {
-        ShapeValues data;
-        localBasis_->evaluateFunction(local, data);
-        return data;
-      });
-    }
-
-    // evaluate local basis-gradients at point
-    template <class Element>
-    ShapeGradients const& evaluateJacobian(Element const& element, DomainType const& local) const
-    {
-      LocalGradientsKey key{element.type().id(),local};
-      return ShapeGradientsCache::get(key, [&](LocalGradientsKey const&)
-      {
-        ShapeGradients data;
-        localBasis_->evaluateJacobian(local, data);
-        return data;
-      });
-    }
-
   private:
-    using ShapeValuesCache = ConcurrentCache<LocalValuesKey, ShapeValues, ThreadLocalPolicy,
-      std::unordered_map<LocalValuesKey, ShapeValues, typename LocalValuesKey::hasher>>;
-    using ShapeGradientsCache = ConcurrentCache<LocalGradientsKey, ShapeGradients, ThreadLocalPolicy,
-      std::unordered_map<LocalGradientsKey, ShapeGradients, typename LocalGradientsKey::hasher>>;
+    template <class Value>
+    using Cache = ConcurrentCache<Key, Value, ThreadLocalPolicy,
+      std::unordered_map<Key, Value, typename Key::hasher>>;
 
-    using ShapeValuesContainerCache = ConcurrentCache<ValuesKey, ShapeValuesContainer, ThreadLocalPolicy,
-      std::unordered_map<ValuesKey, ShapeValuesContainer, typename ValuesKey::hasher>>;
-    using ShapeGradientsContainerCache = ConcurrentCache<GradientsKey, ShapeGradientsContainer, ThreadLocalPolicy,
-      std::unordered_map<GradientsKey, ShapeGradientsContainer, typename GradientsKey::hasher>>;
+    Cache<ShapeValuesContainer> shapeValuesContainer_;
+    Cache<ShapeGradientsContainer> shapeGradientsContainer_;
 
-    LocalBasisType const* localBasis_;
+    LocalBasisType const* localBasis_ = nullptr;
   };
 
 } // end namespace AMDiS
diff --git a/src/amdis/LocalBasisEvaluatorCache.hpp b/src/amdis/LocalBasisEvaluatorCache.hpp
deleted file mode 100644
index d6011ab3..00000000
--- a/src/amdis/LocalBasisEvaluatorCache.hpp
+++ /dev/null
@@ -1,96 +0,0 @@
-#pragma once
-
-#include <vector>
-
-#include <dune/geometry/quadraturerules.hh>
-#include <dune/geometry/type.hh>
-
-#include <amdis/LocalBasisCache.hpp>
-#include <amdis/common/TupleUtility.hpp>
-#include <amdis/utility/ConcurrentCache.hpp>
-
-namespace AMDiS
-{
-  namespace Impl
-  {
-    /// Pair of GeometryType and quadrature order and size
-    template <class LocalBasisType, class Tag>
-    struct EvaluatorCacheKey
-    {
-      using type = std::tuple<HashableGeometryType,typename LocalBasisType::Traits::DomainType>;
-      type value;
-
-      friend std::size_t hash_value(EvaluatorCacheKey const& t)
-      {
-        std::size_t seed = hash_value(std::get<0>(t.value));
-        Dune::hash_range(seed, std::get<1>(t.value).begin(), std::get<1>(t.value).end());
-        return seed;
-      }
-
-      friend bool operator==(EvaluatorCacheKey const& lhs, EvaluatorCacheKey const& rhs)
-      {
-        return lhs.value == rhs.value;
-      }
-    };
-  }
-} // end namespace AMDiS
-
-DUNE_DEFINE_HASH(DUNE_HASH_TEMPLATE_ARGS(class LocalBasisType, class Tag),DUNE_HASH_TYPE(AMDiS::Impl::EvaluatorCacheKey<LocalBasisType,Tag>))
-
-namespace AMDiS
-{
-  template <class LocalBasisType>
-  class LocalBasisEvaluatorCache
-  {
-  public:
-    using Traits = typename LocalBasisType::Traits;
-
-    using DomainType = typename Traits::DomainType;
-    using RangeType = typename Traits::RangeType;
-    using RangeFieldType = typename Traits::RangeFieldType;
-    using JacobianType = typename Traits::JacobianType;
-
-    using QuadratureRule = Dune::QuadratureRule<RangeFieldType,Traits::dimDomain>;
-    using QuadratureRules = Dune::QuadratureRules<RangeFieldType,Traits::dimDomain>;
-
-  private:
-    using ValuesKey = Impl::EvaluatorCacheKey<LocalBasisType, struct ValuesTag>;
-    using GradientsKey = Impl::EvaluatorCacheKey<LocalBasisType, struct GradientsTag>;
-    using Policy = tag::thread_local_policy;
-
-  public:
-    using ShapeValues = std::vector<RangeType>;
-    using ShapeGradients = std::vector<JacobianType>;
-
-    LocalBasisEvaluatorCache(LocalBasisType const& localBasis)
-      : localBasis_(&localBasis)
-    {}
-
-    template <class LocalContext>
-    ShapeValues const& evaluateFunction(LocalContext const& context, DomainType const& local)
-    {
-      ValuesKey key{typename ValuesKey::type{context.type(),local}};
-      return ShapeValuesCache::get(key, [&](ShapeValues* data, ValuesKey const&)
-      {
-        localBasis_->evaluateFunction(local, *data);
-      });
-    }
-
-    template <class LocalContext>
-    ShapeGradients const& evaluateJacobian(LocalContext const& context, DomainType const& local)
-    {
-      GradientsKey key{typename GradientsKey::type{context.type(),local}};
-      return ShapeGradientsCache::get(key, [&](ShapeGradients* data, GradientsKey const&)
-      {
-        localBasis_->evaluateJacobian(local, *data);
-      });
-    }
-
-  private:
-    using ShapeValuesCache = ConcurrentCache<ValuesKey, ShapeValues, Policy>;
-    using ShapeGradientsCache = ConcurrentCache<GradientsKey, ShapeGradients, Policy>;
-
-    LocalBasisType const* localBasis_;
-  };
-
-} // end namespace AMDiS
diff --git a/src/amdis/assembler/ConvectionDiffusionOperator.hpp b/src/amdis/assembler/ConvectionDiffusionOperator.hpp
index 77761d9d..caa98f99 100644
--- a/src/amdis/assembler/ConvectionDiffusionOperator.hpp
+++ b/src/amdis/assembler/ConvectionDiffusionOperator.hpp
@@ -58,8 +58,7 @@ namespace AMDiS
       static_assert(std::is_same<FiniteElementType_t<RowNode>, FiniteElementType_t<ColNode>>{},
         "Galerkin operator requires equal finite elements for test and trial space." );
 
-      using LocalBasisType = typename FiniteElementType_t<RowNode>::Traits::LocalBasisType;
-      using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
+      using RangeFieldType = typename NodeQuadCache<RowNode>::Traits::RangeFieldType;
 
       auto localFctA = localFunction(gridFctA_); localFctA.bind(context.element());
       auto localFctB = localFunction(gridFctB_); localFctB.bind(context.element());
@@ -75,9 +74,9 @@ namespace AMDiS
       using QuadratureRules = Dune::QuadratureRules<typename Context::Geometry::ctype, Context::LocalContext::mydimension>;
       auto const& quad = QuadratureRules::rule(context.type(), quadDeg);
 
-      LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
-      auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad);
-      auto const& shapeValuesCache = cache.evaluateFunctionAtQp(context, quad);
+      NodeQuadCache<RowNode> cache(localFE.localBasis());
+      auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(context, quad);
+      auto const& shapeValuesCache = cache.evaluateFunctionAtQP(context, quad);
 
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
@@ -146,8 +145,6 @@ namespace AMDiS
       static_assert(Node::isLeaf,
         "Operator can be applied to Leaf-Nodes only." );
 
-      using LocalBasisType = typename FiniteElementType_t<Node>::Traits::LocalBasisType;
-
       auto localFctF = localFunction(gridFctF_); localFctF.bind(context.element());
 
       auto const& localFE = node.finiteElement();
@@ -158,8 +155,8 @@ namespace AMDiS
       using QuadratureRules = Dune::QuadratureRules<typename Context::Geometry::ctype, Context::LocalContext::dimension>;
       auto const& quad = QuadratureRules::rule(context.type(), quad_order);
 
-      LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
-      auto const& shapeValuesCache = cache.evaluateFunctionAtQp(context, quad);
+      NodeQuadCache<Node> cache(localFE.localBasis());
+      auto const& shapeValuesCache = cache.evaluateFunctionAtQP(context, quad);
 
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
diff --git a/src/amdis/assembler/FirstOrderDivTestvec.hpp b/src/amdis/assembler/FirstOrderDivTestvec.hpp
index b92ab4e5..615e76d6 100644
--- a/src/amdis/assembler/FirstOrderDivTestvec.hpp
+++ b/src/amdis/assembler/FirstOrderDivTestvec.hpp
@@ -50,12 +50,10 @@ namespace AMDiS
       auto const& localFE = node.child(0).finiteElement();
       std::size_t feSize = localFE.size();
 
-      using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
-      using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
+      using RangeFieldType = typename NodeQuadCache<typename Node::ChildType>::Traits::RangeFieldType;
+      NodeQuadCache<typename Node::ChildType> cache(localFE.localBasis());
 
-      LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
-
-      auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad);
+      auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/FirstOrderGradTest.hpp b/src/amdis/assembler/FirstOrderGradTest.hpp
index 5239a1d1..c0fa4024 100644
--- a/src/amdis/assembler/FirstOrderGradTest.hpp
+++ b/src/amdis/assembler/FirstOrderGradTest.hpp
@@ -48,12 +48,10 @@ namespace AMDiS
       auto const& localFE = node.finiteElement();
       std::size_t feSize = localFE.size();
 
-      using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
-      using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
+      using RangeFieldType = typename NodeQuadCache<Node>::Traits::RangeFieldType;
+      NodeQuadCache<Node> cache(localFE.localBasis());
 
-      LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
-
-      auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad);
+      auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/FirstOrderPartialTest.hpp b/src/amdis/assembler/FirstOrderPartialTest.hpp
index 5d7e02bf..3f7d7d52 100644
--- a/src/amdis/assembler/FirstOrderPartialTest.hpp
+++ b/src/amdis/assembler/FirstOrderPartialTest.hpp
@@ -49,12 +49,10 @@ namespace AMDiS
       auto const& localFE = node.finiteElement();
       std::size_t feSize = localFE.size();
 
-      using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
-      using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
+      using RangeFieldType = typename NodeQuadCache<Node>::Traits::RangeFieldType;
+      NodeQuadCache<Node> cache(localFE.localBasis());
 
-      LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
-
-      auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad);
+      auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/FirstOrderTestDivTrialvec.hpp b/src/amdis/assembler/FirstOrderTestDivTrialvec.hpp
index 447924c7..4eb44a54 100644
--- a/src/amdis/assembler/FirstOrderTestDivTrialvec.hpp
+++ b/src/amdis/assembler/FirstOrderTestDivTrialvec.hpp
@@ -54,16 +54,13 @@ namespace AMDiS
       std::size_t rowSize = rowLocalFE.size();
       std::size_t colSize = colLocalFE.size();
 
-      using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
-      using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
+      using RangeFieldType = typename NodeQuadCache<typename ColNode::ChildType>::Traits::RangeFieldType;
 
-      using RangeFieldType = typename ColLocalBasisType::Traits::RangeFieldType;
+      NodeQuadCache<RowNode> rowCache(rowLocalFE.localBasis());
+      NodeQuadCache<typename ColNode::ChildType> colCache(colLocalFE.localBasis());
 
-      LocalBasisCache<RowLocalBasisType> rowCache(rowLocalFE.localBasis());
-      LocalBasisCache<ColLocalBasisType> colCache(colLocalFE.localBasis());
-
-      auto const& shapeValuesCache = rowCache.evaluateFunctionAtQp(context, quad);
-      auto const& shapeGradientsCache = colCache.evaluateJacobianAtQp(context, quad);
+      auto const& shapeValuesCache = rowCache.evaluateFunctionAtQP(context, quad);
+      auto const& shapeGradientsCache = colCache.evaluateJacobianAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/FirstOrderTestGradTrial.hpp b/src/amdis/assembler/FirstOrderTestGradTrial.hpp
index fe5bfdcf..dc19a87b 100644
--- a/src/amdis/assembler/FirstOrderTestGradTrial.hpp
+++ b/src/amdis/assembler/FirstOrderTestGradTrial.hpp
@@ -52,16 +52,13 @@ namespace AMDiS
       std::size_t rowSize = rowLocalFE.size();
       std::size_t colSize = colLocalFE.size();
 
-      using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
-      using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
+      using RangeFieldType = typename NodeQuadCache<ColNode>::Traits::RangeFieldType;
 
-      using RangeFieldType = typename ColLocalBasisType::Traits::RangeFieldType;
+      NodeQuadCache<RowNode> rowCache(rowLocalFE.localBasis());
+      NodeQuadCache<ColNode> colCache(colLocalFE.localBasis());
 
-      LocalBasisCache<RowLocalBasisType> rowCache(rowLocalFE.localBasis());
-      LocalBasisCache<ColLocalBasisType> colCache(colLocalFE.localBasis());
-
-      auto const& shapeValuesCache = rowCache.evaluateFunctionAtQp(context, quad);
-      auto const& shapeGradientsCache = colCache.evaluateJacobianAtQp(context, quad);
+      auto const& shapeValuesCache = rowCache.evaluateFunctionAtQP(context, quad);
+      auto const& shapeGradientsCache = colCache.evaluateJacobianAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/FirstOrderTestPartialTrial.hpp b/src/amdis/assembler/FirstOrderTestPartialTrial.hpp
index 1b7c8c08..de5600a8 100644
--- a/src/amdis/assembler/FirstOrderTestPartialTrial.hpp
+++ b/src/amdis/assembler/FirstOrderTestPartialTrial.hpp
@@ -55,16 +55,13 @@ namespace AMDiS
       std::size_t rowSize = rowLocalFE.size();
       std::size_t colSize = colLocalFE.size();
 
-      using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
-      using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
+      using RangeFieldType = typename NodeQuadCache<ColNode>::Traits::RangeFieldType;
 
-      using RangeFieldType = typename ColLocalBasisType::Traits::RangeFieldType;
+      NodeQuadCache<RowNode> rowCache(rowLocalFE.localBasis());
+      NodeQuadCache<ColNode> colCache(colLocalFE.localBasis());
 
-      LocalBasisCache<RowLocalBasisType> rowCache(rowLocalFE.localBasis());
-      LocalBasisCache<ColLocalBasisType> colCache(colLocalFE.localBasis());
-
-      auto const& shapeValuesCache = rowCache.evaluateFunctionAtQp(context, quad);
-      auto const& shapeGradientsCache = colCache.evaluateJacobianAtQp(context, quad);
+      auto const& shapeValuesCache = rowCache.evaluateFunctionAtQP(context, quad);
+      auto const& shapeGradientsCache = colCache.evaluateJacobianAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/FirstOrderTestvecGradTrial.hpp b/src/amdis/assembler/FirstOrderTestvecGradTrial.hpp
index 89027134..2c18a817 100644
--- a/src/amdis/assembler/FirstOrderTestvecGradTrial.hpp
+++ b/src/amdis/assembler/FirstOrderTestvecGradTrial.hpp
@@ -54,16 +54,13 @@ namespace AMDiS
       std::size_t rowSize = rowLocalFE.size();
       std::size_t colSize = colLocalFE.size();
 
-      using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
-      using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
+      using RangeFieldType = typename NodeQuadCache<ColNode>::Traits::RangeFieldType;
 
-      using RangeFieldType = typename ColLocalBasisType::Traits::RangeFieldType;
+      NodeQuadCache<typename RowNode::ChildType> rowCache(rowLocalFE.localBasis());
+      NodeQuadCache<ColNode> colCache(colLocalFE.localBasis());
 
-      LocalBasisCache<RowLocalBasisType> rowCache(rowLocalFE.localBasis());
-      LocalBasisCache<ColLocalBasisType> colCache(colLocalFE.localBasis());
-
-      auto const& shapeValuesCache = rowCache.evaluateFunctionAtQp(context, quad);
-      auto const& shapeGradientsCache = colCache.evaluateJacobianAtQp(context, quad);
+      auto const& shapeValuesCache = rowCache.evaluateFunctionAtQP(context, quad);
+      auto const& shapeGradientsCache = colCache.evaluateJacobianAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/SecondOrderDivTestvecDivTrialvec.hpp b/src/amdis/assembler/SecondOrderDivTestvecDivTrialvec.hpp
index ff4f94ff..5657676f 100644
--- a/src/amdis/assembler/SecondOrderDivTestvecDivTrialvec.hpp
+++ b/src/amdis/assembler/SecondOrderDivTestvecDivTrialvec.hpp
@@ -71,17 +71,14 @@ namespace AMDiS
       std::size_t rowSize = rowLocalFE.size();
       std::size_t colSize = colLocalFE.size();
 
-      using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
-      using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
+      using RowFieldType = typename NodeQuadCache<typename RowNode::ChildType>::Traits::RangeFieldType;
+      using ColFieldType = typename NodeQuadCache<typename ColNode::ChildType>::Traits::RangeFieldType;
 
-      using RowFieldType = typename RowLocalBasisType::Traits::RangeFieldType;
-      using ColFieldType = typename ColLocalBasisType::Traits::RangeFieldType;
+      NodeQuadCache<typename RowNode::ChildType> rowCache(rowLocalFE.localBasis());
+      NodeQuadCache<typename ColNode::ChildType> colCache(colLocalFE.localBasis());
 
-      LocalBasisCache<RowLocalBasisType> rowCache(rowLocalFE.localBasis());
-      LocalBasisCache<ColLocalBasisType> colCache(colLocalFE.localBasis());
-
-      auto const& rowGradientsCache = rowCache.evaluateJacobianAtQp(context, quad);
-      auto const& colGradientsCache = colCache.evaluateJacobianAtQp(context, quad);
+      auto const& rowGradientsCache = rowCache.evaluateJacobianAtQP(context, quad);
+      auto const& colGradientsCache = colCache.evaluateJacobianAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
@@ -136,12 +133,12 @@ namespace AMDiS
       auto const& localFE = node.child(0).finiteElement();
       std::size_t size = localFE.size();
 
-      using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
-      using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
+      using LeafNode = typename Node::ChildType;
+      using RangeFieldType = typename NodeQuadCache<LeafNode>::Traits::RangeFieldType;
 
-      LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
+      NodeQuadCache<LeafNode> cache(localFE.localBasis());
 
-      auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad);
+      auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/SecondOrderGradTestGradTrial.hpp b/src/amdis/assembler/SecondOrderGradTestGradTrial.hpp
index 1164a18c..23605c66 100644
--- a/src/amdis/assembler/SecondOrderGradTestGradTrial.hpp
+++ b/src/amdis/assembler/SecondOrderGradTestGradTrial.hpp
@@ -74,12 +74,11 @@ namespace AMDiS
       auto const& localFE = rowNode.finiteElement();
       std::size_t size = localFE.size();
 
-      using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
-      using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
+      using RangeFieldType = typename NodeQuadCache<RowNode>::Traits::RangeFieldType;
 
-      LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
+      NodeQuadCache<RowNode> cache(localFE.localBasis());
 
-      auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad);
+      auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
@@ -123,11 +122,10 @@ namespace AMDiS
       auto const& localFE = node.finiteElement();
       std::size_t size = localFE.size();
 
-      using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
-      using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
+      using RangeFieldType = typename NodeQuadCache<RowNode>::Traits::RangeFieldType;
 
-      LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
-      auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad);
+      NodeQuadCache<RowNode> cache(localFE.localBasis());
+      auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
@@ -176,11 +174,10 @@ namespace AMDiS
       auto const& localFE = node.finiteElement();
       std::size_t size = localFE.size();
 
-      using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
-      using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
+      using RangeFieldType = typename NodeQuadCache<RowNode>::Traits::RangeFieldType;
 
-      LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
-      auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad);
+      NodeQuadCache<RowNode> cache(localFE.localBasis());
+      auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/SecondOrderPartialTestPartialTrial.hpp b/src/amdis/assembler/SecondOrderPartialTestPartialTrial.hpp
index bb558164..bc3301b4 100644
--- a/src/amdis/assembler/SecondOrderPartialTestPartialTrial.hpp
+++ b/src/amdis/assembler/SecondOrderPartialTestPartialTrial.hpp
@@ -57,17 +57,14 @@ namespace AMDiS
       std::size_t rowSize = rowLocalFE.size();
       std::size_t colSize = colLocalFE.size();
 
-      using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
-      using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
+      using RowFieldType = typename NodeQuadCache<RowNode>::Traits::RangeFieldType;
+      using ColFieldType = typename NodeQuadCache<ColNode>::Traits::RangeFieldType;
 
-      using RowFieldType = typename RowLocalBasisType::Traits::RangeFieldType;
-      using ColFieldType = typename ColLocalBasisType::Traits::RangeFieldType;
+      NodeQuadCache<RowNode> rowCache(rowLocalFE.localBasis());
+      NodeQuadCache<ColNode> colCache(colLocalFE.localBasis());
 
-      LocalBasisCache<RowLocalBasisType> rowCache(rowLocalFE.localBasis());
-      LocalBasisCache<ColLocalBasisType> colCache(colLocalFE.localBasis());
-
-      auto const& rowGradientsCache = rowCache.evaluateJacobianAtQp(context, quad);
-      auto const& colGradientsCache = colCache.evaluateJacobianAtQp(context, quad);
+      auto const& rowGradientsCache = rowCache.evaluateJacobianAtQP(context, quad);
+      auto const& colGradientsCache = colCache.evaluateJacobianAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/StokesOperator.hpp b/src/amdis/assembler/StokesOperator.hpp
index 3a42c2ef..5dd8218a 100644
--- a/src/amdis/assembler/StokesOperator.hpp
+++ b/src/amdis/assembler/StokesOperator.hpp
@@ -43,6 +43,7 @@ namespace AMDiS
                           ElementMatrix& elementMatrix)
     {
       using VelocityNode = typename Node::template Child<0>::type;
+      using VelocityCompNode = typename VelocityNode::template Child<0>::type;
       using PressureNode = typename Node::template Child<1>::type;
       static_assert(VelocityNode::isPower && PressureNode::isLeaf, "");
 
@@ -56,14 +57,11 @@ namespace AMDiS
       auto const& pressureLocalFE = tree.child(_1).finiteElement();
       std::size_t numPressureLocalFE = pressureLocalFE.size();
 
-      using VelocityLocalBasisType = typename std::decay_t<decltype(velocityLocalFE)>::Traits::LocalBasisType;
-      using PressureLocalBasisType = typename std::decay_t<decltype(pressureLocalFE)>::Traits::LocalBasisType;
+      NodeQuadCache<VelocityCompNode> velocityCache(velocityLocalFE.localBasis());
+      NodeQuadCache<PressureNode> pressureCache(pressureLocalFE.localBasis());
 
-      LocalBasisCache<VelocityLocalBasisType> velocityCache(velocityLocalFE.localBasis());
-      LocalBasisCache<PressureLocalBasisType> pressureCache(pressureLocalFE.localBasis());
-
-      auto const& shapeGradientsCache = velocityCache.evaluateJacobianAtQp(context, quad);
-      auto const& pressureValuesCache = pressureCache.evaluateFunctionAtQp(context, quad);
+      auto const& shapeGradientsCache = velocityCache.evaluateJacobianAtQP(context, quad);
+      auto const& pressureValuesCache = pressureCache.evaluateFunctionAtQP(context, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
@@ -78,7 +76,7 @@ namespace AMDiS
         auto const& shapeGradients = shapeGradientsCache[iq];
 
         // Compute the shape function gradients on the real element
-        using RangeFieldType = typename VelocityLocalBasisType::Traits::RangeFieldType;
+        using RangeFieldType = typename NodeQuadCache<VelocityCompNode>::Traits::RangeFieldType;
         using WorldVector = Dune::FieldVector<RangeFieldType,Context::dow>;
         thread_local std::vector<WorldVector> gradients;
         gradients.resize(shapeGradients.size());
diff --git a/src/amdis/assembler/ZeroOrderTest.hpp b/src/amdis/assembler/ZeroOrderTest.hpp
index b237a665..40c3397e 100644
--- a/src/amdis/assembler/ZeroOrderTest.hpp
+++ b/src/amdis/assembler/ZeroOrderTest.hpp
@@ -46,10 +46,9 @@ namespace AMDiS
       auto const& localFE = node.finiteElement();
       std::size_t size = localFE.size();
 
-      using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
-      LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
+      NodeQuadCache<Node> cache(localFE.localBasis());
 
-      auto const& shapeValuesCache = cache.evaluateFunctionAtQp(context,quad);
+      auto const& shapeValuesCache = cache.evaluateFunctionAtQP(context,quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/ZeroOrderTestTrial.hpp b/src/amdis/assembler/ZeroOrderTestTrial.hpp
index 8311afe2..9c8980a8 100644
--- a/src/amdis/assembler/ZeroOrderTestTrial.hpp
+++ b/src/amdis/assembler/ZeroOrderTestTrial.hpp
@@ -67,13 +67,11 @@ namespace AMDiS
       std::size_t rowSize = rowLocalFE.size();
       std::size_t colSize = colLocalFE.size();
 
-      using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
-      using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
-      LocalBasisCache<RowLocalBasisType> rowCache(rowLocalFE.localBasis());
-      LocalBasisCache<ColLocalBasisType> colCache(colLocalFE.localBasis());
+      NodeQuadCache<RowNode> rowCache(rowLocalFE.localBasis());
+      NodeQuadCache<ColNode> colCache(colLocalFE.localBasis());
 
-      auto const& rowShapeValuesCache = rowCache.evaluateFunctionAtQp(context,quad);
-      auto const& colShapeValuesCache = colCache.evaluateFunctionAtQp(context,quad);
+      auto const& rowShapeValuesCache = rowCache.evaluateFunctionAtQP(context,quad);
+      auto const& colShapeValuesCache = colCache.evaluateFunctionAtQP(context,quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
@@ -111,10 +109,9 @@ namespace AMDiS
       auto const& localFE = node.finiteElement();
       std::size_t size = localFE.size();
 
-      using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
-      LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
+      NodeQuadCache<RowNode> cache(localFE.localBasis());
 
-      auto const& shapeValuesCache = cache.evaluateFunctionAtQp(context,quad);
+      auto const& shapeValuesCache = cache.evaluateFunctionAtQP(context,quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/ZeroOrderTestTrialvec.hpp b/src/amdis/assembler/ZeroOrderTestTrialvec.hpp
index 6937c72c..369ddab6 100644
--- a/src/amdis/assembler/ZeroOrderTestTrialvec.hpp
+++ b/src/amdis/assembler/ZeroOrderTestTrialvec.hpp
@@ -54,13 +54,11 @@ namespace AMDiS
       std::size_t rowSize = rowLocalFE.size();
       std::size_t colSize = colLocalFE.size();
 
-      using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
-      using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
-      LocalBasisCache<RowLocalBasisType> rowCache(rowLocalFE.localBasis());
-      LocalBasisCache<ColLocalBasisType> colCache(colLocalFE.localBasis());
+      NodeQuadCache<RowNode> rowCache(rowLocalFE.localBasis());
+      NodeQuadCache<typename ColNode::ChildType> colCache(colLocalFE.localBasis());
 
-      auto const& rowShapeValuesCache = rowCache.evaluateFunctionAtQp(context,quad);
-      auto const& colShapeValuesCache = colCache.evaluateFunctionAtQp(context,quad);
+      auto const& rowShapeValuesCache = rowCache.evaluateFunctionAtQP(context,quad);
+      auto const& colShapeValuesCache = colCache.evaluateFunctionAtQP(context,quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/ZeroOrderTestvec.hpp b/src/amdis/assembler/ZeroOrderTestvec.hpp
index 1dc2d5c8..498e98e7 100644
--- a/src/amdis/assembler/ZeroOrderTestvec.hpp
+++ b/src/amdis/assembler/ZeroOrderTestvec.hpp
@@ -50,10 +50,9 @@ namespace AMDiS
       auto const& localFE = node.child(0).finiteElement();
       std::size_t size = localFE.size();
 
-      using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
-      LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
+      NodeQuadCache<typename Node::ChildType> cache(localFE.localBasis());
 
-      auto const& shapeValuesCache = cache.evaluateFunctionAtQp(context,quad);
+      auto const& shapeValuesCache = cache.evaluateFunctionAtQP(context,quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/assembler/ZeroOrderTestvecTrialvec.hpp b/src/amdis/assembler/ZeroOrderTestvecTrialvec.hpp
index 35247141..e9504eda 100644
--- a/src/amdis/assembler/ZeroOrderTestvecTrialvec.hpp
+++ b/src/amdis/assembler/ZeroOrderTestvecTrialvec.hpp
@@ -78,13 +78,11 @@ namespace AMDiS
       auto const& rowLocalFE = rowNode.child(0).finiteElement();
       auto const& colLocalFE = colNode.child(0).finiteElement();
 
-      using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
-      using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
-      LocalBasisCache<RowLocalBasisType> rowCache(rowLocalFE.localBasis());
-      LocalBasisCache<ColLocalBasisType> colCache(colLocalFE.localBasis());
+      NodeQuadCache<typename RowNode::ChildType> rowCache(rowLocalFE.localBasis());
+      NodeQuadCache<typename ColNode::ChildType> colCache(colLocalFE.localBasis());
 
-      auto const& rowShapeValuesCache = rowCache.evaluateFunctionAtQp(context,quad);
-      auto const& colShapeValuesCache = colCache.evaluateFunctionAtQp(context,quad);
+      auto const& rowShapeValuesCache = rowCache.evaluateFunctionAtQP(context,quad);
+      auto const& colShapeValuesCache = colCache.evaluateFunctionAtQP(context,quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
@@ -125,10 +123,9 @@ namespace AMDiS
 
       auto const& localFE = node.child(0).finiteElement();
 
-      using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
-      LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
+      NodeQuadCache<typename RowNode::ChildType> cache(localFE.localBasis());
 
-      auto const& shapeValuesCache = cache.evaluateFunctionAtQp(context,quad);
+      auto const& shapeValuesCache = cache.evaluateFunctionAtQP(context,quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
@@ -175,13 +172,11 @@ namespace AMDiS
       auto const& rowLocalFE = rowNode.child(0).finiteElement();
       auto const& colLocalFE = colNode.child(0).finiteElement();
 
-      using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
-      using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
-      LocalBasisCache<RowLocalBasisType> rowCache(rowLocalFE.localBasis());
-      LocalBasisCache<ColLocalBasisType> colCache(colLocalFE.localBasis());
+      NodeQuadCache<typename RowNode::ChildType> rowCache(rowLocalFE.localBasis());
+      NodeQuadCache<typename ColNode::ChildType> colCache(colLocalFE.localBasis());
 
-      auto const& rowShapeValuesCache = rowCache.evaluateFunctionAtQp(context,quad);
-      auto const& colShapeValuesCache = colCache.evaluateFunctionAtQp(context,quad);
+      auto const& rowShapeValuesCache = rowCache.evaluateFunctionAtQP(context,quad);
+      auto const& colShapeValuesCache = colCache.evaluateFunctionAtQP(context,quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = context.local(quad[iq].position());
diff --git a/src/amdis/gridfunctions/DiscreteFunction.inc.hpp b/src/amdis/gridfunctions/DiscreteFunction.inc.hpp
index 8a66a078..fa93de99 100644
--- a/src/amdis/gridfunctions/DiscreteFunction.inc.hpp
+++ b/src/amdis/gridfunctions/DiscreteFunction.inc.hpp
@@ -152,9 +152,9 @@ LocalFunction::operator()(Domain const& x) const
     auto&& fe = node.finiteElement();
     auto&& localBasis = fe.localBasis();
 
-    LocalBasisCache<std::decay_t<decltype(localBasis)>> localBasisCache(localBasis);
-    auto const& shapeFunctionValues = localBasisCache.evaluateFunction(localView_.element(), x);
-    assert(shapeFunctionValues.size() == localBasis.size());
+    using Node = std::decay_t<decltype(node)>;
+    NodeCache<Node> localBasisCache(localBasis);
+    auto const& shapeFunctionValues = localBasisCache.evaluateFunction(localView_.element().type(),x);
 
     // Get range entry associated to this node
     auto re = Dune::Functions::flatVectorView(nodeToRangeEntry(node, tp, y));
@@ -204,9 +204,9 @@ GradientLocalFunction::operator()(Domain const& x) const
     auto&& fe = node.finiteElement();
     auto&& localBasis = fe.localBasis();
 
-    LocalBasisCache<std::decay_t<decltype(localBasis)>> localBasisCache(localBasis);
-    auto const& referenceGradients = localBasisCache.evaluateJacobian(localView_.element(), x);
-    assert(referenceGradients.size() == localBasis.size());
+    using Node = std::decay_t<decltype(node)>;
+    NodeCache<Node> localBasisCache(localBasis);
+    auto const& referenceGradients = localBasisCache.evaluateJacobian(localView_.element().type(),x);
 
     // The transposed inverse Jacobian of the map from the reference element to the element
     auto&& jacobian = geometry_.value().jacobianInverseTransposed(x);
-- 
GitLab