Commit 2408b1ff authored by Dennis Gläser's avatar Dennis Gläser
Browse files

add mpfa framework

TODO: implement ficks and fouriers law, as well as the interaction volume data handles
accordingly for problems involving diffusion/heat conduction
parent 2162ad04
...@@ -18,35 +18,36 @@ ...@@ -18,35 +18,36 @@
*****************************************************************************/ *****************************************************************************/
/*! /*!
* \file * \file
* \brief Base class for the global interaction volume seeds of mpfa methods. * \brief Stores the face indices corresponding to the neighbors of an element
* that contribute to the derivative calculation
*/ */
#ifndef DUMUX_DISCRETIZATION_MPFA_GLOBALINTERACTIONVOLUMESEEDS_HH #ifndef DUMUX_CC_MPFA_CONNECTIVITY_MAP_HH
#define DUMUX_DISCRETIZATION_MPFA_GLOBALINTERACTIONVOLUMESEEDS_HH #define DUMUX_CC_MPFA_CONNECTIVITY_MAP_HH
#include <dumux/implicit/cellcentered/mpfa/properties.hh> #include <dumux/discretization/cellcentered/connectivitymap.hh>
#include <dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseedsbase.hh> #include <dumux/discretization/cellcentered/mpfa/generalconnectivitymap.hh>
#include "methods.hh"
namespace Dumux namespace Dumux
{ {
//! forward declaration of the actual method-specific implementation //! Forward declaration of method specific implementation of the assembly map
//! By default we simply inherit from the base class
//! Actual implementations for other methods have to be provided below
template<class TypeTag, MpfaMethods method> template<class TypeTag, MpfaMethods method>
class CCMpfaGlobalInteractionVolumeSeedsImplementation; class CCMpfaConnectivityMapImplementation;
/*! // //! The Assembly map for models using mpfa methods
* \ingroup Mpfa
* \brief Base class for the creation and storage of the interaction volume seeds for mpfa methods.
*/
template<class TypeTag> template<class TypeTag>
using CCMpfaGlobalInteractionVolumeSeeds = CCMpfaGlobalInteractionVolumeSeedsImplementation<TypeTag, GET_PROP_VALUE(TypeTag, MpfaMethod)>; using CCMpfaConnectivityMap = CCMpfaConnectivityMapImplementation<TypeTag, GET_PROP_VALUE(TypeTag, MpfaMethod)>;
//! The default is the general assembly map for mpfa schemes
template<class TypeTag, MpfaMethods method>
class CCMpfaConnectivityMapImplementation : public CCMpfaGeneralConnectivityMap<TypeTag> {};
} // end namespace //! The o-method can use the simple assembly map
template<class TypeTag>
class CCMpfaConnectivityMapImplementation<TypeTag, MpfaMethods::oMethod> : public CCSimpleConnectivityMap<TypeTag> {};
// the specializations of this class differing from the default have to be included here //! The o-method with full pressure support can use the simple assembly map
#include <dumux/discretization/cellcentered/mpfa/omethod/globalinteractionvolumeseeds.hh> template<class TypeTag>
#include <dumux/discretization/cellcentered/mpfa/lmethod/globalinteractionvolumeseeds.hh> class CCMpfaConnectivityMapImplementation<TypeTag, MpfaMethods::oMethodFps> : public CCSimpleConnectivityMap<TypeTag> {};
#include <dumux/discretization/cellcentered/mpfa/omethodfps/globalinteractionvolumeseeds.hh> }
#endif #endif
...@@ -25,8 +25,7 @@ ...@@ -25,8 +25,7 @@
#ifndef DUMUX_DISCRETIZATION_CC_MPFA_DARCYS_LAW_HH #ifndef DUMUX_DISCRETIZATION_CC_MPFA_DARCYS_LAW_HH
#define DUMUX_DISCRETIZATION_CC_MPFA_DARCYS_LAW_HH #define DUMUX_DISCRETIZATION_CC_MPFA_DARCYS_LAW_HH
#include <dumux/implicit/properties.hh> #include <dumux/implicit/cellcentered/mpfa/properties.hh>
#include <dumux/discretization/cellcentered/mpfa/facetypes.hh>
namespace Dumux namespace Dumux
{ {
...@@ -63,63 +62,57 @@ class DarcysLawImplementation<TypeTag, DiscretizationMethods::CCMpfa> ...@@ -63,63 +62,57 @@ class DarcysLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
using ElementSolutionVector = typename GET_PROP_TYPE(TypeTag, ElementSolutionVector); using ElementSolutionVector = typename GET_PROP_TYPE(TypeTag, ElementSolutionVector);
// Always use the dynamic type for vectors (compatibility with the boundary) // Always use the dynamic type for vectors (compatibility with the boundary)
using BoundaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, BoundaryInteractionVolume); using PrimaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, PrimaryInteractionVolume);
using CoefficientVector = typename BoundaryInteractionVolume::Vector; using CoefficientVector = typename PrimaryInteractionVolume::Traits::DynamicVector;
using DataHandle = typename PrimaryInteractionVolume::Traits::DataHandle;
static constexpr int numPhases = GET_PROP_VALUE(TypeTag, NumPhases); static constexpr int numPhases = GET_PROP_VALUE(TypeTag, NumPhases);
static constexpr bool useTpfaBoundary = GET_PROP_VALUE(TypeTag, UseTpfaBoundary);
static constexpr bool enableInteriorBoundaries = GET_PROP_VALUE(TypeTag, EnableInteriorBoundaries);
//! The cache used in conjunction with the mpfa Darcy's Law //! The cache used in conjunction with the mpfa Darcy's Law
class MpfaDarcysLawCache class MpfaDarcysLawCache
{ {
// We always use the dynamic types here to be compatible on the boundary // We always use the dynamic types here to be compatible on the boundary
using Stencil = typename BoundaryInteractionVolume::GlobalIndexSet; using Stencil = typename PrimaryInteractionVolume::Traits::DynamicGlobalIndexContainer;
using PositionVector = typename BoundaryInteractionVolume::PositionVector; using DirichletDataContainer = typename PrimaryInteractionVolume::DirichletDataContainer;
public: public:
//! update cached objects //! update cached objects
template<class InteractionVolume> template<class InteractionVolume>
void updateAdvection(const InteractionVolume& iv, const SubControlVolumeFace &scvf) void updateAdvection(const InteractionVolume& iv, const DataHandle& dataHandle, const SubControlVolumeFace &scvf)
{ {
const auto& localFaceData = iv.getLocalFaceData(scvf); const auto& localFaceData = iv.getLocalFaceData(scvf);
// update the quantities that are equal for all phases
advectionVolVarsStencil_ = iv.volVarsStencil();
advectionVolVarsPositions_ = iv.volVarsPositions();
advectionTij_ = iv.getTransmissibilities(localFaceData);
// The neumann fluxes always have to be set per phase // update the quantities that are equal for all phases
for (unsigned int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) advectionSwitchFluxSign_ = localFaceData.isOutside;
phaseNeumannFluxes_[phaseIdx] = iv.getNeumannFlux(localFaceData, phaseIdx); advectionTij_ = &iv.getTransmissibilities(scvf, localFaceData, dataHandle);
advectionVolVarsStencil_ = &dataHandle.volVarsStencil();
advectionDirichletData_ = &dataHandle.dirichletData();
} }
//! Returns the volume variables indices necessary for flux computation //! Returns the stencil for advective scvf flux computation
//! This includes all participating boundary volume variables. Since we
//! do not allow mixed BC for the mpfa this is the same for all phases.
const Stencil& advectionVolVarsStencil() const const Stencil& advectionVolVarsStencil() const
{ return advectionVolVarsStencil_; } { return *advectionVolVarsStencil_; }
//! Returns the position on which the volume variables live. This is
//! necessary as we need to evaluate gravity also for the boundary volvars
const PositionVector& advectionVolVarsPositions() const
{ return advectionVolVarsPositions_; }
//! Returns the transmissibilities associated with the volume variables //! Returns the transmissibilities associated with the volume variables
//! All phases flow through the same rock, thus, tij are equal for all phases //! All phases flow through the same rock, thus, tij are equal for all phases
const CoefficientVector& advectionTij() const const CoefficientVector& advectionTij() const
{ return advectionTij_; } { return *advectionTij_; }
//! If the useTpfaBoundary property is set to false, the boundary conditions //! On faces that are "outside" w.r.t. a face in the interaction volume,
//! are put into the local systems leading to possible contributions on all faces //! we have to take the negative value of the fluxes, i.e. multiply by -1.0
Scalar advectionNeumannFlux(unsigned int phaseIdx) const bool advectionSwitchFluxSign() const
{ return phaseNeumannFluxes_[phaseIdx]; } { return advectionSwitchFluxSign_; }
//! Returns the data on dirichlet boundary conditions affecting
//! the flux computation on this face
const DirichletDataContainer& advectionDirichletData() const
{ return *advectionDirichletData_; }
private: private:
// Quantities associated with advection bool advectionSwitchFluxSign_;
Stencil advectionVolVarsStencil_; const Stencil* advectionVolVarsStencil_;
PositionVector advectionVolVarsPositions_; const CoefficientVector* advectionTij_;
CoefficientVector advectionTij_; const DirichletDataContainer* advectionDirichletData_;
std::array<Scalar, numPhases> phaseNeumannFluxes_;
}; };
//! Class that fills the cache corresponding to mpfa Darcy's Law //! Class that fills the cache corresponding to mpfa Darcy's Law
...@@ -138,10 +131,14 @@ class DarcysLawImplementation<TypeTag, DiscretizationMethods::CCMpfa> ...@@ -138,10 +131,14 @@ class DarcysLawImplementation<TypeTag, DiscretizationMethods::CCMpfa>
const FluxVariablesCacheFiller& fluxVarsCacheFiller) const FluxVariablesCacheFiller& fluxVarsCacheFiller)
{ {
// get interaction volume from the flux vars cache filler & upate the cache // get interaction volume from the flux vars cache filler & upate the cache
if (problem.model().fvGridGeometry().isInBoundaryInteractionVolume(scvf)) if (fvGeometry.fvGridGeometry().vertexUsesSecondaryInteractionVolume(scvf.vertexIndex()))
scvfFluxVarsCache.updateAdvection(fluxVarsCacheFiller.boundaryInteractionVolume(), scvf); scvfFluxVarsCache.updateAdvection(fluxVarsCacheFiller.secondaryInteractionVolume(),
fluxVarsCacheFiller.dataHandle(),
scvf);
else else
scvfFluxVarsCache.updateAdvection(fluxVarsCacheFiller.interactionVolume(), scvf); scvfFluxVarsCache.updateAdvection(fluxVarsCacheFiller.primaryInteractionVolume(),
fluxVarsCacheFiller.dataHandle(),
scvf);
} }
}; };
...@@ -164,27 +161,19 @@ public: ...@@ -164,27 +161,19 @@ public:
static const bool gravity = GET_PARAM_FROM_GROUP(TypeTag, bool, Problem, EnableGravity); static const bool gravity = GET_PARAM_FROM_GROUP(TypeTag, bool, Problem, EnableGravity);
const auto& fluxVarsCache = elemFluxVarsCache[scvf]; const auto& fluxVarsCache = elemFluxVarsCache[scvf];
const auto& volVarsStencil = fluxVarsCache.advectionVolVarsStencil();
const auto& volVarsPositions = fluxVarsCache.advectionVolVarsPositions();
const auto& tij = fluxVarsCache.advectionTij(); const auto& tij = fluxVarsCache.advectionTij();
const bool isInteriorBoundary = enableInteriorBoundaries && fluxVarsCache.isInteriorBoundary();
// For interior Neumann boundaries when using Tpfa on boundaries, return the user-specified flux
// We assume phaseIdx = eqIdx here.
if (isInteriorBoundary
&& useTpfaBoundary
&& fluxVarsCache.interiorBoundaryDataSelf().faceType() == MpfaFaceTypes::interiorNeumann)
return scvf.area()*
elemVolVars[scvf.insideScvIdx()].extrusionFactor()*
problem.neumann(element, fvGeometry, elemVolVars, scvf)[phaseIdx];
// Calculate the interface density for gravity evaluation // Calculate the interface density for gravity evaluation
const auto rho = Implementation::interpolateDensity(fvGeometry, elemVolVars, scvf, fluxVarsCache, phaseIdx, isInteriorBoundary); const auto rho = interpolateDensity(fvGeometry, elemVolVars, scvf, fluxVarsCache, phaseIdx);
// calculate Tij*pj // Variable for the flux to be computed
Scalar flux(0.0); Scalar scvfFlux(0.0);
unsigned int localIdx = 0;
for (const auto volVarIdx : volVarsStencil) // index counter to get corresponding transmissibilities
unsigned int i = 0;
// add contributions from cell-centered unknowns
for (const auto volVarIdx : fluxVarsCache.advectionVolVarsStencil())
{ {
const auto& volVars = elemVolVars[volVarIdx]; const auto& volVars = elemVolVars[volVarIdx];
Scalar h = volVars.pressure(phaseIdx); Scalar h = volVars.pressure(phaseIdx);
...@@ -193,32 +182,41 @@ public: ...@@ -193,32 +182,41 @@ public:
if (gravity) if (gravity)
{ {
// gravitational acceleration in the center of the actual element // gravitational acceleration in the center of the actual element
const auto x = volVarsPositions[localIdx]; const auto x = fvGeometry.scv(volVarIdx).center();
const auto g = problem.gravityAtPos(x); const auto g = problem.gravityAtPos(x);
h -= rho*(g*x); h -= rho*(g*x);
} }
flux += tij[localIdx++]*h; scvfFlux += tij[i++]*h;
} }
// if no interior boundaries are present, return the flux // add contributions from possible dirichlet boundary conditions
if (!enableInteriorBoundaries) for (const auto& d : fluxVarsCache.advectionDirichletData())
return useTpfaBoundary ? flux : flux + fluxVarsCache.advectionNeumannFlux(phaseIdx); {
const auto& volVars = elemVolVars[d.volVarIndex];
Scalar h = volVars.pressure(phaseIdx);
// maybe add gravitational acceleration
if (gravity)
{
const auto x = d.ipGlobal;
const auto g = problem.gravityAtPos(x);
h -= rho*(g*x);
}
// Handle interior boundaries scvfFlux += tij[i++]*h;
flux += Implementation::computeInteriorBoundaryContribution(problem, fvGeometry, elemVolVars, fluxVarsCache, phaseIdx, rho); }
// return overall resulting flux // return the flux (maybe adjust the sign)
return useTpfaBoundary ? flux : flux + fluxVarsCache.advectionNeumannFlux(phaseIdx); return fluxVarsCache.advectionSwitchFluxSign() ? -scvfFlux : scvfFlux;
} }
private:
static Scalar interpolateDensity(const FVElementGeometry& fvGeometry, static Scalar interpolateDensity(const FVElementGeometry& fvGeometry,
const ElementVolumeVariables& elemVolVars, const ElementVolumeVariables& elemVolVars,
const SubControlVolumeFace& scvf, const SubControlVolumeFace& scvf,
const FluxVariablesCache& fluxVarsCache, const FluxVariablesCache& fluxVarsCache,
const unsigned int phaseIdx, const unsigned int phaseIdx)
const bool isInteriorBoundary)
{ {
static const bool gravity = GET_PARAM_FROM_GROUP(TypeTag, bool, Problem, EnableGravity); static const bool gravity = GET_PARAM_FROM_GROUP(TypeTag, bool, Problem, EnableGravity);
...@@ -226,14 +224,6 @@ public: ...@@ -226,14 +224,6 @@ public:
return Scalar(0.0); return Scalar(0.0);
else else
{ {
// Treat interior Dirichlet boundaries differently
if (isInteriorBoundary)
{
const auto& data = fluxVarsCache.interiorBoundaryDataSelf();
if (data.faceType() == MpfaFaceTypes::interiorDirichlet)
return data.facetVolVars(fvGeometry).density(phaseIdx);
}
// use arithmetic mean of the densities around the scvf // use arithmetic mean of the densities around the scvf
if (!scvf.boundary()) if (!scvf.boundary())
{ {
...@@ -246,44 +236,6 @@ public: ...@@ -246,44 +236,6 @@ public:
return elemVolVars[scvf.outsideScvIdx()].density(phaseIdx); return elemVolVars[scvf.outsideScvIdx()].density(phaseIdx);
} }
} }
static Scalar computeInteriorBoundaryContribution(const Problem& problem,
const FVElementGeometry& fvGeometry,
const ElementVolumeVariables& elemVolVars,
const FluxVariablesCache& fluxVarsCache,
unsigned int phaseIdx, Scalar rho)
{
static const bool gravity = GET_PARAM_FROM_GROUP(TypeTag, bool, Problem, EnableGravity);
// obtain the transmissibilites associated with all pressures
const auto& tij = fluxVarsCache.advectionTij();
// the interior dirichlet boundaries local indices start after
// the cell and the domain Dirichlet boundary pressures
const auto startIdx = fluxVarsCache.advectionVolVarsStencil().size();
// add interior Dirichlet boundary contributions
Scalar flux = 0.0;
for (auto&& data : fluxVarsCache.interiorBoundaryData())
{
if (data.faceType() == MpfaFaceTypes::interiorDirichlet)
{
Scalar h = data.facetVolVars(fvGeometry).pressure(phaseIdx);
if (gravity)
{
const auto x = fvGeometry.scvf(data.scvfIndex()).ipGlobal();
const auto g = problem.gravityAtPos(x);
h -= rho*(g*x);
}
flux += tij[startIdx + data.localIndexInInteractionVolume()]*h;
}
}
return flux;
}
}; };
} // end namespace } // end namespace
......
// -*- 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 index sets of the dual grid in mpfa schemes.
*/
#ifndef DUMUX_DISCRETIZATION_MPFA_DUALGRID_INDEXSET_BASE_HH
#define DUMUX_DISCRETIZATION_MPFA_DUALGRID_INDEXSET_BASE_HH
#include <dumux/implicit/cellcentered/mpfa/properties.hh>
#include <dumux/discretization/cellcentered/mpfa/fvgridgeometry.hh>
namespace Dumux
{
/*!
* \ingroup Mpfa
* \brief Nodal index set for the dual grid of mpfa schemes.
*/
template<class TypeTag>
class DualGridNodalIndexSet
{
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 PrimaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, PrimaryInteractionVolume);
using LocalIndexType = typename PrimaryInteractionVolume::Traits::LocalIndexType;
using LocalIndexContainer = typename PrimaryInteractionVolume::Traits::DynamicLocalIndexContainer;
using GlobalIndexContainer = typename PrimaryInteractionVolume::Traits::DynamicGlobalIndexContainer;
static const int dim = GridView::dimension;
public:
//! constructor
DualGridNodalIndexSet() : numBoundaryScvfs_(0) {}
// Inserts data for a given scvf. This should only called once per scvf!
void insert(const SubControlVolumeFace& scvf)
{
insert(scvf.boundary(),
scvf.index(),
scvf.insideScvIdx(),
scvf.outsideScvIndices());
}
// Inserts data for a given scvf. This should only called once per scvf!
void insert(const bool boundary,
const IndexType scvfIdx,
const IndexType insideScvIdx,
const std::vector<IndexType>& outsideScvIndices)
{
// check if it this really hasn't been called yet
assert( std::find_if( scvfIndices_.begin(),
scvfIndices_.end(),
scvfIdx
) == scvfIndices_.end() && "scvf has already been inserted!");
// the local index of the scvf data about to be inserted
const LocalIndexType curScvfLocalIdx = scvfIndices_.size();
// add global scvf data
GlobalIndexContainer scvIndices;
scvIndices.reserve(outsideScvIndices.size()+1);
scvIndices.push_back(insideScvIdx);
scvIndices.insert(scvIndices.end(), outsideScvIndices.begin(), outsideScvIndices.end());
// if scvf is on boundary, increase counter
if (boundary)
numBoundaryScvfs_++;
scvfIndices_.push_back(scvfIdx);
scvfIsOnBoundary_.push_back(boundary);
scvfNeighborScvIndicesGlobal_.emplace_back( std::move(scvIndices) );
// if entry for the inside scv exists append scvf local index, create otherwise
auto it = std::find( scvIndices_.begin(), scvIndices_.end(), insideScvIdx );
if (it != scvIndices_.end())
{
const auto localScvIdx = std::distance(scvIndices_.begin(), it);
localScvfIndicesInScv_[localScvIdx].push_back(curScvfLocalIdx);
}
else
{
LocalIndexContainer localScvfIndices;
localScvfIndices.reserve(dim);
localScvfIndices.push_back(curScvfLocalIdx);
localScvfIndicesInScv_.emplace_back( std::move(localScvfIndices) );
scvIndices_.push_back(insideScvIdx);
}
}
// This should be called AFTER (!) all scvf data has been inserted
void makeLocal()
{
// make sure the created index set makes sense so far
assert(checkGlobalIndexSet_());
// compute local neighboring scv indices for the scvfs
scvfNeighborScvIndices_.resize(numScvfs());
for (unsigned int i = 0; i < numScvfs(); ++i)
{
const auto& neighborsGlobal = scvfNeighborScvIndicesGlobal_[i];
const auto numNeighbors = scvfIsOnBoundary_[i] ? 1 : neighborsGlobal.size();
scvfNeighborScvIndices_[i].resize(numNeighbors);
for (unsigned int j = 0; j < numNeighbors; ++j)
scvfNeighborScvIndices_[i][j] = findLocalScvIdx_(neighborsGlobal[j]);
}
// delete global neighboring scv data (not used anymore)
scvfNeighborScvIndicesGlobal_.clear();
}
//! returns the number of scvs
std::size_t numScvs() const
{ return scvIndices_.size(); }
//! returns the number of scvfs
std::size_t numScvfs() const
{ return scvfIndices_.size(); }
//! returns the number of boundary scvfs
std::size_t numBoundaryScvfs() const
{ return numBoundaryScvfs_; }
//! returns the global scv indices connected to this dual grid node
const GlobalIndexContainer& globalScvIndices() const
{ return scvIndices_; }
//! returns the global scvf indices connected to this dual grid node
const GlobalIndexContainer& globalScvfIndices() const
{ return scvfIndices_; }
//! returns a global scv idx for a given local scv index
IndexType scvIdxGlobal(LocalIndexType scvIdxLocal) const
{ return scvIndices_[scvIdxLocal]; }
//! returns the local indices of the scvfs embedded in a local scv
const LocalIndexContainer& localScvfIndicesInScv(LocalIndexType scvIdxLocal) const
{ return localScvfIndicesInScv_[scvIdxLocal]; }
//! returns a global scvf idx for a given local scvf index
IndexType scvfIdxGlobal(LocalIndexType scvfIdxLocal) const
{ return scvfIndices_[scvfIdxLocal]; }
//! returns whether or not an scvf touches the boundary
bool scvfIsOnBoundary(LocalIndexType scvfIdxLocal) const
{ return scvfIsOnBoundary_[scvfIdxLocal]; }
//! returns the local indices of the neighboring scvs of an scvf
const LocalIndexContainer& localNeighboringScvIndices(LocalIndexType scvfIdxLocal) const
{ return scvfNeighborScvIndices_[scvfIdxLocal]; }
private:
//! returns the node-local scv index to a given global scv index
unsigned int findLocalScvIdx_(IndexType globalScvIdx) const
{
auto it = std::find( scvIndices_.begin(), scvIndices_.end(), globalScvIdx );
assert(it != scvIndices_.end() && "Global scv index not found in local container!");
return std::distance(scvIndices_.begin(), it);
}
//! checks whether or not the inserted index set makes sense
bool checkGlobalIndexSet_() const
{
//! do all scvs have dim embedded scvfs?
for (const auto& scvfIndices : localScvfIndicesInScv_)
if (scvfIndices.size() != dim)
DUNE_THROW(Dune::InvalidStateException, "Number of scv faces found for an scv != dimension");
//! are all scvfs unique?
for (const auto& scvfIdx : scvfIndices_)
for (const auto& scvfIdx2 : scvfIndices_)