Liebe Gitlab-Nutzer, lieber Gitlab-Nutzer, es ist nun möglich sich mittels des ZIH-Logins/LDAP an unserem Dienst anzumelden. Ein Anmelden über dieses erzeugt ein neues Konto. Das alte Konto ist über den Reiter "Standard" erreichbar. Die Administratoren

Dear Gitlab user, it is now possible to log in to our service using the ZIH login/LDAP. Logging in via this will create a new account. The old account can be accessed via the "Standard" tab. The administrators

Commit 32e77c49 authored by Praetorius, Simon's avatar Praetorius, Simon
Browse files

Merge branch 'issue/local_basis_cache' into 'develop'

Local basis cache depending on node type

See merge request !53
parents d68cbeaf 7465e2c2
Pipeline #1677 passed with stage
in 48 minutes and 26 seconds
#pragma once
#include <tuple>
#include <vector>
#include <dune/common/hash.hh>
......@@ -7,99 +8,188 @@
#include <dune/geometry/type.hh>
#include <amdis/utility/ConcurrentCache.hpp>
#include <amdis/utility/FiniteElementType.hpp>
namespace AMDiS
{
template <class LocalBasisType>
class LocalBasisCache
/// \brief Cache of LocalBasis evaluations and jacobians at local coordinates
/**
* Caching is done using the ConcurrentCache data structure with a key that
* depends on the element type and local coordinates inside the element.
* Two methods are provided for evaluation of local basis functions and local
* basis jacobians at the local coordinate.
*
* \tparam Node A leaf basis node in a basis tree
**/
template <class Node>
class NodeCache
{
static_assert(Node::isLeaf, "");
using LocalBasisType = typename Node::FiniteElement::Traits::LocalBasisType;
using DomainType = typename LocalBasisType::Traits::DomainType;
public:
using Traits = typename LocalBasisType::Traits;
using DomainType = typename Traits::DomainType;
using RangeType = typename Traits::RangeType;
using RangeFieldType = typename Traits::RangeFieldType;
using JacobianType = typename Traits::JacobianType;
using QuadratureRule = Dune::QuadratureRule<RangeFieldType,Traits::dimDomain>;
using QuadratureRules = Dune::QuadratureRules<RangeFieldType,Traits::dimDomain>;
using ShapeValues = std::vector<typename Traits::RangeType>;
using ShapeGradients = std::vector<typename Traits::JacobianType>;
private:
/// Pair of GeometryType and quadrature order and size
template <class Tag>
struct QuadKey
// Pair of GeometryType and local coordinates
struct Key
{
unsigned int id; // topologyId
int p; // quadrature order
std::size_t size; // number of quadrature points
DomainType local; // local coordinate
struct hasher
{
std::size_t operator()(QuadKey const& t) const
std::size_t operator()(Key const& t) const
{
std::size_t seed = 0;
Dune::hash_combine(seed, t.id);
Dune::hash_combine(seed, t.p);
Dune::hash_combine(seed, t.size);
Dune::hash_range(seed, t.local.begin(), t.local.end());
return seed;
}
};
friend bool operator==(QuadKey const& lhs, QuadKey const& rhs)
friend bool operator==(Key const& lhs, Key const& rhs)
{
return std::tie(lhs.id,lhs.p,lhs.size) == std::tie(rhs.id,rhs.p,rhs.size);
return std::tie(lhs.id,lhs.local) == std::tie(rhs.id,rhs.local);
}
};
/// Pair of GeometryType and local coordinates
template <class Tag>
struct CoordsKey
public:
/// Create a cache not associated to a local basis.
/// Requires a call to \ref bind() before the first evaluation of the cache.
NodeCache() = default;
/// Associate the cache to a localBasis.
NodeCache(LocalBasisType const& localBasis)
: localBasis_(&localBasis)
{}
/// Bind the cache to a local basis for later evaluations.
// NOTE: Should be called before the first evaluation of the local basis function or
// jacobian.
// NOTE: Binding is only necessary, if the default constructor is used and
// the cache is not associated with a localBasis in the constructor.
void bind(LocalBasisType const& localBasis)
{
localBasis_ = &localBasis;
}
/// Evaluate local basis functions at local coordinates
ShapeValues const& evaluateFunction(Dune::GeometryType const& type, DomainType const& local) const
{
Key key{type.id(), local};
return shapeValues_.get(key, [&](Key const&)
{
ShapeValues data;
localBasis_->evaluateFunction(local, data);
return data;
});
}
/// Evaluate local basis jacobians at local coordinates
ShapeGradients const& evaluateJacobian(Dune::GeometryType const& type, DomainType const& local) const
{
Key key{type.id(), local};
return shapeGradients_.get(key, [&](Key const&)
{
ShapeGradients data;
localBasis_->evaluateJacobian(local, data);
return data;
});
}
private:
template <class Value>
using Cache = ConcurrentCache<Key, Value, ThreadLocalPolicy,
std::unordered_map<Key, Value, typename Key::hasher>>;
Cache<ShapeValues> shapeValues_;
Cache<ShapeGradients> shapeGradients_;
LocalBasisType const* localBasis_ = nullptr;
};
/// \brief Cache of LocalBasis evaluations and jacobians at quadrature points
/**
* Caching is done using the ConcurrentCache data structure with a key that
* depends on the element type and order and size of a quadrature rule.
* Two methods are provided for evaluation of local basis functions and local
* basis jacobians at all quadrature points. A vector of values is returned.
*
* \tparam Node A leaf basis node in a basis tree
**/
template <class Node>
class NodeQuadCache
{
static_assert(Node::isLeaf, "");
using LocalBasisType = typename Node::FiniteElement::Traits::LocalBasisType;
public:
using Traits = typename LocalBasisType::Traits;
using ShapeValues = std::vector<typename Traits::RangeType>;
using ShapeGradients = std::vector<typename Traits::JacobianType>;
using ShapeValuesContainer = std::vector<ShapeValues>;
using ShapeGradientsContainer = std::vector<ShapeGradients>;
private:
// Pair of GeometryType and quadrature order and size
struct Key
{
unsigned int id; // topologyId
DomainType local; // local coordinate
int p; // quadrature order
std::size_t size; // number of quadrature points
struct hasher
{
std::size_t operator()(CoordsKey const& t) const
std::size_t operator()(Key const& t) const
{
std::size_t seed = 0;
Dune::hash_combine(seed, t.id);
Dune::hash_range(seed, t.local.begin(), t.local.end());
Dune::hash_combine(seed, t.p);
Dune::hash_combine(seed, t.size);
return seed;
}
};
friend bool operator==(CoordsKey const& lhs, CoordsKey const& rhs)
friend bool operator==(Key const& lhs, Key const& rhs)
{
return std::tie(lhs.id,lhs.local) == std::tie(rhs.id,rhs.local);
return std::tie(lhs.id,lhs.p,lhs.size) == std::tie(rhs.id,rhs.p,rhs.size);
}
};
using LocalValuesKey = CoordsKey<struct ValuesTag>;
using LocalGradientsKey = CoordsKey<struct GradientsTag>;
using ValuesKey = QuadKey<struct ValuesTag>;
using GradientsKey = QuadKey<struct GradientsTag>;
public:
using ShapeValues = std::vector<RangeType>;
using ShapeGradients = std::vector<JacobianType>;
using ShapeValuesContainer = std::vector<ShapeValues>;
using ShapeGradientsContainer = std::vector<ShapeGradients>;
/// Create a cache not associated to a local basis.
/// Requires a call to \ref bind() before the first evaluation of the cache.
NodeQuadCache() = default;
LocalBasisCache(LocalBasisType const& localBasis)
/// Associate the cache to a localBasis.
NodeQuadCache(LocalBasisType const& localBasis)
: localBasis_(&localBasis)
{}
// evaluate local basis-function at all quadrature points
template <class LocalContext, int d>
ShapeValuesContainer const& evaluateFunctionAtQp(LocalContext const& context, Dune::QuadratureRule<RangeFieldType,d> const& quad) const
/// Bind the cache to a local basis for later evaluations.
// NOTE: Should be called before the first evaluation of the local basis function or
// jacobian.
// NOTE: Binding is only necessary, if the default constructor is used and
// the cache is not associated with a localBasis in the constructor.
void bind(LocalBasisType const& localBasis)
{
localBasis_ = &localBasis;
}
/// Evaluate local basis functions at all quadrature points of a quadrature rule.
template <class LocalContext, class QuadratureRule>
ShapeValuesContainer const& evaluateFunctionAtQP(LocalContext const& context, QuadratureRule const& quad) const
{
ValuesKey key{context.type().id(),quad.order(),quad.size()};
return ShapeValuesContainerCache::get(key, [&](ValuesKey const&)
Key key{context.type().id(), quad.order(), quad.size()};
return shapeValuesContainer_.get(key, [&](Key const&)
{
ShapeValuesContainer data(quad.size());
for (std::size_t iq = 0; iq < quad.size(); ++iq)
......@@ -108,12 +198,12 @@ namespace AMDiS
});
}
// evaluate local basis-gradients at all quadrature points
template <class LocalContext, int d>
ShapeGradientsContainer const& evaluateJacobianAtQp(LocalContext const& context, Dune::QuadratureRule<RangeFieldType,d> const& quad) const
/// Evaluate local basis jacobians at all quadrature points of a quadrature rule.
template <class LocalContext, class QuadratureRule>
ShapeGradientsContainer const& evaluateJacobianAtQP(LocalContext const& context, QuadratureRule const& quad) const
{
GradientsKey key{context.type().id(),quad.order(),quad.size()};
return ShapeGradientsContainerCache::get(key, [&](GradientsKey const&)
Key key{context.type().id(), quad.order(), quad.size()};
return shapeGradientsContainer_.get(key, [&](Key const&)
{
ShapeGradientsContainer data(quad.size());
for (std::size_t iq = 0; iq < quad.size(); ++iq)
......@@ -122,45 +212,15 @@ namespace AMDiS
});
}
// evaluate local basis-function at point
template <class Element>
ShapeValues const& evaluateFunction(Element const& element, DomainType const& local) const
{
LocalValuesKey key{element.type().id(),local};
return ShapeValuesCache::get(key, [&](LocalValuesKey const&)
{
ShapeValues data;
localBasis_->evaluateFunction(local, data);
return data;
});
}
// evaluate local basis-gradients at point
template <class Element>
ShapeGradients const& evaluateJacobian(Element const& element, DomainType const& local) const
{
LocalGradientsKey key{element.type().id(),local};
return ShapeGradientsCache::get(key, [&](LocalGradientsKey const&)
{
ShapeGradients data;
localBasis_->evaluateJacobian(local, data);
return data;
});
}
private:
using ShapeValuesCache = ConcurrentCache<LocalValuesKey, ShapeValues, ThreadLocalPolicy,
std::unordered_map<LocalValuesKey, ShapeValues, typename LocalValuesKey::hasher>>;
using ShapeGradientsCache = ConcurrentCache<LocalGradientsKey, ShapeGradients, ThreadLocalPolicy,
std::unordered_map<LocalGradientsKey, ShapeGradients, typename LocalGradientsKey::hasher>>;
template <class Value>
using Cache = ConcurrentCache<Key, Value, ThreadLocalPolicy,
std::unordered_map<Key, Value, typename Key::hasher>>;
using ShapeValuesContainerCache = ConcurrentCache<ValuesKey, ShapeValuesContainer, ThreadLocalPolicy,
std::unordered_map<ValuesKey, ShapeValuesContainer, typename ValuesKey::hasher>>;
using ShapeGradientsContainerCache = ConcurrentCache<GradientsKey, ShapeGradientsContainer, ThreadLocalPolicy,
std::unordered_map<GradientsKey, ShapeGradientsContainer, typename GradientsKey::hasher>>;
Cache<ShapeValuesContainer> shapeValuesContainer_;
Cache<ShapeGradientsContainer> shapeGradientsContainer_;
LocalBasisType const* localBasis_;
LocalBasisType const* localBasis_ = nullptr;
};
} // end namespace AMDiS
#pragma once
#include <vector>
#include <dune/geometry/quadraturerules.hh>
#include <dune/geometry/type.hh>
#include <amdis/LocalBasisCache.hpp>
#include <amdis/common/TupleUtility.hpp>
#include <amdis/utility/ConcurrentCache.hpp>
namespace AMDiS
{
namespace Impl
{
/// Pair of GeometryType and quadrature order and size
template <class LocalBasisType, class Tag>
struct EvaluatorCacheKey
{
using type = std::tuple<HashableGeometryType,typename LocalBasisType::Traits::DomainType>;
type value;
friend std::size_t hash_value(EvaluatorCacheKey const& t)
{
std::size_t seed = hash_value(std::get<0>(t.value));
Dune::hash_range(seed, std::get<1>(t.value).begin(), std::get<1>(t.value).end());
return seed;
}
friend bool operator==(EvaluatorCacheKey const& lhs, EvaluatorCacheKey const& rhs)
{
return lhs.value == rhs.value;
}
};
}
} // end namespace AMDiS
DUNE_DEFINE_HASH(DUNE_HASH_TEMPLATE_ARGS(class LocalBasisType, class Tag),DUNE_HASH_TYPE(AMDiS::Impl::EvaluatorCacheKey<LocalBasisType,Tag>))
namespace AMDiS
{
template <class LocalBasisType>
class LocalBasisEvaluatorCache
{
public:
using Traits = typename LocalBasisType::Traits;
using DomainType = typename Traits::DomainType;
using RangeType = typename Traits::RangeType;
using RangeFieldType = typename Traits::RangeFieldType;
using JacobianType = typename Traits::JacobianType;
using QuadratureRule = Dune::QuadratureRule<RangeFieldType,Traits::dimDomain>;
using QuadratureRules = Dune::QuadratureRules<RangeFieldType,Traits::dimDomain>;
private:
using ValuesKey = Impl::EvaluatorCacheKey<LocalBasisType, struct ValuesTag>;
using GradientsKey = Impl::EvaluatorCacheKey<LocalBasisType, struct GradientsTag>;
using Policy = tag::thread_local_policy;
public:
using ShapeValues = std::vector<RangeType>;
using ShapeGradients = std::vector<JacobianType>;
LocalBasisEvaluatorCache(LocalBasisType const& localBasis)
: localBasis_(&localBasis)
{}
template <class LocalContext>
ShapeValues const& evaluateFunction(LocalContext const& context, DomainType const& local)
{
ValuesKey key{typename ValuesKey::type{context.type(),local}};
return ShapeValuesCache::get(key, [&](ShapeValues* data, ValuesKey const&)
{
localBasis_->evaluateFunction(local, *data);
});
}
template <class LocalContext>
ShapeGradients const& evaluateJacobian(LocalContext const& context, DomainType const& local)
{
GradientsKey key{typename GradientsKey::type{context.type(),local}};
return ShapeGradientsCache::get(key, [&](ShapeGradients* data, GradientsKey const&)
{
localBasis_->evaluateJacobian(local, *data);
});
}
private:
using ShapeValuesCache = ConcurrentCache<ValuesKey, ShapeValues, Policy>;
using ShapeGradientsCache = ConcurrentCache<GradientsKey, ShapeGradients, Policy>;
LocalBasisType const* localBasis_;
};
} // end namespace AMDiS
......@@ -58,8 +58,7 @@ namespace AMDiS
static_assert(std::is_same<FiniteElementType_t<RowNode>, FiniteElementType_t<ColNode>>{},
"Galerkin operator requires equal finite elements for test and trial space." );
using LocalBasisType = typename FiniteElementType_t<RowNode>::Traits::LocalBasisType;
using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
using RangeFieldType = typename NodeQuadCache<RowNode>::Traits::RangeFieldType;
auto localFctA = localFunction(gridFctA_); localFctA.bind(context.element());
auto localFctB = localFunction(gridFctB_); localFctB.bind(context.element());
......@@ -75,9 +74,9 @@ namespace AMDiS
using QuadratureRules = Dune::QuadratureRules<typename Context::Geometry::ctype, Context::LocalContext::mydimension>;
auto const& quad = QuadratureRules::rule(context.type(), quadDeg);
LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad);
auto const& shapeValuesCache = cache.evaluateFunctionAtQp(context, quad);
NodeQuadCache<RowNode> cache(localFE.localBasis());
auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(context, quad);
auto const& shapeValuesCache = cache.evaluateFunctionAtQP(context, quad);
for (std::size_t iq = 0; iq < quad.size(); ++iq) {
// Position of the current quadrature point in the reference element
......@@ -146,8 +145,6 @@ namespace AMDiS
static_assert(Node::isLeaf,
"Operator can be applied to Leaf-Nodes only." );
using LocalBasisType = typename FiniteElementType_t<Node>::Traits::LocalBasisType;
auto localFctF = localFunction(gridFctF_); localFctF.bind(context.element());
auto const& localFE = node.finiteElement();
......@@ -158,8 +155,8 @@ namespace AMDiS
using QuadratureRules = Dune::QuadratureRules<typename Context::Geometry::ctype, Context::LocalContext::dimension>;
auto const& quad = QuadratureRules::rule(context.type(), quad_order);
LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
auto const& shapeValuesCache = cache.evaluateFunctionAtQp(context, quad);
NodeQuadCache<Node> cache(localFE.localBasis());
auto const& shapeValuesCache = cache.evaluateFunctionAtQP(context, quad);
for (std::size_t iq = 0; iq < quad.size(); ++iq) {
// Position of the current quadrature point in the reference element
......
......@@ -50,12 +50,10 @@ namespace AMDiS
auto const& localFE = node.child(0).finiteElement();
std::size_t feSize = localFE.size();
using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
using RangeFieldType = typename NodeQuadCache<typename Node::ChildType>::Traits::RangeFieldType;
NodeQuadCache<typename Node::ChildType> cache(localFE.localBasis());
LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad);
auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(context, quad);
for (std::size_t iq = 0; iq < quad.size(); ++iq) {
// Position of the current quadrature point in the reference element
decltype(auto) local = context.local(quad[iq].position());
......
......@@ -48,12 +48,10 @@ namespace AMDiS
auto const& localFE = node.finiteElement();
std::size_t feSize = localFE.size();
using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
using RangeFieldType = typename NodeQuadCache<Node>::Traits::RangeFieldType;
NodeQuadCache<Node> cache(localFE.localBasis());
LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad);
auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(context, quad);
for (std::size_t iq = 0; iq < quad.size(); ++iq) {
// Position of the current quadrature point in the reference element
decltype(auto) local = context.local(quad[iq].position());
......
......@@ -49,12 +49,10 @@ namespace AMDiS
auto const& localFE = node.finiteElement();
std::size_t feSize = localFE.size();
using LocalBasisType = typename std::decay_t<decltype(localFE)>::Traits::LocalBasisType;
using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
using RangeFieldType = typename NodeQuadCache<Node>::Traits::RangeFieldType;
NodeQuadCache<Node> cache(localFE.localBasis());
LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
auto const& shapeGradientsCache = cache.evaluateJacobianAtQp(context, quad);
auto const& shapeGradientsCache = cache.evaluateJacobianAtQP(context, quad);
for (std::size_t iq = 0; iq < quad.size(); ++iq) {
// Position of the current quadrature point in the reference element
decltype(auto) local = context.local(quad[iq].position());
......
......@@ -54,16 +54,13 @@ namespace AMDiS
std::size_t rowSize = rowLocalFE.size();
std::size_t colSize = colLocalFE.size();
using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
using RangeFieldType = typename NodeQuadCache<typename ColNode::ChildType>::Traits::RangeFieldType;
using RangeFieldType = typename ColLocalBasisType::Traits::RangeFieldType;
NodeQuadCache<RowNode> rowCache(rowLocalFE.localBasis());
NodeQuadCache<typename ColNode::ChildType> colCache(colLocalFE.localBasis());
LocalBasisCache<RowLocalBasisType> rowCache(rowLocalFE.localBasis());
LocalBasisCache<ColLocalBasisType> colCache(colLocalFE.localBasis());
auto const& shapeValuesCache = rowCache.evaluateFunctionAtQp(context, quad);
auto const& shapeGradientsCache = colCache.evaluateJacobianAtQp(context, quad);
auto const& shapeValuesCache = rowCache.evaluateFunctionAtQP(context, quad);
auto const& shapeGradientsCache = colCache.evaluateJacobianAtQP(context, quad);
for (std::size_t iq = 0; iq < quad.size(); ++iq) {
// Position of the current quadrature point in the reference element
decltype(auto) local = context.local(quad[iq].position());
......
......@@ -52,16 +52,13 @@ namespace AMDiS
std::size_t rowSize = rowLocalFE.size();
std::size_t colSize = colLocalFE.size();
using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
using RangeFieldType = typename NodeQuadCache<ColNode>::Traits::RangeFieldType;
using RangeFieldType = typename ColLocalBasisType::Traits::RangeFieldType;
NodeQuadCache<RowNode> rowCache(rowLocalFE.localBasis());
NodeQuadCache<ColNode> colCache(colLocalFE.localBasis());
LocalBasisCache<RowLocalBasisType> rowCache(rowLocalFE.localBasis());
LocalBasisCache<ColLocalBasisType> colCache(colLocalFE.localBasis());
auto const& shapeValuesCache = rowCache.evaluateFunctionAtQp(context, quad);
auto const& shapeGradientsCache = colCache.evaluateJacobianAtQp(context, quad);
auto const& shapeValuesCache = rowCache.evaluateFunctionAtQP(context, quad);
auto const& shapeGradientsCache = colCache.evaluateJacobianAtQP(context, quad);
for (std::size_t iq = 0; iq < quad.size(); ++iq) {
// Position of the current quadrature point in the reference element
decltype(auto) local = context.local(quad[iq].position());
......
......@@ -55,16 +55,13 @@ namespace AMDiS
std::size_t rowSize = rowLocalFE.size();
std::size_t colSize = colLocalFE.size();
using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
using RangeFieldType = typename NodeQuadCache<ColNode>::Traits::RangeFieldType;
using RangeFieldType = typename ColLocalBasisType::Traits::RangeFieldType;
NodeQuadCache<RowNode> rowCache(rowLocalFE.localBasis());
NodeQuadCache<ColNode> colCache(colLocalFE.localBasis());
LocalBasisCache<RowLocalBasisType> rowCache(rowLocalFE.localBasis());
LocalBasisCache<ColLocalBasisType> colCache(colLocalFE.localBasis());
auto const& shapeValuesCache = rowCache.evaluateFunctionAtQp(context, quad);
auto const& shapeGradientsCache = colCache.evaluateJacobianAtQp(context, quad);
auto const& shapeValuesCache = rowCache.evaluateFunctionAtQP(context, quad);
auto const& shapeGradientsCache = colCache.evaluateJacobianAtQP(context, quad);
for (std::size_t iq = 0; iq < quad.size(); ++iq) {
// Position of the current quadrature point in the reference element
decltype(auto) local = context.local(quad[iq].position());
......
......@@ -54,16 +54,13 @@ namespace AMDiS
std::size_t rowSize = rowLocalFE.size();
std::size_t colSize = colLocalFE.size();
using RowLocalBasisType = typename std::decay_t<decltype(rowLocalFE)>::Traits::LocalBasisType;
using ColLocalBasisType = typename std::decay_t<decltype(colLocalFE)>::Traits::LocalBasisType;
using RangeFieldType = typename NodeQuadCache<ColNode>::Traits::RangeFieldType;