From 78ab641516ab0a8bac3b15bf284100af3cc07391 Mon Sep 17 00:00:00 2001
From: Simon Praetorius <simon.praetorius@tu-dresden.de>
Date: Sat, 11 May 2019 18:59:28 +0200
Subject: [PATCH] Add generalized derivative and local-to-global adapter to
 handle the global derivatives uniformly

---
 src/amdis/Marker.hpp                          |   2 +-
 src/amdis/common/DerivativeTraits.hpp         |  58 ++++
 .../gridfunctions/AnalyticGridFunction.hpp    |  64 ++--
 src/amdis/gridfunctions/CMakeLists.txt        |   5 +-
 .../gridfunctions/ConstantGridFunction.hpp    |  26 +-
 .../gridfunctions/CoordsGridFunction.hpp      |   5 +-
 src/amdis/gridfunctions/DOFVectorView.hpp     |   2 +-
 src/amdis/gridfunctions/Derivative.hpp        |  65 ++++
 .../gridfunctions/DerivativeGridFunction.hpp  |  93 ++++--
 src/amdis/gridfunctions/DiscreteFunction.hpp  |  10 +-
 .../gridfunctions/DiscreteFunction.inc.hpp    | 316 +++++++++++++-----
 .../gridfunctions/FunctorGridFunction.hpp     |  22 +-
 ...dFunctionConcepts.hpp => GridFunction.hpp} |  96 +-----
 src/amdis/gridfunctions/Order.hpp             |  51 +++
 .../localoperators/FirstOrderPartialTest.hpp  |  30 +-
 .../FirstOrderTestPartialTrial.hpp            |  35 +-
 .../SecondOrderPartialTestPartialTrial.hpp    |  54 +--
 src/amdis/localoperators/ZeroOrderTest.hpp    |   4 +-
 src/amdis/utility/LocalBasisCache.hpp         |  21 ++
 src/amdis/utility/LocalToGlobalAdapter.hpp    | 207 ++++++++++++
 src/amdis/utility/QuadratureFactory.hpp       |  27 +-
 test/CMakeLists.txt                           |   3 +
 test/DiscreteFunctionTest.cpp                 |   4 +-
 test/GradientTest.cpp                         |  91 +++++
 test/Tests.hpp                                |   2 +-
 25 files changed, 908 insertions(+), 385 deletions(-)
 create mode 100644 src/amdis/common/DerivativeTraits.hpp
 create mode 100644 src/amdis/gridfunctions/Derivative.hpp
 rename src/amdis/gridfunctions/{GridFunctionConcepts.hpp => GridFunction.hpp} (62%)
 create mode 100644 src/amdis/gridfunctions/Order.hpp
 create mode 100644 src/amdis/utility/LocalToGlobalAdapter.hpp
 create mode 100644 test/GradientTest.cpp

diff --git a/src/amdis/Marker.hpp b/src/amdis/Marker.hpp
index 48f5ccd2..3f99fe7a 100644
--- a/src/amdis/Marker.hpp
+++ b/src/amdis/Marker.hpp
@@ -10,7 +10,7 @@
 #include <amdis/common/ConceptsBase.hpp>
 #include <amdis/common/TypeTraits.hpp>
 
-#include <amdis/gridfunctions/GridFunctionConcepts.hpp>
+#include <amdis/gridfunctions/GridFunction.hpp>
 
 #include <amdis/AdaptInfo.hpp>
 #include <amdis/Flag.hpp>
diff --git a/src/amdis/common/DerivativeTraits.hpp b/src/amdis/common/DerivativeTraits.hpp
new file mode 100644
index 00000000..e86e0ff6
--- /dev/null
+++ b/src/amdis/common/DerivativeTraits.hpp
@@ -0,0 +1,58 @@
+#pragma once
+
+#include <dune/functions/common/defaultderivativetraits.hh>
+
+namespace Dune
+{
+  template <class K, int n>
+  class FieldVector;
+
+  template <class K, int n, int m>
+  class FieldMatrix;
+}
+
+namespace AMDiS
+{
+  namespace tag
+  {
+    struct gradient {};
+    struct divergence {};
+    struct partial { std::size_t comp = 0; };
+
+    // register possible types for derivative traits
+    struct derivative_type : gradient, divergence, partial {};
+  }
+
+  template <class Sig, class Type>
+  struct DerivativeTraits;
+
+  template <class R, class D>
+  struct DerivativeTraits<R(D), tag::gradient>
+      : public Dune::Functions::DefaultDerivativeTraits<R(D)>
+  {};
+
+  template <class R, class D>
+  struct DerivativeTraits<R(D), tag::partial>
+  {
+    using Range = R;
+  };
+
+  template <class K, class D>
+  struct DerivativeTraits<K(D), tag::divergence>
+  {
+    // error
+  };
+
+  template <class K, int n, class D>
+  struct DerivativeTraits<Dune::FieldVector<K,n>(D), tag::divergence>
+  {
+    using Range = K;
+  };
+
+  template <class K, int n, int m, class D>
+  struct DerivativeTraits<Dune::FieldMatrix<K,n,m>(D), tag::divergence>
+  {
+    using Range = Dune::FieldVector<K,m>;
+  };
+
+} // end namespace AMDiS
\ No newline at end of file
diff --git a/src/amdis/gridfunctions/AnalyticGridFunction.hpp b/src/amdis/gridfunctions/AnalyticGridFunction.hpp
index 1c48728c..496c6b19 100644
--- a/src/amdis/gridfunctions/AnalyticGridFunction.hpp
+++ b/src/amdis/gridfunctions/AnalyticGridFunction.hpp
@@ -7,7 +7,9 @@
 #include <dune/functions/gridfunctions/gridviewentityset.hh>
 
 #include <amdis/Operations.hpp>
-#include <amdis/gridfunctions/GridFunctionConcepts.hpp>
+#include <amdis/common/DerivativeTraits.hpp>
+#include <amdis/gridfunctions/GridFunction.hpp>
+#include <amdis/gridfunctions/Order.hpp>
 
 namespace AMDiS
 {
@@ -97,20 +99,19 @@ namespace AMDiS
    * **Requirements:**
    * - The functor `F` must fulfill the concept \ref Concepts::HasPartial
    **/
-  template <class R, class D, class LC, class F>
-  auto derivative(AnalyticLocalFunction<R(D),LC,F> const& lf)
+  template <class R, class D, class LC, class F, class Type>
+  auto derivative(AnalyticLocalFunction<R(D),LC,F> const& lf, Type const& type)
   {
-    static_assert(Concepts::HasPartial<F>,
-      "No partial(...,_0) defined for Functor F of AnalyticLocalFunction.");
+    static_assert(Concepts::HasDerivative<F,Type>,
+      "No derivative(F,DerivativeType) defined for Functor F of AnalyticLocalFunction.");
 
-    auto df = partial(lf.fct(), index_<0>);
+    auto df = derivative(lf.fct(), type);
 
     using RawSignature = typename Dune::Functions::SignatureTraits<R(D)>::RawSignature;
-    using DerivativeSignature = typename Dune::Functions::DefaultDerivativeTraits<RawSignature>::Range(D);
+    using DerivativeSignature = typename DerivativeTraits<RawSignature,Type>::Range(D);
     return AnalyticLocalFunction<DerivativeSignature,LC,decltype(df)>{df};
   }
 
-
   /// \class AnalyticGridFunction
   /// \brief A Gridfunction that evaluates a function with global coordinates.
   /**
@@ -152,7 +153,7 @@ namespace AMDiS
     }
 
     /// Return the \ref AnalyticLocalFunction of the AnalyticGridFunction.
-    LocalFunction localFunction() const
+    LocalFunction makeLocalFunction() const
     {
       return {fct_};
     }
@@ -171,6 +172,30 @@ namespace AMDiS
     EntitySet entitySet_;
   };
 
+
+  namespace Concepts
+  {
+    namespace Definition
+    {
+      template <class F, int dow>
+      constexpr bool CallableDow =
+        Concepts::Callable<F, Dune::FieldVector<double, dow>>;
+    }
+
+    /// \brief Functor F is collable with GlobalCoordinates `F(Dune::FieldVector<double,DOW>)`
+#ifndef WORLDDIM
+    template <class F>
+    constexpr bool CallableDomain =
+      Definition::CallableDow<F, 1> || Definition::CallableDow<F, 2> || Definition::CallableDow<F, 3>;
+#else
+    template <class F>
+    constexpr bool CallableDomain =
+      Definition::CallableDow<F, WORLDDIM>;
+#endif
+
+  } // end namespace Concepts
+
+
   // Creator for the AnalyticGridFunction
   template <class Function>
   struct GridFunctionCreator<Function, std::enable_if_t<Concepts::CallableDomain<Function>>>
@@ -183,30 +208,21 @@ namespace AMDiS
   };
 
 
-  /// \fn localFunction
-  /// \brief Return the LocalFunction of the AnalyticGridFunction.
-  /// \relates AnalyticGridfunction
-  template <class F, class GV>
-  auto localFunction(AnalyticGridFunction<F,GV> const& gf)
-  {
-    return gf.localFunction();
-  }
-
   /// \fn derivative
   /// \brief Return a GridFunction representing the derivative of a functor.
   /**
    * \relates AnalyticGridfunction
    *
    * **Requirements:**
-   * - Functor `F` must fulfill the concept \ref Concepts::HasPartial
+   * - Functor `F` must fulfill the concept \ref Concepts::HasDerivative<Type>
    **/
-  template <class F, class GV>
-  auto derivative(AnalyticGridFunction<F,GV> const& gf)
+  template <class F, class GV, class Type>
+  auto derivative(AnalyticGridFunction<F,GV> const& gf, Type const& type)
   {
-    static_assert(Concepts::HasPartial<F>,
-      "No partial(...,_0) defined for Functor of AnalyticLocalFunction.");
+    static_assert(Concepts::HasDerivative<F,Type>,
+      "No derivative(F,DerivativeType) defined for Functor of AnalyticLocalFunction.");
 
-    auto df = partial(gf.fct(), index_<0>);
+    auto df = derivative(gf.fct(), type);
     return AnalyticGridFunction<decltype(df), GV>{df, gf.entitySet().gridView()};
   }
 
diff --git a/src/amdis/gridfunctions/CMakeLists.txt b/src/amdis/gridfunctions/CMakeLists.txt
index e1a017f5..e9672a2e 100644
--- a/src/amdis/gridfunctions/CMakeLists.txt
+++ b/src/amdis/gridfunctions/CMakeLists.txt
@@ -4,11 +4,14 @@ install(FILES
     AnalyticGridFunction.hpp
     ConstantGridFunction.hpp
     CoordsGridFunction.hpp
+    Derivative.hpp
     DerivativeGridFunction.hpp
+    DerivativeTraits.hpp
     DiscreteFunction.hpp
     DiscreteFunction.inc.hpp
     DOFVectorView.hpp
     FunctorGridFunction.hpp
-    GridFunctionConcepts.hpp
+    GridFunction.hpp
     OperationsGridFunction.hpp
+    Order.hpp
 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amdis/gridfunctions)
diff --git a/src/amdis/gridfunctions/ConstantGridFunction.hpp b/src/amdis/gridfunctions/ConstantGridFunction.hpp
index 1cfe69d7..96134909 100644
--- a/src/amdis/gridfunctions/ConstantGridFunction.hpp
+++ b/src/amdis/gridfunctions/ConstantGridFunction.hpp
@@ -6,11 +6,11 @@
 #include <dune/common/diagonalmatrix.hh>
 #include <dune/common/fmatrix.hh>
 #include <dune/common/fvector.hh>
-#include <dune/functions/common/defaultderivativetraits.hh>
 
+#include <amdis/common/DerivativeTraits.hpp>
 #include <amdis/common/TypeTraits.hpp>
 #include <amdis/gridfunctions/AnalyticGridFunction.hpp>
-#include <amdis/gridfunctions/GridFunctionConcepts.hpp>
+#include <amdis/gridfunctions/GridFunction.hpp>
 
 namespace AMDiS
 {
@@ -53,10 +53,11 @@ namespace AMDiS
 
     /// \brief Create a \ref ConstantLocalFunction representing the derivative
     /// of a constant function, that ist, the value 0.
-    auto derivative() const
+    template <class Type>
+    auto makeDerivative(Type const& type) const
     {
       using RawSignature = typename Dune::Functions::SignatureTraits<R(D)>::RawSignature;
-      using DerivativeRange = typename Dune::Functions::DefaultDerivativeTraits<RawSignature>::Range;
+      using DerivativeRange = typename DerivativeTraits<RawSignature,Type>::Range;
       DerivativeRange diff(0);
       return ConstantLocalFunction<DerivativeRange(D),LC,DerivativeRange>{diff};
     }
@@ -71,13 +72,6 @@ namespace AMDiS
     T value_;
   };
 
-  /// \relates ConstantLocalFunction
-  template <class R, class D, class LC, class T>
-  auto derivative(ConstantLocalFunction<R(D), LC, T> const& lf)
-  {
-    return lf.derivative();
-  }
-
 
   /// \brief Gridfunction returning a constant value.
   /**
@@ -122,7 +116,7 @@ namespace AMDiS
     }
 
     /// \brief Create an \ref ConstantLocalFunction with the stores `value_`.
-    LocalFunction localFunction() const
+    LocalFunction makeLocalFunction() const
     {
       return {value_};
     }
@@ -133,14 +127,6 @@ namespace AMDiS
   };
 
 
-  /// \relates ConstantGridFunction
-  template <class T, class GV>
-  auto localFunction(ConstantGridFunction<T,GV> const& gf)
-  {
-    return gf.localFunction();
-  }
-
-
   namespace Concepts
   {
     /** \addtogroup Concepts
diff --git a/src/amdis/gridfunctions/CoordsGridFunction.hpp b/src/amdis/gridfunctions/CoordsGridFunction.hpp
index 28965dff..cfdfd72c 100644
--- a/src/amdis/gridfunctions/CoordsGridFunction.hpp
+++ b/src/amdis/gridfunctions/CoordsGridFunction.hpp
@@ -5,6 +5,7 @@
 #include <dune/common/diagonalmatrix.hh>
 #include <dune/common/typeutilities.hh>
 
+#include <amdis/common/DerivativeTraits.hpp>
 #include <amdis/gridfunctions/AnalyticGridFunction.hpp>
 
 namespace AMDiS
@@ -48,7 +49,7 @@ namespace AMDiS
           return Dune::DiagonalMatrix<T,N>{T(1)};
         }
       };
-      friend Derivative partial(CoordsFunction const& /*f*/, index_t<0>)
+      friend Derivative derivative(CoordsFunction const& /*f*/, tag::gradient)
       {
         return Derivative{};
       }
@@ -105,7 +106,7 @@ namespace AMDiS
         int comp_;
       };
 
-      friend Derivative partial(Self const& f, index_t<0>)
+      friend Derivative derivative(Self const& f, tag::gradient)
       {
         return Derivative{f.comp_};
       }
diff --git a/src/amdis/gridfunctions/DOFVectorView.hpp b/src/amdis/gridfunctions/DOFVectorView.hpp
index 8a443e2f..aee45435 100644
--- a/src/amdis/gridfunctions/DOFVectorView.hpp
+++ b/src/amdis/gridfunctions/DOFVectorView.hpp
@@ -1,8 +1,8 @@
 #pragma once
 
-#include <amdis/GridFunctions.hpp>
 #include <amdis/functions/Interpolate.hpp>
 #include <amdis/gridfunctions/DiscreteFunction.hpp>
+#include <amdis/gridfunctions/GridFunction.hpp>
 
 namespace AMDiS
 {
diff --git a/src/amdis/gridfunctions/Derivative.hpp b/src/amdis/gridfunctions/Derivative.hpp
new file mode 100644
index 00000000..6516cd6b
--- /dev/null
+++ b/src/amdis/gridfunctions/Derivative.hpp
@@ -0,0 +1,65 @@
+#pragma once
+
+#include <type_traits>
+
+#include <amdis/common/Concepts.hpp>
+#include <amdis/common/DerivativeTraits.hpp>
+#include <amdis/common/Index.hpp>
+
+namespace AMDiS
+{
+  /// The derivative of a localfunction as localfunction itself
+  template <class LocalFunction, class Type,
+    REQUIRES(std::is_convertible<tag::derivative_type, Type>::value),
+    class = void_t<decltype(std::declval<LocalFunction>().makeDerivative(std::declval<Type>()))> >
+  auto derivative(LocalFunction const& lf, Type const& type)
+  {
+    return lf.makeDerivative(type);
+  }
+
+  namespace Concepts
+  {
+    /** \addtogroup Concepts
+     *  @{
+     **/
+
+    namespace Definition
+    {
+      struct HasDerivative
+      {
+        template <class F, class T>
+        auto requires_(F&& f, T&& t) -> decltype( derivative(f,t) );
+      };
+
+      struct HasLocalFunctionDerivative
+      {
+        template <class F, class T>
+        auto requires_(F&& f, T&& t) -> decltype( derivative(localFunction(f),t) );
+      };
+
+      struct HasPartial
+      {
+        template <class F, class I>
+        auto requires_(F&& f, I&& i) -> decltype( partial(f, i) );
+      };
+
+    } // end namespace Definition
+
+
+    /// \brief GridFunction GF has free function `derivative(F)`
+    template <class GF, class Type>
+    constexpr bool HasDerivative = models<Definition::HasDerivative(GF,Type)>;
+
+    /// \brief GridFunction GF has free function `derivative(localFunction(F))`
+    template <class GF, class Type>
+    constexpr bool HasLocalFunctionDerivative = models<Definition::HasLocalFunctionDerivative(GF,Type)>;
+
+    /// \brief Functor F has free function `partial(F,_0)`
+    template <class F>
+    constexpr bool HasPartial = models<Definition::HasPartial(F,index_t<0>)>;
+
+    /** @} **/
+
+  } // end namespace Concepts
+
+} // end namespace AMDiS
diff --git a/src/amdis/gridfunctions/DerivativeGridFunction.hpp b/src/amdis/gridfunctions/DerivativeGridFunction.hpp
index 19693c8d..eb96d470 100644
--- a/src/amdis/gridfunctions/DerivativeGridFunction.hpp
+++ b/src/amdis/gridfunctions/DerivativeGridFunction.hpp
@@ -2,40 +2,65 @@
 
 #include <type_traits>
 
-#include <dune/functions/common/defaultderivativetraits.hh>
 #include <dune/grid/utility/hierarchicsearch.hh>
 
-#include <amdis/gridfunctions/GridFunctionConcepts.hpp>
+#include <amdis/common/Concepts.hpp>
+#include <amdis/common/DerivativeTraits.hpp>
+#include <amdis/gridfunctions/Derivative.hpp>
+#include <amdis/gridfunctions/GridFunction.hpp>
 
 namespace AMDiS
 {
+  namespace Impl
+  {
+    template <class LF, class Sig>
+    struct CheckFunctorConcept
+    {
+      static_assert(Concepts::Functor<LF, Sig>, "Derivative of LocalFunction can not be called as a functor.");
+    };
+
+    template <class Traits>
+    struct CheckValidRange
+    {
+      static_assert(!std::is_same<typename Traits::Range, Dune::Functions::InvalidRange>::value, "Invalid Range.");
+    };
+  }
+
   /// \class DerivativeGridFunction
   /// \brief A Gridfunction that returns the derivative when calling localFunction.
   /**
    * \ingroup GridFunctions
-   * Wrapps the GridFunction so that \ref localFunction returns a LocalFunction
+   * Wraps the GridFunction so that \ref localFunction returns a LocalFunction
    * representing the derivative of the LocalFunction of the GridFunction.
    *
    * \tparam GridFunction The GridFunction that is wrapped.
+   * \tparam The type of derivative, i.e. one of tag::gradient, tag::partial{i}, or tag::divergence
    *
    * **Requirements:**
    * - `GridFunction` models \ref Concepts::GridFunction and the LocalFunction
    *   has a derivative.
    **/
-  template <class GridFunction>
+  template <class GridFunction, class Type>
   class DerivativeGridFunction
   {
     using GridFctRange = typename GridFunction::Range;
     using GridFctDomain = typename GridFunction::Domain;
     using RawSignature = typename Dune::Functions::SignatureTraits<GridFctRange(GridFctDomain)>::RawSignature;
-    using DerivativeTraits = Dune::Functions::DefaultDerivativeTraits<RawSignature>;
-    using LocalFunction = TYPEOF( derivative(localFunction(std::declval<GridFunction>())) ) ;
+
+    using Traits = DerivativeTraits<RawSignature, Type>;
+    using LocalFunction = TYPEOF( derivative(localFunction(std::declval<GridFunction>()), std::declval<Type>()) ) ;
+
+    using LocalFctRange = typename Traits::Range;
+    using LocalFctDomain = typename GridFunction::EntitySet::LocalCoordinate;
+
+    using _CHECK1_ = Impl::CheckValidRange<Traits>;
+    using _CHECK2_ = Impl::CheckFunctorConcept<LocalFunction, LocalFctRange(LocalFctDomain)>;
 
     enum { hasDerivative = false };
 
   public:
     /// The Range of the derivative of the GridFunction
-    using Range = typename DerivativeTraits::Range;
+    using Range = typename Traits::Range;
 
     /// The domain of the GridFunction
     using Domain = GridFctDomain;
@@ -45,12 +70,10 @@ namespace AMDiS
 
   public:
     /// Constructor. Stores a copy of gridFct.
-    explicit DerivativeGridFunction(GridFunction const& gridFct)
+    explicit DerivativeGridFunction(GridFunction const& gridFct, Type const& type)
       : gridFct_{gridFct}
-    {
-      static_assert(isValidRange<DerivativeTraits>(),
-        "Derivative of GridFunction not defined");
-    }
+      , type_{type}
+    {}
 
     /// Evaluate derivative in global coordinates. NOTE: expensive
     Range operator()(Domain const& x) const
@@ -65,15 +88,15 @@ namespace AMDiS
 
       auto element = hsearch.findEntity(x);
       auto geometry = element.geometry();
-      auto localFct = derivative(localFunction(gridFct_));
+      auto localFct = makeLocalFunction();
       localFct.bind(element);
       return localFct(geometry.local(x));
     }
 
     /// Return the derivative-localFunction of the GridFunction.
-    friend LocalFunction localFunction(DerivativeGridFunction const& gf)
+    LocalFunction makeLocalFunction() const
     {
-      return derivative(localFunction(gf.gridFct_));
+      return derivative(localFunction(gridFct_), type_);
     }
 
     /// Return the \ref EntitySet of the \ref GridFunction.
@@ -84,6 +107,7 @@ namespace AMDiS
 
   private:
     GridFunction gridFct_;
+    Type type_;
   };
 
 
@@ -100,19 +124,19 @@ namespace AMDiS
    *   `GridFct::hasDerivative == false`.
    * - The localFunction of the `GridFct` models `Concepts::HasDerivative`.
    **/
-  template <class GridFct,
-            class LocalFct = decltype(localFunction(std::declval<GridFct>())),
+  template <class GridFct, class Type,
+            class LocalFct = decltype( localFunction(std::declval<GridFct>()) ),
     REQUIRES(not GridFct::hasDerivative)>
-  auto derivative(GridFct const& gridFct)
+  auto derivative(GridFct const& gridFct, Type const& type)
   {
-    static_assert(Concepts::HasDerivative<LocalFct>,
-      "derivative(LocalFunction) not defined!");
-    return DerivativeGridFunction<GridFct>{gridFct};
+    static_assert(Concepts::HasDerivative<LocalFct,Type>,
+      "derivative(LocalFunction,type) not defined!");
+    return DerivativeGridFunction<GridFct,Type>{gridFct, type};
   }
 
 
 #ifndef DOXYGEN
-  template <class Expr>
+  template <class Expr, class Type>
   struct DerivativePreGridFunction
   {
     using Self = DerivativePreGridFunction;
@@ -122,17 +146,18 @@ namespace AMDiS
       template <class GridView>
       static auto create(Self const& self, GridView const& gridView)
       {
-        return derivative(makeGridFunction(self.expr_, gridView));
+        return derivative(makeGridFunction(self.expr_, gridView), self.type_);
       }
     };
 
     Expr expr_;
+    Type type_ = {};
   };
 
   namespace Traits
   {
-    template <class Expr>
-    struct IsPreGridFunction<DerivativePreGridFunction<Expr>>
+    template <class Expr,class Type>
+    struct IsPreGridFunction<DerivativePreGridFunction<Expr,Type>>
       : std::true_type {};
   }
 #endif
@@ -143,7 +168,7 @@ namespace AMDiS
   /// \relates DerivativeGridFunction
   /**
    * \ingroup GridFunctions
-   * Generates a Gridfunction representing the derivative of a GridFunction.
+   * Generates a Gridfunction representing the gradient of a GridFunction.
    * See \ref DerivativeGridFunction.
    *
    * **Examples:**
@@ -153,7 +178,21 @@ namespace AMDiS
   template <class Expr>
   auto gradientAtQP(Expr const& expr)
   {
-    return DerivativePreGridFunction<Expr>{expr};
+    return DerivativePreGridFunction<Expr, tag::gradient>{expr};
+  }
+
+  /// Generates a Gridfunction representing the divergence of a vector-valued GridFunction.
+  template <class Expr>
+  auto divergenceAtQP(Expr const& expr)
+  {
+    return DerivativePreGridFunction<Expr, tag::divergence>{expr};
+  }
+
+  /// Generates a Gridfunction representing the partial derivative of a GridFunction.
+  template <class Expr>
+  auto partialAtQP(Expr const& expr, std::size_t i)
+  {
+    return DerivativePreGridFunction<Expr, tag::partial>{expr, tag::partial{i}};
   }
 
   /** @} **/
diff --git a/src/amdis/gridfunctions/DiscreteFunction.hpp b/src/amdis/gridfunctions/DiscreteFunction.hpp
index 9323112d..8a9ba447 100644
--- a/src/amdis/gridfunctions/DiscreteFunction.hpp
+++ b/src/amdis/gridfunctions/DiscreteFunction.hpp
@@ -9,7 +9,6 @@
 #include <dune/functions/gridfunctions/gridviewentityset.hh>
 #include <dune/typetree/childextraction.hh>
 
-#include <amdis/GridFunctions.hpp>
 #include <amdis/LinearAlgebra.hpp>
 #include <amdis/typetree/FiniteElementType.hpp>
 #include <amdis/typetree/RangeType.hpp>
@@ -60,7 +59,12 @@ namespace AMDiS
 
   public:
     /// A LocalFunction representing the derivative of the DOFVector on a bound element
+    template <class Type>
+    class DerivativeLocalFunctionBase;
+
     class GradientLocalFunction;
+    class PartialLocalFunction;
+    class DivergenceLocalFunction;
 
     /// A LocalFunction representing the value the DOFVector on a bound element
     class LocalFunction;
@@ -83,9 +87,9 @@ namespace AMDiS
     Range operator()(Domain const& x) const;
 
     /// \brief Create a local function for this view on the DOFVector. \relates LocalFunction
-    friend LocalFunction localFunction(DiscreteFunction const& self)
+    LocalFunction makeLocalFunction() const
     {
-      return LocalFunction{self};
+      return LocalFunction{*this};
     }
 
     /// \brief Return a \ref Dune::Functions::GridViewEntitySet
diff --git a/src/amdis/gridfunctions/DiscreteFunction.inc.hpp b/src/amdis/gridfunctions/DiscreteFunction.inc.hpp
index 28478de7..4f4af74e 100644
--- a/src/amdis/gridfunctions/DiscreteFunction.inc.hpp
+++ b/src/amdis/gridfunctions/DiscreteFunction.inc.hpp
@@ -1,7 +1,9 @@
 #pragma once
 
+#include <amdis/common/DerivativeTraits.hpp>
 #include <amdis/common/FieldMatVec.hpp>
 #include <amdis/utility/LocalBasisCache.hpp>
+#include <amdis/utility/LocalToGlobalAdapter.hpp>
 
 #include <dune/common/ftraits.hh>
 #include <dune/grid/utility/hierarchicsearch.hh>
@@ -55,9 +57,19 @@ public:
   Range operator()(Domain const& x) const;
 
   /// \brief Create a LocalFunction representing the gradient. \relates GradientLocalFunction
-  friend GradientLocalFunction derivative(LocalFunction const& localFunction)
+  GradientLocalFunction makeDerivative(tag::gradient type) const
   {
-    return GradientLocalFunction{localFunction.globalFunction_};
+    return GradientLocalFunction{globalFunction_, type};
+  }
+
+  DivergenceLocalFunction makeDerivative(tag::divergence type) const
+  {
+    return DivergenceLocalFunction{globalFunction_, type};
+  }
+
+  PartialLocalFunction makeDerivative(tag::partial type) const
+  {
+    return PartialLocalFunction{globalFunction_, type};
   }
 
   /// \brief The \ref polynomialDegree() of the LocalFunctions
@@ -83,16 +95,59 @@ private:
 
 
 template <class GB, class VT, class TP>
-class DiscreteFunction<GB,VT,TP>::GradientLocalFunction
+typename DiscreteFunction<GB,VT,TP>::Range DiscreteFunction<GB,VT,TP>::
+LocalFunction::operator()(Domain const& x) const
+{
+  assert( bound_ );
+  Range y(0);
+
+  auto&& coefficients = *globalFunction_.dofVector_;
+  auto&& nodeToRangeEntry = globalFunction_.nodeToRangeEntry_;
+  for_each_leaf_node(*subTree_, [&,this](auto const& node, auto const& tp)
+  {
+    auto localBasisCache = makeNodeCache(node);
+    auto const& shapeFunctionValues = localBasisCache.evaluateFunction(localView_.element().type(), x);
+    std::size_t size = node.finiteElement().size();
+
+    // Get range entry associated to this node
+    auto re = Dune::Functions::flatVectorView(nodeToRangeEntry(node, tp, y));
+
+    for (std::size_t i = 0; i < size; ++i) {
+      auto&& multiIndex = localView_.index(node.localIndex(i));
+
+      // Get coefficient associated to i-th shape function
+      auto c = Dune::Functions::flatVectorView(coefficients[multiIndex]);
+
+      // Get value of i-th shape function
+      auto v = Dune::Functions::flatVectorView(shapeFunctionValues[i]);
+
+      std::size_t dimC = c.size();
+      std::size_t dimV = v.size();
+      assert(dimC*dimV == std::size_t(re.size()));
+      for(std::size_t j = 0; j < dimC; ++j) {
+        auto&& c_j = c[j];
+        for(std::size_t k = 0; k < dimV; ++k)
+          re[j*dimV + k] += c_j*v[k];
+      }
+    }
+  });
+
+  return y;
+}
+
+
+template <class GB, class VT, class TP>
+  template <class Type>
+class DiscreteFunction<GB,VT,TP>::DerivativeLocalFunctionBase
 {
   using R = typename DiscreteFunction::Range;
   using D = typename DiscreteFunction::Domain;
   using RawSignature = typename Dune::Functions::SignatureTraits<R(D)>::RawSignature;
-  using DerivativeTraits = Dune::Functions::DefaultDerivativeTraits<RawSignature>;
+  using Traits = DerivativeTraits<RawSignature, Type>;
 
 public:
   using Domain = typename EntitySet::LocalCoordinate;
-  using Range = typename DerivativeTraits::Range;
+  using Range = typename Traits::Range;
 
   enum { hasDerivative = false };
 
@@ -103,15 +158,17 @@ private:
 
 public:
   /// Constructor. Stores a copy of the DiscreteFunction.
-  GradientLocalFunction(DiscreteFunction const& globalFunction)
+  DerivativeLocalFunctionBase(DiscreteFunction const& globalFunction, Type const& type)
     : globalFunction_(globalFunction)
+    , type_(type)
     , localView_(globalFunction_.basis()->localView())
     , subTree_(&child(localView_.tree(), globalFunction_.treePath()))
   {}
 
   /// Copy constructor.
-  GradientLocalFunction(GradientLocalFunction const& other)
+  DerivativeLocalFunctionBase(DerivativeLocalFunctionBase const& other)
     : globalFunction_(other.globalFunction_)
+    , type_(other.type_)
     , localView_(globalFunction_.basis()->localView())
     , subTree_(&child(localView_.tree(), globalFunction_.treePath()))
   {}
@@ -130,9 +187,6 @@ public:
     bound_ = false;
   }
 
-  /// Evaluate Gradient at bound element in local coordinates
-  Range operator()(Domain const& x) const;
-
   int order() const
   {
     assert( bound_ );
@@ -146,8 +200,9 @@ public:
     return localView_.element();
   }
 
-private:
+protected:
   DiscreteFunction globalFunction_;
+  Type type_;
   LocalView localView_;
   SubTree const* subTree_;
   Dune::Std::optional<Geometry> geometry_;
@@ -156,105 +211,198 @@ private:
 
 
 template <class GB, class VT, class TP>
-typename DiscreteFunction<GB,VT,TP>::Range DiscreteFunction<GB,VT,TP>::
-LocalFunction::operator()(Domain const& x) const
+class DiscreteFunction<GB,VT,TP>::GradientLocalFunction
+    : public DerivativeLocalFunctionBase<tag::gradient>
 {
-  assert( bound_ );
-  Range y(0);
+  using Super = DerivativeLocalFunctionBase<tag::gradient>;
+public:
+  using Range = typename Super::Range;
+  using Domain = typename Super::Domain;
 
-  auto&& coefficients = *globalFunction_.dofVector_;
-  auto&& nodeToRangeEntry = globalFunction_.nodeToRangeEntry_;
-  for_each_leaf_node(*subTree_, [&,this](auto const& node, auto const& tp)
+  using Super::DerivativeLocalFunctionBase;
+
+  /// Evaluate Gradient at bound element in local coordinates
+  Range operator()(Domain const& x) const
   {
-    auto&& fe = node.finiteElement();
-    auto&& localBasis = fe.localBasis();
+    assert( Super::bound_ );
+    Range dy(0);
+
+    auto&& coefficients = *Super::globalFunction_.dofVector_;
+    auto&& nodeToRangeEntry = Super::globalFunction_.nodeToRangeEntry_;
+    for_each_leaf_node(*Super::subTree_, [&,this](auto const& node, auto const& tp)
+    {
+      auto localBasis = makeLocalToGlobalBasisAdapter(node, Super::geometry_.value());
+      auto const& gradients = localBasis.gradientsAt(x);
+
+      // Get range entry associated to this node
+      auto re = Dune::Functions::flatVectorView(nodeToRangeEntry(node, tp, dy));
+
+      for (std::size_t i = 0; i < localBasis.size(); ++i) {
+        auto&& multiIndex = Super::localView_.index(node.localIndex(i));
+
+        // Get coefficient associated to i-th shape function
+        auto c = Dune::Functions::flatVectorView(coefficients[multiIndex]);
+
+        // Get value of i-th transformed reference gradient
+        auto grad = Dune::Functions::flatVectorView(gradients[i]);
+
+        std::size_t dimC = c.size();
+        std::size_t dimV = grad.size();
+        assert(dimC*dimV == std::size_t(re.size()));
+        for(std::size_t j = 0; j < dimC; ++j) {
+          auto&& c_j = c[j];
+          for(std::size_t k = 0; k < dimV; ++k)
+            re[j*dimV + k] += c_j*grad[k];
+        }
+      }
+    });
 
-    NodeCache<TYPEOF(node)> localBasisCache(localBasis);
-    auto const& shapeFunctionValues = localBasisCache.evaluateFunction(localView_.element().type(),x);
+    return dy;
+  }
+};
 
-    // Get range entry associated to this node
-    auto re = Dune::Functions::flatVectorView(nodeToRangeEntry(node, tp, y));
 
-    for (std::size_t i = 0; i < localBasis.size(); ++i) {
-      auto&& multiIndex = localView_.index(node.localIndex(i));
+template <class GB, class VT, class TP>
+class DiscreteFunction<GB,VT,TP>::DivergenceLocalFunction
+    : public DerivativeLocalFunctionBase<tag::divergence>
+{
+  using Super = DerivativeLocalFunctionBase<tag::divergence>;
 
-      // Get coefficient associated to i-th shape function
-      auto c = Dune::Functions::flatVectorView(coefficients[multiIndex]);
+public:
+  using Range = typename Super::Range;
+  using Domain = typename Super::Domain;
 
-      // Get value of i-th shape function
-      auto v = Dune::Functions::flatVectorView(shapeFunctionValues[i]);
+  using Super::DerivativeLocalFunctionBase;
 
-      std::size_t dimC = c.size();
-      std::size_t dimV = v.size();
-      assert(dimC*dimV == std::size_t(re.size()));
-      for(std::size_t j = 0; j < dimC; ++j) {
-        auto&& c_j = c[j];
-        for(std::size_t k = 0; k < dimV; ++k)
-          re[j*dimV + k] += c_j*v[k];
+  /// Evaluate divergence at bound element in local coordinates
+  Range operator()(Domain const& x) const
+  {
+    return evaluate_node(x, bool_t<SubTree::isPower && SubTree::degree() == GridView::dimensionworld>{});
+  }
+
+private:
+  Range evaluate_node(Domain const& x, std::false_type) const
+  {
+    error_exit("Cannot calculate divergence(node) where node is not a power node.");
+    return Range(0);
+  }
+
+  Range evaluate_node(Domain const& x, std::true_type) const
+  {
+    static_assert(Size_v<Range> == 1, "Implemented for scalar coefficients only.");
+
+    assert( Super::bound_ );
+    Range dy(0);
+
+    auto&& coefficients = *Super::globalFunction_.dofVector_;
+    auto&& node = *Super::subTree_;
+
+    auto localBasis = makeLocalToGlobalBasisAdapter(node.child(0), Super::geometry_.value());
+    auto const& gradients = localBasis.gradientsAt(x);
+
+    auto re = Dune::Functions::flatVectorView(dy);
+    assert(int(re.size()) == 1);
+    for (std::size_t i = 0; i < localBasis.size(); ++i) {
+      auto grad = Dune::Functions::flatVectorView(gradients[i]);
+
+      assert(int(grad.size()) == GridView::dimensionworld);
+      for (std::size_t j = 0; j < GridView::dimensionworld; ++j) {
+        auto&& multiIndex = Super::localView_.index(node.child(j).localIndex(i));
+        re[0] += coefficients[multiIndex] * grad[j];
       }
     }
-  });
 
-  return y;
-}
+    return dy;
+  }
+};
 
 
 template <class GB, class VT, class TP>
-typename DiscreteFunction<GB,VT,TP>::GradientLocalFunction::Range DiscreteFunction<GB,VT,TP>::
-GradientLocalFunction::operator()(Domain const& x) const
+class DiscreteFunction<GB,VT,TP>::PartialLocalFunction
+    : public DerivativeLocalFunctionBase<tag::partial>
 {
-  assert( bound_ );
-  Range dy(0);
+  using Super = DerivativeLocalFunctionBase<tag::partial>;
 
-  auto&& coefficients = *globalFunction_.dofVector_;
-  auto&& nodeToRangeEntry = globalFunction_.nodeToRangeEntry_;
-  for_each_leaf_node(*subTree_, [&,this](auto const& node, auto const& tp)
-  {
-    // TODO: may DOFVectorView::Range to FieldVector type if necessary
-    using LocalDerivativeTraits
-      = Dune::Functions::DefaultDerivativeTraits<VT(Domain)>;
-    using GradientBlock = typename LocalDerivativeTraits::Range;
-
-    auto&& fe = node.finiteElement();
-    auto&& localBasis = fe.localBasis();
-    std::size_t size = localBasis.size();
-
-    NodeCache<TYPEOF(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);
+public:
+  using Range = typename Super::Range;
+  using Domain = typename Super::Domain;
 
-    // Compute the shape function gradients on the real element
-    std::vector<GradientBlock> gradients(referenceGradients.size());
-    for (std::size_t i = 0; i < gradients.size(); ++i) // J^(-T) * D[phi] -> grad^T
-      Dune::MatVec::as_vector(gradients[i]) = jacobian * Dune::MatVec::as_vector(referenceGradients[i]);
+  using Super::DerivativeLocalFunctionBase;
 
-    // Get range entry associated to this node
-    auto re = Dune::Functions::flatVectorView(nodeToRangeEntry(node, tp, dy));
+  /// Evaluate partial derivative at bound element in local coordinates
+  Range operator()(Domain const& x) const
+  {
+    assert( Super::bound_ );
+    Range dy(0);
+
+    auto&& coefficients = *Super::globalFunction_.dofVector_;
+    auto&& nodeToRangeEntry = Super::globalFunction_.nodeToRangeEntry_;
+    for_each_leaf_node(*Super::subTree_, [&,this](auto const& node, auto const& tp)
+    {
+      auto localBasis = makeLocalToGlobalBasisAdapter(node, Super::geometry_.value());
+      auto const& partial = localBasis.partialsAt(x, Super::type_.comp);
+
+      // Get range entry associated to this node
+      auto re = Dune::Functions::flatVectorView(nodeToRangeEntry(node, tp, dy));
+
+      for (std::size_t i = 0; i < localBasis.size(); ++i) {
+        auto&& multiIndex = Super::localView_.index(node.localIndex(i));
+
+        // Get coefficient associated to i-th shape function
+        auto c = Dune::Functions::flatVectorView(coefficients[multiIndex]);
+
+        // Get value of i-th transformed reference partial_derivative
+        auto d_comp = Dune::Functions::flatVectorView(partial[i]);
+
+        std::size_t dimC = c.size();
+        std::size_t dimV = d_comp.size();
+        assert(dimC*dimV == std::size_t(re.size()));
+        for(std::size_t j = 0; j < dimC; ++j) {
+          auto&& c_j = c[j];
+          for(std::size_t k = 0; k < dimV; ++k)
+            re[j*dimV + k] += c_j*d_comp[k];
+        }
+      }
+    });
 
-    for (std::size_t i = 0; i < size; ++i) {
-      auto&& multiIndex = localView_.index(node.localIndex(i));
+    return dy;
+  }
 
-      // Get coefficient associated to i-th shape function
-      auto c = Dune::Functions::flatVectorView(coefficients[multiIndex]);
+private:
+  template <class T, class J, class RG>
+  void test_partial(std::vector<T> const& partial, std::size_t comp, J const& jacobian, RG const& referenceGradients) const
+  {
+    for (std::size_t i = 0; i < referenceGradients.size(); ++i) { // J^(-T) * D[phi] -> grad^T
+      auto grad = jacobian * Dune::MatVec::as_vector(referenceGradients[i]);
+      auto grad_ = Dune::Functions::flatVectorView(grad);
 
-      // Get value of i-th transformed reference gradient
-      auto grad = Dune::Functions::flatVectorView(gradients[i]);
+      if (std::abs(grad_[comp] - partial[i]) > 1.e-10) {
+        print_error(partial[i], comp, grad, jacobian, referenceGradients[i][0]);
+        error_exit("wrong partial derivatives");
+      }
+    }
+  }
 
-      std::size_t dimC = c.size();
-      std::size_t dimV = grad.size();
-      assert(dimC*dimV == std::size_t(re.size()));
-      for(std::size_t j = 0; j < dimC; ++j) {
-        auto&& c_j = c[j];
-        for(std::size_t k = 0; k < dimV; ++k)
-          re[j*dimV + k] += c_j*grad[k];
+  template <class T, class G, class J, class RG>
+  void print_error(T const& partial_i, std::size_t comp, G const& grad_i, J const& jacobian, RG const& referenceGradient) const
+  {
+    msg(__PRETTY_FUNCTION__);
+    msg("jacobian = {}", jacobian);
+    auto&& jacobian_mat = Dune::MatVec::as_matrix(jacobian);
+    msg("jacobian_ = [");
+    for (std::size_t i = 0; i < jacobian_mat.N(); ++i) {
+      for (std::size_t j = 0; j < jacobian_mat.M(); ++j) {
+        msg_("{} ",jacobian_mat[i][j]);
       }
+      msg(";");
     }
-  });
+    msg("]");
 
-  return dy;
-}
+    msg("ref-grad = {}", referenceGradient);
+    auto grad_ = Dune::Functions::flatVectorView(grad_i);
+    msg("comp = {}, grad_ = ({}, {}), partial = {}", comp, grad_[0], grad_[1], partial_i);
+  }
+};
 
 
 template <class GB, class VT, class TP>
diff --git a/src/amdis/gridfunctions/FunctorGridFunction.hpp b/src/amdis/gridfunctions/FunctorGridFunction.hpp
index 2054b342..3c6fe3c4 100644
--- a/src/amdis/gridfunctions/FunctorGridFunction.hpp
+++ b/src/amdis/gridfunctions/FunctorGridFunction.hpp
@@ -8,7 +8,9 @@
 #include <amdis/Operations.hpp>
 #include <amdis/common/ForEach.hpp>
 #include <amdis/common/Logical.hpp>
-#include <amdis/gridfunctions/GridFunctionConcepts.hpp>
+#include <amdis/gridfunctions/Derivative.hpp>
+#include <amdis/gridfunctions/GridFunction.hpp>
+#include <amdis/gridfunctions/Order.hpp>
 
 namespace AMDiS
 {
@@ -120,9 +122,9 @@ namespace AMDiS
    * **Requirements:**
    * - The Functor `F` must model `Concepts::HasPartial`
    **/
-  template <class Sig, class F, class... LFs,
+  template <class Sig, class F, class... LFs, class Type,
     REQUIRES(Concepts::HasPartial<F>)>
-  auto derivative(FunctorLocalFunction<Sig,F,LFs...> const& lf)
+  auto derivative(FunctorLocalFunction<Sig,F,LFs...> const& lf, Type const& type)
   {
     auto index_seq = std::index_sequence_for<LFs...>{};
 
@@ -135,7 +137,7 @@ namespace AMDiS
 
       using std::get;
       auto const& lgfs_i = *get<VALUE(_i)>(lf.localFcts());
-      return makeFunctorGridFunction(Operation::Multiplies{}, di_f, derivative(lgfs_i));
+      return makeFunctorGridFunction(Operation::Multiplies{}, di_f, derivative(lgfs_i, type));
     };
 
     // sum_i [ d_i(f)[lgfs...] * derivative(lgfs_i)
@@ -226,7 +228,7 @@ namespace AMDiS
       return std::get<0>(gridFcts_).entitySet();
     }
 
-    LocalFunction localFunction() const
+    LocalFunction makeLocalFunction() const
     {
       return Tools::apply([&](auto const&... gridFcts) { return LocalFunction{fct_, gridFcts...}; }, gridFcts_);
     }
@@ -237,16 +239,6 @@ namespace AMDiS
   };
 
 
-  /// \fn localFunction
-  /// \brief Creates a LocalFunction from the LocalFunctions of the GridFunctions.
-  /// \relates FunctorLocalFunction
-  template <class F, class... GFs>
-  auto localFunction(FunctorGridFunction<F,GFs...> const& gf)
-  {
-    return gf.localFunction();
-  }
-
-
   // Generator function for FunctorGridFunction expressions
   template <class Functor, class... GridFcts>
   auto makeFunctorGridFunction(Functor const& f, GridFcts const&... gridFcts)
diff --git a/src/amdis/gridfunctions/GridFunctionConcepts.hpp b/src/amdis/gridfunctions/GridFunction.hpp
similarity index 62%
rename from src/amdis/gridfunctions/GridFunctionConcepts.hpp
rename to src/amdis/gridfunctions/GridFunction.hpp
index 14c62890..434ec4c6 100644
--- a/src/amdis/gridfunctions/GridFunctionConcepts.hpp
+++ b/src/amdis/gridfunctions/GridFunction.hpp
@@ -4,16 +4,29 @@
 
 #include <amdis/common/Concepts.hpp>
 #include <amdis/common/Logical.hpp>
+#include <amdis/gridfunctions/Derivative.hpp>
+#include <amdis/gridfunctions/Order.hpp>
 
 namespace AMDiS
 {
+  // The localFunction of a GridFunction
+  template <class GridFunction,
+    class = void_t<decltype(std::declval<GridFunction>().makeLocalFunction())> >
+  auto localFunction(GridFunction const& gf)
+  {
+    return gf.makeLocalFunction();
+  }
+
+
   namespace Traits
   {
     template <class T>
     struct IsPreGridFunction
       : std::false_type {};
+
   } // end namespace Traits
 
+
   namespace Concepts
   {
     /** \addtogroup Concepts
@@ -28,36 +41,6 @@ namespace AMDiS
         auto requires_(F&& f) -> decltype( localFunction(f) );
       };
 
-      struct HasDerivative
-      {
-        template <class F>
-        auto requires_(F&& f) -> decltype( derivative(f) );
-      };
-
-      struct HasLocalFunctionDerivative
-      {
-        template <class F>
-        auto requires_(F&& f) -> decltype( derivative(localFunction(f)) );
-      };
-
-      struct HasLocalFunctionOrder
-      {
-        template <class F>
-        auto requires_(F&& f) -> decltype( order(localFunction(f)) );
-      };
-
-      struct HasPartial
-      {
-        template <class F>
-        auto requires_(F&& f) -> decltype( partial(f, index_<0>) );
-      };
-
-      struct HasOrder
-      {
-        template <class F>
-        auto requires_(F&& f) -> decltype( order(f) );
-      };
-
       struct HasGridFunctionTypes
       {
         template <class GF>
@@ -67,10 +50,6 @@ namespace AMDiS
           typename GF::EntitySet >;
       };
 
-      template <class F, int dow>
-      constexpr bool CallableDow =
-        Concepts::Callable<F, Dune::FieldVector<double, dow>>;
-
     } // end namespace Definition
 
 
@@ -78,38 +57,6 @@ namespace AMDiS
     template <class GF>
     constexpr bool HasLocalFunction = models<Definition::HasLocalFunction(GF)>;
 
-    /// \brief GridFunction GF has free function `derivative(F)`
-    template <class GF>
-    constexpr bool HasDerivative = models<Definition::HasDerivative(GF)>;
-
-    /// \brief GridFunction GF has free function `derivative(localFunction(F))`
-    template <class GF>
-    constexpr bool HasLocalFunctionDerivative = models<Definition::HasLocalFunctionDerivative(GF)>;
-
-    /// \brief GridFunction GF has free function `order(localFunction(F))`
-    template <class GF>
-    constexpr bool HasLocalFunctionOrder = models<Definition::HasLocalFunctionOrder(GF)>;
-
-    /// \brief Functor F has free function `partial(F,_0)`
-    template <class F>
-    constexpr bool HasPartial = models<Definition::HasPartial(F)>;
-
-    /// \brief LocalFuncion LF has free function `order(F)`
-    template <class LF>
-    constexpr bool HasOrder = models<Definition::HasOrder(LF)>;
-
-
-    /// \brief Functor F is collable with GlobalCoordinates `F(Dune::FieldVector<double,DOW>)`
-#ifndef WORLDDIM
-    template <class F>
-    constexpr bool CallableDomain =
-      Definition::CallableDow<F, 1> || Definition::CallableDow<F, 2> || Definition::CallableDow<F, 3>;
-#else
-    template <class F>
-    constexpr bool CallableDomain =
-      Definition::CallableDow<F, WORLDDIM>;
-#endif
-
     /// \brief GridFunction GF is a Type that has LocalFunction and provides some
     /// typedefs for `Domain`, `Range`, and `EntitySet`.
     template <class GF>
@@ -127,23 +74,6 @@ namespace AMDiS
 
   } // end namespace Concepts
 
-  template <class Traits,
-    bool valid = !std::is_same<typename Traits::Range, Dune::Functions::InvalidRange>::value>
-  constexpr bool isValidRange()
-  {
-    static_assert(valid, "Traits::Range is invalid");
-    return valid;
-  }
-
-
-  // polynomial order of local functions
-  template <class LocalFunction,
-    class = void_t<decltype(std::declval<LocalFunction>().order())> >
-  int order(LocalFunction const& lf)
-  {
-    return lf.order();
-  }
-
 
 #ifndef DOXYGEN
   template <class PreGridFct, class = void>
diff --git a/src/amdis/gridfunctions/Order.hpp b/src/amdis/gridfunctions/Order.hpp
new file mode 100644
index 00000000..ff2cef28
--- /dev/null
+++ b/src/amdis/gridfunctions/Order.hpp
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <amdis/common/Concepts.hpp>
+
+namespace AMDiS
+{
+  // polynomial order of local functions
+  template <class LocalFunction,
+    class = void_t<decltype(std::declval<LocalFunction>().order())> >
+  int order(LocalFunction const& lf)
+  {
+    return lf.order();
+  }
+
+
+  namespace Concepts
+  {
+    /** \addtogroup Concepts
+     *  @{
+     **/
+
+    namespace Definition
+    {
+      struct HasLocalFunctionOrder
+      {
+        template <class F>
+        auto requires_(F&& f) -> decltype( order(localFunction(f)) );
+      };
+
+      struct HasOrder
+      {
+        template <class F>
+        auto requires_(F&& f) -> decltype( order(f) );
+      };
+
+    } // end namespace Definition
+
+
+    /// \brief GridFunction GF has free function `order(localFunction(F))`
+    template <class GF>
+    constexpr bool HasLocalFunctionOrder = models<Definition::HasLocalFunctionOrder(GF)>;
+
+    /// \brief LocalFuncion LF has free function `order(F)`
+    template <class LF>
+    constexpr bool HasOrder = models<Definition::HasOrder(LF)>;
+
+    /** @} **/
+
+  } // end namespace Concepts
+
+} // end namespace AMDiS
diff --git a/src/amdis/localoperators/FirstOrderPartialTest.hpp b/src/amdis/localoperators/FirstOrderPartialTest.hpp
index 46fcb9f3..eb0192ef 100644
--- a/src/amdis/localoperators/FirstOrderPartialTest.hpp
+++ b/src/amdis/localoperators/FirstOrderPartialTest.hpp
@@ -43,40 +43,22 @@ namespace AMDiS
       static_assert(Node::isLeaf, "Operator can be applied to Leaf-Nodes only.");
 
       auto const& quad = this->getQuadratureRule(contextGeo.type(), node);
-      auto const& localFE = node.finiteElement();
-      std::size_t feSize = localFE.size();
+      auto localBasis = makeLocalToGlobalBasisAdapter(node, contextGeo.geometry());
 
-      using RangeFieldType = typename NodeQuadCache<Node>::Traits::RangeFieldType;
-      NodeQuadCache<Node> cache(localFE.localBasis());
-
-      auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(contextGeo, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = contextGeo.local(quad[iq].position());
 
-        // The transposed inverse Jacobian of the map from the reference element to the element
-        const auto jacobian = contextGeo.geometry().jacobianInverseTransposed(local);
-        assert(jacobian.M() == CG::dim);
-
         // The multiplicative factor in the integral transformation formula
-        const auto dx = contextGeo.integrationElement(quad[iq].position()) * quad[iq].weight();
-        const auto c = Super::coefficient(local);
-
-        // The gradients of the shape functions on the reference element
-        auto const& shapeGradients = shapeGradientsCache[iq];
+        auto dx = contextGeo.integrationElement(quad[iq].position()) * quad[iq].weight();
+        dx *= Super::coefficient(local);
 
         // Compute the shape function gradients on the real element
-        thread_local std::vector<RangeFieldType> partial;
-        partial.resize(shapeGradients.size());
-        for (std::size_t i = 0; i < partial.size(); ++i) {
-          partial[i] = jacobian[comp_][0] * shapeGradients[i][0][0];
-          for (std::size_t j = 1; j < jacobian.M(); ++j)
-            partial[i] += jacobian[comp_][j] * shapeGradients[i][0][j];
-        }
+        auto const& partial = localBasis.partialsAt(local, comp_);
 
-        for (std::size_t j = 0; j < feSize; ++j) {
+        for (std::size_t j = 0; j < localBasis.size(); ++j) {
           const auto local_j = node.localIndex(j);
-          elementVector[local_j] += dx * (c * partial[j]);
+          elementVector[local_j] += dx * partial[j];
         }
       }
     }
diff --git a/src/amdis/localoperators/FirstOrderTestPartialTrial.hpp b/src/amdis/localoperators/FirstOrderTestPartialTrial.hpp
index b3d67ff3..08a5c6f5 100644
--- a/src/amdis/localoperators/FirstOrderTestPartialTrial.hpp
+++ b/src/amdis/localoperators/FirstOrderTestPartialTrial.hpp
@@ -3,9 +3,9 @@
 #include <type_traits>
 
 #include <amdis/GridFunctionOperator.hpp>
-#include <amdis/utility/LocalBasisCache.hpp>
 #include <amdis/Output.hpp>
 #include <amdis/common/StaticSize.hpp>
+#include <amdis/utility/LocalToGlobalAdapter.hpp>
 
 namespace AMDiS
 {
@@ -51,49 +51,30 @@ namespace AMDiS
         "Operator can be applied to Leaf-Nodes only.");
 
       auto const& quad = this->getQuadratureRule(contextGeo.type(), rowNode, colNode);
+
       auto const& rowLocalFE = rowNode.finiteElement();
-      auto const& colLocalFE = colNode.finiteElement();
       std::size_t rowSize = rowLocalFE.size();
-      std::size_t colSize = colLocalFE.size();
-
-      using RangeFieldType = typename NodeQuadCache<CN>::Traits::RangeFieldType;
-
       NodeQuadCache<RN> rowCache(rowLocalFE.localBasis());
-      NodeQuadCache<CN> colCache(colLocalFE.localBasis());
 
+      auto colLocalBasis = makeLocalToGlobalBasisAdapter(colNode, contextGeo.geometry());
       auto const& shapeValuesCache = rowCache.evaluateFunctionAtQP(contextGeo, quad);
-      auto const& shapeGradientsCache = colCache.evaluateJacobianAtQP(contextGeo, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = contextGeo.local(quad[iq].position());
 
-        // The transposed inverse Jacobian of the map from the reference element to the element
-        const auto jacobian = contextGeo.geometry().jacobianInverseTransposed(local);
-        auto&& jacobian_mat = Dune::MatVec::as_matrix(jacobian);
-        assert(jacobian.M() == CG::dim);
-
         // The multiplicative factor in the integral transformation formula
-        const auto factor = contextGeo.integrationElement(quad[iq].position()) * quad[iq].weight();
-        const auto c = Super::coefficient(local);
+        auto factor = contextGeo.integrationElement(quad[iq].position()) * quad[iq].weight();
+        factor *= Super::coefficient(local);
 
         // the values of the shape functions on the reference element at the quadrature point
         auto const& shapeValues = shapeValuesCache[iq];
 
-        // The gradients of the shape functions on the reference element
-        auto const& shapeGradients = shapeGradientsCache[iq];
-
         // Compute the shape function gradients on the real element
-        thread_local std::vector<RangeFieldType> colPartial;
-        colPartial.resize(shapeGradients.size());
-        for (std::size_t i = 0; i < colPartial.size(); ++i) {
-          colPartial[i] = jacobian_mat[comp_][0] * shapeGradients[i][0][0];
-          for (std::size_t j = 1; j < jacobian_mat.M(); ++j)
-            colPartial[i] += jacobian_mat[comp_][j] * shapeGradients[i][0][j];
-        }
+        auto const& colPartial = colLocalBasis.partialsAt(local, comp_);
 
-        for (std::size_t j = 0; j < colSize; ++j) {
+        for (std::size_t j = 0; j < colLocalBasis.size(); ++j) {
           const auto local_j = colNode.localIndex(j);
-          const auto value = factor * (c * colPartial[j]);
+          const auto value = factor * colPartial[j];
           for (std::size_t i = 0; i < rowSize; ++i) {
             const auto local_i = rowNode.localIndex(i);
             elementMatrix[local_i][local_j] += value * shapeValues[i];
diff --git a/src/amdis/localoperators/SecondOrderPartialTestPartialTrial.hpp b/src/amdis/localoperators/SecondOrderPartialTestPartialTrial.hpp
index 32dd3b3e..1724a6a9 100644
--- a/src/amdis/localoperators/SecondOrderPartialTestPartialTrial.hpp
+++ b/src/amdis/localoperators/SecondOrderPartialTestPartialTrial.hpp
@@ -3,9 +3,9 @@
 #include <type_traits>
 
 #include <amdis/GridFunctionOperator.hpp>
-#include <amdis/utility/LocalBasisCache.hpp>
 #include <amdis/Output.hpp>
 #include <amdis/common/StaticSize.hpp>
+#include <amdis/utility/LocalToGlobalAdapter.hpp>
 
 namespace AMDiS
 {
@@ -44,60 +44,28 @@ namespace AMDiS
     template <class CG, class RN, class CN, class Mat>
     void getElementMatrix(CG const& contextGeo, RN const& rowNode, CN const& colNode, Mat& elementMatrix)
     {
-      static_assert(RN::isLeaf && CN::isLeaf,
-        "Operator can be applied to Leaf-Nodes only.");
+      static_assert(RN::isLeaf && CN::isLeaf, "Operator can be applied to Leaf-Nodes only.");
 
       auto const& quad = this->getQuadratureRule(contextGeo.type(), rowNode, colNode);
-      auto const& rowLocalFE = rowNode.finiteElement();
-      auto const& colLocalFE = colNode.finiteElement();
-      std::size_t rowSize = rowLocalFE.size();
-      std::size_t colSize = colLocalFE.size();
 
-      using RowFieldType = typename NodeQuadCache<RN>::Traits::RangeFieldType;
-      using ColFieldType = typename NodeQuadCache<CN>::Traits::RangeFieldType;
+      auto rowLocalBasis = makeLocalToGlobalBasisAdapter(rowNode, contextGeo.geometry());
+      auto colLocalBasis = makeLocalToGlobalBasisAdapter(colNode, contextGeo.geometry());
 
-      NodeQuadCache<RN> rowCache(rowLocalFE.localBasis());
-      NodeQuadCache<CN> colCache(colLocalFE.localBasis());
-
-      auto const& rowGradientsCache = rowCache.evaluateJacobianAtQP(contextGeo, quad);
-      auto const& colGradientsCache = colCache.evaluateJacobianAtQP(contextGeo, quad);
       for (std::size_t iq = 0; iq < quad.size(); ++iq) {
         // Position of the current quadrature point in the reference element
         decltype(auto) local = contextGeo.local(quad[iq].position());
 
-        // The transposed inverse Jacobian of the map from the reference element to the element
-        const auto jacobian = contextGeo.geometry().jacobianInverseTransposed(local);
-        auto&& jacobian_mat = Dune::MatVec::as_matrix(jacobian);
-
         // The multiplicative factor in the integral transformation formula
-        const auto factor = contextGeo.integrationElement(quad[iq].position()) * quad[iq].weight();
-        const auto c = Super::coefficient(local);
-
-        // The gradients of the shape functions on the reference element
-        auto const& rowShapeGradients = rowGradientsCache[iq];
-        auto const& colShapeGradients = colGradientsCache[iq];
-
-        // Compute the shape function gradients on the real element
-        thread_local std::vector<RowFieldType> rowPartial;
-        rowPartial.resize(rowShapeGradients.size());
-        for (std::size_t i = 0; i < rowPartial.size(); ++i) {
-          rowPartial[i] = jacobian_mat[compTest_][0] * rowShapeGradients[i][0][0];
-          for (std::size_t j = 1; j < jacobian_mat.M(); ++j)
-            rowPartial[i] += jacobian_mat[compTest_][j] * rowShapeGradients[i][0][j];
-        }
+        auto factor = contextGeo.integrationElement(quad[iq].position()) * quad[iq].weight();
+        factor *= Super::coefficient(local);
 
-        thread_local std::vector<ColFieldType> colPartial;
-        colPartial.resize(colShapeGradients.size());
-        for (std::size_t i = 0; i < colPartial.size(); ++i) {
-          colPartial[i] = jacobian_mat[compTrial_][0] * colShapeGradients[i][0][0];
-          for (std::size_t j = 1; j < jacobian_mat.M(); ++j)
-            colPartial[i] += jacobian_mat[compTrial_][j] * colShapeGradients[i][0][j];
-        }
+        auto const& rowPartial = rowLocalBasis.partialsAt(local, compTest_);
+        auto const& colPartial = colLocalBasis.partialsAt(local, compTrial_);
 
-        for (std::size_t j = 0; j < colSize; ++j) {
+        for (std::size_t j = 0; j < colLocalBasis.size(); ++j) {
           const auto local_j = colNode.localIndex(j);
-          const auto value = factor * (c * colPartial[j]);
-          for (std::size_t i = 0; i < rowSize; ++i) {
+          const auto value = factor * colPartial[j];
+          for (std::size_t i = 0; i < rowLocalBasis.size(); ++i) {
             const auto local_i = rowNode.localIndex(i);
             elementMatrix[local_i][local_j] += value * rowPartial[i];
           }
diff --git a/src/amdis/localoperators/ZeroOrderTest.hpp b/src/amdis/localoperators/ZeroOrderTest.hpp
index a6bf83bf..a064d44f 100644
--- a/src/amdis/localoperators/ZeroOrderTest.hpp
+++ b/src/amdis/localoperators/ZeroOrderTest.hpp
@@ -51,8 +51,8 @@ namespace AMDiS
         decltype(auto) local = contextGeo.local(quad[iq].position());
 
         // The multiplicative factor in the integral transformation formula
-        const auto factor = Super::coefficient(local) * contextGeo.integrationElement(quad[iq].position())
-                                                      * quad[iq].weight();
+        auto factor = contextGeo.integrationElement(quad[iq].position()) * quad[iq].weight();
+        factor *= Super::coefficient(local);
 
         auto const& shapeValues = shapeValuesCache[iq];
         for (std::size_t i = 0; i < size; ++i) {
diff --git a/src/amdis/utility/LocalBasisCache.hpp b/src/amdis/utility/LocalBasisCache.hpp
index 1ee17656..3ef62785 100644
--- a/src/amdis/utility/LocalBasisCache.hpp
+++ b/src/amdis/utility/LocalBasisCache.hpp
@@ -68,6 +68,10 @@ namespace AMDiS
       : localBasis_(&localBasis)
     {}
 
+    NodeCache(Node const& node)
+      : NodeCache(node.finiteElement().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.
@@ -113,6 +117,12 @@ namespace AMDiS
     LocalBasisType const* localBasis_ = nullptr;
   };
 
+  /// Generator function for \ref NodeCache, \relates NodeCache.
+  template <class Node>
+  NodeCache<Node> makeNodeCache(Node const& node)
+  {
+    return NodeCache<Node>{node};
+  }
 
   /// \brief Cache of LocalBasis evaluations and jacobians at quadrature points
   /**
@@ -174,6 +184,10 @@ namespace AMDiS
       : localBasis_(&localBasis)
     {}
 
+    NodeQuadCache(Node const& node)
+      : NodeQuadCache(node.finiteElement().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.
@@ -223,4 +237,11 @@ namespace AMDiS
     LocalBasisType const* localBasis_ = nullptr;
   };
 
+  /// Generator function for \ref NodeQuadCache, \relates NodeQuadCache.
+  template <class Node>
+  NodeQuadCache<Node> makeNodeQuadCache(Node const& node)
+  {
+    return NodeQuadCache<Node>{node};
+  }
+
 } // end namespace AMDiS
diff --git a/src/amdis/utility/LocalToGlobalAdapter.hpp b/src/amdis/utility/LocalToGlobalAdapter.hpp
new file mode 100644
index 00000000..b0df3522
--- /dev/null
+++ b/src/amdis/utility/LocalToGlobalAdapter.hpp
@@ -0,0 +1,207 @@
+#pragma once
+
+#include <cstddef>
+#include <vector>
+
+#include <dune/common/fmatrix.hh>
+#include <dune/common/fvector.hh>
+#include <dune/common/typetraits.hh>
+#include <dune/geometry/type.hh>
+
+#include <amdis/common/DerivativeTraits.hpp>
+#include <amdis/common/FieldMatVec.hpp>
+#include <amdis/utility/LocalBasisCache.hpp>
+
+namespace AMDiS
+{
+  /// Traits class for local-to-global basis adaptors
+  /**
+   * \tparam LocalBasisTraits Traits class of the LocalBasis to be adapted.
+   * \tparam dimGlobal        Dimension of the global coordinates,
+   *                          i.e. Geometry::coorddimension, if the global
+   *                          coordinates are determined by a Geometry.
+   *
+   * \implements BasisInterface::Traits
+   */
+  template <class LocalBasisTraits, std::size_t dimGlobal>
+  struct LocalToGlobalBasisAdapterTraits
+  {
+    static const std::size_t dimDomainLocal = LocalBasisTraits::dimDomain;
+    static const std::size_t dimDomainGlobal = dimGlobal;
+    static const std::size_t dimRange = LocalBasisTraits::dimRange;
+
+    using DomainField = typename LocalBasisTraits::DomainFieldType;
+    using DomainLocal = typename LocalBasisTraits::DomainType;
+    using DomainGlobal = FieldVector<DomainField, dimDomainGlobal>;
+
+    using RangeField = typename LocalBasisTraits::RangeFieldType;
+    using Range = typename LocalBasisTraits::RangeType;
+
+    using GradientRange = typename DerivativeTraits<Range(DomainGlobal), tag::gradient>::Range;
+    using PartialRange = typename DerivativeTraits<Range(DomainGlobal), tag::partial>::Range;
+  };
+
+  /// \brief Convert a simple (scalar) local basis into a global basis
+  /**
+   * The local basis must be scalar, i.e. LocalBasis::Traits::dimRange must be 1
+   * It's values are not transformed.
+   *
+   * For scalar function \f$f\f$, the gradient is equivalent to the transposed
+   * Jacobian \f$\nabla f|_x = J_f^T(x)\f$.  The Jacobian is thus transformed
+   * using
+   * \f[
+   *   \nabla f|_{\mu(\hat x)} =
+   *       \hat J_\mu^{-T}(\hat x) \cdot \hat\nabla\hat f|_{\hat x}
+   * \f]
+   * Here the hat \f$\hat{\phantom x}\f$ denotes local quantities and
+   * \f$\mu\f$ denotes the local-to-global map of the geometry.
+   *
+   * \tparam BasisNode  Type of the typetree node containing a local basis to adopt.
+   * \tparam Geometry   Type of the local-to-global transformation.
+   *
+   * NOTE: The adapter implements a caching of local basis evaluations at coordinates.
+   */
+  template <class BasisNode, class Geometry>
+  class LocalToGlobalBasisAdapter
+  {
+    using LocalBasis = typename BasisNode::FiniteElement::Traits::LocalBasisType;
+
+    static_assert(std::is_same<typename LocalBasis::Traits::DomainFieldType, typename Geometry::ctype>::value,
+      "LocalToGlobalBasisAdapter: LocalBasis must use the same ctype as Geometry");
+
+    static_assert(std::size_t(LocalBasis::Traits::dimDomain) == std::size_t(Geometry::mydimension),
+      "LocalToGlobalBasisAdapter: LocalBasis domain dimension must match local dimension of Geometry");
+
+  public:
+    using Cache = NodeCache<BasisNode>;
+    using Traits = LocalToGlobalBasisAdapterTraits<typename LocalBasis::Traits, Geometry::coorddimension>;
+
+    /// \biref Construct a LocalToGlobalBasisAdapter
+    /**
+     * \param node     The basis node in the typetree containing the local basis to adopt.
+     * \param geometry The geometry object to use for adaption.
+     *
+     * \note This class stores the references passed here.  Any use of this
+     *       class after these references have become invalid results in
+     *       undefined behaviour.  The exception is that the destructor of
+     *       this class may still be called.
+     */
+    LocalToGlobalBasisAdapter(BasisNode const& node, Geometry const& geometry)
+      : localBasis_(node.finiteElement().localBasis())
+      , localBasisCache_(localBasis_)
+      , geometry_(geometry)
+      , size_(localBasis_.size())
+    {}
+
+    /// Return the number of local basis functions
+    std::size_t size() const { return size_; }
+
+    /// \brief Return maximum polynomial order of the base function
+    /**
+     * This is to determine the required quadrature order.  For an affine
+     * geometry this is the same order as for the local basis.  For other
+     * geometries this returns the order of the local basis plus the global
+     * dimension minus 1.  The assumption for non-affine geometries is that
+     * they are still multi-linear.
+     */
+    std::size_t order() const
+    {
+      if (geometry_.affine())
+        // affine linear
+        return localBasis_.order();
+      else
+        // assume at most order dim
+        return localBasis_.order() + Traits::dimDomainGlobal - 1;
+    }
+
+    /// Evaluate the local basis functions in the local coordinate `in`
+    void evaluateFunction(typename Traits::DomainLocal const& x,
+                          std::vector<typename Traits::Range>& out) const
+    {
+      out = localBasisCache_.evaluateFunction(geometry_.type(), x);
+    }
+
+    /// Evaluate the local basis functions in the local coordinate `in` and
+    /// return the result using a reference to a thread_local (or static) vector.
+    auto const& valuesAt(typename Traits::DomainLocal const& x) const
+    {
+      return localBasisCache_.evaluateFunction(geometry_.type(), x);
+    }
+
+    /// Return the full (global) gradient of the local basis functions in
+    /// the local coordinate `in`
+    void evaluateGradient(typename Traits::DomainLocal const& x,
+                          std::vector<typename Traits::GradientRange>& out) const
+    {
+      auto const& localJacobian = localBasisCache_.evaluateJacobian(geometry_.type(), x);
+      auto const& geoJacobian = geometry_.jacobianInverseTransposed(x);
+
+      out.resize(size_);
+      for (std::size_t i = 0; i < size_; ++i)
+        geoJacobian.mv(Dune::MatVec::as_vector(localJacobian[i]),
+                       Dune::MatVec::as_vector(out[i]));
+    }
+
+    /// Return the full (global) gradient of the local basis functions in
+    /// the local coordinate `in` and return the result using a reference
+    /// to a thread_local (or static) vector.
+    auto const& gradientsAt(typename Traits::DomainLocal const& x) const
+    {
+      thread_local std::vector<typename Traits::GradientRange> grad;
+      evaluateGradient(x, grad);
+      return grad;
+    }
+
+    /// Return the (global) partial derivative in direction `comp` of the
+    /// local basis functions in the local coordinate `in`
+    void evaluatePartial(typename Traits::DomainLocal const& x,
+                         std::size_t comp,
+                         std::vector<typename Traits::PartialRange>& out) const
+    {
+      auto const& localJacobian = localBasisCache_.evaluateJacobian(geometry_.type(), x);
+      // NOTE: geoJacobian might be a Dune::DiagonalMatrix with limited interface!
+      auto const& geoJacobian = geometry_.jacobianInverseTransposed(x);
+
+      out.resize(size_);
+      typename Traits::GradientRange grad;
+      auto&& grad_ = Dune::MatVec::as_vector(grad);
+      for (std::size_t i = 0; i < size_; ++i) {
+        geoJacobian.mv(Dune::MatVec::as_vector(localJacobian[i]), grad_);
+        out[i] = grad_[comp];
+      }
+    }
+
+    /// Return the (global) partial derivative in direction `comp` of the
+    /// local basis functions in the local coordinate `in` and return the
+    /// result using a reference to a thread_local (or static) vector.
+    auto const& partialsAt(typename Traits::DomainLocal const& x, std::size_t comp) const
+    {
+      thread_local std::vector<typename Traits::PartialRange> d_comp;
+      evaluatePartial(x, comp, d_comp);
+      return d_comp;
+    }
+
+  private:
+    /// Reference to the local basis bound to the adapter
+    LocalBasis const& localBasis_;
+
+    /// A basis cache for the evaluation at local coordinates
+    Cache localBasisCache_;
+
+    /// Reference to the geometry this adapter is bound to
+    Geometry const& geometry_;
+
+    /// The number of basis functions
+    std::size_t size_;
+  };
+
+
+  /// Generator function for the \ref LocalToGlobalBasisAdapter. \relates LocalToGlobalBasisAdapter.
+  template <class BasisNode, class Geometry>
+  LocalToGlobalBasisAdapter<BasisNode, Geometry>
+  makeLocalToGlobalBasisAdapter(BasisNode const& node, Geometry const& geometry)
+  {
+    return LocalToGlobalBasisAdapter<BasisNode, Geometry>{node, geometry};
+  }
+
+} // namespace AMDiS
diff --git a/src/amdis/utility/QuadratureFactory.hpp b/src/amdis/utility/QuadratureFactory.hpp
index 865c6cd8..cf326b53 100644
--- a/src/amdis/utility/QuadratureFactory.hpp
+++ b/src/amdis/utility/QuadratureFactory.hpp
@@ -5,7 +5,7 @@
 #include <dune/geometry/quadraturerules.hh>
 #include <dune/geometry/type.hh>
 
-#include <amdis/GridFunctions.hpp>
+#include <amdis/gridfunctions/Order.hpp>
 
 namespace AMDiS
 {
@@ -40,7 +40,7 @@ namespace AMDiS
 
 
   template <class LF>
-  using HasLocalFunctionOrder = decltype( AMDiS::order(std::declval<LF>()) );
+  using HasLocalFunctionOrder = decltype( order(std::declval<LF>()) );
 
 
   /// \brief Factory for quadrature rule, that calculates the coefficient order from
@@ -81,14 +81,6 @@ namespace AMDiS
     return QuadFactoryFromLocalFunction<ctype, dim, LocalFunction>{};
   }
 
-  template <class ctype, int dim, class LocalFunction>
-  auto makeQuadratureFactoryPtr(PreQuadFactoryFromLocalFunction const& /*pre*/)
-  {
-    using Factory = QuadFactoryFromLocalFunction<ctype, dim, LocalFunction>;
-    return std::make_unique<Factory>();
-  }
-
-
 
   /// \brief Factory for quadrature rule, that takes to order of the localFunction
   /// and a quadrature-type
@@ -137,14 +129,6 @@ namespace AMDiS
     return QuadFactoryFromOrder<ctype, dim, LocalFunction>{pre.order, pre.qt};
   }
 
-  template <class ctype, int dim, class LocalFunction>
-  auto makeQuadratureFactoryPtr(PreQuadFactoryFromOrder const& pre)
-  {
-    using Factory = QuadFactoryFromOrder<ctype, dim, LocalFunction>;
-    return std::make_unique<Factory>(pre.order, pre.qt);
-  }
-
-
 
   /// \brief Factory for quadrature rule, that is based on an existing rule
   template <class ctype, int dim, class LocalFunction>
@@ -187,11 +171,4 @@ namespace AMDiS
     return QuadFactoryFromRule<ctype, dim, LocalFunction>{pre.rule};
   }
 
-  template <class ctype, int dim, class LocalFunction, class QuadRule>
-  auto makeQuadratureFactoryPtr(PreQuadFactoryFromRule<QuadRule> const& pre)
-  {
-    using Factory = QuadFactoryFromRule<ctype, dim, LocalFunction>;
-    return std::make_unique<Factory>(pre.rule);
-  }
-
 } // end namespace AMDiS
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index fe265d45..282214b3 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -46,6 +46,9 @@ dune_add_test(SOURCES FiniteElementTypeTest.cpp
 dune_add_test(SOURCES FilesystemTest.cpp
   LINK_LIBRARIES amdis)
 
+dune_add_test(SOURCES GradientTest.cpp
+  LINK_LIBRARIES amdis)
+
 dune_add_test(SOURCES HybridSizeTest.cpp
   LINK_LIBRARIES amdis)
 
diff --git a/test/DiscreteFunctionTest.cpp b/test/DiscreteFunctionTest.cpp
index e330eaaf..143811dd 100644
--- a/test/DiscreteFunctionTest.cpp
+++ b/test/DiscreteFunctionTest.cpp
@@ -75,9 +75,9 @@ int main(int argc, char** argv)
   AMDIS_TEST( comp(U0, U1) );
   AMDIS_TEST( comp(U0, U2) );
 
-  auto du0 = derivative(u0);
+  auto du0 = derivative(u0, tag::gradient{});
   auto localFct = localFunction(u0);
-  auto dlocalFct = derivative(localFct);
+  auto dlocalFct = derivative(localFct, tag::gradient{});
   for (auto const& e : elements(prob.gridView()))
   {
     localFct.bind(e);
diff --git a/test/GradientTest.cpp b/test/GradientTest.cpp
new file mode 100644
index 00000000..17b62282
--- /dev/null
+++ b/test/GradientTest.cpp
@@ -0,0 +1,91 @@
+// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// vi: set et ts=4 sw=2 sts=2:
+
+#include <iostream>
+
+#include <amdis/AMDiS.hpp>
+#include <amdis/LinearAlgebra.hpp>
+#include <amdis/GridFunctions.hpp>
+#include <amdis/Integrate.hpp>
+
+#include "Tests.hpp"
+
+using namespace AMDiS;
+
+template <std::size_t p>
+void test()
+{
+  using Grid = Dune::YaspGrid<2>;
+  using Basis1 = LagrangeBasis<Grid,p,p>;
+
+  Grid grid({1.0, 1.0}, {16,16});
+  auto gridView = grid.leafGridView();
+  auto basis = Basis1::create(gridView);
+
+  auto uVector = makeDOFVector(basis);
+
+  auto u = makeDOFVectorView(uVector);
+  auto u_0 = makeDOFVectorView(uVector, 0);
+  auto u_1 = makeDOFVectorView(uVector, 1);
+
+  // eval a functor at global coordinates (at quadrature points)
+  u_0 << evalAtQP([](auto const& x) { return x[0] + x[1]; });
+  u_1 << evalAtQP([](auto const& x) { return -2*x[0] + 3*x[1]; });
+
+  // test gradient
+  AMDIS_TEST_APPROX((std::sqrt(integrate(unary_dot(gradientAtQP(u_0)), gridView))), std::sqrt(2.0));
+  AMDIS_TEST_APPROX((std::sqrt(integrate(unary_dot(gradientAtQP(u_1)), gridView))), std::sqrt(13.0));
+
+  // test divergence
+  AMDIS_TEST_APPROX((integrate(divergenceAtQP(u), gridView)), 4.0);
+
+  // test partial derivative
+  double d0u_0 = integrate(partialAtQP(u_0,0), gridView);
+  double d1u_1 = integrate(partialAtQP(u_1,1), gridView);
+  AMDIS_TEST_APPROX(d0u_0, 1.0);
+  AMDIS_TEST_APPROX(d1u_1, 3.0);
+
+  auto gf_d0u_0 = makeGridFunction(partialAtQP(u_0,0), gridView);
+  auto lf_d0u_0 = localFunction(gf_d0u_0);
+  for (auto const& e : elements(gridView)) {
+    lf_d0u_0.bind(e);
+    double v = lf_d0u_0(e.geometry().center());
+    AMDIS_TEST_APPROX(v, 1.0);
+    lf_d0u_0.unbind();
+  }
+
+  u_0 << evalAtQP([](auto const& x) { return std::sin(x[0]) + std::cos(x[1]); });
+  u_1 << evalAtQP([](auto const& x) { return -std::sin(x[0]) * std::cos(x[1]); });
+
+  AMDIS_TEST_APPROX((integrate(divergenceAtQP(u), gridView)), (integrate(partialAtQP(u_0,0) + partialAtQP(u_1,1), gridView)));
+
+  auto vVector(uVector);
+  auto v = makeDOFVectorView(vVector);
+  auto v_0 = makeDOFVectorView(vVector, 0);
+  auto v_1 = makeDOFVectorView(vVector, 1);
+
+  // test gradient
+  v << evalAtQP([](auto const& x) { return Dune::FieldVector<double,2>{std::cos(x[0]), -std::sin(x[1])}; });
+  AMDIS_TEST((std::sqrt(integrate(unary_dot(v - gradientAtQP(u_0)), gridView))) < 0.05);
+
+  // test divergence
+  v_0 << evalAtQP([](auto const& x) { return std::cos(x[0]) + std::sin(x[0])*std::sin(x[1]); });
+  AMDIS_TEST((integrate(v_0 - divergenceAtQP(u), gridView)) < 0.05);
+
+  // test partial derivative
+  v_0 << evalAtQP([](auto const& x) { return std::cos(x[0]); });
+  v_1 << evalAtQP([](auto const& x) { return std::sin(x[0])*std::sin(x[1]); });
+  AMDIS_TEST((integrate(v_0 - partialAtQP(u_0,0), gridView)) < 0.05);
+  AMDIS_TEST((integrate(v_1 - partialAtQP(u_1,1), gridView)) < 0.05);
+}
+
+int main(int argc, char** argv)
+{
+  Environment env(argc, argv);
+
+  test<1u>();
+  test<2u>();
+  test<3u>();
+
+  return report_errors();
+}
diff --git a/test/Tests.hpp b/test/Tests.hpp
index c44226e5..319e565d 100644
--- a/test/Tests.hpp
+++ b/test/Tests.hpp
@@ -129,7 +129,7 @@ namespace AMDiS
 
     // implementation for ranges
     template <class T1, class T2,
-      class = decltype(((void)std::declval<T1>().cbegin(), (void)std::declval<T2>().cbegin())) >
+      class = decltype(std::declval<T1>().cbegin(), std::declval<T2>().cbegin()) >
     inline void _test_approx(T1 const& expr_range, T2 const& range,
                              char const* expr_str, char const* value_str, char const* file, size_t line)
     {
-- 
GitLab