Commit 4056c771 authored by Timo Koch's avatar Timo Koch
Browse files

[fvGeometry] introduce global-local-bind concept

The global object can create an empty temporary local object that
can be bound to an element/stencil. This is closer to the
current dumux-stable implementation and still allows to switch
between global caching (for smaller problems where memory is not an issue)
and local caching (variables are precomputed for each element assemble temporarily)

Works for cc and box (in 2d). Box needs some performance improvement,
probably when creating the box geometries. But also other parts
might be possbile to optimize.
parent 0eeb3161
......@@ -314,14 +314,16 @@ public:
// check in which subcontrolvolume(s) we are
// TODO mapper/problem in bboxtree would allow to make this much better
const auto element = boundingBoxTree.entity(eIdx);
auto fvGeometry = problem.model().fvGeometries(element);
auto fvGeometry = localView(problem.model().globalFvGeometry());
fvGeometry.bindElement(element);
const auto globalPos = source.position();
// loop over all sub control volumes and check if the point source is inside
std::vector<unsigned int> scvIndices;
for (const auto& scv : scvs(fvGeometry))
for (auto&& scv : scvs(fvGeometry))
{
if (BoundingBoxTreeHelper<dimworld>::pointInGeometry(scv.geometry(), globalPos))
scvIndices.push_back(scv.indexInElement());
scvIndices.push_back(scv.index());
}
// for all scvs that where tested positiv add the point sources
// to the element/scv to point source map
......
This diff is collapsed.
......@@ -52,50 +52,47 @@ NEW_PROP_TAG(ProblemEnableGravity);
template <class TypeTag>
class DarcysLaw<TypeTag, typename std::enable_if<GET_PROP_VALUE(TypeTag, DiscretizationMethod) == GET_PROP(TypeTag, DiscretizationMethods)::Box>::type >
{
typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem;
typedef typename GET_PROP_TYPE(TypeTag, SubControlVolume) SubControlVolume;
typedef typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace) SubControlVolumeFace;
typedef typename GET_PROP_TYPE(TypeTag, FluxVariablesCache) FluxVariablesCache;
typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView;
typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar;
typedef typename GridView::template Codim<0>::Entity Element;
typedef typename GridView::IndexSet::IndexType IndexType;
typedef typename GridView::ctype CoordScalar;
typedef std::vector<IndexType> Stencil;
enum { dim = GridView::dimension} ;
enum { dimWorld = GridView::dimensionworld} ;
typedef Dune::FieldMatrix<Scalar, dimWorld, dimWorld> DimWorldMatrix;
typedef Dune::FieldVector<Scalar, dimWorld> DimVector;
typedef Dune::PQkLocalFiniteElementCache<CoordScalar, Scalar, dim, 1> FeCache;
typedef typename FeCache::FiniteElementType::Traits::LocalBasisType FeLocalBasis;
typedef typename FluxVariablesCache::FaceData FaceData;
using Problem = typename GET_PROP_TYPE(TypeTag, Problem);
using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume);
using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace);
using FluxVariablesCache = typename GET_PROP_TYPE(TypeTag, FluxVariablesCache);
using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry);
using GridView = typename GET_PROP_TYPE(TypeTag, GridView);
using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar);
using Element = typename GridView::template Codim<0>::Entity;
using IndexType = typename GridView::IndexSet::IndexType;
using CoordScalar = typename GridView::ctype;
using Stencil = std::vector<IndexType>;
enum { dim = GridView::dimension};
enum { dimWorld = GridView::dimensionworld};
using DimWorldMatrix = Dune::FieldMatrix<Scalar, dimWorld, dimWorld>;
using DimVector = Dune::FieldVector<Scalar, dimWorld>;
using FaceData = typename FluxVariablesCache::FaceData;
public:
static Scalar flux(const Problem& problem,
const Element& element,
const SubControlVolumeFace& scvFace,
const FVElementGeometry& fvGeometry,
const SubControlVolumeFace& scvf,
const IndexType phaseIdx)
{
// get the precalculated local jacobian and shape values at the integration point
const auto& faceData = problem.model().fluxVarsCache()[scvFace].faceData();
const auto& faceData = problem.model().fluxVarsCache()[scvf].faceData();
const auto& fvGeometry = problem.model().fvGeometries(element);
const auto& insideScv = problem.model().fvGeometries().subControlVolume(scvFace.insideScvIdx());
const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx());
const auto extrusionFactor = problem.model().curVolVars(insideScv).extrusionFactor();
const auto K = problem.spatialParams().intrinsicPermeability(insideScv);
// evaluate gradP - rho*g at integration point
DimVector gradP(0.0);
for (const auto& scv : scvs(fvGeometry))
for (auto&& scv : scvs(fvGeometry))
{
// the global shape function gradient
DimVector gradI;
faceData.jacInvT.mv(faceData.localJacobian[scv.indexInElement()][0], gradI);
faceData.jacInvT.mv(faceData.shapeJacobian[scv.index()][0], gradI);
gradI *= problem.model().curVolVars(scv).pressure(phaseIdx);
gradP += gradI;
......@@ -103,11 +100,11 @@ public:
if (GET_PARAM_FROM_GROUP(TypeTag, bool, Problem, EnableGravity))
{
// gravitational acceleration
DimVector g(problem.gravityAtPos(scvFace.center()));
DimVector g(problem.gravityAtPos(scvf.center()));
// interpolate the density at the IP
const auto& insideVolVars = problem.model().curVolVars(insideScv);
const auto& outsideScv = problem.model().fvGeometries().subControlVolume(scvFace.outsideScvIdx());
const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx());
const auto& outsideVolVars = problem.model().curVolVars(outsideScv);
Scalar rho = 0.5*(insideVolVars.density(phaseIdx) + outsideVolVars.density(phaseIdx));
......@@ -120,7 +117,7 @@ public:
// apply the permeability and return the flux
auto KGradP = applyPermeability(K, gradP);
return -1.0*(KGradP*scvFace.unitOuterNormal())*scvFace.area()*extrusionFactor;
return -1.0*(KGradP*scvf.unitOuterNormal())*scvf.area()*extrusionFactor;
}
static DimVector applyPermeability(const DimWorldMatrix& K, const DimVector& gradI)
......@@ -139,22 +136,30 @@ public:
}
// This is for compatibility with the cc methods. The flux stencil info is obsolete for the box method.
static Stencil stencil(const Problem& problem, const Element& element, const SubControlVolumeFace& scvFace)
static Stencil stencil(const Problem& problem,
const Element& element,
const FVElementGeometry& fvGeometry,
const SubControlVolumeFace& scvf)
{
return Stencil(0);
}
static FaceData calculateFaceData(const Problem& problem, const Element& element, const typename Element::Geometry& geometry, const FeLocalBasis& localBasis, const SubControlVolumeFace& scvFace)
static FaceData calculateFaceData(const Problem& problem,
const Element& element,
const FVElementGeometry& fvGeometry,
const SubControlVolumeFace& scvf)
{
FaceData faceData;
const auto geometry = element.geometry();
const auto& localBasis = fvGeometry.feLocalBasis();
// evaluate shape functions and gradients at the integration point
const auto ipLocal = geometry.local(scvFace.center());
const auto ipLocal = geometry.local(scvf.center());
faceData.jacInvT = geometry.jacobianInverseTransposed(ipLocal);
localBasis.evaluateJacobian(ipLocal, faceData.localJacobian);
localBasis.evaluateFunction(ipLocal, faceData.shapeValues);
localBasis.evaluateJacobian(ipLocal, faceData.shapeJacobian);
localBasis.evaluateFunction(ipLocal, faceData.shapeValue);
return std::move(faceData);
return faceData;
}
};
......
......@@ -28,12 +28,22 @@
namespace Dumux
{
/*!
* \ingroup ImplicitModel
* \brief Base class for the finite volume geometry vector for box models
* This builds up the sub control volumes and sub control volume faces
* for each element.
*/
template<class TypeTag, bool EnableGlobalFluxVariablesCache>
class BoxFluxVariablesCacheVector
{};
/*!
* \ingroup ImplicitModel
* \brief Base class for the flux variables cache vector, we store one cache per face
*/
template<class TypeTag>
class BoxFluxVariablesCacheVector
class BoxFluxVariablesCacheVector<TypeTag, true>
{
using Problem = typename GET_PROP_TYPE(TypeTag, Problem);
using GridView = typename GET_PROP_TYPE(TypeTag, GridView);
......@@ -41,87 +51,107 @@ class BoxFluxVariablesCacheVector
using Element = typename GridView::template Codim<0>::Entity;
using FluxVariablesCache = typename GET_PROP_TYPE(TypeTag, FluxVariablesCache);
using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace);
using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry);
public:
template <typename T = TypeTag>
typename std::enable_if<GET_PROP_VALUE(T, EnableGlobalFluxVariablesCache)>::type update(Problem& problem)
void update(Problem& problem)
{
problemPtr_ = &problem;
fluxVarsCache_.resize(problem.model().fvGeometries().numScvf());
fluxVarsCache_.resize(problem.gridView.size(0));
for (const auto& element : elements(problem.gridView()))
{
eIdx_ = problem.elementMapper().index(element);
// bind the geometries and volume variables to the element (all the elements in stencil)
problem.model().fvGeometries_().bind(element);
auto fvGeometry = localView(problem.model().globalFvGeometries());
fvGeometry.bind(element);
const auto& elementGeometry = element.geometry();
const auto& localBasis = problem.model().fvGeometries().feLocalBasis(elementGeometry.type());
const auto& fvGeometry = problem.model().fvGeometries(element);
for (const auto& scvf : scvfs(fvGeometry))
const auto& localBasis = fvGeometry.feLocalBasis();
fluxVarsCache_[eIdx_].resize(fvGeometry.numScvf());
for (auto&& scvf : scvfs(fvGeometry))
{
(*this)[scvf].update(problem, element, elementGeometry, localBasis, scvf);
(*this)[scvf].update(problem, element, localBasis, scvf);
}
}
}
template <typename T = TypeTag>
typename std::enable_if<!GET_PROP_VALUE(T, EnableGlobalFluxVariablesCache)>::type update(Problem& problem)
// Function is called by the BoxLocalJacobian prior to flux calculations on the element.
// We assume the FVGeometries to be bound at this point
void bind(const Element& element, const FVElementGeometry& fvGeometry)
{
bindElement(element, fvGeometry);
}
void bindElement(const Element& element, const FVElementGeometry& fvGeometry)
{ eIdx_ = problem_().elementMapper().index(element); }
// access operator
const FluxVariablesCache& operator [](const SubControlVolumeFace& scvf) const
{ return fluxVarsCache_[eIdx_][scvf.index()]; }
// access operator
FluxVariablesCache& operator [](const SubControlVolumeFace& scvf)
{ return fluxVarsCache_[eIdx_][scvf.index()]; }
private:
const Problem& problem_() const
{ return *problemPtr_; }
// currently bound element
IndexType eIdx_;
const Problem* problemPtr_;
std::vector<std::vector<FluxVariablesCache>> fluxVarsCache_;
};
/*!
* \ingroup ImplicitModel
* \brief Base class for the flux variables cache vector, we store one cache per face
*/
template<class TypeTag>
class BoxFluxVariablesCacheVector<TypeTag, false>
{
using Problem = typename GET_PROP_TYPE(TypeTag, Problem);
using GridView = typename GET_PROP_TYPE(TypeTag, GridView);
using IndexType = typename GridView::IndexSet::IndexType;
using Element = typename GridView::template Codim<0>::Entity;
using FluxVariablesCache = typename GET_PROP_TYPE(TypeTag, FluxVariablesCache);
using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace);
using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry);
public:
void update(Problem& problem)
{
problemPtr_ = &problem;
}
// Function is called by the BoxLocalJacobian prior to flux calculations on the element.
// We assume the FVGeometries to be bound at this point
void bind(const Element& element)
void bind(const Element& element, const FVElementGeometry& fvGeometry)
{
bindElement(element);
bindElement(element, fvGeometry);
}
template <typename T = TypeTag>
typename std::enable_if<!GET_PROP_VALUE(T, EnableGlobalFluxVariablesCache)>::type
bindElement(const Element& element)
void bindElement(const Element& element, const FVElementGeometry& fvGeometry)
{
const auto& fvGeometry = problem_().model().fvGeometries(element);
const auto& elementGeometry = element.geometry();
const auto& localBasis = problem_().model().fvGeometries().feLocalBasis(elementGeometry.type());
// temporary resizing of the cache
const auto numScvf = fvGeometry.numScvf();
fluxVarsCache_.resize(numScvf);
IndexType localScvfIdx = 0;
for (const auto& scvf : scvfs(fvGeometry))
fluxVarsCache_[localScvfIdx++].update(problem_(), element, elementGeometry, localBasis, scvf);
fluxVarsCache_.resize(fvGeometry.numScvf());
for (auto&& scvf : scvfs(fvGeometry))
(*this)[scvf].update(problem_(), element, fvGeometry, scvf);
}
template <typename T = TypeTag>
typename std::enable_if<GET_PROP_VALUE(T, EnableGlobalFluxVariablesCache)>::type
bindElement(const Element& element) {}
// access operators in the case of caching
template <typename T = TypeTag>
const typename std::enable_if<GET_PROP_VALUE(T, EnableGlobalFluxVariablesCache), FluxVariablesCache>::type&
operator [](const SubControlVolumeFace& scvf) const
// access operator
const FluxVariablesCache& operator [](const SubControlVolumeFace& scvf) const
{ return fluxVarsCache_[scvf.index()]; }
template <typename T = TypeTag>
typename std::enable_if<GET_PROP_VALUE(T, EnableGlobalFluxVariablesCache), FluxVariablesCache>::type&
operator [](const SubControlVolumeFace& scvf)
// access operator
FluxVariablesCache& operator [](const SubControlVolumeFace& scvf)
{ return fluxVarsCache_[scvf.index()]; }
// access operators in the case of no caching
template <typename T = TypeTag>
const typename std::enable_if<!GET_PROP_VALUE(T, EnableGlobalFluxVariablesCache), FluxVariablesCache>::type&
operator [](const SubControlVolumeFace& scvf) const
{ return fluxVarsCache_[scvf.indexInElement()]; }
template <typename T = TypeTag>
typename std::enable_if<!GET_PROP_VALUE(T, EnableGlobalFluxVariablesCache), FluxVariablesCache>::type&
operator [](const SubControlVolumeFace& scvf)
{ return fluxVarsCache_[scvf.indexInElement()]; }
private:
const Problem& problem_() const
{ return *problemPtr_; }
// currently bound element
const Problem* problemPtr_;
std::vector<FluxVariablesCache> fluxVarsCache_;
};
......
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*****************************************************************************
* See the file COPYING for full copying permissions. *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
/*!
* \file
* \brief Base class for the local finite volume geometry for box models
* This builds up the sub control volumes and sub control volume faces
* for an element.
*/
#ifndef DUMUX_DISCRETIZATION_BOX_FV_ELEMENT_GEOMETRY_HH
#define DUMUX_DISCRETIZATION_BOX_FV_ELEMENT_GEOMETRY_HH
#include <dune/geometry/referenceelements.hh>
#include <dune/localfunctions/lagrange/pqkfactory.hh>
#include <dumux/discretization/scvandscvfiterators.hh>
#include <dumux/discretization/box/boxgeometryhelper.hh>
#include <dumux/implicit/box/properties.hh>
namespace Dumux
{
//! forward declaration of the global finite volume geometry
template<class TypeTag, bool EnableGlobalFVGeometryCache>
class BoxGlobalFVGeometry;
/*!
* \ingroup ImplicitModel
* \brief Base class for the finite volume geometry vector for box models
* This builds up the sub control volumes and sub control volume faces
* for each element.
*/
template<class TypeTag, bool EnableGlobalFVGeometryCache>
class BoxFVElementGeometry
{};
//! specialization in case the FVElementGeometries are stored
template<class TypeTag>
class BoxFVElementGeometry<TypeTag, true>
{
using ThisType = typename GET_PROP_TYPE(TypeTag, FVElementGeometry);
using Problem = typename GET_PROP_TYPE(TypeTag, Problem);
using GridView = typename GET_PROP_TYPE(TypeTag, GridView);
using IndexType = typename GridView::IndexSet::IndexType;
using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume);
using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace);
using Element = typename GridView::template Codim<0>::Entity;
using GlobalFVGeometry = typename GET_PROP_TYPE(TypeTag, GlobalFVGeometry);
using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar);
using CoordScalar = typename GridView::ctype;
static const int dim = GridView::dimension;
static const int dimWorld = GridView::dimensionworld;
using FeCache = Dune::PQkLocalFiniteElementCache<CoordScalar, Scalar, dim, 1>;
using FeLocalBasis = typename FeCache::FiniteElementType::Traits::LocalBasisType;
using ReferenceElements = typename Dune::ReferenceElements<CoordScalar, dim>;
using ScvIterator = ScvIterator<SubControlVolume, std::vector<IndexType>, ThisType>;
using ScvfIterator = ScvfIterator<SubControlVolumeFace, std::vector<IndexType>, ThisType>;
public:
//! Constructor
BoxFVElementGeometry(const GlobalFVGeometry& globalFvGeometry)
: globalFvGeometryPtr_(&globalFvGeometry) {}
//! Get a sub control volume with a local scv index
const SubControlVolume& scv(IndexType scvIdx) const
{
return globalFvGeometry().scv(scvIdx, eIdx_);
}
//! Get a sub control volume face with a local scvf index
const SubControlVolumeFace& scvf(IndexType scvfIdx) const
{
return globalFvGeometry().scvf(scvfIdx, eIdx_);
}
//! iterator range for sub control volumes. Iterates over
//! all scvs of the bound element.
//! This is a free function found by means of ADL
//! To iterate over all sub control volumes of this FVElementGeometry use
//! for (auto&& scv : scvs(fvGeometry))
friend inline Dune::IteratorRange<typename std::vector<SubControlVolume>::const_iterator>
scvs(const BoxFVElementGeometry& fvGeometry)
{
const auto& g = fvGeometry.globalFvGeometry();
using Iter = typename std::vector<SubControlVolume>::const_iterator;
return Dune::IteratorRange<Iter>(g.scvs(fvGeometry.eIdx_).begin(), g.scvs(fvGeometry.eIdx_).end());
}
//! iterator range for sub control volumes faces. Iterates over
//! all scvfs of the bound element.
//! This is a free function found by means of ADL
//! To iterate over all sub control volume faces of this FVElementGeometry use
//! for (auto&& scvf : scvfs(fvGeometry))
friend inline Dune::IteratorRange<typename std::vector<SubControlVolumeFace>::const_iterator>
scvfs(const BoxFVElementGeometry& fvGeometry)
{
const auto& g = fvGeometry.globalFvGeometry();
using Iter = typename std::vector<SubControlVolumeFace>::const_iterator;
return Dune::IteratorRange<Iter>(g.scvfs(fvGeometry.eIdx_).begin(), g.scvfs(fvGeometry.eIdx_).end());
}
//! Get a local finite element basis
const FeLocalBasis& feLocalBasis() const
{
return globalFvGeometry().feCache().get(elementPtr_->geometry().type()).localBasis();
}
//! The total number of sub control volumes
std::size_t numScv() const
{
return globalFvGeometry().scvs(eIdx_).size();
}
//! The total number of sub control volume faces
std::size_t numScvf() const
{
return globalFvGeometry().scvfs(eIdx_).size();
}
//! this function is for compatibility reasons with cc methods
//! The box stencil is always element-local so bind and bindElement
//! are identical.
void bind(const Element& element)
{
this->bindElement(element);
}
//! Binding of an element, has to be called before using the fvgeometries
//! Prepares all the volume variables within the element
//! For compatibility reasons with the FVGeometry cache being disabled
void bindElement(const Element& element)
{
elementPtr_ = &element;
eIdx_ = globalFvGeometry().problem_().elementMapper().index(element);
}
//! The global finite volume geometry we are a restriction of
const GlobalFVGeometry& globalFvGeometry() const
{ return *globalFvGeometryPtr_; }
private:
const Element* elementPtr_;
const GlobalFVGeometry* globalFvGeometryPtr_;
IndexType eIdx_;
};
//! specialization in case the FVElementGeometries are not stored
template<class TypeTag>
class BoxFVElementGeometry<TypeTag, false>
{
using Problem = typename GET_PROP_TYPE(TypeTag, Problem);
using GridView = typename GET_PROP_TYPE(TypeTag, GridView);
using IndexType = typename GridView::IndexSet::IndexType;
using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume);
using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace);
using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry);
using GlobalFVGeometry = typename GET_PROP_TYPE(TypeTag, GlobalFVGeometry);
static const int dim = GridView::dimension;
static const int dimWorld = GridView::dimensionworld;
using Element = typename GridView::template Codim<0>::Entity;
using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar);
using CoordScalar = typename GridView::ctype;
using FeCache = Dune::PQkLocalFiniteElementCache<CoordScalar, Scalar, dim, 1>;
using FeLocalBasis = typename FeCache::FiniteElementType::Traits::LocalBasisType;
using ReferenceElements = typename Dune::ReferenceElements<CoordScalar, dim>;
using GeometryHelper = BoxGeometryHelper<GridView, dim>;
public:
//! Constructor
BoxFVElementGeometry(const GlobalFVGeometry& globalFvGeometry)
: globalFvGeometryPtr_(&globalFvGeometry) {}
//! Get a sub control volume with a local scv index
const SubControlVolume& scv(IndexType scvIdx) const
{
return scvs_[scvIdx];
}
//! Get a sub control volume face with a local scvf index
const SubControlVolumeFace& scvf(IndexType scvfIdx) const
{
return scvfs_[scvfIdx];
}
//! iterator range for sub control volumes. Iterates over
//! all scvs of the bound element.
//! This is a free function found by means of ADL
//! To iterate over all sub control volumes of this FVElementGeometry use
//! for (auto&& scv : scvs(fvGeometry))
friend inline Dune::IteratorRange<typename std::vector<SubControlVolume>::const_iterator>
scvs(const BoxFVElementGeometry& fvGeometry)
{
using Iter = typename std::vector<SubControlVolume>::const_iterator;
return Dune::IteratorRange<Iter>(fvGeometry.scvs_.begin(), fvGeometry.scvs_.end());
}
//! iterator range for sub control volumes faces. Iterates over
//! all scvfs of the bound element.
//! This is a free function found by means of ADL
//! To iterate over all sub control volume faces of this FVElementGeometry use
//! for (auto&& scvf : scvfs(fvGeometry))
friend inline Dune::IteratorRange<typename std::vector<SubControlVolumeFace>::const_iterator>
scvfs(const BoxFVElementGeometry& fvGeometry)
{
using Iter = typename std::vector<SubControlVolumeFace>::const_iterator;
return Dune::IteratorRange<Iter>(fvGeometry.scvfs_.begin(), fvGeometry.scvfs_.end());
}
//! Get a local finite element basis
const FeLocalBasis& feLocalBasis() const
{
return globalFvGeometry().feCache().get(elementPtr_->geometry().type()).localBasis();
}
//! The total number of sub control volumes
std::size_t numScv() const
{
return scvs_.size();
}