diff --git a/dune/gfe/linearalgebra.hh b/dune/gfe/linearalgebra.hh
index 1a08a863cbfb905f8dd8f2edd568bc7ea5485edb..57a1c66ae8016329459649e28c2781f39711615b 100644
--- a/dune/gfe/linearalgebra.hh
+++ b/dune/gfe/linearalgebra.hh
@@ -1,6 +1,8 @@
 #ifndef DUNE_GFE_LINEARALGEBRA_HH
 #define DUNE_GFE_LINEARALGEBRA_HH
 
+#include <random>
+
 #include <dune/common/fmatrix.hh>
 #include <dune/common/version.hh>
 #include <dune/istl/scaledidmatrix.hh>
@@ -134,6 +136,66 @@ namespace Dune {
   }
 #endif
 
+    /** \brief Return a segment of a FieldVector from lower up to lower+size-1 */
+    template< int lower, int size,typename field_type,int n>
+    static FieldVector<field_type,size> segment(const FieldVector<field_type,n>& v)
+    {
+      FieldVector<field_type,size> res;
+      std::copy(v.begin()+lower,v.begin()+lower+size,res.begin());
+      return res;
+    }
+
+    /** \brief Return a segment of a FieldVector from lower up to lower+size-1
+     * lower is unkown at compile time*/
+    template< int size,typename field_type,int n>
+    static FieldVector<field_type,size> segmentAt(const FieldVector<field_type,n>& v,const size_t lower)
+    {
+      FieldVector<field_type,size> res;
+      std::copy(v.begin()+lower,v.begin()+lower+size,res.begin());
+      return res;
+    }
+
+    /** \brief Return a block of a FieldMatrix  (lower1...lower1+size1-1,lower2...lower2+size2-1 */
+    template< int lower1, int size1, int lower2,int size2,typename field_type,int n,int m>
+    static auto block(const FieldMatrix<field_type,n,m>& v)
+    {
+      static_assert(lower1+size1<=n && lower2+size2<=m, "Size mismatch for Block!");
+      FieldMatrix<field_type,size1,size2> res;
+
+      for(int i=lower1; i<lower1+size1; ++i)
+        for(int j=lower2; j<lower2+size2; ++j)
+          res[i-lower1][j-lower2] = v[i][j];
+      return res;
+    }
+
+        /** \brief Return a block of a FieldMatrix  (lower1...lower1+size1-1,lower2...lower2+size2-1
+         * * lower1 and lower2 is unkown at compile time*/
+        template< int size1,int size2,typename field_type,int n,int m>
+        static auto blockAt(const FieldMatrix<field_type,n,m>& v, const size_t& lower1, const size_t& lower2)
+        {
+            assert(lower1+size1<=n && lower2+size2<=m);
+            FieldMatrix<field_type,size1,size2> res;
+
+            for(size_t i=lower1; i<lower1+size1; ++i)
+                for(size_t j=lower2; j<lower2+size2; ++j)
+                    res[i-lower1][j-lower2] = v[i][j];
+            return res;
+        }
+
+    /** \brief Generates FieldVector with random entries in the range -1..1 */
+    template<typename field_type,int n>
+    auto randomFieldVector(field_type lower=-1, field_type upper=1)
+    {
+      std::random_device rd;
+      std::mt19937 mt(rd());
+      std::uniform_real_distribution<field_type> dist(lower, upper);
+      auto rand = [&dist,&mt](){
+                    return dist(mt);
+                  };
+      FieldVector<field_type,n> vec;
+      std::generate(vec.begin(), vec.end(), rand);
+      return vec;
+    }
   }
 }
 
diff --git a/dune/gfe/productmanifold.hh b/dune/gfe/productmanifold.hh
new file mode 100644
index 0000000000000000000000000000000000000000..48e86df8a9d44ce355f9ab6715a91b9c8b5f8d3f
--- /dev/null
+++ b/dune/gfe/productmanifold.hh
@@ -0,0 +1,503 @@
+#ifndef DUNE_GFE_PRODUCTMANIFOLD_HH
+#define DUNE_GFE_PRODUCTMANIFOLD_HH
+
+#include <iostream>
+#include <tuple>
+
+#include <dune/common/fvector.hh>
+#include <dune/common/math.hh>
+#include <dune/common/hybridutilities.hh>
+#include <dune/common/tuplevector.hh>
+#include <dune/common/power.hh>
+
+#include <dune/gfe/linearalgebra.hh>
+
+namespace Dune::GFE
+{
+  namespace Impl
+  {
+    template<typename T, typename ... Ts>
+    constexpr auto sumDim()
+    {
+      return (T::dim + ... + sumDim<Ts>());
+    }
+
+    template<typename T, typename ... Ts>
+    constexpr auto sumEmbeddedDim()
+    {
+      return (T::embeddedDim + ... + sumEmbeddedDim<Ts>());
+    }
+
+    template<typename T, typename ... Ts>
+    constexpr auto variadicConvexityRadiusMin()
+    {
+      if constexpr (sizeof...(Ts)!=0)
+        return std::min(T::convexityRadius ,  variadicConvexityRadiusMin<Ts ...>());
+      else
+        return T::convexityRadius;
+    }
+
+    template <typename TS, typename ... TargetSpaces>
+    class ProductManifold;
+
+    template<class U,typename Tfirst,typename ... TargetSpaces2>
+    struct rebindHelper
+    {
+        typedef ProductManifold<typename Tfirst::template rebind<U>::other ,typename rebindHelper<U,TargetSpaces2...>::other> other;
+    };
+
+    template<class U,typename Tlast>
+    struct rebindHelper<U,Tlast>
+    {
+        typedef  typename Tlast::template rebind<U>::other other;
+    };
+  }
+
+  /** \brief A Product manifold */
+  template <typename TS, typename ... TargetSpaces>
+  class ProductManifold
+  {
+  public:
+    template<std::size_t I>
+    using IC = Dune::index_constant<I>;
+    /** \brief The type used for coordinates. */
+    using ctype = typename std::common_type<typename TS::ctype, typename TargetSpaces::ctype...>::type ;
+    using field_type =  typename std::common_type<typename TS::ctype, typename TargetSpaces::ctype...>::type ;
+
+    /** \brief Number of factors */
+    static constexpr int numTS = 1 + sizeof...(TargetSpaces);
+
+    /** \brief Dimension of manifold */
+    static constexpr int dim = TS::dim + Impl::sumDim<TargetSpaces ...>();
+
+    /** \brief Dimension of the embedding space */
+    static constexpr int embeddedDim = TS::embeddedDim + Impl::sumEmbeddedDim<TargetSpaces ...>();
+
+    /** \brief Type of a tangent of the ProductManifold with inner dimensions*/
+    typedef Dune::FieldVector<field_type, dim> TangentVector;
+
+    /** \brief Type of a tangent of the ProductManifold represented in the embedding space*/
+    typedef Dune::FieldVector<field_type, embeddedDim> EmbeddedTangentVector;
+
+    /** \brief The global convexity radius of the Product space */
+    static constexpr double convexityRadius = Impl::variadicConvexityRadiusMin<TS,TargetSpaces ...>();
+
+    /** \brief The type used for global coordinates */
+    typedef Dune::FieldVector<field_type ,embeddedDim> CoordinateType;
+
+    /** \brief Default constructor */
+    ProductManifold()    = default;
+
+    /** \brief Constructor from another ProductManifold */
+    ProductManifold(const ProductManifold<TS,TargetSpaces ...>& productManifold)
+      : data_(productManifold.data_)
+    {}
+
+    /** \brief Constructor from a coordinates vector of the embedding space */
+    explicit ProductManifold(const CoordinateType& globalCoordinates)
+    {
+      DUNE_ASSERT_BOUNDS(globalCoordinates.size()== sumEmbeddedDim)
+      auto constructorFunctor =[]  (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& res = std::get<0>(argsTuple)[manifoldInt];
+          const auto& globalCoords =std::get<1>(argsTuple);
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(res)> >;
+          res = Manifold(Dune::GFE::segmentAt<Manifold::embeddedDim>(globalCoords,posHelper[0]));
+          posHelper[0] += Manifold::embeddedDim;
+      };
+      foreachManifold(constructorFunctor,*this,globalCoordinates);
+    }
+
+    /** \brief Assignment from a coordinates vector of the embedding space */
+    ProductManifold<TS,TargetSpaces ...>& operator=(const CoordinateType& globalCoordinates)
+    {
+      ProductManifold<TS,TargetSpaces ...> res(globalCoordinates);
+      data_ = res.data_;
+      return *this;
+    }
+
+    /** \brief Assignment from ProductManifold with different type -- used for automatic differentiation with ADOL-C */
+    template <typename TS2, typename ... TargetSpaces2>
+    ProductManifold<TS,TargetSpaces ...>& operator <<=(const ProductManifold<TS2,TargetSpaces2 ...>& other)
+    {
+      forEach(integralRange(IC<numTS>()), [&](auto&& i) {
+        (*this)[i] <<= other[i];
+      });
+      return *this;
+    }
+
+    /** \brief Rebind the ProductManifold to another coordinate type using an embedded tangent vector*/
+    template<class U,typename ... TargetSpaces2>
+    struct rebind
+    {
+      typedef  typename Impl::rebindHelper<U,TS,TargetSpaces...>::other other;
+    };
+
+    /** \brief The exponential map from a given point. */
+    static ProductManifold<TS,TargetSpaces ...> exp(const ProductManifold<TS,TargetSpaces ...>& p, const EmbeddedTangentVector& v)
+    {
+      ProductManifold<TS,TargetSpaces ...> res;
+      auto expFunctor =[]  (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& res        = std::get<0>(argsTuple)[manifoldInt];
+          const auto& p    = std::get<1>(argsTuple)[manifoldInt];
+          const auto& tang = std::get<2>(argsTuple);
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(res)> >;
+          auto currentEmbeddedTangentVector = Dune::GFE::segmentAt<Manifold::embeddedDim>(tang,posHelper[0]);
+          res =  Manifold::exp(p,currentEmbeddedTangentVector);
+          posHelper[0] += Manifold::embeddedDim;
+      };
+      foreachManifold(expFunctor,res,p,v);
+      return res;
+    }
+
+    /** \brief The exponential map from a given point using an intrinsic tangent vector*/
+    static ProductManifold<TS,TargetSpaces ...> exp(const ProductManifold<TS,TargetSpaces ...>& p, const TangentVector& v)
+    {
+      auto basis = p.orthonormalFrame();
+      EmbeddedTangentVector embeddedTangent;
+      basis.mtv(v, embeddedTangent);
+      return exp(p,embeddedTangent);
+    }
+
+    /** \brief Compute difference vector from a to b on the tangent space of a */
+    static TangentVector log(const ProductManifold<TS,TargetSpaces...>& a, const ProductManifold<TS,TargetSpaces...>& b)
+    {
+      TangentVector diff;
+      auto logFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& res     = std::get<0>(argsTuple);
+          const auto& a = std::get<1>(argsTuple)[manifoldInt];
+          const auto& b = std::get<2>(argsTuple)[manifoldInt];
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >;
+          const auto diffLoc =  Manifold::log(a,b);
+          std::copy(diffLoc.begin(),diffLoc.end(),res.begin()+posHelper[0]);
+          posHelper[0] += Manifold::dim;
+      };
+      foreachManifold(logFunctor,diff,a, b);
+      return diff;
+    }
+
+    /** \brief Compute geodesic distance from a to b */
+    static field_type distance(const ProductManifold<TS,TargetSpaces...>& a, const ProductManifold<TS,TargetSpaces...>& b)
+    {
+      field_type dist=0.0;
+      auto distanceFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& res     = std::get<0>(argsTuple);
+          const auto& a = std::get<1>(argsTuple)[manifoldInt];
+          const auto& b = std::get<2>(argsTuple)[manifoldInt];
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >;
+          res += Dune::Power<2>().eval(Manifold::distance(a,b));
+      };
+      foreachManifold(distanceFunctor,dist,a, b);
+      return sqrt(dist);
+    }
+
+    /** \brief Compute the gradient of the squared distance function keeping the first argument fixed */
+    static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const ProductManifold<TS,TargetSpaces...>& a,
+                                                                              const ProductManifold<TS,TargetSpaces...>& b)
+    {
+      EmbeddedTangentVector derivative;
+      derivative= 0.0;
+
+      auto derivOfDistSqdWRTSecArgFunctor = [] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& derivative = std::get<0>(argsTuple);
+          const auto& a    = std::get<1>(argsTuple)[manifoldInt];
+          const auto& b    = std::get<2>(argsTuple)[manifoldInt];
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >;
+          const auto diffLoc = Manifold::derivativeOfDistanceSquaredWRTSecondArgument(a,b);
+          std::copy(diffLoc.begin(),diffLoc.end(),derivative.begin()+posHelper[0]);
+          posHelper[0] += Manifold::embeddedDim;
+      };
+      foreachManifold(derivOfDistSqdWRTSecArgFunctor, derivative,a, b);
+      return derivative;
+    }
+
+    /** \brief Compute the Hessian of the squared distance function keeping the first argument fixed */
+    static auto secondDerivativeOfDistanceSquaredWRTSecondArgument(const ProductManifold<TS,TargetSpaces...>&  a,
+                                                                   const ProductManifold<TS, TargetSpaces...>& b)
+    {
+      Dune::SymmetricMatrix<field_type,embeddedDim> result;
+      auto secDerivOfDistSqWRTSecArgFunctor = [] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& deriv   = std::get<0>(argsTuple);
+          const auto& a = std::get<1>(argsTuple)[manifoldInt];
+          const auto& b = std::get<2>(argsTuple)[manifoldInt];
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >;
+          const auto diffLoc = Manifold::secondDerivativeOfDistanceSquaredWRTSecondArgument(a,b);
+          for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::embeddedDim; ++i )
+            for(size_t j=posHelper[0]; j<=i; ++j )
+                deriv(i,j)  = diffLoc(i - posHelper[0], j - posHelper[0]);
+          posHelper[0] += Manifold::embeddedDim;
+      };
+      foreachManifold(secDerivOfDistSqWRTSecArgFunctor,result,a, b);
+      return result;
+    }
+
+    /** \brief Compute the mixed second derivate \partial d^2 / \partial da db     */
+    static Dune::FieldMatrix<field_type ,embeddedDim,embeddedDim>
+    secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const ProductManifold<TS,TargetSpaces ...>&  a,
+                                                               const ProductManifold<TS, TargetSpaces ...>& b)
+    {
+      Dune::FieldMatrix<field_type,embeddedDim,embeddedDim> result(0);
+      auto secDerivOfDistSqWRTFirstAndSecArgFunctor = [] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto&   deriv = std::get<0>(argsTuple);
+          const auto& a = std::get<1>(argsTuple)[manifoldInt];
+          const auto& b = std::get<2>(argsTuple)[manifoldInt];
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >;
+          const auto diffLoc = Manifold::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(a,b);
+          for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::embeddedDim; ++i )
+            for(size_t j=posHelper[0]; j<posHelper[0]+Manifold::embeddedDim; ++j )
+                deriv[i][j] = diffLoc[i - posHelper[0]][ j - posHelper[0]];
+          posHelper[0] += Manifold::embeddedDim;
+      };
+      foreachManifold(secDerivOfDistSqWRTFirstAndSecArgFunctor,result,a, b);
+      return result;
+    }
+
+    /** \brief Compute the third derivative \partial d^3 / \partial dq^3  */
+    static Tensor3<field_type ,embeddedDim,embeddedDim,embeddedDim>
+    thirdDerivativeOfDistanceSquaredWRTSecondArgument(const ProductManifold<TS,TargetSpaces ...>&  a,
+                                                      const ProductManifold<TS, TargetSpaces ...>& b)
+    {
+      Tensor3<field_type,embeddedDim,embeddedDim,embeddedDim> result(0);
+      auto thirdDerivOfDistSqWRTSecArgFunctor =[](auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& deriv   = std::get<0>(argsTuple);
+          const auto& a = std::get<1>(argsTuple)[manifoldInt];
+          const auto& b = std::get<2>(argsTuple)[manifoldInt];
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)>>;
+          const auto diffLoc = Manifold::thirdDerivativeOfDistanceSquaredWRTSecondArgument(a,b);
+          for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::embeddedDim; ++i )
+              for(size_t j=posHelper[0]; j<posHelper[0]+Manifold::embeddedDim; ++j )
+                  for(size_t k=posHelper[0]; k<posHelper[0]+Manifold::embeddedDim; ++k )
+                      deriv[i][j][k] = diffLoc[i - posHelper[0]][ j - posHelper[0]][k - posHelper[0]];
+          posHelper[0] += Manifold::embeddedDim;
+      };
+      foreachManifold(thirdDerivOfDistSqWRTSecArgFunctor,result,a, b);
+      return result;
+    }
+
+    /** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2  */
+    static Tensor3<field_type ,embeddedDim,embeddedDim,embeddedDim>
+    thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const ProductManifold<TS,TargetSpaces ...>&  a,
+                                                                const ProductManifold<TS, TargetSpaces ...>& b)
+    {
+      Tensor3<field_type,embeddedDim,embeddedDim,embeddedDim> result(0);
+      auto thirdDerivOfDistSqWRT1And2ArgFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& deriv   = std::get<0>(argsTuple);
+          const auto& a = std::get<1>(argsTuple)[manifoldInt];
+          const auto& b = std::get<2>(argsTuple)[manifoldInt];
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)>>;
+          const auto diffLoc =  Manifold::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(a,b);
+          for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::embeddedDim; ++i )
+              for(size_t j=posHelper[0]; j<posHelper[0]+Manifold::embeddedDim; ++j )
+                  for(size_t k=posHelper[0]; k<posHelper[0]+Manifold::embeddedDim; ++k )
+                      deriv[i][j][k] = diffLoc[i - posHelper[0]][ j - posHelper[0]][k - posHelper[0]];
+          posHelper[0] += Manifold::embeddedDim;
+      };
+      foreachManifold(thirdDerivOfDistSqWRT1And2ArgFunctor,result,a, b);
+      return result;
+    }
+
+    /** \brief Project tangent vector of R^n onto the tangent space */
+    EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const
+    {
+      EmbeddedTangentVector result {v};
+      auto projectOntoTangentSpaceFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& v       = std::get<0>(argsTuple);
+          const auto& a = std::get<1>(argsTuple)[manifoldInt];
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >;
+          const auto vLoc = a.projectOntoTangentSpace(Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0]));
+          std::copy(vLoc.begin(),vLoc.end(),v.begin()+posHelper[0]);
+          posHelper[0] += Manifold::embeddedDim;
+      };
+      foreachManifold(projectOntoTangentSpaceFunctor,result,*this);
+      return result;
+    }
+
+    /** \brief Project tangent vector of R^n onto the normal space space */
+    EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const
+    {
+      EmbeddedTangentVector result {v};
+      auto projectOntoNormalSpaceFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& v       = std::get<0>(argsTuple);
+          const auto& a = std::get<1>(argsTuple)[manifoldInt];
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >;
+          const auto vLoc = a.projectOntoNormalSpace(Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0]));
+          std::copy(vLoc.begin(),vLoc.end(),v.begin()+posHelper[0]);
+          posHelper[0] += Manifold::embeddedDim;
+      };
+      foreachManifold(projectOntoNormalSpaceFunctor,result,*this);
+      return result;
+    }
+
+    /** \brief Project tangent vector of R^n onto the normal space space */
+    static ProductManifold<TS,TargetSpaces ...> projectOnto(const CoordinateType& v)
+    {
+      ProductManifold<TS,TargetSpaces ...> result {v};
+      auto projectOntoFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& res = std::get<0>(argsTuple)[manifoldInt];
+          auto& v   = std::get<1>(argsTuple);
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(res)> >;
+          res =  Manifold::projectOnto(Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0]));
+          posHelper[0] += Manifold::embeddedDim;
+      };
+      foreachManifold(projectOntoFunctor,result, v);
+      return result;
+    }
+
+    /** \brief Project tangent vector of R^n onto the normal space space */
+    static auto derivativeOfProjection(const CoordinateType& v)
+    {
+      Dune::FieldMatrix<typename CoordinateType::value_type, embeddedDim, embeddedDim> result;
+      auto derivativeOfProjectionFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& res     = std::get<0>(argsTuple);
+          const auto& v = std::get<1>(argsTuple);
+          const Dune::TupleVector<TS,TargetSpaces...> ManifoldTuple;
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(ManifoldTuple[manifoldInt])> >;
+          const auto vLoc =  Manifold::derivativeOfProjection(Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0]));
+          for(size_t k=posHelper[0]; k<posHelper[0]+Manifold::embeddedDim; ++k )
+              for(size_t j=posHelper[0]; j<posHelper[0]+Manifold::embeddedDim; ++j )
+                  res[k][j] = vLoc[k - posHelper[0]][j-posHelper[0]];
+          posHelper[0] += Manifold::embeddedDim;
+      };
+      foreachManifold(derivativeOfProjectionFunctor,result, v);
+      return result;
+    }
+
+    /** \brief The Weingarten map */
+    EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const
+    {
+      EmbeddedTangentVector result;
+      auto weingartenFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& res     = std::get<0>(argsTuple);
+          const auto& a = std::get<1>(argsTuple)[manifoldInt];
+          const auto& z = std::get<2>(argsTuple);
+          const auto& v = std::get<3>(argsTuple);
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >;
+          const auto resLoc =  a.weingarten(Dune::GFE::segmentAt<Manifold::embeddedDim>(z,posHelper[0]),
+                                            Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0]));
+          std::copy(resLoc.begin(),resLoc.end(),res.begin()+posHelper[0]);
+          posHelper[0] +=Manifold::embeddedDim;
+      };
+      foreachManifold(weingartenFunctor,result, *this,z, v);
+      return result;
+    }
+
+    /** \brief Compute an orthonormal basis of the tangent space of the ProductManifold
+       This basis may not be globally continuous.     */
+    Dune::FieldMatrix<field_type ,dim,embeddedDim> orthonormalFrame() const
+    {
+      Dune::FieldMatrix<field_type,dim,embeddedDim> result(0);
+      auto orthonormalFrameFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
+      {
+          auto& res     = std::get<0>(argsTuple);
+          const auto& a = std::get<1>(argsTuple)[manifoldInt];
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >;
+          const auto resLoc =  a.orthonormalFrame();
+          for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::dim; ++i )
+              for(size_t j=posHelper[1]; j<posHelper[1]+Manifold::embeddedDim; ++j )
+                  res[i][j] = resLoc[i - posHelper[0]][j-posHelper[1]];
+          posHelper[0] += Manifold::dim;
+          posHelper[1] += Manifold::embeddedDim;
+      };
+      foreachManifold(orthonormalFrameFunctor,result,*this);
+      return result;
+    }
+
+    /** \brief The global coordinates */
+    CoordinateType globalCoordinates() const
+    {
+      CoordinateType returnValue;
+      auto globalCoordinatesFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& i)
+      {
+          auto& res       = std::get<0>(argsTuple);
+          const auto& p   = std::get<1>(argsTuple)[i];
+          const auto vLoc =  p.globalCoordinates();
+          using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(p)> >;
+          std::copy(vLoc.begin(),vLoc.end(),res.begin()+posHelper[0]);
+          posHelper[0] += Manifold::embeddedDim;
+      };
+      foreachManifold(globalCoordinatesFunctor,returnValue,*this);
+      return returnValue;
+    }
+
+    /** \brief Const access to the tuple elements  */
+    template<std::size_t i>
+    constexpr decltype(auto) operator[] (const Dune::index_constant<i>&) const
+    {
+      return std::get<i>(data_);
+    }
+
+    /** \brief Non-const access to the tuple elements     */
+    template<std::size_t i>
+    decltype(auto) operator[] (const Dune::index_constant<i>&)
+    {
+      return std::get<i>(data_);
+    }
+
+    /** \brief Number of elements of the tuple */
+    static constexpr std::size_t size()
+    {
+      return numTS;
+    }
+
+    template<class TS2, class... TargetSpaces2>
+    friend std::ostream& operator<<(std::ostream& s, const ProductManifold<TS2, TargetSpaces2 ...>& c);
+
+  private:
+    /**
+     * \brief Range based for loop over all Manifolds in ProductManifold
+     *
+     * \tparam FunctorType the functor which should be applied for all manifolds
+     * \tparam Args... The arguments which are passed to the functor
+     *
+     * The functor has to extract the current manifold from its arguments.
+     * Therefore, the current index i is passed to the functor.
+     *
+     * Furthermore, the functor gets two integers to deal with FieldVectors
+     * or FieldMatrices which span the whole (embedding) coordinate range of the ProductManifold.
+     *
+     * Example:
+     * If a argument of the functor is
+     * ProductManifold<Realtuple<double,3>,UnitVector<double,3>>::EmbeddedTangentVector,
+     * the embedded coordinate vector has 6 entries. The functor needs to know where
+     * to insert the next subvector of the submanifold. Therefore, posHelper[0] should be 0 at the first
+     * iteration and 3 at the second one. Then first indices [0..2] stores the result of
+     * Realtuple<double,3> and [3..5] stores
+     * the result of UnitVector<double,3>.
+     * See e.g. the globalCoordinates() function
+     */
+    template<typename FunctorType, typename ... Args>
+    static void foreachManifold( FunctorType&& functor,Args&&... args)
+    {
+      auto argsTuple = std::forward_as_tuple(args ...);
+      std::array<std::size_t,2> posHelper({0,0});
+      Dune::Hybrid::forEach(Dune::Hybrid::integralRange(IC<numTS>()),[&](auto&& i) {
+          functor(argsTuple,posHelper,i);
+      });
+    }
+
+    std::tuple<TS,TargetSpaces ...> data_;
+};
+
+  template< typename TS,typename ... TargetSpaces>
+  std::ostream& operator<<(std::ostream& s, const ProductManifold<TS, TargetSpaces ...>& c)
+  {
+    Dune::Hybrid::forEach(Dune::Hybrid::integralRange(Dune::index_constant<ProductManifold<TS, TargetSpaces ...>::numTS>()), [&](auto&& i) {
+        s<<Dune::className<decltype(c[i])>()<<" "<< c[i]<<"\n";
+    });
+    return s;
+  }
+}
+#endif
diff --git a/dune/gfe/realtuple.hh b/dune/gfe/realtuple.hh
index c1f52db2e40acabdc6345ae94e88ed3beddf0616..e4bc4df8561ab9c5fff83bd529b29c7ffe97f61d 100644
--- a/dune/gfe/realtuple.hh
+++ b/dune/gfe/realtuple.hh
@@ -106,7 +106,14 @@ public:
 
     Simply the Euclidean distance */
     static T distance(const RealTuple& a, const RealTuple& b) {
-        return (a.data_ - b.data_).two_norm();
+        return log(a.data_,b.data_).two_norm();
+    }
+
+    /** \brief The logarithmic map
+     * Simply the difference vector for RealTuple
+     * */
+    static auto log(const RealTuple& a, const RealTuple& b) {
+        return a.data_ - b.data_;
     }
 
 #if ADOLC_ADOUBLE_H
@@ -215,6 +222,11 @@ public:
         return data_;
     }
 
+     Dune::FieldVector<T,N>& globalCoordinates()  {
+        return data_;
+    }
+
+
     /** \brief Compute an orthonormal basis of the tangent space of R^n.
 
     In general this frame field, may of course not be continuous, but for RealTuples it is.
diff --git a/dune/gfe/rotation.hh b/dune/gfe/rotation.hh
index 96ea13db09cfbd34112410fdb530091c8ede82f0..3ad4023be07c66fc0941471f44128c46d7a54799 100644
--- a/dune/gfe/rotation.hh
+++ b/dune/gfe/rotation.hh
@@ -146,7 +146,7 @@ class Rotation<T,3> : public Quaternion<T>
     /** \brief Computes sin(x/2) / x without getting unstable for small x */
     static T sincHalf(const T& x) {
         using std::sin;
-        return (x < 1e-4) ? 0.5 - (x*x/48)  + (x*x*x*x)/3840 : sin(x/2)/x;
+        return (x < 1e-1) ? 0.5 - (x*x/48)  + Dune::power(x,4)/3840 - Dune::power(x,6)/645120  : sin(x/2)/x;
     }
 
     /** \brief Computes sin(sqrt(x)/2) / sqrt(x) without getting unstable for small x
@@ -168,7 +168,15 @@ class Rotation<T,3> : public Quaternion<T>
     static T sincOfSquare(const T& x) {
         using std::sin;
         using std::sqrt;
-        return (x < 1e-15) ? 1 - (x/6)  + (x*x)/120 : sin(sqrt(x))/sqrt(x);
+        // we need here lots of terms to be sure that the numerical derivatives are also within maschine precission
+        return (x < 1e-2) ?
+        1-x/6
+        +x*x/120
+        -Dune::power(x,3)/5040
+        +Dune::power(x,4)/362880
+        -Dune::power(x,5)/39916800
+        +Dune::power(x,6)/6227020800
+        -Dune::power(x,7)/1307674368000: sin(sqrt(x))/sqrt(x);
     }
 
 public:
@@ -289,7 +297,7 @@ public:
         // hence the series of cos(x/2) is
         // 1 - x*x/8 + x*x*x*x/384 - ...
         q[3] = (normV2 < 1e-4)
-          ? 1 - normV2/8 + normV2*normV2 / 384
+          ? 1 - normV2/8 + normV2*normV2 / 384-Dune::power(normV2,3)/46080 + Dune::power(normV2,4)/10321920
           : cos(sqrt(normV2)/2);
 
         return q;
@@ -321,8 +329,8 @@ public:
 
         // The series expansion of cos(x) at x=0 is
         // 1 - x*x/2 + x*x*x*x/24 - ...
-        T cosValue = (norm2 < 1e-4)
-          ? 1 - norm2/2 + norm2*norm2 / 24
+        T cosValue = (norm2 < 1e-5)
+          ? 1 - norm2/2 + norm2*norm2 / 24 - Dune::power(norm2,3)/720+Dune::power(norm2,4)/40320
           : cos(sqrt(norm2));
         result *= cosValue;
 
@@ -604,7 +612,7 @@ public:
     /** \brief Compute the vector in T_aSO(3) that is mapped by the exponential map
         to the geodesic from a to b
     */
-    static SkewMatrix<T,3> difference(const Rotation<T,3>& a, const Rotation<T,3>& b) {
+    static SkewMatrix<T,3> log(const Rotation<T,3>& a, const Rotation<T,3>& b) {
 
         Quaternion<T> diff = a;
         diff.invert();
@@ -622,7 +630,7 @@ public:
 
             // TODO: ADOL-C does not like this part of the code,
             // because arccos is not differentiable at -1 and 1.
-            // (Even though the overall 'difference' function is differentiable.)
+            // (Even though the overall 'log' function is differentiable.)
             using std::acos;
             T dist = 2*acos( diff[3] );
 
@@ -635,7 +643,7 @@ public:
 
             T invSinc = 1/sincHalf(dist);
 
-            // Compute difference on T_a SO(3)
+            // Compute log on T_a SO(3)
             v[0] = diff[0] * invSinc;
             v[1] = diff[1] * invSinc;
             v[2] = diff[2] * invSinc;
@@ -907,8 +915,8 @@ public:
     /** \brief Interpolate between two rotations */
     static Rotation<T,3> interpolate(const Rotation<T,3>& a, const Rotation<T,3>& b, T omega) {
 
-        // Compute difference on T_a SO(3)
-        SkewMatrix<T,3> v = difference(a,b);
+        // Compute log on T_a SO(3)
+        SkewMatrix<T,3> v = log(a,b);
 
         v *= omega;
 
@@ -922,8 +930,8 @@ public:
                                                T omega) {
         Quaternion<T> result(0);
 
-        // Compute difference on T_a SO(3)
-        SkewMatrix<T,3> xi = difference(a,b);
+        // Compute log on T_a SO(3)
+        SkewMatrix<T,3> xi = log(a,b);
 
         SkewMatrix<T,3> v = xi;
         v *= omega;
diff --git a/dune/gfe/skewmatrix.hh b/dune/gfe/skewmatrix.hh
index 083cd19152d2ca588af070715cf2bec0042a3ef4..76614d7cfefdcb15bac52e3de919378f50f0f457 100644
--- a/dune/gfe/skewmatrix.hh
+++ b/dune/gfe/skewmatrix.hh
@@ -48,7 +48,34 @@ public:
     {
         return data_;
     }
-   
+
+    typedef typename Dune::FieldVector<T,3>::Iterator Iterator;
+
+    //! begin iterator
+    Iterator begin ()
+    {
+        return Iterator(data_,0);
+    }
+
+    //! end iterator
+    Iterator end ()
+    {
+        return Iterator(data_,3);
+    }
+
+    typedef typename Dune::FieldVector<T,3>::ConstIterator ConstIterator;
+    //! begin iterator
+    ConstIterator begin () const
+    {
+        return ConstIterator(data_,0);
+    }
+
+    //! end iterator
+    ConstIterator end () const
+    {
+        return ConstIterator(data_,3);
+    }
+
     /** \brief Embedd the skey-symmetric matrix in R^3x3 */ 
     Dune::FieldMatrix<T,3,3> toMatrix() const
     {
diff --git a/dune/gfe/unitvector.hh b/dune/gfe/unitvector.hh
index 90e498086693e6df381a8e0b9ccc6c9b4ba6f295..7122d1cda48f90638b8072f0211a6f1482a40baf 100644
--- a/dune/gfe/unitvector.hh
+++ b/dune/gfe/unitvector.hh
@@ -26,15 +26,24 @@ class UnitVector
     /** \brief Computes sin(x) / x without getting unstable for small x */
     static T sinc(const T& x) {
         using std::sin;
-        return (x < 1e-4) ? 1 - (x*x/6) : sin(x)/x;
+        return (x < 1e-2) ? 1.0-x*x/6.0+ Dune::power(x,4)/120.0-Dune::power(x,6)/5040.0+Dune::power(x,8)/362880.0 : sin(x)/x;
     }
 
     /** \brief Compute arccos^2 without using the (at 1) nondifferentiable function acos x close to 1 */
     static T arcCosSquared(const T& x) {
         using std::acos;
-        const T eps = 1e-4;
-        if (x > 1-eps) {  // acos is not differentiable, use the series expansion instead
-            return -2 * (x-1) + 1.0/3 * (x-1)*(x-1) - 4.0/45 * (x-1)*(x-1)*(x-1);
+        const T eps = 1e-2;
+        if (x > 1-eps) {  // acos is not differentiable, use the series expansion instead,
+            // we need here lots of terms to be sure that the numerical derivatives are also within maschine precission
+            //return -2 * (x-1) + 1.0/3 * (x-1)*(x-1) - 4.0/45 * (x-1)*(x-1)*(x-1);
+            return 11665028.0/4729725.0
+            -141088.0/45045.0*x
+            +   413.0/429.0*x*x
+            -  5344.0/12285.0*Dune::power(x,3)
+            +    245.0/1287.0*Dune::power(x,4)
+            -  1632.0/25025.0*Dune::power(x,5)
+            +     56.0/3861.0*Dune::power(x,6)
+            -    32.0/21021.0*Dune::power(x,7);
         } else {
             return Dune::power(acos(x),2);
         }
@@ -44,9 +53,19 @@ class UnitVector
     static T derivativeOfArcCosSquared(const T& x) {
         using std::acos;
         using std::sqrt;
-        const T eps = 1e-4;
+        const T eps = 1e-2;
         if (x > 1-eps) {  // regular expression is unstable, use the series expansion instead
-            return -2 + 2*(x-1)/3 - 4/15*(x-1)*(x-1);
+            // we need here lots of terms to be sure that the numerical derivatives are also within maschine precission
+            //return -2 + 2*(x-1)/3 - 4/15*(x-1)*(x-1);
+            return -47104.0/15015.0
+            +12614.0/6435.0*x
+            -63488.0/45045.0*x*x
+            + 1204.0/1287.0*Dune::power(x,3)
+            - 2048.0/4095.0*Dune::power(x,4)
+            +   112.0/585.0*Dune::power(x,5)
+            -2048.0/45045.0*Dune::power(x,6)
+            +   32.0/6435.0*Dune::power(x,7);
+
         } else if (x < -1+eps) {  // The function is not differentiable
             DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!");
         } else
@@ -57,9 +76,20 @@ class UnitVector
     static T secondDerivativeOfArcCosSquared(const T& x) {
         using std::acos;
         using std::pow;
-        const T eps = 1e-4;
+        const T eps = 1e-2;
         if (x > 1-eps) {  // regular expression is unstable, use the series expansion instead
-            return 2.0/3 - 8*(x-1)/15;
+            // we need here lots of terms to be sure that the numerical derivatives are also within maschine precission
+            //return 2.0/3 - 8*(x-1)/15;
+            return 1350030.0/676039.0+5632.0/2028117.0*Dune::power(x,10)
+            -1039056896.0/334639305.0*x
+            +150876.0/39767.0*x*x
+            -445186048.0/111546435.0*Dune::power(x,3)
+            +       343728.0/96577.0*Dune::power(x,4)
+            -  57769984.0/22309287.0*Dune::power(x,5)
+            +      710688.0/482885.0*Dune::power(x,6)
+            -  41615360.0/66927861.0*Dune::power(x,7)
+            +     616704.0/3380195.0*Dune::power(x,8)
+            -     245760.0/7436429.0*Dune::power(x,9);
         } else if (x < -1+eps) {  // The function is not differentiable
             DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!");
         } else
@@ -70,10 +100,22 @@ class UnitVector
     static T thirdDerivativeOfArcCosSquared(const T& x) {
         using std::acos;
         using std::sqrt;
-        const T eps = 1e-4;
+        const T eps = 1e-2;
         if (x > 1-eps) {  // regular expression is unstable, use the series expansion instead
-            return -8.0/15 + 24*(x-1)/35;
+            // we need here lots of terms to be sure that the numerical derivatives are also within maschine precission
+            //return -8.0/15 + 24*(x-1)/35;
+            return -1039056896.0/334639305.0
+            +301752.0/39767.0*x
+            -445186048.0/37182145.0*x*x
+            +1374912.0/96577.0*Dune::power(x,3)
+            -288849920.0/22309287.0*Dune::power(x,4)
+            +4264128.0/482885.0*Dune::power(x,5)
+            -41615360.0/9561123.0*Dune::power(x,6)
+            +4933632.0/3380195.0*Dune::power(x,7)
+            -2211840.0/7436429.0*Dune::power(x,8)
+            +56320.0/2028117.0*Dune::power(x,9);
         } else if (x < -1+eps) {  // The function is not differentiable
+
             DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!");
         } else {
             T d = 1-x*x;
diff --git a/test/averagedistanceassemblertest.cc b/test/averagedistanceassemblertest.cc
index 983b9707daf25fcac5cef1a211b83decdb4ea79c..03f97eac08b6ed3f0d99d94295ff3feb636123ea 100644
--- a/test/averagedistanceassemblertest.cc
+++ b/test/averagedistanceassemblertest.cc
@@ -9,6 +9,7 @@
 #include <dune/gfe/rotation.hh>
 #include <dune/gfe/realtuple.hh>
 #include <dune/gfe/unitvector.hh>
+#include <dune/gfe/productmanifold.hh>
 
 #include <dune/gfe/localgeodesicfefunction.hh>
 
@@ -229,9 +230,29 @@ void testRotations()
 }
 
 
+void testProductManifold()
+{
+    typedef Dune::GFE::ProductManifold<RealTuple<double,5>,UnitVector<double,3>, Rotation<double,3>> TargetSpace;
+
+    std::vector<TargetSpace> corners(dim+1);
+
+    std::generate(corners.begin(), corners.end(), []()  {
+        return Dune::GFE::randomFieldVector<typename TargetSpace::field_type,TargetSpace::CoordinateType::dimension>(0.9,1.1);
+    });
+
+    TargetSpace argument = corners[0];
+    testWeightSet(corners, argument);
+    argument = corners[1];
+    testWeightSet(corners, argument);
+    argument = corners[2];
+    testWeightSet(corners, argument);
+}
+
+
 int main()
 {
     testRealTuples();
     testUnitVectors();
     testRotations();
+    testProductManifold();
 }
diff --git a/test/localgeodesicfefunctiontest.cc b/test/localgeodesicfefunctiontest.cc
index a2fa30ba11d9fd40d18180f39a36141ee3fef364..613fbda902bda20a398d7e7b313e40990108f89a 100644
--- a/test/localgeodesicfefunctiontest.cc
+++ b/test/localgeodesicfefunctiontest.cc
@@ -14,6 +14,7 @@
 #include <dune/gfe/rotation.hh>
 #include <dune/gfe/realtuple.hh>
 #include <dune/gfe/unitvector.hh>
+#include <dune/gfe/productmanifold.hh>
 
 #include <dune/gfe/localgeodesicfefunction.hh>
 #include "multiindex.hh"
@@ -303,6 +304,8 @@ int main()
     test<UnitVector<double,3>,1>(GeometryTypes::simplex(1));
     test<Rotation<double,3>,1>(GeometryTypes::simplex(1));
     test<RigidBodyMotion<double,3>,1>(GeometryTypes::simplex(1));
+    typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold;
+    test<CrazyManifold,1>(GeometryTypes::simplex(1));
     
     ////////////////////////////////////////////////////////////////
     //  Test functions on 2d simplex elements
@@ -313,6 +316,8 @@ int main()
     test<UnitVector<double,3>,2>(GeometryTypes::simplex(2));
     test<Rotation<double,3>,2>(GeometryTypes::simplex(2));
     test<RigidBodyMotion<double,3>,2>(GeometryTypes::simplex(2));
+    typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold;
+    test<CrazyManifold,2>(GeometryTypes::simplex(2));
 
     ////////////////////////////////////////////////////////////////
     //  Test functions on 2d quadrilateral elements
@@ -323,5 +328,7 @@ int main()
     test<UnitVector<double,3>,2>(GeometryTypes::cube(2));
     test<Rotation<double,3>,2>(GeometryTypes::cube(2));
     test<RigidBodyMotion<double,3>,2>(GeometryTypes::cube(2));
+    typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold;
+    test<CrazyManifold,2>(GeometryTypes::cube(2));
 
 }
diff --git a/test/localgfetestfunctiontest.cc b/test/localgfetestfunctiontest.cc
index 4b9769c95f7348e66363ed16f9eaa88d8ec5ae38..2890f50e383039173b7d4581a44368bd93525866 100644
--- a/test/localgfetestfunctiontest.cc
+++ b/test/localgfetestfunctiontest.cc
@@ -10,6 +10,7 @@
 #include <dune/gfe/rotation.hh>
 #include <dune/gfe/realtuple.hh>
 #include <dune/gfe/unitvector.hh>
+#include <dune/gfe/productmanifold.hh>
 
 #include <dune/gfe/localgeodesicfefunction.hh>
 #include <dune/gfe/localgfetestfunctionbasis.hh>
@@ -79,6 +80,8 @@ int main() try
         
     test<Rotation<double,3>, 1>();
     test<Rotation<double,3>, 2>();
+    typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold;
+    test<CrazyManifold, 2>();
 
 } catch (Exception& e) {
     std::cout << e << std::endl;
diff --git a/test/localprojectedfefunctiontest.cc b/test/localprojectedfefunctiontest.cc
index 5d1fa21fe65589709880d0aab13e31543cc5304f..e9e7a2e52a5fe2991ba47d352ed23973c8de096f 100644
--- a/test/localprojectedfefunctiontest.cc
+++ b/test/localprojectedfefunctiontest.cc
@@ -15,6 +15,7 @@
 #include <dune/gfe/rotation.hh>
 #include <dune/gfe/realtuple.hh>
 #include <dune/gfe/unitvector.hh>
+#include <dune/gfe/productmanifold.hh>
 
 #include <dune/gfe/localprojectedfefunction.hh>
 #include "multiindex.hh"
@@ -105,6 +106,20 @@ void testDerivativeTangentiality(const RigidBodyMotion<double,3>& x,
 {
 }
 
+// the columns of the derivative must be tangential to the manifold
+template <int domainDim, int vectorDim,typename ...TargetSpaces>
+void testDerivativeTangentiality(const Dune::GFE::ProductManifold<TargetSpaces...>& x,
+                                 const FieldMatrix<double,vectorDim,domainDim>& derivative)
+{
+    size_t posHelper=0;
+    using namespace Dune::Hybrid;
+    forEach(integralRange(Dune::Hybrid::size(x)), [&](auto&& i) {
+        using Manifold = std::remove_reference_t<decltype(x[i])>;
+        testDerivativeTangentiality(x[i],Dune::GFE::blockAt<Manifold::embeddedDim,domainDim>( derivative,posHelper,0));
+        posHelper +=Manifold::embeddedDim;
+    });
+}
+
 /** \brief Test whether interpolation is invariant under permutation of the simplex vertices
  * \todo Implement this for all dimensions
  */
@@ -256,6 +271,8 @@ int main()
     test<UnitVector<double,3>,1>(GeometryTypes::line);
     test<Rotation<double,3>,1>(GeometryTypes::line);
     test<RigidBodyMotion<double,3>,1>(GeometryTypes::line);
+    typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold;
+    test<CrazyManifold, 1>(GeometryTypes::line);
 
     ////////////////////////////////////////////////////////////////
     //  Test functions on 2d simplex elements
@@ -267,6 +284,8 @@ int main()
     test<UnitVector<double,3>,2>(GeometryTypes::triangle);
     test<Rotation<double,3>,2>(GeometryTypes::triangle);
     test<RigidBodyMotion<double,3>,2>(GeometryTypes::triangle);
+    typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold;
+    test<CrazyManifold, 2>(GeometryTypes::triangle);
 
     ////////////////////////////////////////////////////////////////
     //  Test functions on 2d quadrilateral elements
@@ -277,5 +296,7 @@ int main()
     test<UnitVector<double,3>,2>(GeometryTypes::quadrilateral);
     test<Rotation<double,3>,2>(GeometryTypes::quadrilateral);
     test<RigidBodyMotion<double,3>,2>(GeometryTypes::quadrilateral);
+    typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold;
+    test<CrazyManifold, 2>(GeometryTypes::quadrilateral);
 
 }
diff --git a/test/targetspacetest.cc b/test/targetspacetest.cc
index cadfe4818e3f3c0c8bfce2ff69c7b0e22acaa9cb..b5e1c650bf1e91361fb1bf8fd7111ac40414bff8 100644
--- a/test/targetspacetest.cc
+++ b/test/targetspacetest.cc
@@ -3,6 +3,7 @@
 #include <dune/gfe/unitvector.hh>
 #include <dune/gfe/realtuple.hh>
 #include <dune/gfe/rotation.hh>
+#include <dune/gfe/productmanifold.hh>
 #include <dune/gfe/hyperbolichalfspacepoint.hh>
 
 #include "valuefactory.hh"
@@ -138,8 +139,8 @@ void testDerivativeOfSquaredDistance(const TargetSpace& a, const TargetSpace& b)
     // transform into embedded coordinates
     typename TargetSpace::EmbeddedTangentVector d2_fd_embedded;
     B.mtv(d2_fd,d2_fd_embedded);
-    
-    if ( (d2 - d2_fd_embedded).infinity_norm() > 100*eps ) {
+
+    if ( (d2 - d2_fd_embedded).infinity_norm() > 200*eps ) {
         std::cout << className(a) << ": Analytical gradient does not match fd approximation." << std::endl;
         std::cout << "d2 Analytical: " << d2 << std::endl;
         std::cout << "d2 FD        : " << d2_fd << std::endl;
@@ -156,14 +157,14 @@ void testHessianOfSquaredDistance(const TargetSpace& a, const TargetSpace& b)
     ///////////////////////////////////////////////////////////////////
     //  Test second derivative with respect to second argument
     ///////////////////////////////////////////////////////////////////
-    FieldMatrix<double,embeddedDim,embeddedDim> d2d2 = TargetSpace::secondDerivativeOfDistanceSquaredWRTSecondArgument(a, b);
-    
+    FieldMatrix<double,embeddedDim,embeddedDim> d2d2 = (TargetSpace::secondDerivativeOfDistanceSquaredWRTSecondArgument(a, b)).matrix();
+
     // finite-difference approximation
     FieldMatrix<double,embeddedDim,embeddedDim> d2d2_fd = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(a,b);
     
     FieldMatrix<double,embeddedDim,embeddedDim> d2d2_diff = d2d2;
     d2d2_diff -= d2d2_fd;
-    if ( (d2d2_diff).infinity_norm() > 100*eps) {
+    if ( (d2d2_diff).infinity_norm() > 200*eps) {
         std::cout << className(a) << ": Analytical second derivative does not match fd approximation." << std::endl;
         std::cout << "d2d2 Analytical:" << std::endl << d2d2 << std::endl;
         std::cout << "d2d2 FD        :" << std::endl << d2d2_fd << std::endl;
@@ -207,7 +208,8 @@ void testMixedDerivativesOfSquaredDistance(const TargetSpace& a, const TargetSpa
     
     FieldMatrix<double,embeddedDim,embeddedDim> d1d2_diff = d1d2;
     d1d2_diff -= d1d2_fd;
-    if ( (d1d2_diff).infinity_norm() > 100*eps ) {
+
+    if ( (d1d2_diff).infinity_norm() > 200*eps ) {
         std::cout << className(a) << ": Analytical mixed second derivative does not match fd approximation." << std::endl;
         std::cout << "d1d2 Analytical:" << std::endl << d1d2 << std::endl;
         std::cout << "d1d2 FD        :" << std::endl << d1d2_fd << std::endl;
@@ -245,8 +247,8 @@ void testDerivativeOfHessianOfSquaredDistance(const TargetSpace& a, const Target
         d2d2d2_fd[i] /= 2*eps;
         
     }
-    
-    if ( (d2d2d2 - d2d2d2_fd).infinity_norm() > 100*eps) {
+
+    if ( (d2d2d2 - d2d2d2_fd).infinity_norm() > 200*eps) {
         std::cout << className(a) << ": Analytical third derivative does not match fd approximation." << std::endl;
         std::cout << "d2d2d2 Analytical:" << std::endl << d2d2d2 << std::endl;
         std::cout << "d2d2d2 FD        :" << std::endl << d2d2d2_fd << std::endl;
@@ -284,8 +286,8 @@ void testMixedDerivativeOfHessianOfSquaredDistance(const TargetSpace& a, const T
         d1d2d2_fd[i] /= 2*eps;
         
     }
-    
-    if ( (d1d2d2 - d1d2d2_fd).infinity_norm() > 100*eps ) {
+
+    if ( (d1d2d2 - d1d2d2_fd).infinity_norm() > 200*eps ) {
         std::cout << className(a) << ": Analytical mixed third derivative does not match fd approximation." << std::endl;
         std::cout << "d1d2d2 Analytical:" << std::endl << d1d2d2 << std::endl;
         std::cout << "d1d2d2 FD        :" << std::endl << d1d2d2_fd << std::endl;
@@ -360,7 +362,7 @@ void test()
             std::cout << "p: " << testPointPair[0] << ",   q: " << testPointPair[1] << std::endl;
             std::cout << TargetSpace::exp(p, TargetSpace::log(p,q)) << std::endl;
             
-            //testDerivativesOfSquaredDistance<TargetSpace>(testPoints[i], testPoints[j]);
+            testDerivativesOfSquaredDistance<TargetSpace>(testPoints[i], testPoints[j]);
             
         }
         
@@ -378,6 +380,9 @@ int main() try
     test<UnitVector<double,3> >();
     test<UnitVector<double,4> >();
 
+    test<Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>>>();
+    test<Dune::GFE::ProductManifold<Rotation<double,3>,UnitVector<double,5>>>();
+
 //     test<Rotation<double,3> >();
 //
 //     test<RigidBodyMotion<double,3> >();
diff --git a/test/valuefactory.hh b/test/valuefactory.hh
index 5a1a795696a85a38d6edaf2d3df1968416dcebb1..9d23579177b486e56e17bb168efda579c454c7bf 100644
--- a/test/valuefactory.hh
+++ b/test/valuefactory.hh
@@ -6,6 +6,7 @@
 #include <dune/gfe/unitvector.hh>
 #include <dune/gfe/rotation.hh>
 #include <dune/gfe/rigidbodymotion.hh>
+#include <dune/gfe/productmanifold.hh>
 #include <dune/gfe/orthogonalmatrix.hh>
 #include <dune/gfe/hyperbolichalfspacepoint.hh>
 
@@ -328,4 +329,30 @@ public:
 
 
 
+
+
+/** \brief A class that creates sets of values of various types, to be used in unit tests
+*
+* This is the specialization for ProducManifold<...>
+*/
+template <typename ...TargetSpaces>
+class ValueFactory<Dune::GFE::ProductManifold<TargetSpaces...> >
+{
+using TargetSpace = Dune::GFE::ProductManifold<TargetSpaces...>;
+
+public:
+    static void get(std::vector<TargetSpace >& values) {
+
+        std::vector<typename TargetSpace::CoordinateType > testPoints(10);
+
+        std::generate(testPoints.begin(), testPoints.end(), [](){
+            return Dune::GFE::randomFieldVector<typename TargetSpace::field_type,TargetSpace::CoordinateType::dimension>(0.9,1.1) ; });
+
+        values.resize(testPoints.size());
+
+        std::transform(testPoints.cbegin(),testPoints.cend(),values.begin(),[](const auto& testPoint){return TargetSpace(testPoint);});
+    }
+};
+
+
 #endif
\ No newline at end of file