Commit b01c33f7 authored by Praetorius, Simon's avatar Praetorius, Simon

caching in local operators and dofvectors

parent 7d87fdfe
......@@ -20,4 +20,4 @@ foreach(project ${projects3d})
target_link_libraries(${project}.3d amdis)
target_compile_definitions(${project}.3d PRIVATE AMDIS_DIM=3 AMDIS_DOW=3)
add_dependencies(examples ${project}.3d)
endforeach()
\ No newline at end of file
endforeach()
......@@ -32,7 +32,7 @@ int main(int argc, char** argv)
auto _p = Dune::Indices::_1;
auto opStokes = makeOperator(tag::stokes{}, viscosity);
prob.addMatrixOperator(opStokes, treepath(), treepath());
prob.addMatrixOperator(opStokes);
auto opZero = makeOperator(tag::test_trial{}, 0.0);
prob.addMatrixOperator(opZero, _p, _p);
......
......@@ -2,117 +2,163 @@
#include <vector>
#include <dune/common/hash.hh>
#include <dune/geometry/quadraturerules.hh>
#include <dune/geometry/type.hh>
#include <amdis/common/TupleUtility.hpp>
#include <amdis/utility/ConcurrentCache.hpp>
namespace AMDiS
{
namespace Impl
template <class LocalBasisType>
class LocalBasisCache
{
struct HashableGeometryType
{
using type = std::tuple<unsigned int,unsigned int,bool>;
type value;
public:
using Traits = typename LocalBasisType::Traits;
HashableGeometryType(Dune::GeometryType const& type)
: value{type.id(), type.dim(), type.isNone()}
{}
using DomainType = typename Traits::DomainType;
using RangeType = typename Traits::RangeType;
using RangeFieldType = typename Traits::RangeFieldType;
using JacobianType = typename Traits::JacobianType;
friend std::size_t hash_value(HashableGeometryType const& t)
{
std::size_t seed = 0;
Impl::HashTupleImpl<HashableGeometryType::type>::apply(seed, t.value);
return seed;
}
using QuadratureRule = Dune::QuadratureRule<RangeFieldType,Traits::dimDomain>;
using QuadratureRules = Dune::QuadratureRules<RangeFieldType,Traits::dimDomain>;
friend bool operator==(HashableGeometryType const& lhs, HashableGeometryType const& rhs)
{
return lhs.value == rhs.value;
}
};
private:
/// Pair of GeometryType and quadrature order and size
template <class LocalBasisType, class Tag>
struct LocalBasisCacheKey
template <class Tag>
struct QuadKey
{
using type = std::tuple<HashableGeometryType,int,int>;
type value;
unsigned int id; // topologyId
int p; // quadrature order
std::size_t size; // number of quadrature points
friend std::size_t hash_value(LocalBasisCacheKey const& t)
struct hasher
{
std::size_t seed = 0;
Impl::HashTupleImpl<LocalBasisCacheKey::type>::apply(seed, t.value);
return seed;
std::size_t operator()(QuadKey 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);
return seed;
}
};
friend bool operator==(QuadKey const& lhs, QuadKey const& rhs)
{
return std::tie(lhs.id,lhs.p,lhs.size) == std::tie(rhs.id,rhs.p,rhs.size);
}
};
/// Pair of GeometryType and local coordinates
template <class Tag>
struct CoordsKey
{
unsigned int id; // topologyId
DomainType local; // local coordinate
friend bool operator==(LocalBasisCacheKey const& lhs, LocalBasisCacheKey const& rhs)
struct hasher
{
std::size_t operator()(CoordsKey const& t) const
{
std::size_t seed = 0;
Dune::hash_combine(seed, t.id);
Dune::hash_range(seed, t.local.begin(), t.local.end());
return seed;
}
};
friend bool operator==(CoordsKey const& lhs, CoordsKey const& rhs)
{
return lhs.value == rhs.value;
return std::tie(lhs.id,lhs.local) == std::tie(rhs.id,rhs.local);
}
};
}
} // end namespace AMDiS
DUNE_DEFINE_STD_HASH( , AMDiS::Impl::HashableGeometryType)
DUNE_DEFINE_HASH(DUNE_HASH_TEMPLATE_ARGS(class LocalBasisType, class Tag),DUNE_HASH_TYPE(AMDiS::Impl::LocalBasisCacheKey<LocalBasisType,Tag>))
using LocalValuesKey = CoordsKey<struct ValuesTag>;
using LocalGradientsKey = CoordsKey<struct GradientsTag>;
namespace AMDiS
{
template <class LocalBasisType>
class LocalBasisCache
{
public:
using Traits = typename LocalBasisType::Traits;
using RangeType = typename Traits::RangeType;
using RangeFieldType = typename Traits::RangeFieldType;
using JacobianType = typename Traits::JacobianType;
using ValuesKey = QuadKey<struct ValuesTag>;
using GradientsKey = QuadKey<struct GradientsTag>;
using QuadratureRules = Dune::QuadratureRules<RangeFieldType,Traits::dimDomain>;
public:
using ShapeValues = std::vector<RangeType>;
using ShapeGradients = std::vector<JacobianType>;
private:
using ValuesKey = Impl::LocalBasisCacheKey<LocalBasisType, struct ValuesTag>;
using GradientsKey = Impl::LocalBasisCacheKey<LocalBasisType, struct GradientsTag>;
using Policy = tag::thread_local_policy;
using ShapeValuesContainer = std::vector<ShapeValues>;
using ShapeGradientsContainer = std::vector<ShapeGradients>;
public:
using ShapeValuesContainer = std::vector<std::vector<RangeType>>;
using ShapeGradientsContainer = std::vector<std::vector<JacobianType>>;
LocalBasisCache(LocalBasisType const& localBasis)
: localBasis_(&localBasis)
{}
template <class LocalContext, class QuadratureRule>
ShapeValuesContainer const& values(LocalContext const& context, QuadratureRule const& quad)
// evaluate local basis-function at all quadrature points
template <class LocalContext>
ShapeValuesContainer const& evaluateFunctionAtQp(LocalContext const& context, QuadratureRule const& quad) const
{
ValuesKey key{typename ValuesKey::type{context.type(),quad.order(),quad.size()}};
return ShapeValuesCache::get(key, [&](ShapeValuesContainer* data, ValuesKey const&)
ValuesKey key{context.type().id(),quad.order(),quad.size()};
return ShapeValuesContainerCache::get(key, [&](ValuesKey const&)
{
data->resize(quad.size());
ShapeValuesContainer data(quad.size());
for (std::size_t iq = 0; iq < quad.size(); ++iq)
localBasis_->evaluateFunction(context.local(quad[iq].position()), (*data)[iq]);
localBasis_->evaluateFunction(context.local(quad[iq].position()), data[iq]);
return data;
});
}
template <class LocalContext, class QuadratureRule>
ShapeGradientsContainer const& gradients(LocalContext const& context, QuadratureRule const& quad)
// evaluate local basis-gradients at all quadrature points
template <class LocalContext>
ShapeGradientsContainer const& evaluateJacobianAtQp(LocalContext const& context, QuadratureRule const& quad) const
{
GradientsKey key{typename GradientsKey::type{context.type(),quad.order(),quad.size()}};
return ShapeGradientsCache::get(key, [&](ShapeGradientsContainer* data, GradientsKey const&)
GradientsKey key{context.type().id(),quad.order(),quad.size()};
return ShapeGradientsContainerCache::get(key, [&](GradientsKey const&)
{
data->resize(quad.size());
ShapeGradientsContainer data(quad.size());
for (std::size_t iq = 0; iq < quad.size(); ++iq)
localBasis_->evaluateJacobian(context.local(quad[iq].position()), (*data)[iq]);
localBasis_->evaluateJacobian(context.local(quad[iq].position()), data[iq]);
return data;
});
}
// 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<ValuesKey, ShapeValuesContainer, Policy>;
using ShapeGradientsCache = ConcurrentCache<GradientsKey, ShapeGradientsContainer, Policy>;
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>>;
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>>;
LocalBasisType const* localBasis_;
};
......
#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
......@@ -56,16 +56,14 @@ namespace AMDiS
"Galerkin operator requires equal finite elements for test and trial space." );
using LocalBasisType = typename FiniteElementType_t<RowNode>::Traits::LocalBasisType;
using RangeType = typename LocalBasisType::Traits::RangeType;
using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
using JacobianType = typename LocalBasisType::Traits::JacobianType;
auto localFctA = localFunction(gridFctA_); localFctA.bind(context.element());
auto localFctB = localFunction(gridFctB_); localFctB.bind(context.element());
auto localFctC = localFunction(gridFctC_); localFctC.bind(context.element());
auto const& localFE = rowNode.finiteElement();
std::size_t numLocalFe = localFE.size();
int quadDeg = std::max({this->getDegree(2,coeffOrder(localFctA),rowNode,colNode),
this->getDegree(1,coeffOrder(localFctB),rowNode,colNode),
......@@ -74,23 +72,25 @@ namespace AMDiS
using QuadratureRules = Dune::QuadratureRules<typename Context::Geometry::ctype, Context::LocalContext::mydimension>;
auto const& quad = QuadratureRules::rule(context.type(), quadDeg);
for (auto const& qp : quad) {
LocalBasisCache<LocalBasisType> 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
decltype(auto) local = context.local(qp.position());
decltype(auto) local = context.local(quad[iq].position());
// The transposed inverse Jacobian of the map from the reference element to the element
const auto jacobian = context.geometry().jacobianInverseTransposed(local);
// The multiplicative factor in the integral transformation formula
const auto factor = context.integrationElement(qp.position()) * qp.weight();
const auto factor = context.integrationElement(quad[iq].position()) * quad[iq].weight();
// the values of the shape functions on the reference element at the quadrature point
thread_local std::vector<RangeType> shapeValues;
localFE.localBasis().evaluateFunction(local, shapeValues);
auto const& shapeValues = shapeValuesCache[iq];
// The gradients of the shape functions on the reference element
thread_local std::vector<JacobianType> shapeGradients;
localFE.localBasis().evaluateJacobian(local, shapeGradients);
auto const& shapeGradients = shapeGradientsCache[iq];
// Compute the shape function gradients on the real element
using WorldVector = FieldVector<RangeFieldType,Context::dow>;
......@@ -106,22 +106,22 @@ namespace AMDiS
IF_CONSTEXPR(conserving) {
WorldVector gradAi, gradBi;
for (std::size_t i = 0; i < localFE.size(); ++i) {
for (std::size_t i = 0; i < numLocalFe; ++i) {
const int local_i = rowNode.localIndex(i);
gradAi = A * gradients[i];
gradBi = b * gradients[i];
for (std::size_t j = 0; j < localFE.size(); ++j) {
for (std::size_t j = 0; j < numLocalFe; ++j) {
const int local_j = colNode.localIndex(j);
elementMatrix[local_i][local_j] += (dot(gradAi, gradients[j]) + (c * shapeValues[i] - gradBi) * shapeValues[j]) * factor;
}
}
} else {
WorldVector grad_i;
for (std::size_t i = 0; i < localFE.size(); ++i) {
for (std::size_t i = 0; i < numLocalFe; ++i) {
const int local_i = rowNode.localIndex(i);
grad_i = A * gradients[i];
grad_i+= b * shapeValues[i];
for (std::size_t j = 0; j < localFE.size(); ++j) {
for (std::size_t j = 0; j < numLocalFe; ++j) {
const int local_j = colNode.localIndex(j);
elementMatrix[local_i][local_j] += (dot(grad_i, gradients[j]) + c * shapeValues[i] * shapeValues[j]) * factor;
}
......@@ -143,33 +143,34 @@ namespace AMDiS
static_assert(Node::isLeaf,
"Operator can be applied to Leaf-Nodes only." );
using RangeType = typename FiniteElementType_t<Node>::Traits::LocalBasisType::Traits::RangeType;
using LocalBasisType = typename FiniteElementType_t<Node>::Traits::LocalBasisType;
auto localFctF = localFunction(gridFctF_); localFctF.bind(context.element());
auto const& localFE = node.finiteElement();
std::size_t numLocalFe = localFE.size();
int quad_order = this->getDegree(0,coeffOrder(localFctF),node);
using QuadratureRules = Dune::QuadratureRules<typename Context::Geometry::ctype, Context::LocalContext::dimension>;
auto const& quad = QuadratureRules::rule(context.type(), quad_order);
for (auto const& qp : quad) {
// Position of the current quadrature point in the reference element
decltype(auto) local = context.local(qp.position());
LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
auto const& shapeValuesCache = cache.evaluateFunctionAtQp(context, quad);
// The multiplicative factor in the integral transformation formula
const auto factor = context.integrationElement(qp.position()) * qp.weight();
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());
// the values of the shape functions on the reference element at the quadrature point
std::vector<RangeType> shapeValues;
localFE.localBasis().evaluateFunction(local, shapeValues);
auto const& shapeValues = shapeValuesCache[iq];
const auto f = localFctF(local);
// The multiplicative factor in the integral transformation formula
const auto factor = localFctF(local) * context.integrationElement(quad[iq].position()) * quad[iq].weight();
for (std::size_t i = 0; i < localFE.size(); ++i) {
for (std::size_t i = 0; i < numLocalFe; ++i) {
const int local_i = node.localIndex(i);
elementVector[local_i] += f * shapeValues[i] * factor;
elementVector[local_i] += shapeValues[i] * factor;
}
}
......
......@@ -119,7 +119,7 @@ namespace AMDiS
using RangeFieldType = typename LocalBasisType::Traits::RangeFieldType;
LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
auto const& shapeGradientsCache = cache.gradients(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,7 +48,10 @@ namespace AMDiS
using namespace Dune::Indices;
auto const& velocityLocalFE = tree.child(_0,0).finiteElement();
std::size_t numVelocityLocalFE = velocityLocalFE.size();
auto const& pressureLocalFE = tree.child(_1).finiteElement();
std::size_t numPressureLocalFE = pressureLocalFE.size();
using VelocityLocalBasisType = typename std::decay_t<decltype(velocityLocalFE)>::Traits::LocalBasisType;
using PressureLocalBasisType = typename std::decay_t<decltype(pressureLocalFE)>::Traits::LocalBasisType;
......@@ -56,14 +59,13 @@ namespace AMDiS
LocalBasisCache<VelocityLocalBasisType> velocityCache(velocityLocalFE.localBasis());
LocalBasisCache<PressureLocalBasisType> pressureCache(pressureLocalFE.localBasis());
using PressureRange = typename PressureLocalBasisType::Traits::RangeType;
using RangeFieldType = typename VelocityLocalBasisType::Traits::RangeFieldType;
using VelocityJacobian = typename VelocityLocalBasisType::Traits::JacobianType;
auto const& quad = this->getQuadratureRule(context.type(), tree, tree);
auto const& shapeGradientsCache = velocityCache.gradients(context, quad);
auto const& pressureValuesCache = pressureCache.values(context, quad);
auto const& shapeGradientsCache = velocityCache.evaluateJacobianAtQp(context, quad);
auto const& pressureValuesCache = pressureCache.evaluateFunctionAtQp(context, quad);
for (std::size_t iq = 0; iq < quad.size(); ++iq) {
// Position of the current quadrature point in the reference element
......@@ -87,14 +89,14 @@ namespace AMDiS
auto const& pressureValues = pressureValuesCache[iq];
// <viscosity * grad(u), grad(v)>
for (std::size_t i = 0; i < velocityLocalFE.size(); ++i) {
for (std::size_t i = 0; i < numVelocityLocalFE; ++i) {
const auto value_ii = vel_factor * (gradients[i] * gradients[i]);
for (std::size_t k = 0; k < Context::dow; ++k) {
const auto local_ki = tree.child(_0,k).localIndex(i);
elementMatrix[local_ki][local_ki] += value_ii;
}
for (std::size_t j = i+1; j < velocityLocalFE.size(); ++j) {
for (std::size_t j = i+1; j < numVelocityLocalFE; ++j) {
const auto value = vel_factor * (gradients[i] * gradients[j]);
for (std::size_t k = 0; k < Context::dow; ++k) {
const auto local_ki = tree.child(_0,k).localIndex(i);
......@@ -106,8 +108,8 @@ namespace AMDiS
}
// <p, div(v)> + <div(u), q>
for (std::size_t i = 0; i < velocityLocalFE.size(); ++i) {
for (std::size_t j = 0; j < pressureLocalFE.size(); ++j) {
for (std::size_t i = 0; i < numVelocityLocalFE; ++i) {
for (std::size_t j = 0; j < numPressureLocalFE; ++j) {
const auto value = factor * pressureValues[j];
for (std::size_t k = 0; k < Context::dow; ++k) {
const auto local_v = tree.child(_0,k).localIndex(i);
......
......@@ -48,7 +48,7 @@ namespace AMDiS
LocalBasisCache<LocalBasisType> cache(localFE.localBasis());
auto const& quad = this->getQuadratureRule(context.type(), node);
auto const& shapeValuesCache = cache.values(context,quad);
auto const& shapeValuesCache = cache.evaluateFunctionAtQp(context,quad);
for (std::size_t iq = 0; iq < quad.size(); ++iq) {
// Position of the current quadrature point in the reference element
decltype(auto) local = context.local(quad[iq].position());
......
#pragma once
#include <amdis/common/FieldMatVec.hpp>
#include <amdis/LocalBasisCache.hpp>
namespace AMDiS {
......@@ -18,8 +19,8 @@ LocalFunction::operator()(LocalDomain const& x) const
auto&& fe = node.finiteElement();
auto&& localBasis = fe.localBasis();
auto&& shapeFunctionValues = shapeFunctionValueContainer_[node];
localBasis.evaluateFunction(x, shapeFunctionValues);
LocalBasisCache<std::decay_t<decltype(localBasis)>> localBasisCache(localBasis);
auto const& shapeFunctionValues = localBasisCache.evaluateFunction(localView_.element(),x);
// Get range entry associated to this node
auto re = Dune::Functions::flatVectorView(nodeToRangeEntry(node, tp, y));
......@@ -68,12 +69,12 @@ GradientLocalFunction::operator()(LocalDomain const& x) const
auto&& fe = node.finiteElement();
auto&& localBasis = fe.localBasis();
LocalBasisCache<std::decay_t<decltype(localBasis)>> localBasisCache(localBasis);
auto const& referenceGradients = localBasisCache.evaluateJacobian(localView_.element(),x);
// The transposed inverse Jacobian of the map from the reference element to the element
auto&& jacobian = geometry_.value().jacobianInverseTransposed(x);
auto&& referenceGradients = referenceGradientContainer_[node];
localBasis.evaluateJacobian(x, referenceGradients);
// 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)
......
#pragma once
#include <mutex>
#include <shared_mutex>
#include <thread>
#include <tuple>
#include <unordered_map>
#include <amdis/common/Concepts.hpp>
#include <dune/common/hash.hh>
#include <dune/common/std/type_traits.hh>
namespace AMDiS
{
namespace tag
{
struct thread_local_policy {};
struct shared_policy {};
}
/// Store cache thread local, requires no locking.
template <class Container>
struct ThreadLocalPolicy;