diff --git a/dumux/discretization/cellcentered/mpfa/darcyslaw.hh b/dumux/discretization/cellcentered/mpfa/darcyslaw.hh index 50bf47b25a56cefb6e836247d1d307ebec9fce20..568c88befa292f696f3eeaf49f5752fd60861638 100644 --- a/dumux/discretization/cellcentered/mpfa/darcyslaw.hh +++ b/dumux/discretization/cellcentered/mpfa/darcyslaw.hh @@ -33,6 +33,7 @@ #include <dumux/common/parameters.hh> #include <dumux/implicit/properties.hh> +#include <dumux/discretization/cellcentered/mpfa/methods.hh> namespace Dumux @@ -53,6 +54,7 @@ class DarcysLawImplementation<TypeTag, DiscretizationMethods::CCMpfa> { using Problem = typename GET_PROP_TYPE(TypeTag, Problem); using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); + using InteractionVolume = typename GET_PROP_TYPE(TypeTag, InteractionVolume); using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); @@ -64,6 +66,8 @@ class DarcysLawImplementation<TypeTag, DiscretizationMethods::CCMpfa> using IndexType = typename GridView::IndexSet::IndexType; using Stencil = std::vector<IndexType>; + static const MpfaMethods method = GET_PROP_VALUE(TypeTag, MpfaMethod); + public: static Scalar flux(const Problem& problem, @@ -79,19 +83,10 @@ public: const auto& volVarsPositions = fluxVarsCache.advectionVolVarsPositions(phaseIdx); const auto& tij = fluxVarsCache.advectionTij(phaseIdx); - // interface density as arithmetic mean of the two neighbors (when gravity is on) + // interface density as arithmetic mean of the neighbors (when gravity is on) Scalar rho; if (gravity) - { - if (!scvf.boundary()) - { - rho = elemVolVars[scvf.outsideScvIdx()].density(phaseIdx); - rho += elemVolVars[scvf.insideScvIdx()].density(phaseIdx); - rho /= 2.0; - } - else - rho = elemVolVars[scvf.outsideScvIdx()].density(phaseIdx); - } + rho = interpolateDensity(elemVolVars, scvf, phaseIdx); // calculate Tij*pj Scalar flux(0.0); @@ -110,7 +105,6 @@ public: h -= rho*(g*x); } - flux += tij[localIdx++]*h; } @@ -130,6 +124,27 @@ public: else return globalFvGeometry.interactionVolumeSeed(scvf).globalScvIndices(); } + +private: + static Scalar interpolateDensity(const ElementVolumeVariables& elemVolVars, + const SubControlVolumeFace& scvf, + const unsigned int phaseIdx) + { + // use arithmetic mean of the densities around the scvf + if (!scvf.boundary()) + { + const auto& outsideScvIndices = scvf.outsideScvIndices(); + + Scalar rho = elemVolVars[scvf.insideScvIdx()].density(phaseIdx); + for (auto outsideIdx : outsideScvIndices) + rho += elemVolVars[outsideIdx].density(phaseIdx); + rho /= outsideScvIndices.size(); + + return rho; + } + else + return elemVolVars[scvf.outsideScvIdx()].density(phaseIdx); + } }; } // end namespace diff --git a/dumux/discretization/cellcentered/mpfa/elementfluxvariablescache.hh b/dumux/discretization/cellcentered/mpfa/elementfluxvariablescache.hh index ed0c3e10e681e8b522b7106aa7ba548289cdd9a6..0d8352e5e0da8f5dc234182539d630d42c4e4947 100644 --- a/dumux/discretization/cellcentered/mpfa/elementfluxvariablescache.hh +++ b/dumux/discretization/cellcentered/mpfa/elementfluxvariablescache.hh @@ -44,6 +44,9 @@ class CCMpfaElementFluxVariablesCache; template<class TypeTag> class CCMpfaElementFluxVariablesCache<TypeTag, true> { + // the local jacobian needs to be able to update the cache during assembly + friend typename GET_PROP_TYPE(TypeTag, LocalJacobian); + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using IndexType = typename GridView::IndexSet::IndexType; @@ -68,11 +71,6 @@ public: const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars) {} - // Specialization for the global caching being enabled - do nothing here - void update(const Element& element, - const FVElementGeometry& fvGeometry, - const ElementVolumeVariables& elemVolVars) {} - // Specialization for the global caching being enabled - do nothing here void bindScvf(const Element& element, const FVElementGeometry& fvGeometry, @@ -89,6 +87,11 @@ public: private: const GlobalFluxVariablesCache* globalFluxVarsCachePtr_; + + // Specialization for the global caching being enabled - do nothing here + void update(const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars) {} }; /*! @@ -98,6 +101,9 @@ private: template<class TypeTag> class CCMpfaElementFluxVariablesCache<TypeTag, false> { + // the local jacobian needs to be able to update the cache during assembly + friend typename GET_PROP_TYPE(TypeTag, LocalJacobian); + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using IndexType = typename GridView::IndexSet::IndexType; @@ -119,6 +125,7 @@ public: const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars) { + // TODO DUNE_THROW(Dune::InvalidStateException, "Does local flux var cache binding make sense in general?"); } @@ -129,52 +136,53 @@ public: const ElementVolumeVariables& elemVolVars) { clear(); + const auto& problem = globalFluxVarsCache().problem_(); const auto& globalFvGeometry = problem.model().globalFvGeometry(); + const auto& neighborStencil = problem.model().stencils(element).neighborStencil(); + const auto& assemblyMap = problem.model().localJacobian().assemblyMap(); + const auto globalI = problem.elementMapper().index(element); - // // first prepare all the global indices + // reserve initial guess of memory (won't be enough though - several scvfs per neighbor will be required) + globalScvfIndices_.reserve(fvGeometry.numScvf() + assemblyMap[globalI].size()); + + // first add all the indices inside the element for (auto&& scvf : scvfs(fvGeometry)) - { - const bool boundary = globalFvGeometry.scvfTouchesBoundary(scvf); + globalScvfIndices_.push_back(scvf.index()); + + // for the indices in the neighbors, use assembly map of the local jacobian + for (unsigned int j = 0; j < neighborStencil.size(); ++j) + for (auto fluxVarIdx : assemblyMap[globalI][j]) + globalScvfIndices_.push_back(fluxVarIdx); - if (boundary) - { - const auto& ivSeed = globalFvGeometry.boundaryInteractionVolumeSeed(scvf); - for (auto scvfIdx : ivSeed.globalScvfIndices()) - globalScvfIndices_.push_back(scvfIdx); - } - else - { - const auto& ivSeed = globalFvGeometry.interactionVolumeSeed(scvf); - for (auto scvfIdx : ivSeed.globalScvfIndices()) - globalScvfIndices_.push_back(scvfIdx); - } - } // make global indices unique std::sort(globalScvfIndices_.begin(), globalScvfIndices_.end()); globalScvfIndices_.erase(std::unique(globalScvfIndices_.begin(), globalScvfIndices_.end()), globalScvfIndices_.end()); - // prepare all the caches of the scvfs inside the corresponding interaction volume using helper class + // prepare all the caches of the scvfs inside the corresponding interaction volumes using helper class fluxVarsCache_.resize(globalScvfIndices_.size()); for (auto&& scvf : scvfs(fvGeometry)) { if (!(*this)[scvf].isUpdated()) FluxVariablesCacheFiller::fillFluxVarCache(problem, element, fvGeometry, elemVolVars, scvf, *this); } - } - // This function updates the transmissibilities after the solution has been deflected during jacobian assembly - void update(const Element& element, - const FVElementGeometry& fvGeometry, - const ElementVolumeVariables& elemVolVars) - { - for (auto&& scvf : scvfs(fvGeometry)) - (*this)[scvf].setOutdated(); - - for (auto&& scvf : scvfs(fvGeometry)) + // prepare the caches in the remaining neighbors + unsigned int j = 0; + for (auto globalJ : neighborStencil) { - if (!(*this)[scvf].isUpdated()) - FluxVariablesCacheFiller::updateFluxVarCache(globalFluxVarsCache().problem_(), element, fvGeometry, elemVolVars, scvf, *this); + for (auto fluxVarIdx : assemblyMap[globalI][j]) + { + const auto& scvf = fvGeometry.scvf(fluxVarIdx); + if (!(*this)[scvf].isUpdated()) + { + auto elementJ = globalFvGeometry.element(globalJ); + FluxVariablesCacheFiller::fillFluxVarCache(problem, elementJ, fvGeometry, elemVolVars, scvf, *this); + } + } + + // increment counter + j++; } } @@ -183,6 +191,7 @@ public: const ElementVolumeVariables& elemVolVars, const SubControlVolumeFace& scvf) { + // TODO DUNE_THROW(Dune::InvalidStateException, "Does binding of one scvf make sense in general?"); } @@ -212,11 +221,25 @@ private: globalScvfIndices_.clear(); } + // This function updates the transmissibilities after the solution has been deflected during jacobian assembly + void update(const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars) + { + for (auto&& scvf : scvfs(fvGeometry)) + (*this)[scvf].setUpdateStatus(false); + + for (auto&& scvf : scvfs(fvGeometry)) + { + if (!(*this)[scvf].isUpdated()) + FluxVariablesCacheFiller::updateFluxVarCache(globalFluxVarsCache().problem_(), element, fvGeometry, elemVolVars, scvf, *this); + } + } + // get index of scvf in the local container int getLocalScvfIdx_(const int scvfIdx) const { auto it = std::lower_bound(globalScvfIndices_.begin(), globalScvfIndices_.end(), scvfIdx); - assert(it != globalScvfIndices_.end() && "Could not find the flux vars cache for scvfIdx"); assert(globalScvfIndices_[std::distance(globalScvfIndices_.begin(), it)] == scvfIdx && "Could not find the flux vars cache for scvfIdx"); return std::distance(globalScvfIndices_.begin(), it); } diff --git a/dumux/discretization/cellcentered/mpfa/elementvolumevariables.hh b/dumux/discretization/cellcentered/mpfa/elementvolumevariables.hh index 1df49787cfc5aa9561468ed171e8a7609a1ae7b5..8dc18e813da59e936bb80cf5f7c8e866d22df51d 100644 --- a/dumux/discretization/cellcentered/mpfa/elementvolumevariables.hh +++ b/dumux/discretization/cellcentered/mpfa/elementvolumevariables.hh @@ -91,6 +91,7 @@ class CCMpfaElementVolumeVariables<TypeTag, /*enableGlobalVolVarsCache*/false> { using Problem = typename GET_PROP_TYPE(TypeTag, Problem); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using MpfaHelper = typename GET_PROP_TYPE(TypeTag, MpfaHelper); using SolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); using GlobalVolumeVariables = typename GET_PROP_TYPE(TypeTag, GlobalVolumeVariables); @@ -114,7 +115,7 @@ public: const SolutionVector& sol) { const auto& problem = globalVolVars().problem_(); - auto eIdx = problem.elementMapper().index(element); + const auto& globalFvGeometry = problem.model().globalFvGeometry(); // stencil information const auto& neighborStencil = problem.model().stencils(element).neighborStencil(); @@ -126,6 +127,7 @@ public: int localIdx = 0; // update the volume variables of the element at hand + auto eIdx = problem.elementMapper().index(element); auto&& scvI = fvGeometry.scv(eIdx); volumeVariables_[localIdx].update(sol[eIdx], problem, element, scvI); volVarIndices_[localIdx] = scvI.index(); @@ -134,86 +136,92 @@ public: // Update the volume variables of the neighboring elements for (auto globalJ : neighborStencil) { - const auto& elementJ = fvGeometry.globalFvGeometry().element(globalJ); + const auto& elementJ = globalFvGeometry.element(globalJ); auto&& scvJ = fvGeometry.scv(globalJ); volumeVariables_[localIdx].update(sol[globalJ], problem, elementJ, scvJ); volVarIndices_[localIdx] = scvJ.index(); ++localIdx; } - std::size_t neighborScvfEstimate = neighborStencil.size()*dim*(2*dim-2); - std::vector<IndexType> finishedBoundaries; - finishedBoundaries.reserve(neighborScvfEstimate); + // if the element is connected to a boundary, additionally prepare BCs + bool boundary = element.hasBoundaryIntersections(); + if (!boundary) + for (auto&& scvf : scvfs(fvGeometry)) + if (globalFvGeometry.scvfTouchesBoundary(scvf)) + { + boundary = true; + break; + } - // if the element is connected to a boundary, additionally treat the BC - if (element.hasBoundaryIntersections()) + if (boundary) { - // reserve enough space for the boundary volume variables - volumeVariables_.reserve(numDofs+neighborScvfEstimate); - volVarIndices_.reserve(numDofs+neighborScvfEstimate); - - for (auto&& scvf : scvfs(fvGeometry)) + // reserve memory (12 is the case of 3d hexahedron with one face on boundary) + std::size_t estimate = 12; + std::vector<IndexType> finishedBoundaries; + finishedBoundaries.reserve(estimate); + volumeVariables_.reserve(numDofs+estimate); + volVarIndices_.reserve(numDofs+estimate); + + // treat the BCs inside the element + if (element.hasBoundaryIntersections()) { - // if we are not on a boundary, skip to the next scvf - if (!scvf.boundary()) - continue; - - // boundary volume variables - VolumeVariables dirichletVolVars; - const auto dirichletPriVars = problem.dirichlet(element, scvf); - dirichletVolVars.update(dirichletPriVars, problem, element, scvI); - volumeVariables_.emplace_back(std::move(dirichletVolVars)); - - // boundary vol var index - auto bVolVarIdx = scvf.outsideScvIdx(); - volVarIndices_.push_back(bVolVarIdx); - finishedBoundaries.push_back(bVolVarIdx); + for (auto&& scvf : scvfs(fvGeometry)) + { + // if we are not on a boundary, skip to the next scvf + if (!scvf.boundary()) + continue; + + // boundary volume variables + VolumeVariables dirichletVolVars; + const auto dirichletPriVars = problem.dirichlet(element, scvf); + dirichletVolVars.update(dirichletPriVars, problem, element, scvI); + volumeVariables_.emplace_back(std::move(dirichletVolVars)); + + // boundary vol var index + auto bVolVarIdx = scvf.outsideScvIdx(); + volVarIndices_.push_back(bVolVarIdx); + finishedBoundaries.push_back(bVolVarIdx); + } } - } - // Update boundary volume variables in the neighbors - const auto& globalFvGeometry = problem.model().globalFvGeometry(); - for (auto&& scvf : scvfs(fvGeometry)) - { - // reserve enough space for the boundary volume variables - volumeVariables_.reserve(numDofs+neighborScvfEstimate); - volVarIndices_.reserve(numDofs+neighborScvfEstimate); - - // skip the rest if the scvf does not touch a boundary - const bool boundary = globalFvGeometry.scvfTouchesBoundary(scvf); - if (!boundary) - continue; - - // loop over all the scvfs in the interaction region - const auto& ivSeed = globalFvGeometry.boundaryInteractionVolumeSeed(scvf); - for (auto scvfIdx : ivSeed.globalScvfIndices()) + // Update boundary volume variables in the neighbors + for (auto&& scvf : scvfs(fvGeometry)) { - auto&& ivScvf = fvGeometry.scvf(scvfIdx); - - if (!ivScvf.boundary() || contains(ivScvf.outsideScvIdx(), finishedBoundaries)) + // skip the rest if the scvf does not touch a boundary + if (!globalFvGeometry.scvfTouchesBoundary(scvf)) continue; - // that means we are on a not yet handled boundary scvf - auto insideScvIdx = ivScvf.insideScvIdx(); - auto&& ivScv = fvGeometry.scv(insideScvIdx); - auto ivElement = globalFvGeometry.element(insideScvIdx); - - // boundary volume variables - VolumeVariables dirichletVolVars; - const auto dirichletPriVars = problem.dirichlet(ivElement, ivScvf); - dirichletVolVars.update(dirichletPriVars, problem, ivElement, ivScv); - volumeVariables_.emplace_back(std::move(dirichletVolVars)); - - // boundary vol var index - auto bVolVarIdx = ivScvf.outsideScvIdx(); - volVarIndices_.push_back(bVolVarIdx); - finishedBoundaries.push_back(bVolVarIdx); + // loop over all the scvfs in the interaction region + const auto& ivSeed = globalFvGeometry.boundaryInteractionVolumeSeed(scvf); + for (auto scvfIdx : ivSeed.globalScvfIndices()) + { + auto&& ivScvf = fvGeometry.scvf(scvfIdx); + + if (!ivScvf.boundary() || MpfaHelper::contains(finishedBoundaries, ivScvf.outsideScvIdx())) + continue; + + // that means we are on a not yet handled boundary scvf + auto insideScvIdx = ivScvf.insideScvIdx(); + auto&& ivScv = fvGeometry.scv(insideScvIdx); + auto ivElement = globalFvGeometry.element(insideScvIdx); + + // boundary volume variables + VolumeVariables dirichletVolVars; + const auto dirichletPriVars = problem.dirichlet(ivElement, ivScvf); + dirichletVolVars.update(dirichletPriVars, problem, ivElement, ivScv); + volumeVariables_.emplace_back(std::move(dirichletVolVars)); + + // boundary vol var index + auto bVolVarIdx = ivScvf.outsideScvIdx(); + volVarIndices_.push_back(bVolVarIdx); + finishedBoundaries.push_back(bVolVarIdx); + } } - } - // free unused memory - volumeVariables_.shrink_to_fit(); - volVarIndices_.shrink_to_fit(); + // free unused memory + volumeVariables_.shrink_to_fit(); + volVarIndices_.shrink_to_fit(); + } } // Binding of an element, prepares only the volume variables of the element @@ -258,10 +266,6 @@ private: return std::distance(volVarIndices_.begin(), it); } - const bool contains(const IndexType idx, - const std::vector<IndexType>& indices) const - { return std::find(indices.begin(), indices.end(), idx) != indices.end(); } - std::vector<IndexType> volVarIndices_; std::vector<VolumeVariables> volumeVariables_; }; diff --git a/dumux/discretization/cellcentered/mpfa/facetypes.hh b/dumux/discretization/cellcentered/mpfa/facetypes.hh index f1768e84be166ee38cbb12f11d05524614f54cd6..8da25323436a11e6e15353e9f3853f38ef69c04e 100644 --- a/dumux/discretization/cellcentered/mpfa/facetypes.hh +++ b/dumux/discretization/cellcentered/mpfa/facetypes.hh @@ -29,9 +29,7 @@ namespace Dumux { interior, neumann, - dirichlet, - interiorNeumann, - interiorDirichlet + dirichlet }; } // end namespace diff --git a/dumux/discretization/cellcentered/mpfa/fluxvariablescachefiller.hh b/dumux/discretization/cellcentered/mpfa/fluxvariablescachefiller.hh index 1db15ed006c0aaed16f68d33cdcd04bb3d9d3db9..bc27e2f94d96071e683f9d5a9d25d962f555cb1a 100644 --- a/dumux/discretization/cellcentered/mpfa/fluxvariablescachefiller.hh +++ b/dumux/discretization/cellcentered/mpfa/fluxvariablescachefiller.hh @@ -108,7 +108,7 @@ public: { if (!updateNeumannOnly) cache.updateAdvection(problem, element, fvGeometry, elemVolVars, scvf, iv); - cache.setUpdated(); + cache.setUpdateStatus(true); } // update flux variable caches of the other scvfs of the interaction volume @@ -124,7 +124,7 @@ public: { if (!updateNeumannOnly) cacheJ.updateAdvection(problem, elementJ, fvGeometry, elemVolVars, scvfJ, iv); - cacheJ.setUpdated(); + cacheJ.setUpdateStatus(true); } } } @@ -140,7 +140,7 @@ public: const auto scvfIdx = scvf.index(); auto& cache = fluxVarsCache[scvfIdx]; cache.updateAdvection(problem, element, fvGeometry, elemVolVars, scvf, iv); - cache.setUpdated(); + cache.setUpdateStatus(true); // update flux variable caches of the other scvfs of the interaction volume for (const auto scvfIdxJ : iv.globalScvfs()) @@ -151,7 +151,7 @@ public: const auto elementJ = problem.model().globalFvGeometry().element(scvfJ.insideScvIdx()); auto& cacheJ = fluxVarsCache[scvfIdxJ]; cacheJ.updateAdvection(problem, elementJ, fvGeometry, elemVolVars, scvfJ, iv); - cacheJ.setUpdated(); + cacheJ.setUpdateStatus(true); } } } @@ -168,7 +168,7 @@ public: { if (!solDependentParams) { - fluxVarsCache[scvf.index()].setUpdated(); + fluxVarsCache[scvf.index()].setUpdateStatus(true); // if we do not use tpfa on the boundary, we have to update the neumann fluxes if (!useTpfaBoundary) diff --git a/dumux/discretization/cellcentered/mpfa/fvelementgeometry.hh b/dumux/discretization/cellcentered/mpfa/fvelementgeometry.hh index 83982b7d3b5f5670c5d3bb9fd48b39160d1cd737..9dcd2ff583d4f0175ee101840c45b7feea8d6451 100644 --- a/dumux/discretization/cellcentered/mpfa/fvelementgeometry.hh +++ b/dumux/discretization/cellcentered/mpfa/fvelementgeometry.hh @@ -30,8 +30,6 @@ #include <dumux/discretization/scvandscvfiterators.hh> #include <dumux/implicit/cellcentered/mpfa/properties.hh> -#include "mpfageometryhelper.hh" - namespace Dumux { /*! @@ -148,6 +146,7 @@ class CCMpfaFVElementGeometry<TypeTag, false> using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using IndexType = typename GridView::IndexSet::IndexType; + using MpfaHelper = typename GET_PROP_TYPE(TypeTag, MpfaHelper); using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); using Element = typename GridView::template Codim<0>::Entity; @@ -157,10 +156,10 @@ class CCMpfaFVElementGeometry<TypeTag, false> using ScvfIterator = Dumux::ScvfIterator<SubControlVolumeFace, std::vector<IndexType>, ThisType>; static const int dim = GridView::dimension; + static const int dimWorld = GridView::dimensionworld; using CoordScalar = typename GridView::ctype; using ReferenceElements = typename Dune::ReferenceElements<CoordScalar, dim>; - - using MpfaGeometryHelper = Dumux::MpfaGeometryHelper<GridView, dim>; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; public: //! Constructor @@ -226,36 +225,34 @@ public: { bindElement(element); + // get some references for convenience + const auto& problem = globalFvGeometry().problem_(); + const auto& assemblyMap = problem.model().localJacobian().assemblyMap(); + const auto& neighborStencil = problem.model().stencils(element).neighborStencil(); + const auto globalI = problem.elementMapper().index(element); + // reserve memory - auto numNeighbors = globalFvGeometry().problem_().model().stencils(element).neighborStencil().size(); - auto numNeighborScvf = numNeighbors*dim*(2*dim-2); + const auto numNeighbors = neighborStencil.size(); + const auto estimate = numNeighbors*dim*(4*dim-4); // estimate holds for quadrilaterals in 2D/3D, overestimates else neighborScvs_.reserve(numNeighbors); neighborScvIndices_.reserve(numNeighbors); - neighborScvfIndices_.reserve(numNeighborScvf); - neighborScvfs_.reserve(numNeighborScvf); - - // build scvfs connected to the interaction regions around the element vertices - std::vector<IndexType> finishedVertices; - finishedVertices.reserve(element.geometry().corners()); - const auto eIdx = globalFvGeometry().problem_().elementMapper().index(element); - for (auto&& scvf : scvfs_) + neighborScvfIndices_.reserve(estimate); + neighborScvfs_.reserve(estimate); + + // build scvfs in neighbors + // use the assembly map to determine which faces are necessary + unsigned int j = 0; + for (auto globalJ : neighborStencil) { - // skip the rest if scvf has been handled already - if (contains(scvf.vertexIndex(), finishedVertices)) - continue; + // get data on the neighbor + auto elementJ = globalFvGeometry().element(globalJ); + const auto& scvfIndices = assemblyMap[globalI][j]; - if (globalFvGeometry().scvfTouchesBoundary(scvf)) - { - const auto& ivSeed = globalFvGeometry().boundaryInteractionVolumeSeed(scvf); - handleInteractionRegion(ivSeed); - } - else - { - const auto& ivSeed = globalFvGeometry().interactionVolumeSeed(scvf); - handleInteractionRegion(ivSeed); - } + // make neighbor geometries + makeNeighborGeometries(elementJ, globalJ, scvfIndices); - finishedVertices.push_back(scvf.vertexIndex()); + // increment counter + j++; } // free unused memory @@ -294,12 +291,8 @@ private: const auto& scvFaceIndices = globalFvGeometry().scvfIndicesOfScv(eIdx); const auto& neighborVolVarIndices = globalFvGeometry().neighborVolVarIndices(eIdx); - // the element geometry and reference element - auto g = element.geometry(); - const auto& referenceElement = ReferenceElements::general(g.type()); - - // The geometry helper class - MpfaGeometryHelper geomHelper(g); + // Instantiate helper class to pass it to scvf constructors + MpfaHelper helper; // the quadrature point to be used on the scvf const Scalar q = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Mpfa, Q); @@ -309,114 +302,139 @@ private: scvfIndices_.reserve(numLocalScvf); scvfs_.reserve(numLocalScvf); + // for dim < dimWorld we'll have multiple intersections at one point + // we store here the centers of those we have handled already + std::vector<GlobalPosition> finishedCenters; + int scvfCounter = 0; for (const auto& is : intersections(globalFvGeometry().gridView(), element)) { - auto isGeometry = is.geometry(); + // if we are dealing with a lower dimensional network + // only make a new scvf if we haven't handled it yet + if (dim < dimWorld) + { + auto isCenter = is.geometry().center(); + if(MpfaHelper::contains(finishedCenters, isCenter)) + continue; + else + finishedCenters.push_back(isCenter); + } + + // get the intersection corners according to generic numbering + auto numCorners = is.geometry().corners(); + std::vector<GlobalPosition> isCorners(numCorners); + + // if outside level > inside level, use the outside element in the following + bool useNeighbor = is.neighbor() && is.outside().level() > element.level(); + const auto& e = useNeighbor ? is.outside() : element; + const auto indexInElement = useNeighbor ? is.indexInOutside() : is.indexInInside(); + const auto eg = e.geometry(); + const auto& refElement = ReferenceElements::general(eg.type()); - // make the scv faces of the intersection - for (unsigned int faceScvfIdx = 0; faceScvfIdx < isGeometry.corners(); ++faceScvfIdx) + for (unsigned int c = 0; c < numCorners; ++c) + isCorners[c] = eg.global(refElement.position(refElement.subEntity(indexInElement, 1, c, dim), dim)); + + // make the scv faces belonging to each corner of the intersection + for (unsigned int c = 0; c < numCorners; ++c) { - // get the global vertex index the scv face is connected to (mpfa-o method does not work for hanging nodes!) - const auto vIdxLocal = referenceElement.subEntity(is.indexInInside(), 1, faceScvfIdx, dim); - const auto vIdxGlobal = problem.vertexMapper().subIndex(element, vIdxLocal, dim); + // get the global vertex index the scv face is connected to + auto vIdxLocal = refElement.subEntity(indexInElement, 1, c, dim); + auto vIdxGlobal = problem.vertexMapper().subIndex(e, vIdxLocal, dim); // do not build scvfs connected to a processor boundary if (globalFvGeometry().isGhostVertex(vIdxGlobal)) continue; - scvfs_.emplace_back(geomHelper, - geomHelper.getScvfCorners(isGeometry, faceScvfIdx), + scvfs_.emplace_back(helper, + helper.getScvfCorners(isCorners, c), is.centerUnitOuterNormal(), vIdxGlobal, vIdxLocal, scvFaceIndices[scvfCounter], - std::array<IndexType, 2>({{eIdx, neighborVolVarIndices[scvfCounter]}}), + eIdx, + neighborVolVarIndices[scvfCounter], q, is.boundary() ); scvfIndices_.emplace_back(scvFaceIndices[scvfCounter]); scvfCounter++; - } } - - // free unused memory - scvfs_.shrink_to_fit(); - scvfIndices_.shrink_to_fit(); } - template<typename Seed> - void handleInteractionRegion(const Seed& ivSeed) - { - // the element index of the element which we are binding to - auto eIdx = globalFvGeometry().problem_().elementMapper().index(*elementPtr_); - - // the scvf indices in the interaction region - auto scvfIndices = ivSeed.globalScvfIndices(); - - // make the scvs/scvfs for each element in the interaction region - for (auto eIdxGlobal : ivSeed.globalScvIndices()) - { - if (eIdxGlobal != eIdx) - { - auto element = globalFvGeometry().element(eIdxGlobal); - makeNeighborGeometries(element, eIdxGlobal, scvfIndices); - } - } - } - - //! create the necessary scvs and scvfs of the neighbor elements to the bound elements + //! create the scv and necessary scvfs of a neighboring element template<typename IndexVector> - void makeNeighborGeometries(const Element& element, IndexType eIdxGlobal, const IndexVector& ivScvfs) + void makeNeighborGeometries(const Element& element, IndexType eIdxGlobal, const IndexVector& scvfIndices) { // create the neighbor scv if it doesn't exist yet - if (!contains(eIdxGlobal, neighborScvIndices_)) - { - neighborScvs_.emplace_back(element.geometry(), eIdxGlobal); - neighborScvIndices_.push_back(eIdxGlobal); - } + neighborScvs_.emplace_back(element.geometry(), eIdxGlobal); + neighborScvIndices_.push_back(eIdxGlobal); + // get data on the scv faces const auto& scvFaceIndices = globalFvGeometry().scvfIndicesOfScv(eIdxGlobal); const auto& neighborVolVarIndices = globalFvGeometry().neighborVolVarIndices(eIdxGlobal); - // the element geometry and reference element - auto g = element.geometry(); - const auto& referenceElement = ReferenceElements::general(g.type()); - - // The geometry helper class - MpfaGeometryHelper geomHelper(g); + // Instantiate a helper class to pass it to scvf constructors + MpfaHelper helper; // the quadrature point to be used on the scvf const Scalar q = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Mpfa, Q); + // for dim < dimWorld we'll have multiple intersections at one point + // we store here the centers of those we have handled already + std::vector<GlobalPosition> finishedCenters; + int scvfCounter = 0; for (const auto& is : intersections(globalFvGeometry().gridView(), element)) { - auto isGeometry = is.geometry(); + // if we are dealing with a lower dimensional network + // only make a new scvf if we haven't handled it yet + if (dim < dimWorld) + { + auto isCenter = is.geometry().center(); + if(MpfaHelper::contains(finishedCenters, isCenter)) + continue; + else + finishedCenters.push_back(isCenter); + } + + // get the intersection corners according to generic numbering + auto numCorners = is.geometry().corners(); + std::vector<GlobalPosition> isCorners(numCorners); + + // if outside level > inside level, use the outside element in the following + bool useNeighbor = is.neighbor() && is.outside().level() > element.level(); + const auto& e = useNeighbor ? is.outside() : element; + const auto indexInElement = useNeighbor ? is.indexInOutside() : is.indexInInside(); + const auto eg = e.geometry(); + const auto& refElement = ReferenceElements::general(eg.type()); - // make the scv faces of the intersection - for (unsigned int faceScvfIdx = 0; faceScvfIdx < isGeometry.corners(); ++faceScvfIdx) + for (unsigned int c = 0; c < numCorners; ++c) + isCorners[c] = eg.global(refElement.position(refElement.subEntity(indexInElement, 1, c, dim), dim)); + + // make the scv faces belonging to each corner of the intersection + for (unsigned int c = 0; c < numCorners; ++c) { - // get the global vertex index the scv face is connected to (mpfa-o method does not work for hanging nodes!) - const auto vIdxLocal = referenceElement.subEntity(is.indexInInside(), 1, faceScvfIdx, dim); - const auto vIdxGlobal = globalFvGeometry().problem_().vertexMapper().subIndex(element, vIdxLocal, dim); + // get the global vertex index the scv face is connected to + auto vIdxLocal = refElement.subEntity(indexInElement, 1, c, dim); + auto vIdxGlobal = globalFvGeometry().problem_().vertexMapper().subIndex(e, vIdxLocal, dim); // do not build scvfs connected to a processor boundary if (globalFvGeometry().isGhostVertex(vIdxGlobal)) continue; - // if the actual global scvf index is in the container, make scvf - if (contains(scvFaceIndices[scvfCounter], ivScvfs)) + // only build the scvf if it is in the list of necessary indices + if (MpfaHelper::contains(scvfIndices, scvFaceIndices[scvfCounter])) { - neighborScvfs_.emplace_back(geomHelper, - geomHelper.getScvfCorners(isGeometry, faceScvfIdx), + neighborScvfs_.emplace_back(helper, + helper.getScvfCorners(isCorners, c), is.centerUnitOuterNormal(), vIdxGlobal, vIdxLocal, scvFaceIndices[scvfCounter], - std::array<IndexType, 2>({{eIdxGlobal, neighborVolVarIndices[scvfCounter]}}), + eIdxGlobal, + neighborVolVarIndices[scvfCounter], q, is.boundary() ); @@ -424,6 +442,7 @@ private: neighborScvfIndices_.emplace_back(scvFaceIndices[scvfCounter]); } + // increment counter either way scvfCounter++; } } @@ -437,11 +456,6 @@ private: return std::distance(indices.begin(), it); } - //! Returns whether or not an index is stored in a given vector - const bool contains(const IndexType idx, - const std::vector<IndexType>& indices) const - { return std::find(indices.begin(), indices.end(), idx) != indices.end(); } - void clear() { scvIndices_.clear(); diff --git a/dumux/discretization/cellcentered/mpfa/globalfvgeometry.hh b/dumux/discretization/cellcentered/mpfa/globalfvgeometry.hh index e8c8a148fc47cca4ef2253e1dcee27a94044db54..e8e02feadc1cad1f7c31ed1c70cdf00908ab6949 100644 --- a/dumux/discretization/cellcentered/mpfa/globalfvgeometry.hh +++ b/dumux/discretization/cellcentered/mpfa/globalfvgeometry.hh @@ -25,14 +25,23 @@ #ifndef DUMUX_DISCRETIZATION_CC_MPFA_GLOBALFVGEOMETRY_HH #define DUMUX_DISCRETIZATION_CC_MPFA_GLOBALFVGEOMETRY_HH +#include <dumux/discretization/cellcentered/mpfa/globalfvgeometrybase.hh> #include "methods.hh" namespace Dumux { -//! forward declaration of the method-specific specialization +//! Forward declaration of the method-specific specialization +//! By default, the methods simply inherit from the base class +//! Methods that use a different implementation have to provide this below template<class TypeTag, MpfaMethods method, bool EnableFVElementGeometryCache> -class CCMpfaGlobalFVGeometryImplementation -{}; +class CCMpfaGlobalFVGeometryImplementation : public CCMpfaGlobalFVGeometryBase<TypeTag, EnableFVElementGeometryCache> +{ + using ParentType = CCMpfaGlobalFVGeometryBase<TypeTag, EnableFVElementGeometryCache>; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + +public: + CCMpfaGlobalFVGeometryImplementation(const GridView& gridView) : ParentType(gridView) {} +}; /*! * \ingroup ImplicitModel @@ -45,8 +54,6 @@ using CCMpfaGlobalFVGeometry = CCMpfaGlobalFVGeometryImplementation<TypeTag, GET } // end namespace -// the specializations of this class for the available methods have to be included here -#include <dumux/discretization/cellcentered/mpfa/omethod/globalfvgeometry.hh> -#include <dumux/discretization/cellcentered/mpfa/omethodfps/globalfvgeometry.hh> +// further specializations of this class for the available methods have to be included here #endif diff --git a/dumux/discretization/cellcentered/mpfa/globalfvgeometrybase.hh b/dumux/discretization/cellcentered/mpfa/globalfvgeometrybase.hh index 0f9e80e1ff92c7ea99324e4fa77ec0ef5049ed30..ef5420f94e8f38d68ef10e1a0dc75bbfc15749c4 100644 --- a/dumux/discretization/cellcentered/mpfa/globalfvgeometrybase.hh +++ b/dumux/discretization/cellcentered/mpfa/globalfvgeometrybase.hh @@ -18,7 +18,7 @@ *****************************************************************************/ /*! * \file - * \brief Base class for the finite volume geometry vector for box models + * \brief Base class for the finite volume geometry vector for mpfa models * This builds up the sub control volumes and sub control volume faces * for each element. */ @@ -31,82 +31,8 @@ #include <dumux/implicit/cellcentered/mpfa/properties.hh> #include <dumux/common/elementmap.hh> -#include "mpfageometryhelper.hh" - namespace Dumux { -//! Helper class for the global fv geometries -template<class TypeTag> -class CCMpfaGlobalFVGeometryHelper -{ - using Problem = typename GET_PROP_TYPE(TypeTag, Problem); - using GridView = typename GET_PROP_TYPE(TypeTag, GridView); - using CoordScalar = typename GridView::ctype; - - static const int dim = GridView::dimension; - using ReferenceElements = typename Dune::ReferenceElements<CoordScalar, dim>; - -public: - static std::vector<bool> findGhostVertices(const Problem& problem, const GridView& gridView) - { - std::vector<bool> ghostVertices(gridView.size(dim), false); - - // if not run in parallel, skip the rest - if (Dune::MPIHelper::getCollectiveCommunication().size() == 1) - return ghostVertices; - - // mpfa methods can not yet handle ghost cells - if (gridView.ghostSize(0) > 0) - DUNE_THROW(Dune::InvalidStateException, "Mpfa methods in parallel do not work with ghost cells. Use overlap cells instead."); - - // mpfa methods have to have overlapping cells - if (gridView.overlapSize(0) == 0) - DUNE_THROW(Dune::InvalidStateException, "Grid no overlaping cells. This is required by mpfa methods in parallel."); - - for (const auto& element : elements(gridView)) - { - for (const auto& is : intersections(gridView, element)) - { - if (!is.neighbor() && !is.boundary()) - { - const auto& refElement = ReferenceElements::general(element.geometry().type()); - for (unsigned int isVertex = 0; isVertex < is.geometry().corners(); ++isVertex) - { - const auto vIdxLocal = refElement.subEntity(is.indexInInside(), 1, isVertex, dim); - const auto vIdxGlobal = problem.vertexMapper().subIndex(element, vIdxLocal, dim); - - ghostVertices[vIdxGlobal] = true; - } - } - } - } - - return ghostVertices; - } - - template<int d = dim> - static typename std::enable_if<d == 2, std::size_t>::type getGlobalNumScvf(const GridView& gridView) - { - Dune::GeometryType triangle, quadrilateral; - triangle.makeTriangle(); - quadrilateral.makeQuadrilateral(); - - return gridView.size(triangle)*6 + gridView.size(quadrilateral)*8; - } - - template<int d = dim> - static typename std::enable_if<d == 3, std::size_t>::type getGlobalNumScvf(const GridView& gridView) - { - Dune::GeometryType simplex, pyramid, prism, cube; - simplex.makeTetrahedron(); - pyramid.makePyramid(); - prism.makePrism(); - cube.makeHexahedron(); - - return gridView.size(simplex)*12 + gridView.size(pyramid)*16 + gridView.size(prism)*18 + gridView.size(cube)*24; - } -}; - /*! * \ingroup ImplicitModel * \brief Base class for the finite volume geometry vector for mpfa models @@ -121,13 +47,16 @@ class CCMpfaGlobalFVGeometryBase template<class TypeTag> class CCMpfaGlobalFVGeometryBase<TypeTag, true> { - //! The local class needs access to the scv, scvfs and the fv element geometry - //! as they are globally cached + using Implementation = typename GET_PROP_TYPE(TypeTag, GlobalFVGeometry); + + //! The actual implementation might overwrite the update() routine + friend Implementation; + //! The local class needs access to the problem friend typename GET_PROP_TYPE(TypeTag, FVElementGeometry); - using Implementation = typename GET_PROP_TYPE(TypeTag, GlobalFVGeometry); using Problem = typename GET_PROP_TYPE(TypeTag, Problem); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using MpfaHelper = typename GET_PROP_TYPE(TypeTag, MpfaHelper); using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); @@ -146,9 +75,8 @@ class CCMpfaGlobalFVGeometryBase<TypeTag, true> static const int dim = GridView::dimension; static const int dimWorld = GridView::dimensionworld; - using DimVector = Dune::FieldVector<Scalar, dimWorld>; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; using ReferenceElements = typename Dune::ReferenceElements<CoordScalar, dim>; - using MpfaGeometryHelper = Dumux::MpfaGeometryHelper<GridView, dim>; public: //! Constructor @@ -201,7 +129,7 @@ public: // reserve memory std::size_t numScvs = gridView_.size(0); - std::size_t numScvfs = CCMpfaGlobalFVGeometryHelper<TypeTag>::getGlobalNumScvf(gridView_); + std::size_t numScvfs = MpfaHelper::getGlobalNumScvf(gridView_); scvs_.resize(numScvs); scvfs_.reserve(numScvfs); scvfIndicesOfScv_.resize(numScvs); @@ -209,11 +137,14 @@ public: elementMap_.resize(numScvs); // find vertices on processor boundaries - auto ghostVertices = CCMpfaGlobalFVGeometryHelper<TypeTag>::findGhostVertices(problem, gridView_); + auto isGhostVertex = MpfaHelper::findGhostVertices(problem, gridView_); // the quadrature point to be used on the scvf const Scalar q = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Mpfa, Q); + // Instantiate a helper class to pass it to the scvf constructors + MpfaHelper helper; + // Build the SCVs and SCV faces IndexType scvfIdx = 0; numBoundaryScvf_ = 0; @@ -221,48 +152,89 @@ public: { auto eIdx = problem.elementMapper().index(element); + // the element geometry + auto elementGeometry = element.geometry(); + // fill the element map with seeds elementMap_[eIdx] = element.seed(); - // the element geometry and reference element - auto elemGeometry = element.geometry(); - const auto& referenceElement = ReferenceElements::general(elemGeometry.type()); - - // The geometry helper class - MpfaGeometryHelper geomHelper(elemGeometry); - - // create sub control volume for this element - scvs_[eIdx] = SubControlVolume(std::move(elemGeometry), eIdx); - // The local scvf index set std::vector<IndexType> scvfIndexSet; - scvfIndexSet.reserve(geomHelper.getNumLocalScvfs()); + scvfIndexSet.reserve(MpfaHelper::getNumLocalScvfs(elementGeometry.type())); + + // for dim < dimWorld we'll have multiple intersections at one point + // we store here the centers of those we have handled already + std::vector<GlobalPosition> finishedCenters; // construct the sub control volume faces - for (const auto& intersection : intersections(gridView_, element)) + for (const auto& is : intersections(gridView_, element)) { - // get the intersection geometry and some of its bools - auto isGeometry = intersection.geometry(); - bool boundary = intersection.boundary(); - - // determine the outside volvar idx - // we initialize nIdx so that the compiler does not complain in overlap elements in parallel - // nIdx will not be used in this case anyway (see continue call below) - IndexType nIdx = eIdx; - if (intersection.neighbor()) - nIdx = problem.elementMapper().index(intersection.outside()); + // if we are dealing with a lower dimensional network + // only make a new scvf if we haven't handled it yet + if (dim < dimWorld) + if(MpfaHelper::contains(finishedCenters, is.geometry().center())) + continue; + + // get some of the intersection's bools + bool boundary = is.boundary(); + bool neighbor = is.neighbor(); + + // determine the outside volvar indices + std::vector<IndexType> nIndices; + if (neighbor) + { + nIndices = std::vector<IndexType>( {problem.elementMapper().index(is.outside())} ); + + // check for further neighbors in case of lower dimensional networks + if (dim < dimWorld) + { + auto insideIndex = is.indexInInside(); + auto isCenter = is.geometry().center(); + + // find further possible intersections at the same point (bifurcations etc) + for (const auto& nextIs : intersections(gridView_, element)) + { + if (nextIs.indexInInside() == insideIndex && nextIs.geometry().center() == isCenter) + { + auto nIdx = problem.elementMapper().index(nextIs.outside()); + if (nIdx != nIndices[0]) + nIndices.push_back(nIdx); + } + } + + // store this center as a handled one + finishedCenters.push_back(isCenter); + } + } else if (boundary) - nIdx = numScvs + numBoundaryScvf_++; + { + nIndices.resize(1); + nIndices[0] = numScvs + numBoundaryScvf_++; + } - // make the scv faces of the intersection - for (unsigned int faceScvfIdx = 0; faceScvfIdx < isGeometry.corners(); ++faceScvfIdx) + // get the intersection corners according to generic numbering + auto numCorners = is.geometry().corners(); + std::vector<GlobalPosition> isCorners(numCorners); + + // if outside level > inside level, use the outside element in the following + bool useNeighbor = neighbor && is.outside().level() > element.level(); + const auto& e = useNeighbor ? is.outside() : element; + const auto indexInElement = useNeighbor ? is.indexInOutside() : is.indexInInside(); + const auto eg = e.geometry(); + const auto& refElement = ReferenceElements::general(eg.type()); + + for (unsigned int c = 0; c < numCorners; ++c) + isCorners[c] = eg.global(refElement.position(refElement.subEntity(indexInElement, 1, c, dim), dim)); + + // make the scv faces belonging to each corner of the intersection + for (unsigned int c = 0; c < numCorners; ++c) { - // get the global vertex index the scv face is connected to (mpfa-o method does not work for hanging nodes!) - const auto vIdxLocal = referenceElement.subEntity(intersection.indexInInside(), 1, faceScvfIdx, dim); - const auto vIdxGlobal = problem.vertexMapper().subIndex(element, vIdxLocal, dim); + // get the global vertex index the scv face is connected to + auto vIdxLocal = refElement.subEntity(indexInElement, 1, c, dim); + auto vIdxGlobal = problem.vertexMapper().subIndex(e, vIdxLocal, dim); // do not build scvfs connected to a processor boundary - if (ghostVertices[vIdxGlobal]) + if (isGhostVertex[vIdxGlobal]) continue; // store info on which vertices are on the domain boundary @@ -271,13 +243,14 @@ public: // make the scv face scvfIndexSet.push_back(scvfIdx); - scvfs_.emplace_back(geomHelper, - geomHelper.getScvfCorners(isGeometry, faceScvfIdx), - intersection.centerUnitOuterNormal(), + scvfs_.emplace_back(helper, + helper.getScvfCorners(isCorners, c), + is.centerUnitOuterNormal(), vIdxGlobal, vIdxLocal, scvfIdx, - std::array<IndexType, 2>({{eIdx, nIdx}}), + eIdx, + nIndices, q, boundary ); @@ -287,6 +260,9 @@ public: } } + // create sub control volume for this element + scvs_[eIdx] = SubControlVolume(std::move(elementGeometry), eIdx); + // Save the scvf indices belonging to this scv to build up fv element geometries fast scvfIndicesOfScv_[eIdx] = scvfIndexSet; } @@ -321,6 +297,10 @@ public: const std::vector<IndexType>& scvfIndicesOfScv(IndexType scvIdx) const { return scvfIndicesOfScv_[scvIdx]; } + //! Return a const reference to the grid view + const GridView& gridView() const + { return gridView_; } + private: const Problem& problem_() const @@ -353,6 +333,7 @@ class CCMpfaGlobalFVGeometryBase<TypeTag, false> using Implementation = typename GET_PROP_TYPE(TypeTag, GlobalFVGeometry); using Problem = typename GET_PROP_TYPE(TypeTag, Problem); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using MpfaHelper = typename GET_PROP_TYPE(TypeTag, MpfaHelper); using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); @@ -371,9 +352,8 @@ class CCMpfaGlobalFVGeometryBase<TypeTag, false> static const int dim = GridView::dimension; static const int dimWorld = GridView::dimensionworld; - using DimVector = Dune::FieldVector<Scalar, dimWorld>; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; using ReferenceElements = typename Dune::ReferenceElements<CoordScalar, dim>; - using MpfaGeometryHelper = Dumux::MpfaGeometryHelper<GridView, dim>; public: //! Constructor @@ -441,7 +421,7 @@ public: boundaryVertices_.resize(gridView_.size(dim), false); // find vertices on processor boundaries - ghostVertices_ = CCMpfaGlobalFVGeometryHelper<TypeTag>::findGhostVertices(problem, gridView_); + ghostVertices_ = MpfaHelper::findGhostVertices(problem, gridView_); // Store necessary info on SCVs and SCV faces for (const auto& element : elements(gridView_)) @@ -452,38 +432,72 @@ public: elementMap_[eIdx] = element.seed(); // the element geometry and reference element - auto elemGeometry = element.geometry(); - const auto& referenceElement = ReferenceElements::general(elemGeometry.type()); - - // The geometry helper class - MpfaGeometryHelper geomHelper(elemGeometry); + auto eg = element.geometry(); + const auto& referenceElement = ReferenceElements::general(eg.type()); // the element-wise index sets for finite volume geometry - auto numLocalFaces = geomHelper.getNumLocalScvfs(); - std::vector<IndexType> scvfsIndexSet(numLocalFaces); - std::vector<IndexType> neighborVolVarIndexSet(numLocalFaces); - IndexType localFaceIdx = 0; + auto numLocalFaces = MpfaHelper::getNumLocalScvfs(eg.type()); + std::vector<IndexType> scvfsIndexSet; + std::vector< std::vector<IndexType> > neighborVolVarIndexSet; + scvfsIndexSet.reserve(numLocalFaces); + neighborVolVarIndexSet.reserve(numLocalFaces); + + // for dim < dimWorld we'll have multiple intersections at one point + // we store here the centers of those we have handled already + std::vector<GlobalPosition> finishedCenters; + + unsigned int localFaceIdx = 0; // construct the sub control volume faces - for (const auto& intersection : intersections(gridView_, element)) + for (const auto& is : intersections(gridView_, element)) { + // if we are dealing with a lower dimensional network + // only make a new scvf if we haven't handled it yet + if (dim < dimWorld) + if(MpfaHelper::contains(finishedCenters, is.geometry().center())) + continue; + // get some of the intersection bools - bool boundary = intersection.boundary(); - bool neighbor = intersection.neighbor(); + bool boundary = is.boundary(); + bool neighbor = is.neighbor(); - // determine the outside volvar idx - // we initialize nIdx so that the compiler does not complain in overlap elements in parallel - // nIdx will not be used in this case anyway (see continue call below) - IndexType nIdx = eIdx; + // determine the outside volvar indices + std::vector<IndexType> nIndices; if (neighbor) - nIdx = problem.elementMapper().index(intersection.outside()); + { + nIndices = std::vector<IndexType>( {problem.elementMapper().index(is.outside())} ); + + // check for further neighbors in case of lower dimensional networks + if (dim < dimWorld) + { + auto insideIndex = is.indexInInside(); + auto isCenter = is.geometry().center(); + + // find further possible intersections at the same point (bifurcations etc) + for (const auto& nextIs : intersections(gridView_, element)) + { + if (nextIs.indexInInside() == insideIndex && nextIs.geometry().center() == isCenter) + { + auto nIdx = problem.elementMapper().index(nextIs.outside()); + if (nIdx != nIndices[0]) + nIndices.push_back(nIdx); + } + } + + // store this center as a handled one + finishedCenters.push_back(isCenter); + } + } else if (boundary) - nIdx = numScvs_ + numBoundaryScvf_++; + { + nIndices.resize(1); + nIndices[0] = numScvs_ + numBoundaryScvf_++; + } // make the scv faces of the intersection - for (unsigned int faceScvfIdx = 0; faceScvfIdx < intersection.geometry().corners(); ++faceScvfIdx) + for (unsigned int faceScvfIdx = 0; faceScvfIdx < is.geometry().corners(); ++faceScvfIdx) { // get the global vertex index the scv face is connected to (mpfa-o method does not work for hanging nodes!) - const auto vIdxLocal = referenceElement.subEntity(intersection.indexInInside(), 1, faceScvfIdx, dim); + const auto vIdxLocal = referenceElement.subEntity(is.indexInInside(), 1, faceScvfIdx, dim); const auto vIdxGlobal = problem.vertexMapper().subIndex(element, vIdxLocal, dim); // do not build scvfs connected to a processor boundary @@ -495,8 +509,11 @@ public: boundaryVertices_[vIdxGlobal] = true; // store information on the scv face - scvfsIndexSet[localFaceIdx] = numScvf_++; - neighborVolVarIndexSet[localFaceIdx++] = nIdx; + scvfsIndexSet.push_back(numScvf_++); + neighborVolVarIndexSet.push_back(nIndices); + + // increment counter + localFaceIdx++; } } @@ -515,7 +532,7 @@ public: const std::vector<IndexType>& scvfIndicesOfScv(IndexType scvIdx) const { return scvfIndicesOfScv_[scvIdx]; } - const std::vector<IndexType>& neighborVolVarIndices(IndexType scvIdx) const + const std::vector< std::vector<IndexType> >& neighborVolVarIndices(IndexType scvIdx) const { return neighborVolVarIndices_[scvIdx]; } /*! @@ -543,7 +560,7 @@ private: // vectors that store the global data Dumux::ElementMap<GridView> elementMap_; std::vector<std::vector<IndexType>> scvfIndicesOfScv_; - std::vector<std::vector<IndexType>> neighborVolVarIndices_; + std::vector< std::vector< std::vector<IndexType> > > neighborVolVarIndices_; std::vector<bool> boundaryVertices_; std::vector<bool> ghostVertices_; diff --git a/dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseeds.hh b/dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseeds.hh index d1db5c93a2fc7482bf080f42d58682cd758d2bdd..87af94939467c06d328631046c2f2655949dabf4 100644 --- a/dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseeds.hh +++ b/dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseeds.hh @@ -24,14 +24,23 @@ #define DUMUX_DISCRETIZATION_MPFA_GLOBALINTERACTIONVOLUMESEEDS_HH #include <dumux/implicit/cellcentered/mpfa/properties.hh> +#include <dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseedsbase.hh> #include "methods.hh" namespace Dumux { -//! forward declaration of the actual implementation +//! forward declaration of the actual method-specific implementation +//! By default we simply inherit from the base class +//! Actual implementations for other methods have to be provided below template<class TypeTag, MpfaMethods method> -class CCMpfaGlobalInteractionVolumeSeedsImplementation -{}; +class CCMpfaGlobalInteractionVolumeSeedsImplementation : public CCMpfaGlobalInteractionVolumeSeedsBase<TypeTag> +{ + using ParentType = CCMpfaGlobalInteractionVolumeSeedsBase<TypeTag>; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + +public: + CCMpfaGlobalInteractionVolumeSeedsImplementation(const GridView gridView) : ParentType(gridView) {} +}; /*! * \ingroup Mpfa @@ -42,8 +51,7 @@ using CCMpfaGlobalInteractionVolumeSeeds = CCMpfaGlobalInteractionVolumeSeedsImp } // end namespace -// the specializations of this class for the available methods have to be included here -#include <dumux/discretization/cellcentered/mpfa/omethod/globalinteractionvolumeseeds.hh> -#include <dumux/discretization/cellcentered/mpfa/omethodfps/globalinteractionvolumeseeds.hh> +// the specializations of this class differing from the default have to be included here +#include <dumux/discretization/cellcentered/mpfa/lmethod/globalinteractionvolumeseeds.hh> #endif diff --git a/dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseedsbase.hh b/dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseedsbase.hh index b036f9717f49530f0680b5f1cfe5edb9a07a891d..73ec5c9a1275821ca75407f65e44ae70ca65e742 100644 --- a/dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseedsbase.hh +++ b/dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseedsbase.hh @@ -35,6 +35,10 @@ namespace Dumux template<class TypeTag> class CCMpfaGlobalInteractionVolumeSeedsBase { + using Implementation = typename GET_PROP_TYPE(TypeTag, GlobalInteractionVolumeSeeds); + // the actual implementation needs to be friend + friend Implementation; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using Problem = typename GET_PROP_TYPE(TypeTag, Problem); using Helper = typename GET_PROP_TYPE(TypeTag, MpfaHelper); @@ -58,12 +62,12 @@ public: seeds_.clear(); boundarySeeds_.clear(); - // -1 indicates that the scvf has not been handled yet auto numScvf = problem_().model().globalFvGeometry().numScvf(); - scvfIndexMap_.resize(numScvf, -1); + scvfIndexMap_.resize(numScvf); + std::vector<bool> isFaceHandled(numScvf, false); - // detect and handle the boundary first - initializeSeeds_(boundaryVertices); + // initialize the seeds according to the mpfa method + asImp_().initializeSeeds_(boundaryVertices, isFaceHandled); } const InteractionVolumeSeed& seed(const SubControlVolumeFace& scvf) const @@ -74,7 +78,8 @@ public: private: - void initializeSeeds_(std::vector<bool>& boundaryVertices) + void initializeSeeds_(std::vector<bool>& boundaryVertices, + std::vector<bool>& isFaceHandled) { const auto numScvf = problem_().model().globalFvGeometry().numScvf(); const auto numBoundaryScvf = problem_().model().globalFvGeometry().numBoundaryScvf(); @@ -92,7 +97,7 @@ private: for (const auto& scvf : scvfs(fvGeometry)) { // skip the rest if we already handled this face - if (scvfIndexMap_[scvf.index()] != -1) + if (isFaceHandled[scvf.index()]) continue; if (boundaryVertices[scvf.vertexIndex()]) @@ -102,7 +107,10 @@ private: // update the index map entries for the global scv faces in the interaction volume for (auto scvfIdxGlobal : boundarySeeds_.back().globalScvfIndices()) + { scvfIndexMap_[scvfIdxGlobal] = boundarySeedIndex; + isFaceHandled[scvfIdxGlobal] = true; + } // increment counter boundarySeedIndex++; @@ -114,7 +122,10 @@ private: // update the index map entries for the global scv faces in the interaction volume for (auto scvfIdxGlobal : seeds_.back().globalScvfIndices()) + { scvfIndexMap_[scvfIdxGlobal] = seedIndex; + isFaceHandled[scvfIdxGlobal] = true; + } // increment counter seedIndex++; @@ -130,6 +141,12 @@ private: const Problem& problem_() const { return *problemPtr_; } + const Implementation& asImp_() const + { return *static_cast<Implementation*>(this); } + + Implementation& asImp_() + { return *static_cast<Implementation*>(this); } + const Problem* problemPtr_; GridView gridView_; std::vector<IndexType> scvfIndexMap_; diff --git a/dumux/discretization/cellcentered/mpfa/helper.hh b/dumux/discretization/cellcentered/mpfa/helper.hh index 164ab6d0e642105c311e3fec2ce976a926941e98..a2f04306595427d2c25b550bfac154d4a18974dc 100644 --- a/dumux/discretization/cellcentered/mpfa/helper.hh +++ b/dumux/discretization/cellcentered/mpfa/helper.hh @@ -29,19 +29,17 @@ namespace Dumux { -// Mpfa method-specific implementation of the helper class -template<class TypeTag, MpfaMethods Method, int dim> -class MpfaHelperBase {}; +// Mpfa method-specific implementation of the helper class (again dimension-dependent) +template<class TypeTag, MpfaMethods Method, int dim, int dimWorld> +class MpfaMethodHelper; // dimension-specific implementation of the helper class (common for all methods) -template<class TypeTag, int dim> -class MpfaHelperImplementation {}; +template<class TypeTag, int dim, int dimWorld> +class MpfaDimensionHelper; -// Specialization for dim == 2 +// Specialization for dim == 2, dimWorld == 2 template<class TypeTag> -class MpfaHelperImplementation<TypeTag, 2> : public MpfaHelperBase<TypeTag, - GET_PROP_VALUE(TypeTag, MpfaMethod), - 2> +class MpfaDimensionHelper<TypeTag, /*dim*/2, /*dimWorld*/2> { using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); @@ -51,15 +49,19 @@ class MpfaHelperImplementation<TypeTag, 2> : public MpfaHelperBase<TypeTag, using LocalIndexType = typename InteractionVolume::LocalIndexType; + // We know that dim = 2 and dimworld = 2 static const int dim = GridView::dimension; static const int dimWorld = GridView::dimensionworld; - using DimVector = Dune::FieldVector<Scalar, dimWorld>; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; using ScvfVector = std::array<const SubControlVolumeFace*, dim>; - using LocalBasis = std::array<DimVector, dim>; + using LocalBasis = std::array<GlobalPosition, dim>; + + using PointVector = std::vector<GlobalPosition>; + using FaceReferenceElements = typename Dune::ReferenceElements<Scalar, dim-1>; public: - // Gets the common scv face in the outer element and the other scv face sharing the same vertex + // Gets the two scv faces in the outer element, that share the vertex // orders them to form a right hand system, local indices can be deduced from on the rotational direction static ScvfVector getCommonAndNextScvFace(const SubControlVolumeFace& outsideScvf, const FVElementGeometry& fvGeometry, @@ -67,19 +69,29 @@ public: { LocalIndexType commonFaceIdx = clockWise ? 0 : 1; LocalIndexType nextFaceIdx = clockWise ? 1 : 0; + auto vIdxGlobal = outsideScvf.vertexIndex(); + unsigned int count = 0; ScvfVector scvfVector; for (const auto& scvf : scvfs(fvGeometry)) { - if (outsideScvf.vertexIndex() == scvf.vertexIndex()) + if (scvf.vertexIndex() == vIdxGlobal) { if (scvf.outsideScvIdx() == outsideScvf.insideScvIdx()) + { scvfVector[commonFaceIdx] = &scvf; + count++; + } else + { scvfVector[nextFaceIdx] = &scvf; + count++; + } } } + // make sure we found two faces + assert(count == 2 && "did not find two scv faces sharing the vertex in the outside element"); return scvfVector; } @@ -98,32 +110,74 @@ public: return innerNormals; } - // calculates the determinant of the local basis + // the determinant of the local basis is equal to the cross product for dim = dimWorld = 2 static Scalar calculateDetX(const LocalBasis& localBasis) { - static const Dune::FieldMatrix<Scalar, dim, dim> R = {{0.0, 1.0}, {-1.0, 0.0}}; // make sure the basis forms a right hand system assert(isRightHandSystem(localBasis) > 0 && "Local basis does not form a right hand system"); + return crossProduct<Scalar>(localBasis[0], localBasis[1]); + } - DimVector tmp(0.0); - R.mv(localBasis[1], tmp); + // returns the global number of scvfs in the grid + static std::size_t getGlobalNumScvf(const GridView& gridView) + { + Dune::GeometryType triangle, quadrilateral; + triangle.makeTriangle(); + quadrilateral.makeQuadrilateral(); - return tmp*localBasis[0]; + return gridView.size(triangle)*6 + gridView.size(quadrilateral)*8; } + //! Check whether or not the local basis forms a right hand system static bool isRightHandSystem(const LocalBasis& localBasis) + { return !std::signbit(crossProduct<Scalar>(localBasis[0], localBasis[1])); } + + //! get sub control volume face corners on a given face geometry for the given local index + static PointVector getScvfCorners(const PointVector& isCorners, unsigned int cornerIdx) { - if (std::signbit(crossProduct<Scalar>(localBasis[0], localBasis[1]))) - return false; - return true; + // compute intersection center (in 2d intersections have two corners) + GlobalPosition center = isCorners[0] + isCorners[1]; + center /= 2; + + if (cornerIdx == 0) + return PointVector({center, isCorners[0]}); + else if (cornerIdx == 1) + return PointVector({center, isCorners[1]}); + else + DUNE_THROW(Dune::InvalidStateException, "local index exceeds the number of corners of 2d intersections"); + } + + //! calculate integration point on an scvf + static GlobalPosition getScvfIntegrationPoint(const PointVector& scvfCorners, Scalar q) + { + // scvfs in 3d are always quadrilaterals + // ordering -> first corner: facet center, last corner: vertex + if (q == 0.0) + return scvfCorners[0]; + + auto d = scvfCorners[1] - scvfCorners[0]; + d *= q; + return scvfCorners[0] + d; + } + + //! calculate the area of a scvf + static Scalar getScvfArea(const PointVector& scvfCorners) + { return (scvfCorners[1]-scvfCorners[0]).two_norm(); } + + static std::size_t getNumLocalScvfs(const Dune::GeometryType gt) + { + if (gt == Dune::GeometryType(Dune::GeometryType::simplex, 2)) + return 6; + else if (gt == Dune::GeometryType(Dune::GeometryType::cube, 2)) + return 8; + else + DUNE_THROW(Dune::InvalidStateException, "unknown 2d geometry type " << gt); } }; -// Specialization for dim == 3 +// Specialization for dim == 2, dimWorld == 3 template<class TypeTag> -class MpfaHelperImplementation<TypeTag, 3> : public MpfaHelperBase<TypeTag, - GET_PROP_VALUE(TypeTag, MpfaMethod), - 3> +class MpfaDimensionHelper<TypeTag, /*dim*/2, /*dimWorld*/3> { using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); @@ -133,12 +187,91 @@ class MpfaHelperImplementation<TypeTag, 3> : public MpfaHelperBase<TypeTag, using LocalIndexType = typename InteractionVolume::LocalIndexType; + // We know that dim = 2 and dimworld = 3 static const int dim = GridView::dimension; static const int dimWorld = GridView::dimensionworld; - using DimVector = Dune::FieldVector<Scalar, dimWorld>; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; using ScvfVector = std::array<const SubControlVolumeFace*, dim>; - using LocalBasis = std::array<DimVector, dim>; + using LocalBasis = std::array<GlobalPosition, dim>; + using PointVector = std::vector<GlobalPosition>; + +public: + // calculates the inner normal vectors + static LocalBasis calculateInnerNormals(const LocalBasis& localBasis) + { + // make sure the basis forms a right hand system + assert(isRightHandSystem(localBasis) > 0 && "Local basis does not form a right hand system"); + + // compute vector normal to the basis plane + auto normal = crossProduct<Scalar>(localBasis[0], localBasis[1]); + normal /= normal.two_norm(); + + // compute inner normals using the normal vector + LocalBasis innerNormals; + innerNormals[0] = crossProduct<Scalar>(localBasis[1], normal); + innerNormals[1] = crossProduct<Scalar>(normal, localBasis[0]); + + return innerNormals; + } + + // This is actually not the determinant of the basis for dim = 2 < dimWorld = 3 + // It simply is the area of the parallelogram spanned by the basis vectors + static Scalar calculateDetX(const LocalBasis& localBasis) + { return std::abs( crossProduct<Scalar>(localBasis[0], localBasis[1]).two_norm() ); } + + // returns the global number of scvfs in the grid + static std::size_t getGlobalNumScvf(const GridView& gridView) + { + Dune::GeometryType triangle, quadrilateral; + triangle.makeTriangle(); + quadrilateral.makeQuadrilateral(); + + return gridView.size(triangle)*6 + gridView.size(quadrilateral)*8; + } + + //! For 2d in 3d there is no unique basis forming a right hand system + static bool isRightHandSystem(const LocalBasis& localBasis) + { return true; } + + //! get sub control volume face corners on a given face geometry for the given local index + static PointVector getScvfCorners(const PointVector& isCorners, unsigned int cornerIdx) + { return MpfaDimensionHelper<TypeTag, dim, dim>::getScvfCorners(isCorners, cornerIdx); } + + //! calculate integration point on an scvf + static GlobalPosition getScvfIntegrationPoint(const PointVector& scvfCorners, Scalar q) + { return MpfaDimensionHelper<TypeTag, dim, dim>::getScvfIntegrationPoint(scvfCorners, q); } + + //! calculate the area of a scvf + static Scalar getScvfArea(const PointVector& scvfCorners) + { return MpfaDimensionHelper<TypeTag, dim, dim>::getScvfArea(scvfCorners); } + + static std::size_t getNumLocalScvfs(const Dune::GeometryType gt) + { return MpfaDimensionHelper<TypeTag, dim, dim>::getNumLocalScvfs(gt); } +}; + +// Specialization for dim == 3 (dimWorld has to be 3) +template<class TypeTag> +class MpfaDimensionHelper<TypeTag, /*dim*/3, /*dimWorld*/3> +{ + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); + using InteractionVolume = typename GET_PROP_TYPE(TypeTag, InteractionVolume); + + using LocalIndexType = typename InteractionVolume::LocalIndexType; + + // We know that dim = 3 and dimworld = 3 + static const int dim = GridView::dimension; + static const int dimWorld = GridView::dimensionworld; + + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; + using ScvfVector = std::array<const SubControlVolumeFace*, dim>; + using LocalBasis = std::array<GlobalPosition, dim>; + + using PointVector = std::vector<GlobalPosition>; + using FaceReferenceElements = typename Dune::ReferenceElements<Scalar, dim-1>; public: @@ -161,11 +294,134 @@ public: return tripleProduct<Scalar>(localBasis[0], localBasis[1], localBasis[2]); } + // returns the global number of scvfs in the grid + static std::size_t getGlobalNumScvf(const GridView& gridView) + { + Dune::GeometryType simplex, pyramid, prism, cube; + simplex.makeTetrahedron(); + pyramid.makePyramid(); + prism.makePrism(); + cube.makeHexahedron(); + + return gridView.size(simplex)*12 + gridView.size(pyramid)*16 + gridView.size(prism)*18 + gridView.size(cube)*24; + } + + //! Check whether or not the local basis forms a right hand system static bool isRightHandSystem(const LocalBasis& localBasis) + { return !std::signbit(tripleProduct<Scalar>(localBasis[0], localBasis[1], localBasis[2])); } + + //! get sub control volume face corners on a given face geometry for the given local index + static PointVector getScvfCorners(const PointVector& isCorners, unsigned int cornerIdx) + { + // maximum number of necessary points is 9 (for quadrilateral) + GlobalPosition p[9]; + auto numCorners = isCorners.size(); + + // fill point vector with center and corners + p[0] = 0.0; + for (int i = 0; i < numCorners; ++i) + { + p[0] += isCorners[i]; + p[i+1] = isCorners[i]; + } + p[0] /= numCorners; + + // proceed according to number of corners + switch (numCorners) + { + case 3: // triangle + { + // add edge midpoints, there are 3 for triangles + p[numCorners+1] = isCorners[1] + isCorners[0]; + p[numCorners+1] /= 2; + p[numCorners+2] = isCorners[2] + isCorners[0]; + p[numCorners+2] /= 2; + p[numCorners+3] = isCorners[2] + isCorners[1]; + p[numCorners+3] /= 2; + + //! Only build the maps the first time we encounter a triangle + static const std::uint8_t vo = 1; //! vertex offset in point vector p + static const std::uint8_t eo = 4; //! edge offset in point vector p + static const std::uint8_t map[3][4] = + { + {0, eo+1, eo+0, vo+0}, + {0, eo+0, eo+2, vo+1}, + {0, eo+2, eo+1, vo+2} + }; + + return PointVector( {p[map[cornerIdx][0]], + p[map[cornerIdx][1]], + p[map[cornerIdx][2]], + p[map[cornerIdx][3]]} ); + } + case 4: // quadrilateral + { + // add edge midpoints, there are 4 for quadrilaterals + p[numCorners+1] = isCorners[2] + isCorners[0]; + p[numCorners+1] /= 2; + p[numCorners+2] = isCorners[3] + isCorners[1]; + p[numCorners+2] /= 2; + p[numCorners+3] = isCorners[1] + isCorners[0]; + p[numCorners+3] /= 2; + p[numCorners+4] = isCorners[3] + isCorners[2]; + p[numCorners+4] /= 2; + + //! Only build the maps the first time we encounter a quadrilateral + static const std::uint8_t vo = 1; //! vertex offset in point vector p + static const std::uint8_t eo = 5; //! face offset in point vector p + static const std::uint8_t map[4][4] = + { + {0, eo+0, eo+2, vo+0}, + {0, eo+2, eo+1, vo+1}, + {0, eo+3, eo+0, vo+2}, + {0, eo+1, eo+3, vo+3} + }; + + return PointVector( {p[map[cornerIdx][0]], + p[map[cornerIdx][1]], + p[map[cornerIdx][2]], + p[map[cornerIdx][3]]} ); + } + default: + DUNE_THROW(Dune::NotImplemented, "Mpfa scvf corners for dim=" << dim + << " dimWorld=" << dimWorld + << " corners=" << numCorners); + } + } + + //! calculate integration point on a scvf + static GlobalPosition getScvfIntegrationPoint(const PointVector& scvfCorners, Scalar q) + { + // scvfs in 3d are always quadrilaterals + // ordering -> first corner: facet center, last corner: vertex + if (q == 0.0) + return scvfCorners[0]; + + auto d = scvfCorners[3] - scvfCorners[0]; + d *= q; + return scvfCorners[0] + d; + } + + //! calculate scvf area + static Scalar getScvfArea(const PointVector& scvfCorners) + { + // after Wolfram alpha quadrilateral area + return 0.5*Dumux::crossProduct(scvfCorners[3]-scvfCorners[0], scvfCorners[2]-scvfCorners[1]).two_norm(); + } + + //! determine the number of scvfs of an element + static std::size_t getNumLocalScvfs(const Dune::GeometryType gt) { - if (std::signbit(tripleProduct<Scalar>(localBasis[0], localBasis[1], localBasis[2]))) - return false; - return true; + if (gt == Dune::GeometryType(Dune::GeometryType::simplex, 3)) + return 12; + else if (gt == Dune::GeometryType(Dune::GeometryType::pyramid, 3)) + return 16; + else if (gt == Dune::GeometryType(Dune::GeometryType::prism, 3)) + return 18; + else if (gt == Dune::GeometryType(Dune::GeometryType::cube, 3)) + return 24; + else + DUNE_THROW(Dune::InvalidStateException, "unknown 3d geometry type " << gt); } }; @@ -174,11 +430,11 @@ public: * \brief Helper class to get the required information on an interaction volume. * Specializations depending on the method and dimension are provided. */ -template<class TypeTag> -class MpfaHelper : public MpfaHelperImplementation<TypeTag, GET_PROP_TYPE(TypeTag, GridView)::dimension> +template<class TypeTag, MpfaMethods Method, int dim, int dimWorld> +class CCMpfaHelperImplementation : public MpfaDimensionHelper<TypeTag, dim, dimWorld>, + public MpfaMethodHelper<TypeTag, Method, dim, dimWorld> { - using ParentType = MpfaHelperImplementation<TypeTag, GET_PROP_TYPE(TypeTag, GridView)::dimension>; - + using Implementation = typename GET_PROP_TYPE(TypeTag, MpfaHelper); using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using Problem = typename GET_PROP_TYPE(TypeTag, Problem); @@ -190,15 +446,15 @@ class MpfaHelper : public MpfaHelperImplementation<TypeTag, GET_PROP_TYPE(TypeTa using GlobalIndexType = typename GridView::IndexSet::IndexType; using LocalIndexType = typename InteractionVolume::LocalIndexType; - static const int dim = GridView::dimension; - static const int dimWorld = GridView::dimensionworld; - - using DimVector = Dune::FieldVector<Scalar, dimWorld>; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; using ScvfVector = std::array<const SubControlVolumeFace*, dim>; - using LocalBasis = std::array<DimVector, dim>; + using LocalBasis = std::array<GlobalPosition, dim>; + + using CoordScalar = typename GridView::ctype; + using ReferenceElements = typename Dune::ReferenceElements<CoordScalar, dim>; public: - // returns shared pointers to the two scv faces that share a vertex in the order of a right hand system + // returns shared pointers to the scv faces that share a vertex in the order of a right hand system static ScvfVector getScvFacesAtVertex(const GlobalIndexType vIdxGlobal, const Element& element, const FVElementGeometry& fvGeometry) @@ -225,7 +481,7 @@ public: assert(count == dim); // sort the scv faces to form a right hand system - if (!ParentType::isRightHandSystem(basisVectors)) + if (!Implementation::isRightHandSystem(basisVectors)) std::swap(scvfVector[0], scvfVector[1]); return scvfVector; @@ -235,8 +491,20 @@ public: static LocalIndexType getCommonFaceLocalIndex(const SubControlVolumeFace& outsideScvf, const ScvfVector& insideScvFaces) { + auto outsideScvIdx = outsideScvf.insideScvIdx(); for (int i = 0; i < insideScvFaces.size(); i++) - if (insideScvFaces[i]->outsideScvIdx() == outsideScvf.insideScvIdx()) + if (contains(insideScvFaces[i]->outsideScvIndices(), outsideScvIdx)) + return i; + + DUNE_THROW(Dune::InvalidStateException, "Could not find corresponding scvf in the provided vector of scvfs."); + } + + // Finds the local index in an ScvfVector that corresponds to a given scvf + static LocalIndexType getLocalFaceIndex(const SubControlVolumeFace& scvf, + const ScvfVector& scvFaces) + { + for (int i = 0; i < scvFaces.size(); i++) + if (scvFaces[i]->index() == scvf.index()) return i; DUNE_THROW(Dune::InvalidStateException, "Could not find corresponding scvf in the provided vector of scvfs."); @@ -264,10 +532,66 @@ public: DUNE_THROW(Dune::InvalidStateException, "unknown boundary condition type"); } + + // returns a vector, which maps a bool (true if ghost vertex) to each vertex index + static std::vector<bool> findGhostVertices(const Problem& problem, const GridView& gridView) + { + std::vector<bool> ghostVertices(gridView.size(dim), false); + + // if not run in parallel, skip the rest + if (Dune::MPIHelper::getCollectiveCommunication().size() == 1) + return ghostVertices; + + // mpfa methods can not yet handle ghost cells + if (gridView.ghostSize(0) > 0) + DUNE_THROW(Dune::InvalidStateException, "Mpfa methods in parallel do not work with ghost cells. Use overlap cells instead."); + + // mpfa methods have to have overlapping cells + if (gridView.overlapSize(0) == 0) + DUNE_THROW(Dune::InvalidStateException, "Grid no overlaping cells. This is required by mpfa methods in parallel."); + + for (const auto& element : elements(gridView)) + { + for (const auto& is : intersections(gridView, element)) + { + if (!is.neighbor() && !is.boundary()) + { + const auto& refElement = ReferenceElements::general(element.geometry().type()); + for (unsigned int isVertex = 0; isVertex < is.geometry().corners(); ++isVertex) + { + const auto vIdxLocal = refElement.subEntity(is.indexInInside(), 1, isVertex, dim); + const auto vIdxGlobal = problem.vertexMapper().subIndex(element, vIdxLocal, dim); + + ghostVertices[vIdxGlobal] = true; + } + } + } + } + + return ghostVertices; + } + + //! returns whether or not a value exists in a vector + template<typename V1, typename V2> + static bool contains(const std::vector<V1>& vector, const V2 value) + { return std::find(vector.begin(), vector.end(), value) != vector.end(); } }; + +/*! + * \ingroup Mpfa + * \brief Helper class for the mpfa methods for the construction of the interaction regions etc. + * It inherits from dimension-, dimensionworld- and method-specific implementations. + */ +template<class TypeTag> +using CCMpfaHelper = CCMpfaHelperImplementation<TypeTag, + GET_PROP_VALUE(TypeTag, MpfaMethod), + GET_PROP_TYPE(TypeTag, GridView)::dimension, + GET_PROP_TYPE(TypeTag, GridView)::dimensionworld>; + } // end namespace // The implemented helper classes need to be included here +#include <dumux/discretization/cellcentered/mpfa/lmethod/helper.hh> #include <dumux/discretization/cellcentered/mpfa/omethod/helper.hh> #include <dumux/discretization/cellcentered/mpfa/omethodfps/helper.hh> diff --git a/dumux/discretization/cellcentered/mpfa/hybridfps/globalfvgeometry.hh b/dumux/discretization/cellcentered/mpfa/hybridfps/globalfvgeometry.hh new file mode 100644 index 0000000000000000000000000000000000000000..ba4526ae8fb618bc0c8db48f60f5a0cab4ca99b7 --- /dev/null +++ b/dumux/discretization/cellcentered/mpfa/hybridfps/globalfvgeometry.hh @@ -0,0 +1,281 @@ +// -*- 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 finite volume geometry vector for box models + * This builds up the sub control volumes and sub control volume faces + * for each element. + */ +#ifndef DUMUX_DISCRETIZATION_CC_MPFA_O_HYBRIDFPS_GLOBALFVGEOMETRY_HH +#define DUMUX_DISCRETIZATION_CC_MPFA_O_HYBRIDFPS_GLOBALFVGEOMETRY_HH + +#include <dune/geometry/multilineargeometry.hh> +#include <dune/geometry/referenceelements.hh> + +#include <dumux/implicit/cellcentered/mpfa/properties.hh> +#include <dumux/common/elementmap.hh> + +#include <dumux/discretization/cellcentered/mpfa/mpfageometryhelper.hh> + +namespace Dumux +{ +/*! + * \ingroup ImplicitModel + * \brief Base class for the finite volume geometry vector for mpfa models + * This builds up the sub control volumes and sub control volume faces + * for each element. + */ +template<class TypeTag, bool EnableFVElementGeometryCache> +class CCMpfaOHybridFpsGlobalFVGeometry +{}; + +// specialization in case the FVElementGeometries are stored +template<class TypeTag> +class CCMpfaOHybridFpsGlobalFVGeometry<TypeTag, true> +{ + //! The local class needs access to the scv, scvfs and the fv element geometry + //! as they are globally cached + friend typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + 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 GlobalInteractionVolumeSeeds = typename GET_PROP_TYPE(TypeTag, GlobalInteractionVolumeSeeds); + using BoundaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, BoundaryInteractionVolume); + using InteractionVolume = typename GET_PROP_TYPE(TypeTag, InteractionVolume); + using InteractionVolumeSeed = typename InteractionVolume::Seed; + using BoundaryInteractionVolumeSeed = typename BoundaryInteractionVolume::Seed; + using Element = typename GridView::template Codim<0>::Entity; + + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using CoordScalar = typename GridView::ctype; + using IndexType = typename GridView::IndexSet::IndexType; + using LocalIndexType = typename InteractionVolume::LocalIndexType; + + static const int dim = GridView::dimension; + static const int dimWorld = GridView::dimensionworld; + + using DimVector = Dune::FieldVector<Scalar, dimWorld>; + using ReferenceElements = typename Dune::ReferenceElements<CoordScalar, dim>; + using MpfaGeometryHelper = Dumux::MpfaGeometryHelper<GridView, dim>; + +public: + //! Constructor + CCMpfaOHybridFpsGlobalFVGeometry(const GridView gridView) + : gridView_(gridView), elementMap_(gridView), globalInteractionVolumeSeeds_(gridView) {} + + //! The total number of sub control volumes + std::size_t numScv() const + { return scvs_.size(); } + + //! The total number of sun control volume faces + std::size_t numScvf() const + { return scvfs_.size(); } + + //! The total number of boundary sub control volume faces + std::size_t numBoundaryScvf() const + { return numBoundaryScvf_; } + + // Get an element from a sub control volume contained in it + Element element(const SubControlVolume& scv) const + { return elementMap_.element(scv.elementIndex()); } + + // Get an element from a global element index + Element element(IndexType eIdx) const + { return elementMap_.element(eIdx); } + + //! Get the inner interaction volume seed corresponding to an scvf + const InteractionVolumeSeed& interactionVolumeSeed(const SubControlVolumeFace& scvf) const + { return globalInteractionVolumeSeeds_.seed(scvf); } + + //! Get the boundary interaction volume seed corresponding to an scvf + const BoundaryInteractionVolumeSeed& boundaryInteractionVolumeSeed(const SubControlVolumeFace& scvf, const LocalIndexType eqIdx) const + { return globalInteractionVolumeSeeds_.boundarySeed(scvf, eqIdx); } + + //! Returns whether or not a scvf touches the boundary (has to be called before getting an interaction volume) + bool scvfTouchesBoundary(const SubControlVolumeFace& scvf) const + { return fpsVertices_[scvf.vertexIndex()]; } + + //! update all fvElementGeometries (do this again after grid adaption) + void update(const Problem& problem) + { + problemPtr_ = &problem; + + scvfs_.clear(); + // reserve memory + IndexType numScvs = gridView_.size(0); + scvs_.resize(numScvs); + scvfs_.reserve(getNumScvf_()); + scvfIndicesOfScv_.resize(numScvs); + fpsVertices_.resize(gridView_.size(dim), false); + boundaryVertices_.resize(gridView_.size(dim), false); + elementMap_.resize(numScvs); + + // the quadrature point to be used on the scvf + const Scalar q = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Mpfa, Q); + + // Build the SCVs and SCV faces + IndexType scvfIdx = 0; + numBoundaryScvf_ = 0; + for (const auto& element : elements(gridView_)) + { + auto eIdx = problem.elementMapper().index(element); + + // fill the element map with seeds + elementMap_[eIdx] = element.seed(); + + // the element geometry and reference element + auto elemGeometry = element.geometry(); + const auto& referenceElement = ReferenceElements::general(elemGeometry.type()); + + // create sub control volume for this element + scvs_[eIdx] = SubControlVolume(std::move(elemGeometry), eIdx); + + // The geometry helper class + MpfaGeometryHelper geomHelper(elemGeometry); + + // The local scvf index set + std::vector<IndexType> scvfIndexSet; + scvfIndexSet.reserve(geomHelper.getNumLocalScvfs()); + + // determine if element has to be handled by the fps scheme + bool isFps = problem.spatialParams().isFpsElement(element); + + // construct the sub control volume faces + for (const auto& intersection : intersections(gridView_, element)) + { + // get the intersection geometry and some of its bools + auto isGeometry = intersection.geometry(); + bool boundary = intersection.boundary(); + bool neighbor = intersection.neighbor(); + + if (neighbor && + element.partitionType() == Dune::GhostEntity && + intersection.outside().partitionType() == Dune::GhostEntity) + continue; + + // determine the outside volvar idx + IndexType nIdx; + if (neighbor) + nIdx = problem.elementMapper().index(intersection.outside()); + else if (boundary) + nIdx = numScvs + numBoundaryScvf_++; + else + continue; + + // make the scv faces of the intersection + for (unsigned int faceScvfIdx = 0; faceScvfIdx < isGeometry.corners(); ++faceScvfIdx) + { + // get the global vertex index the scv face is connected to (mpfa-o method does not work for hanging nodes!) + const auto vIdxLocal = referenceElement.subEntity(intersection.indexInInside(), 1, faceScvfIdx, dim); + const auto vIdxGlobal = problem.vertexMapper().subIndex(element, vIdxLocal, dim); + + if (boundary || isFps || (!boundary && problem.spatialParams().isFpsElement(intersection.outside()))) + fpsVertices_[vIdxGlobal] = true; + if (boundary) + boundaryVertices_[vIdxGlobal] = true; + + // make the scv face + scvfIndexSet.push_back(scvfIdx); + scvfs_.emplace_back(SubControlVolumeFace(geomHelper, + geomHelper.getScvfCorners(isGeometry, faceScvfIdx), + intersection.centerUnitOuterNormal(), + vIdxGlobal, + scvfIdx, + std::array<IndexType, 2>({{eIdx, nIdx}}), + q, + boundary + )); + + // increment scvf counter + scvfIdx++; + } + } + + // Save the scvf indices belonging to this scv to build up fv element geometries fast + scvfIndicesOfScv_[eIdx] = scvfIndexSet; + } + + // Initialize the interaction volume seeds, this will also initialize the vector of boundary vertices + globalInteractionVolumeSeeds_.update(problem, fpsVertices_, boundaryVertices_); + } + + /*! + * \brief Return a local restriction of this global object + * The local object is only functional after calling its bind/bindElement method + * This is a free function that will be found by means of ADL + */ + friend inline FVElementGeometry localView(const CCMpfaOHybridFpsGlobalFVGeometry& global) + { return FVElementGeometry(global); } + + //! Get a sub control volume with a global scv index + const SubControlVolume& scv(IndexType scvIdx) const + { return scvs_[scvIdx]; } + + //! Get a sub control volume face with a global scvf index + const SubControlVolumeFace& scvf(IndexType scvfIdx) const + { return scvfs_[scvfIdx]; } + + //! Get the sub control volume face indices of an scv by global index + const std::vector<IndexType>& scvfIndicesOfScv(IndexType scvIdx) const + { return scvfIndicesOfScv_[scvIdx]; } + + template<int d = dim> + typename std::enable_if<d == 2, IndexType>::type getNumScvf_() const + { + Dune::GeometryType triangle, quadrilateral; + triangle.makeTriangle(); + quadrilateral.makeQuadrilateral(); + + return gridView_.size(triangle)*6 + gridView_.size(quadrilateral)*8; + } + + template<int d = dim> + typename std::enable_if<d == 3, IndexType>::type getNumScvf_() const + { + Dune::GeometryType simplex, pyramid, prism, cube; + simplex.makeTetrahedron(); + pyramid.makePyramid(); + prism.makePrism(); + cube.makeHexahedron(); + + return gridView_.size(simplex)*12 + gridView_.size(pyramid)*16 + gridView_.size(prism)*18 + gridView_.size(cube)*24; + } + +private: + const Problem& problem_() const + { return *problemPtr_; } + + const Problem* problemPtr_; + GridView gridView_; + Dumux::ElementMap<GridView> elementMap_; + std::vector<SubControlVolume> scvs_; + std::vector<SubControlVolumeFace> scvfs_; + std::vector<std::vector<IndexType>> scvfIndicesOfScv_; + std::vector<bool> fpsVertices_; + std::vector<bool> boundaryVertices_; + IndexType numBoundaryScvf_; + GlobalInteractionVolumeSeeds globalInteractionVolumeSeeds_; +}; + +} // end namespace + +#endif diff --git a/dumux/discretization/cellcentered/mpfa/hybridfps/globalinteractionvolumeseeds.hh b/dumux/discretization/cellcentered/mpfa/hybridfps/globalinteractionvolumeseeds.hh new file mode 100644 index 0000000000000000000000000000000000000000..f14ac446c7739e0168a90c273208ab8cdd0d9bee --- /dev/null +++ b/dumux/discretization/cellcentered/mpfa/hybridfps/globalinteractionvolumeseeds.hh @@ -0,0 +1,158 @@ +// -*- 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 global interaction volumes of the mpfa-o method. + */ +#ifndef DUMUX_DISCRETIZATION_MPFA_O_HYBRIDFPS_GLOBALINTERACTIONVOLUMESEEDS_HH +#define DUMUX_DISCRETIZATION_MPFA_O_HYBRIDFPS_GLOBALINTERACTIONVOLUMESEEDS_HH + +#include <dumux/implicit/cellcentered/mpfa/properties.hh> +#include <dumux/discretization/cellcentered/mpfa/methods.hh> +#include <dumux/discretization/cellcentered/mpfa/facetypes.hh> + +namespace Dumux +{ +/*! + * \ingroup MpfaO + * \brief Base class for the creation and storage of the interaction volume seeds for mpfa methods. + */ +template<class TypeTag> +class CCMpfaOHybridFpsGlobalInteractionVolumeSeeds +{ + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using Helper = typename GET_PROP_TYPE(TypeTag, MpfaHelper); + using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); + using InteractionVolumeOMethod = typename GET_PROP_TYPE(TypeTag, InteractionVolume); + using OSeed = typename InteractionVolumeOMethod::Seed; + using InteractionVolumeFpsMethod = typename GET_PROP_TYPE(TypeTag, BoundaryInteractionVolume); + using FpsSeed = typename InteractionVolumeFpsMethod::Seed; + using Element = typename GridView::template Codim<0>::Entity; + + using IndexType = typename GridView::IndexSet::IndexType; + using LocalIndexType = typename InteractionVolumeOMethod::LocalIndexType; + + static const int dim = GridView::dimension; + static const int numEq = GET_PROP_VALUE(TypeTag, NumEq); + +public: + CCMpfaOHybridFpsGlobalInteractionVolumeSeeds(const GridView gridView) : gridView_(gridView) {} + + // initializes the interaction volumes or the seeds + void update(const Problem& problem, const std::vector<bool>& fpsVertices, const std::vector<bool>& boundaryVertices) + { + problemPtr_ = &problem; + fpsSeeds_.clear(); + oSeeds_.clear(); + + // -1 indicates that the scvf has not been handled yet + auto numScvf = problem_().model().globalFvGeometry().numScvf(); + scvfIndexMap_.resize(numScvf, -1); + + // detect and handle the boundary first + initializeFpsSeeds_(fpsVertices, boundaryVertices); + initializeOSeeds_(); + } + + const OSeed& seed(const SubControlVolumeFace& scvf) const + { return oSeeds_[scvfIndexMap_[scvf.index()]]; } + + const FpsSeed& boundarySeed(const SubControlVolumeFace& scvf, const LocalIndexType eqIdx) const + { return fpsSeeds_[scvfIndexMap_[scvf.index()]]; } + +private: + void initializeFpsSeeds_(const std::vector<bool>& fpsVertices, const std::vector<bool>& boundaryVertices) + { + fpsSeeds_.reserve(fpsVertices.size()); + + IndexType fpsSeedIndex = 0; + for (const auto& element : elements(gridView_)) + { + auto fvGeometry = localView(problem_().model().globalFvGeometry()); + fvGeometry.bind(element); + for (const auto& scvf : scvfs(fvGeometry)) + { + // skip the rest if we already handled this face or if face doesn't belong to an fps element + if (scvfIndexMap_[scvf.index()] != -1 || !fpsVertices[scvf.vertexIndex()]) + continue; + + // the fps interaction volume seed + auto fpsSeed = Helper::makeBoundaryInteractionVolumeSeed(problem_(), element, fvGeometry, scvf, boundaryVertices[scvf.vertexIndex()]); + + // update the index map entries for the global scv faces in the interaction volume + for (const auto& localScvfSeed : fpsSeed.scvfSeeds()) + for (const auto scvfIdxGlobal : localScvfSeed.globalScvfIndices()) + scvfIndexMap_[scvfIdxGlobal] = fpsSeedIndex; + + // store interaction volume and increment counter + fpsSeeds_.emplace_back(std::move(fpsSeed)); + fpsSeedIndex++; + } + } + + // shrink fps seed vector to actual size + fpsSeeds_.shrink_to_fit(); + } + + void initializeOSeeds_() + { + oSeeds_.reserve(problem_().gridView().size(dim)); + + IndexType oSeedIndex = 0; + for (const auto& element : elements(gridView_)) + { + auto fvGeometry = localView(problem_().model().globalFvGeometry()); + fvGeometry.bind(element); + for (const auto& scvf : scvfs(fvGeometry)) + { + if (scvfIndexMap_[scvf.index()] != -1) + continue; + + // the inner interaction volume seed + auto seed = Helper::makeInnerInteractionVolumeSeed(problem_(), element, fvGeometry, scvf); + + // update the index map entries for the global scv faces in the interaction volume + for (const auto& localScvf : seed.scvfSeeds()) + for (const auto scvfIdxGlobal : localScvf.globalScvfIndices()) + scvfIndexMap_[scvfIdxGlobal] = oSeedIndex; + + // store interaction volume and increment counter + oSeeds_.emplace_back(std::move(seed)); + oSeedIndex++; + } + } + + // shrink seed vector to actual size + oSeeds_.shrink_to_fit(); + } + + const Problem& problem_() const + { return *problemPtr_; } + + const Problem* problemPtr_; + GridView gridView_; + std::vector<IndexType> scvfIndexMap_; + std::vector<FpsSeed> fpsSeeds_; + std::vector<OSeed> oSeeds_; +}; +} // end namespace + + +#endif diff --git a/dumux/discretization/cellcentered/mpfa/hybridfps/helper.hh b/dumux/discretization/cellcentered/mpfa/hybridfps/helper.hh new file mode 100644 index 0000000000000000000000000000000000000000..695fc18d6a33da953caafbf4e67284e42d4a895c --- /dev/null +++ b/dumux/discretization/cellcentered/mpfa/hybridfps/helper.hh @@ -0,0 +1,125 @@ +// -*- 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 Helper class to get the required information on an interaction volume. + */ +#ifndef DUMUX_DISCRETIZATION_CC_MPFAO_HYBRID_FPS_HELPER_HH +#define DUMUX_DISCRETIZATION_CC_MPFAO_HYBRID_FPS_HELPER_HH + +#include <dumux/discretization/cellcentered/mpfa/omethod/helper.hh> + +namespace Dumux +{ +/*! + * \ingroup Mpfa + * \brief Helper class to get the required information on an interaction volume. + * Specialization for the Mpfa-O method in two dimensions. + */ +template<class TypeTag, int dim> +class MpfaHelperBase<TypeTag, MpfaMethods::oMethodFpsHybrid, dim> : public MpfaHelperBase<TypeTag, MpfaMethods::oMethod, dim> +{ + using Base = MpfaHelperBase<TypeTag, MpfaMethods::oMethod, dim>; + + using Implementation = typename GET_PROP_TYPE(TypeTag, MpfaHelper); + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); + using InteractionVolume = typename GET_PROP_TYPE(TypeTag, InteractionVolume); + + using GlobalIndexType = typename Base::GlobalIndexType; + using LocalIndexType = typename Base::LocalIndexType; + using GlobalIndexSet = typename Base::GlobalIndexSet; + using LocalIndexSet = typename Base::LocalIndexSet; + + using InteractionVolumeSeed = typename InteractionVolume::Seed; + using ScvSeed = typename InteractionVolumeSeed::LocalScvSeed; + using ScvfSeed = typename InteractionVolumeSeed::LocalScvfSeed; + + using Element = typename GridView::template Codim<0>::Entity; + +public: + static InteractionVolumeSeed makeInnerInteractionVolumeSeed(const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const SubControlVolumeFace& scvf) + { + std::vector<ScvSeed> scvSeeds; + std::vector<ScvfSeed> scvfSeeds; + + fillEntitySeeds_(scvSeeds, scvfSeeds, problem, element, fvGeometry, scvf, /*dummy*/0); + return InteractionVolumeSeed(std::move(scvSeeds), std::move(scvfSeeds), false); + } + + static InteractionVolumeSeed makeBoundaryInteractionVolumeSeed(const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const SubControlVolumeFace& scvf, + const bool isBoundary) + { + std::vector<ScvSeed> scvSeeds; + std::vector<ScvfSeed> scvfSeeds; + + fillEntitySeeds_(scvSeeds, scvfSeeds, problem, element, fvGeometry, scvf, isBoundary); + return InteractionVolumeSeed(std::move(scvSeeds), std::move(scvfSeeds), isBoundary); + } + +private: + static void fillEntitySeeds_(std::vector<ScvSeed>& scvSeeds, + std::vector<ScvfSeed>& scvfSeeds, + const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const SubControlVolumeFace& scvf, + const bool isBoundary) + { + // Get the two scv faces in the first scv + auto scvfVector = Implementation::getScvFacesAtVertex(scvf.vertexIndex(), element, fvGeometry); + + // The global index of the first scv of the interaction region + auto scvIdx0 = scvf.insideScvIdx(); + + // rotate counter clockwise and create the entities + Implementation::performRotation_(problem, scvfVector, scvSeeds, scvfSeeds, scvIdx0, /*dummy*/0); + + if (isBoundary) + { + // the local scvf index of the second local face of the first local scv + LocalIndexType storeIdx = scvfSeeds.size(); + + // clockwise rotation until hitting the boundary again + Implementation::performRotation_(problem, scvfVector, scvSeeds, scvfSeeds, scvIdx0, /*dummy*/0, /*clockwise*/true); + + // Finish by creating the first scv + scvSeeds.emplace(scvSeeds.begin(), ScvSeed(GlobalIndexSet({scvfVector[0]->index(), scvfVector[1]->index()}), + LocalIndexSet({0, storeIdx}), + scvIdx0)); + } + else + // Finish by creating the first scv + scvSeeds.emplace(scvSeeds.begin(), ScvSeed(GlobalIndexSet({scvfVector[0]->index(), scvfVector[1]->index()}), + LocalIndexSet({0, static_cast<LocalIndexType>(scvfSeeds.size()-1)}), + scvIdx0)); + } +}; + +} // end namespace + +#endif diff --git a/dumux/discretization/cellcentered/mpfa/hybridfps/interactionvolume.hh b/dumux/discretization/cellcentered/mpfa/hybridfps/interactionvolume.hh new file mode 100644 index 0000000000000000000000000000000000000000..6e0b3242680b82ee10ae501de918ba080159786a --- /dev/null +++ b/dumux/discretization/cellcentered/mpfa/hybridfps/interactionvolume.hh @@ -0,0 +1,72 @@ +// -*- 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 classes for interaction volume of mpfa methods. + */ +#ifndef DUMUX_DISCRETIZATION_CC_MPFA_O_HYBRID_FPS_INTERACTIONVOLUME_HH +#define DUMUX_DISCRETIZATION_CC_MPFA_O_HYBRID_FPS_INTERACTIONVOLUME_HH + +#include <dumux/common/math.hh> + +#include <dune/localfunctions/lagrange/pqkfactory.hh> +#include <dumux/implicit/cellcentered/mpfa/properties.hh> + +#include <dumux/discretization/cellcentered/mpfa/omethodfps/interactionvolume.hh> + +namespace Dumux +{ +//! Specialization of the interaction volume traits class +template<class TypeTag> +class CCMpfaOHybridFpsInteractionVolumeTraits : public CCMpfaOInteractionVolumeTraits<TypeTag> +{ +public: + // we will use the o method's interaction volume where necessary + using BoundaryInteractionVolume = CCMpfaInteractionVolumeImplementation<TypeTag, MpfaMethods::oMethodFps>; +}; + +/*! + * \ingroup Mpfa + * \brief Base class for the interaction volumes of the mpfa-o method with full pressure support. + */ +template<class TypeTag> +class CCMpfaInteractionVolumeImplementation<TypeTag, MpfaMethods::oMethodFpsHybrid> : public CCMpfaOInteractionVolume<TypeTag, CCMpfaOHybridFpsInteractionVolumeTraits<TypeTag>> +{ + using Traits = CCMpfaOHybridFpsInteractionVolumeTraits<TypeTag>; + using ParentType = CCMpfaOInteractionVolume<TypeTag, Traits>; + + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables); + + using IVSeed = typename Traits::Seed; + +public: + + CCMpfaInteractionVolumeImplementation(const IVSeed& seed, + const Problem& problem, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars) + : ParentType(seed, problem, fvGeometry, elemVolVars) + {} +}; + +} // end namespace + +#endif diff --git a/dumux/discretization/cellcentered/mpfa/interactionvolume.hh b/dumux/discretization/cellcentered/mpfa/interactionvolume.hh index 2ef7555a9a0a2e083a4f412764816120200b46c6..8d8f233b95d10045126e4b55e0c4542659f85c19 100644 --- a/dumux/discretization/cellcentered/mpfa/interactionvolume.hh +++ b/dumux/discretization/cellcentered/mpfa/interactionvolume.hh @@ -42,6 +42,7 @@ using CCMpfaInteractionVolume = CCMpfaInteractionVolumeImplementation<TypeTag, G } // end namespace // the specializations of this class for the available methods have to be included here +#include <dumux/discretization/cellcentered/mpfa/lmethod/interactionvolume.hh> #include <dumux/discretization/cellcentered/mpfa/omethod/interactionvolume.hh> #include <dumux/discretization/cellcentered/mpfa/omethodfps/interactionvolume.hh> diff --git a/dumux/discretization/cellcentered/mpfa/interactionvolumebase.hh b/dumux/discretization/cellcentered/mpfa/interactionvolumebase.hh index 4af5b4591cd409dc570cb4cd90fe137ab4c90f55..aa02689f9d739bc5efa6fb7cd23871dc35f34f63 100644 --- a/dumux/discretization/cellcentered/mpfa/interactionvolumebase.hh +++ b/dumux/discretization/cellcentered/mpfa/interactionvolumebase.hh @@ -27,6 +27,26 @@ namespace Dumux { +//! Base class for the interaction volume traits +template<class TypeTag> +class CCMpfaInteractionVolumeTraitsBase +{ + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + + static const int dim = GridView::dimension; + static const int dimWorld = GridView::dimensionworld; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; + +public: + using LocalIndexType = std::uint8_t; + using LocalIndexSet = std::vector<LocalIndexType>; + using GlobalIndexType = typename GridView::IndexSet::IndexType; + using GlobalIndexSet = std::vector<GlobalIndexType>; + + using Tensor = Dune::FieldMatrix<Scalar, dim, dim>; +}; + /*! * \ingroup Mpfa * \brief Base class for the interaction volumes of mpfa methods. @@ -43,13 +63,26 @@ public: using BoundaryInteractionVolume = typename Traits::BoundaryInteractionVolume; using LocalIndexType = typename Traits::LocalIndexType; using LocalIndexSet = typename Traits::LocalIndexSet; - using LocalIndexPair = typename Traits::LocalIndexPair; using GlobalIndexType = typename Traits::GlobalIndexType; using GlobalIndexSet = typename Traits::GlobalIndexSet; using Vector = typename Traits::Vector; using PositionVector = typename Traits::PositionVector; using Seed = typename Traits::Seed; + struct LocalFaceData + { + LocalIndexType localScvfIndex; + LocalIndexType localScvIndex; + bool isOutside; + + LocalFaceData(const LocalIndexType faceIndex, + const LocalIndexType scvIndex, + bool isOut) + : localScvfIndex(faceIndex), + localScvIndex(scvIndex), + isOutside(isOut) {} + }; + //! solves the local equation system for the computation of the transmissibilities template<typename GetTensorFunction> void solveLocalSystem(const GetTensorFunction& getTensor) @@ -68,15 +101,15 @@ public: { DUNE_THROW(Dune::NotImplemented, "Actual interaction volume implementation does not provide a globalScvfs() method."); } //! returns the local index of an scvf in the IV and a boolean whether or not it is on the negative side of the local scvf (flux has to be inverted) - LocalIndexPair getLocalIndexPair(const SubControlVolumeFace& scvf) const - { DUNE_THROW(Dune::NotImplemented, "Actual interaction volume implementation does not provide a getLocalIndexPair() method."); } + LocalFaceData getLocalFaceData(const SubControlVolumeFace& scvf) const + { DUNE_THROW(Dune::NotImplemented, "Actual interaction volume implementation does not provide a getLocalFaceData() method."); } //! returns the transmissibilities corresponding to a local scvf - Vector getTransmissibilities(const LocalIndexPair& localIndexPair) const + Vector getTransmissibilities(const LocalFaceData& localFaceData) const { DUNE_THROW(Dune::NotImplemented, "Actual interaction volume implementation does not provide a getTransmissibilities() method."); } //! returns the neumann flux corresponding to a local scvf - Scalar getNeumannFlux(LocalIndexPair& localIndexPair) const + Scalar getNeumannFlux(const LocalFaceData& localFaceData) const { DUNE_THROW(Dune::NotImplemented, "Actual interaction volume implementation does not provide a getNeumannFlux() method."); } //! returns the local index in a vector for a given global index diff --git a/dumux/discretization/cellcentered/mpfa/lmethod/globalinteractionvolumeseeds.hh b/dumux/discretization/cellcentered/mpfa/lmethod/globalinteractionvolumeseeds.hh new file mode 100644 index 0000000000000000000000000000000000000000..86778212839be40182a94e40aa358a1e2a97f14f --- /dev/null +++ b/dumux/discretization/cellcentered/mpfa/lmethod/globalinteractionvolumeseeds.hh @@ -0,0 +1,131 @@ +// -*- 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 global interaction volume seeds of mpfa methods. + */ +#ifndef DUMUX_DISCRETIZATION_MPFA_L_GLOBALINTERACTIONVOLUMESEEDS_HH +#define DUMUX_DISCRETIZATION_MPFA_L_GLOBALINTERACTIONVOLUMESEEDS_HH + +#include <dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseedsbase.hh> +#include <dumux/discretization/cellcentered/mpfa/methods.hh> + +namespace Dumux +{ +/*! + * \ingroup Mpfa + * \brief Specialization of the class for the mpfa-l method. + */ +template<class TypeTag> +class CCMpfaGlobalInteractionVolumeSeedsImplementation<TypeTag, MpfaMethods::lMethod> : public CCMpfaGlobalInteractionVolumeSeedsBase<TypeTag> +{ + using ParentType = CCMpfaGlobalInteractionVolumeSeedsBase<TypeTag>; + // the parent needs to be friend to access the private initialization method + friend ParentType; + + using InteractionVolume = typename GET_PROP_TYPE(TypeTag, InteractionVolume); + using SubControlVolume = typename GET_PROP_TYPE(TypeTag, SubControlVolume); + using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Helper = typename GET_PROP_TYPE(TypeTag, MpfaHelper); + + using IndexType = typename InteractionVolume::GlobalIndexSet::value_type; + using Element = typename GridView::template Codim<0>::Entity; + +public: + CCMpfaGlobalInteractionVolumeSeedsImplementation(const GridView gridView) : ParentType(gridView) {} + +private: + void initializeSeeds_(std::vector<bool>& boundaryVertices, std::vector<bool>& isFaceHandled) + { + const auto numScvf = this->problem_().model().globalFvGeometry().numScvf(); + const auto numBoundaryScvf = this->problem_().model().globalFvGeometry().numBoundaryScvf(); + + // reserve memory + this->seeds_.reserve(numScvf - numBoundaryScvf); + this->boundarySeeds_.reserve(numBoundaryScvf); + + IndexType boundarySeedIndex = 0; + IndexType seedIndex = 0; + for (const auto& element : elements(this->gridView_)) + { + auto fvGeometry = localView(this->problem_().model().globalFvGeometry()); + fvGeometry.bindElement(element); + for (const auto& scvf : scvfs(fvGeometry)) + { + // skip the rest if we already handled this face + if (isFaceHandled[scvf.index()]) + continue; + + if (boundaryVertices[scvf.vertexIndex()]) + { + // make the boundary interaction volume seed + this->boundarySeeds_.emplace_back(Helper::makeBoundaryInteractionVolumeSeed(this->problem_(), element, fvGeometry, scvf)); + + // update the index map entries for the global scv faces in the interaction volume + for (auto scvfIdxGlobal : this->boundarySeeds_.back().globalScvfIndices()) + { + this->scvfIndexMap_[scvfIdxGlobal] = boundarySeedIndex; + isFaceHandled[scvfIdxGlobal] = true; + } + + // increment counter + boundarySeedIndex++; + } + else + { + // make the inner interaction volume seed only if we are on highest level of all connected elements + if (isLocalMaxLevel(element, scvf)) + { + this->seeds_.emplace_back(Helper::makeInnerInteractionVolumeSeed(this->problem_(), element, fvGeometry, scvf)); + + // update the index map entries for the global scv faces in the interaction volume + for (auto scvfIdxGlobal : this->seeds_.back().globalScvfIndices()) + { + this->scvfIndexMap_[scvfIdxGlobal] = seedIndex; + isFaceHandled[scvfIdxGlobal] = true; + } + + // increment counter + seedIndex++; + } + } + } + } + + // shrink vectors to actual size + this->seeds_.shrink_to_fit(); + this->boundarySeeds_.shrink_to_fit(); + } + + bool isLocalMaxLevel(const Element& element, const SubControlVolumeFace& scvf) const + { + auto inLevel = element.level(); + for (auto outsideIdx : scvf.outsideScvIndices()) + if (this->problem_().model().globalFvGeometry().element(outsideIdx).level() > inLevel) + return false; + return true; + } +}; + +} // end namespace + + +#endif diff --git a/dumux/discretization/cellcentered/mpfa/lmethod/helper.hh b/dumux/discretization/cellcentered/mpfa/lmethod/helper.hh new file mode 100644 index 0000000000000000000000000000000000000000..34e5699952acf4ce3afb117137ef10935e84b679 --- /dev/null +++ b/dumux/discretization/cellcentered/mpfa/lmethod/helper.hh @@ -0,0 +1,183 @@ +// -*- 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 Helper class to get the required information on an interaction volume. + */ +#ifndef DUMUX_DISCRETIZATION_CC_MPFA_L_HELPER_HH +#define DUMUX_DISCRETIZATION_CC_MPFA_L_HELPER_HH + +#include <dumux/common/math.hh> +#include <dumux/discretization/cellcentered/mpfa/facetypes.hh> +#include <dumux/discretization/cellcentered/mpfa/methods.hh> + +#include "localsubcontrolentityseeds.hh" + +namespace Dumux +{ +/*! + * \ingroup Mpfa + * \brief Helper class to get the required information on an interaction volume. + * Specialization for the Mpfa-L method in two dimensions. + */ +template<class TypeTag> +class MpfaMethodHelper<TypeTag, MpfaMethods::lMethod, /*dim*/2, /*dimWorld*/2> +{ + using Implementation = typename GET_PROP_TYPE(TypeTag, MpfaHelper); + + static const int dim = 2; + static const int dimWorld = 2; + + // The mpfa-o helper class used to construct the boundary interaction volume seeds + using oMethodHelper = CCMpfaHelperImplementation<TypeTag, MpfaMethods::oMethod, 2, 2>; + + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); + using InteractionVolume = typename GET_PROP_TYPE(TypeTag, InteractionVolume); + using BoundaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, BoundaryInteractionVolume); + + using Element = typename GridView::template Codim<0>::Entity; + + using InteractionVolumeSeed = typename InteractionVolume::Seed; + using ScvSeed = typename InteractionVolumeSeed::LocalScvSeed; + using OuterScvSeed = typename InteractionVolumeSeed::LocalOuterScvSeed; + using BoundaryInteractionVolumeSeed = typename BoundaryInteractionVolume::Seed; + + using GlobalIndexSet = typename InteractionVolume::GlobalIndexSet; + using GlobalIndexType = typename InteractionVolume::GlobalIndexType; + using LocalIndexSet = typename InteractionVolume::LocalIndexSet; + using LocalIndexType = typename InteractionVolume::LocalIndexType; + using Matrix = typename InteractionVolume::Matrix; +public: + static InteractionVolumeSeed makeInnerInteractionVolumeSeed(const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const SubControlVolumeFace& scvf) + { + std::vector<ScvSeed> scvSeeds; + std::vector<OuterScvSeed> outerScvSeeds; + std::vector<GlobalIndexType> globalScvfIndices(2); + + // we'll have maximal 2 ScvSeeds and 2 OuterScvSeeds + scvSeeds.reserve(2); + outerScvSeeds.reserve(2); + + fillEntitySeeds_(scvSeeds, outerScvSeeds, globalScvfIndices, problem, element, fvGeometry, scvf); + + // free unused memory + scvSeeds.shrink_to_fit(); + outerScvSeeds.shrink_to_fit(); + + // return interaction volume seed + return InteractionVolumeSeed(std::move(scvSeeds), std::move(outerScvSeeds), std::move(globalScvfIndices)); + } + + static BoundaryInteractionVolumeSeed makeBoundaryInteractionVolumeSeed(const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const SubControlVolumeFace& scvf) + { return oMethodHelper::makeBoundaryInteractionVolumeSeed(problem, element, fvGeometry, scvf); } + + template<class InteractionRegion> + static LocalIndexType selectionCriterion(const InteractionRegion& I1, + const InteractionRegion& I2, + const Matrix& M1, + const Matrix& M2) + { + Scalar eps = 1e-10; + Scalar t11, t12, t21, t22; + t11 = M1[I1.contiFaceLocalIdx][0]; + t21 = M2[I2.contiFaceLocalIdx][0]; + if (I1.contiFaceLocalIdx == 0) + { + t12 = M1[I1.contiFaceLocalIdx][1]; + t22 = M2[I2.contiFaceLocalIdx][2]; + } + else + { + t12 = M1[I1.contiFaceLocalIdx][2]; + t22 = M2[I2.contiFaceLocalIdx][1]; + } + + Scalar s1 = std::abs(t11-t12); + Scalar s2 = std::abs(t22-t21); + + if (s1 < s2 + eps*s1) + return 0; + else + return 1; + } + +private: + static void fillEntitySeeds_(std::vector<ScvSeed>& scvSeeds, + std::vector<OuterScvSeed>& outerScvSeeds, + std::vector<GlobalIndexType>& globalScvfIndices, + const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const SubControlVolumeFace& scvf) + { + // make the first scv seed, we know this element will NOT be on the lowest local level + auto scvfVector = Implementation::getScvFacesAtVertex(scvf.vertexIndex(), element, fvGeometry); + auto localScvfIdx = Implementation::getLocalFaceIndex(scvf, scvfVector); + auto globalScvIdx = scvf.insideScvIdx(); + scvSeeds.emplace_back( GlobalIndexSet({scvfVector[0]->index(), scvfVector[1]->index()}), + globalScvIdx, + localScvfIdx ); + + // get the surrounding elements and "outside" data + LocalIndexType otherScvfIdx = localScvfIdx == 1 ? 0 : 1; + auto e2 = problem.model().globalFvGeometry().element(scvf.outsideScvIdx()); + auto e3 = problem.model().globalFvGeometry().element(scvfVector[otherScvfIdx]->outsideScvIdx()); + auto e2Geometry = localView(problem.model().globalFvGeometry()); + auto e3Geometry = localView(problem.model().globalFvGeometry()); + e2Geometry.bindElement(e2); + e3Geometry.bindElement(e3); + auto e2Scvfs = Implementation::getCommonAndNextScvFace(scvf, e2Geometry, /*clockwise?*/localScvfIdx == 1); + auto e3Scvfs = Implementation::getCommonAndNextScvFace(*scvfVector[otherScvfIdx], e3Geometry, /*clockwise?*/localScvfIdx == 0); + + // we now know the two faces for which flux calculation will happen using this iv seed + globalScvfIndices[0] = scvf.index(); + globalScvfIndices[1] = e2Scvfs[otherScvfIdx]->index(); + + // scv seed for e2, we know the local common scvf index will be otherScvfIdx in 2d + scvSeeds.emplace_back( GlobalIndexSet({e2Scvfs[0]->index(), e2Scvfs[1]->index()}), + e2Scvfs[0]->insideScvIdx(), + otherScvfIdx ); + + // Outer seed for e3, we know the local common scvf index will be localScvfIdx in 2d + const auto& f3 = *e3Scvfs[localScvfIdx]; + outerScvSeeds.emplace_back(f3.insideScvIdx(), f3.index()); + + // Outer seed for outside of e2, we know the local scvf index here will be localScvfIdx in 2d + auto e4 = problem.model().globalFvGeometry().element(e2Scvfs[localScvfIdx]->outsideScvIdx()); + auto e4Geometry = localView(problem.model().globalFvGeometry()); + e4Geometry.bindElement(e4); + auto e4Scvfs = Implementation::getCommonAndNextScvFace(*e2Scvfs[localScvfIdx], e4Geometry, /*clockwise?*/localScvfIdx == 1); + const auto& f4 = *e4Scvfs[otherScvfIdx]; + outerScvSeeds.emplace_back(f4.insideScvIdx(), f4.index()); + } +}; + +} // end namespace + +#endif diff --git a/dumux/discretization/cellcentered/mpfa/lmethod/interactionregions.hh b/dumux/discretization/cellcentered/mpfa/lmethod/interactionregions.hh new file mode 100644 index 0000000000000000000000000000000000000000..2a9a4287fcee3d73ca46574cabfda23419ffd1f4 --- /dev/null +++ b/dumux/discretization/cellcentered/mpfa/lmethod/interactionregions.hh @@ -0,0 +1,129 @@ +// -*- 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 classes for interaction volume of mpfa methods. + */ +#ifndef DUMUX_DISCRETIZATION_CC_MPFA_L_INTERACTIONREGIONS_HH +#define DUMUX_DISCRETIZATION_CC_MPFA_L_INTERACTIONREGIONS_HH + +namespace Dumux +{ +/*! + * \ingroup Mpfa + * \brief Base class for the interaction regions of the mpfa-l method. + */ +template<class TypeTag> +struct InteractionRegion +{ +private: + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + using InteractionVolume = typename GET_PROP_TYPE(TypeTag, InteractionVolume); + + static const int dim = GridView::dimension; + static const int dimWorld = GridView::dimensionworld; + + using Element = typename GridView::template Codim<0>::Entity; + using LocalIndexType = typename InteractionVolume::LocalIndexType; + using GlobalIndexType = typename InteractionVolume::GlobalIndexType; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; + +public: + LocalIndexType contiFaceLocalIdx; + std::vector<GlobalIndexType> scvIndices; + std::vector<GlobalPosition> scvCenters; + std::vector<GlobalPosition> normal; + std::vector<GlobalPosition> nu; + std::vector<Scalar> detX; + std::vector<Element> elements; + + std::array<GlobalIndexType, 2> globalScvfs; + + // Constructor for dim == 2 + template<class ScvSeedType, class OuterScvSeedType> + InteractionRegion(const Problem& problem, + const FVElementGeometry& fvGeometry, + const ScvSeedType& scvSeed, + const OuterScvSeedType& outerSeed1, + const OuterScvSeedType& outerSeed2, + const Element& element1, + const Element& element2, + const Element& element3) + { + contiFaceLocalIdx = scvSeed.contiFaceLocalIdx(); + globalScvfs[0] = scvSeed.globalScvfIndices()[contiFaceLocalIdx]; + globalScvfs[1] = contiFaceLocalIdx == 0 ? outerSeed1.globalScvfIndex() : outerSeed2.globalScvfIndex(); + + elements.resize(3); + elements[0] = element1; + elements[1] = element2; + elements[2] = element3; + + // The participating sub control entities + auto&& scv1 = fvGeometry.scv(scvSeed.globalIndex()); + auto&& scv2 = fvGeometry.scv(outerSeed1.globalIndex()); + auto&& scv3 = fvGeometry.scv(outerSeed2.globalIndex()); + auto&& scvf1 = fvGeometry.scvf(scvSeed.globalScvfIndices()[0]); + auto&& scvf2 = fvGeometry.scvf(scvSeed.globalScvfIndices()[1]); + + // The necessary coordinates and normals + GlobalPosition v = scvf1.vertexCorner(); + GlobalPosition f1 = scvf1.facetCorner(); + GlobalPosition f2 = scvf2.facetCorner(); + + scvCenters.resize(3); + scvCenters[0] = scv1.center(); + scvCenters[1] = scv2.center(); + scvCenters[2] = scv3.center(); + + normal.resize(2); + normal[0] = scvf1.unitOuterNormal(); + normal[0] *= scvf1.area(); + normal[1] = scvf2.unitOuterNormal(); + normal[1] *= scvf2.area(); + + // indices of the participating scvs + scvIndices.resize(3); + scvIndices[0] = scv1.index(); + scvIndices[1] = scv2.index(); + scvIndices[2] = scv3.index(); + + // calculate nus and detXs + static const Dune::FieldMatrix<Scalar, dim, dim> R = {{0.0, 1.0}, {-1.0, 0.0}}; + nu.resize(7); + R.mv(f2-scvCenters[0], nu[0]); + R.mv(scvCenters[0]-f1, nu[1]); + R.mv(f1-scvCenters[1], nu[2]); + R.mv(scvCenters[1]-v, nu[3]); + R.mv(v-scvCenters[2], nu[4]); + R.mv(scvCenters[2]-f2, nu[5]); + R.mv(v-scvCenters[0], nu[6]); + + detX.resize(3); + detX[0] = (f1-scvCenters[0])*nu[0]; + detX[1] = (v-scvCenters[1])*nu[2]; + detX[2] = (f2-scvCenters[2])*nu[4]; + } +}; +}; //end namespace + +#endif \ No newline at end of file diff --git a/dumux/discretization/cellcentered/mpfa/lmethod/interactionvolume.hh b/dumux/discretization/cellcentered/mpfa/lmethod/interactionvolume.hh new file mode 100644 index 0000000000000000000000000000000000000000..e597c04bb7ab6cd77b3de86247a655a1685bb57f --- /dev/null +++ b/dumux/discretization/cellcentered/mpfa/lmethod/interactionvolume.hh @@ -0,0 +1,364 @@ +// -*- 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 classes for interaction volume of mpfa methods. + */ +#ifndef DUMUX_DISCRETIZATION_CC_MPFA_L_INTERACTIONVOLUME_HH +#define DUMUX_DISCRETIZATION_CC_MPFA_L_INTERACTIONVOLUME_HH + +#include <dumux/common/math.hh> +#include <dumux/implicit/cellcentered/mpfa/properties.hh> +#include <dumux/discretization/cellcentered/mpfa/interactionvolumebase.hh> +#include <dumux/discretization/cellcentered/mpfa/omethod/interactionvolume.hh> +#include <dumux/discretization/cellcentered/mpfa/facetypes.hh> +#include <dumux/discretization/cellcentered/mpfa/methods.hh> + +#include "interactionvolumeseed.hh" +#include "interactionregions.hh" + +namespace Dumux +{ +//! Specialization of the interaction volume traits class for the mpfa-o method +template<class TypeTag> +class CCMpfaLInteractionVolumeTraits : public CCMpfaInteractionVolumeTraitsBase<TypeTag> +{ + using BaseTraits = CCMpfaInteractionVolumeTraitsBase<TypeTag>; + + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + + static const int dim = GridView::dimension; + static const int dimWorld = GridView::dimensionworld; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; + +public: + using BoundaryInteractionVolume = CCMpfaInteractionVolumeImplementation<TypeTag, MpfaMethods::oMethod>; + + // in the interaction region there will be dim faces and dim+1 cell pressures + using PositionVector = std::vector<GlobalPosition>; + using Matrix = Dune::FieldMatrix<Scalar, dim, dim+1>; + using Vector = Dune::FieldVector<Scalar, dim+1>; + + using typename BaseTraits::LocalIndexSet; + using typename BaseTraits::GlobalIndexSet; + using Seed = CCMpfaLInteractionVolumeSeed<GlobalIndexSet, LocalIndexSet>; +}; + +/*! + * \ingroup Mpfa + * \brief Base class for the interaction volumes of the mpfa-l method. + */ +template<class TypeTag> +class CCMpfaInteractionVolumeImplementation<TypeTag, MpfaMethods::lMethod> : public CCMpfaInteractionVolumeBase<TypeTag, CCMpfaLInteractionVolumeTraits<TypeTag>> +{ + // The interaction volume implementation has to be friend + friend typename GET_PROP_TYPE(TypeTag, InteractionVolume); + using Traits = CCMpfaLInteractionVolumeTraits<TypeTag>; + using ParentType = CCMpfaInteractionVolumeBase<TypeTag, Traits>; + + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using MpfaHelper = typename GET_PROP_TYPE(TypeTag, MpfaHelper); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); + using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables); + + static const int dim = GridView::dimension; + static const int dimWorld = GridView::dimensionworld; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; + using InteractionRegion = InteractionRegion<TypeTag>; + + using Vector = typename Traits::Vector; + using Tensor = typename Traits::Tensor; + +public: + using Matrix = typename Traits::Matrix; + using typename ParentType::LocalIndexType; + using typename ParentType::LocalIndexSet; + using typename ParentType::LocalFaceData; + using typename ParentType::GlobalIndexType; + using typename ParentType::GlobalIndexSet; + using typename ParentType::PositionVector; + using typename ParentType::Seed; + + using ScvSeedType = typename Seed::LocalScvSeed; + using OuterScvSeedType = typename Seed::LocalOuterScvSeed; + + CCMpfaInteractionVolumeImplementation(const Seed& seed, + const Problem& problem, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars) + : problemPtr_(&problem), + fvGeometryPtr_(&fvGeometry), + elemVolVarsPtr_(&elemVolVars), + regionUnique_(seed.isUnique()), + systemSolved_(false) + { + // set up the possible interaction regions + setupInteractionRegions_(seed, fvGeometry); + } + + template<typename GetTensorFunction> + void solveLocalSystem(const GetTensorFunction& getTensor) + { + if (regionUnique_) + { + const auto& ir = interactionRegions_[0]; + T_ = assembleMatrix_(getTensor, ir); + postSolve_(ir); + } + else + { + std::array<Matrix, 2> M; + M[0] = assembleMatrix_(getTensor, interactionRegions_[0]); + M[1] = assembleMatrix_(getTensor, interactionRegions_[1]); + const auto id = MpfaHelper::selectionCriterion(interactionRegions_[0], interactionRegions_[1], M[0], M[1]); + + const auto& ir = interactionRegions_[id]; + T_ = M[id]; + postSolve_(ir); + } + } + + //! returns the local index pair. This holds the scvf's local index and a boolean whether or not the flux has to be inverted + LocalFaceData getLocalFaceData(const SubControlVolumeFace& scvf) const + { + assert(systemSolved_ && "Scvf Indices not set yet. You have to call solveLocalSystem() beforehand."); + assert(scvf.index() == globalScvfIndices_[0] || scvf.index() == globalScvfIndices_[1] && "The provided scvf is not the flux face of the interaction volume."); + + if (globalScvfIndices_[0] == scvf.index()) + return LocalFaceData(contiFaceLocalIdx_, /*dummy*/0, false); + else + return LocalFaceData(contiFaceLocalIdx_, /*dummy*/0, true); + } + + //! returns the transmissibilities corresponding to the bound scvf + Vector getTransmissibilities(const LocalFaceData& localFaceData) const + { + assert(systemSolved_ && "Transmissibilities not calculated yet. You have to call solveLocalSystem() beforehand."); + + auto tij = T_[localFaceData.localScvfIndex]; + if (localFaceData.isOutside) + tij *= -1.0; + return tij; + } + + const GlobalIndexSet& volVarsStencil() const + { + assert(systemSolved_ && "volVarsStencil not set yet. You have to call solveLocalSystem() beforehand."); + return volVarsStencil_; + } + + const PositionVector& volVarsPositions() const + { + assert(systemSolved_ && "globalScvfs not set yet. You have to call solveLocalSystem() beforehand."); + return volVarsPositions_; + } + + const GlobalIndexSet& globalScvfs() const + { + assert(systemSolved_ && "globalScvfs not set yet. You have to call solveLocalSystem() beforehand."); + return globalScvfIndices_; + } + + const Matrix& matrix() const + { return T_; } + +private: + //! Assembles and solves the local equation system + //! Specialization for dim = 2 + template<typename GetTensorFunction, int d = dim> + typename std::enable_if<d == 2, Matrix>::type + assembleMatrix_(const GetTensorFunction& getTensor, const InteractionRegion& ir) + { + // the elements the scvs live in + const auto& e1 = ir.elements[0]; + const auto& e2 = ir.elements[1]; + const auto& e3 = ir.elements[2]; + + // the corresponding scvs + const auto scv1 = fvGeometry_().scv(ir.scvIndices[0]); + const auto scv2 = fvGeometry_().scv(ir.scvIndices[1]); + const auto scv3 = fvGeometry_().scv(ir.scvIndices[2]); + + // Get diffusion tensors in the three scvs + const auto T1 = getTensor(e1, elemVolVars_()[scv1], scv1); + const auto T2 = getTensor(e2, elemVolVars_()[scv2], scv2); + const auto T3 = getTensor(e3, elemVolVars_()[scv3], scv3); + + // required omega factors + Scalar w111 = calculateOmega_(ir.normal[0], ir.nu[0], ir.detX[0], T1); + Scalar w112 = calculateOmega_(ir.normal[0], ir.nu[1], ir.detX[0], T1); + Scalar w123 = calculateOmega_(ir.normal[0], ir.nu[2], ir.detX[1], T2); + Scalar w124 = calculateOmega_(ir.normal[0], ir.nu[3], ir.detX[1], T2); + + Scalar w211 = calculateOmega_(ir.normal[1], ir.nu[0], ir.detX[0], T1); + Scalar w212 = calculateOmega_(ir.normal[1], ir.nu[1], ir.detX[0], T1); + Scalar w235 = calculateOmega_(ir.normal[1], ir.nu[4], ir.detX[2], T3); + Scalar w236 = calculateOmega_(ir.normal[1], ir.nu[5], ir.detX[2], T3); + + Scalar xi711 = calculateXi_(ir.nu[6], ir.nu[0], ir.detX[0]); + Scalar xi712 = calculateXi_(ir.nu[6], ir.nu[1], ir.detX[0]); + + Dune::FieldMatrix<Scalar, dim, dim> C, A; + Dune::FieldMatrix<Scalar, dim, dim+1> B; + + C[0][0] = -w111; + C[0][1] = -w112; + C[1][0] = -w211; + C[1][1] = -w212; + + A[0][0] = w111 - w124 - w123*xi711; + A[0][1] = w112 - w123*xi712; + A[1][0] = w211 - w236*xi711; + A[1][1] = w212 - w235 - w236*xi712; + + B[0][0] = w111 + w112 + w123*(1 - xi711 -xi712); + B[0][1] = -w123 - w124; + B[0][2] = 0.0; + B[1][0] = w211 + w212 + w236*(1 - xi711 - xi712); + B[1][1] = 0.0; + B[1][2] = -w235 - w236; + + // T = CA^-1B + D + A.invert(); + auto T = (A.leftmultiply(C)).rightmultiplyany(B); + T[0][0] += w111 + w112; + T[1][0] += w211 + w212; + + return T; + } + + //! sets up the interaction regions for later transmissibility matrix calculation + //! Specialization for dim = 2 + template<int d = dim> + typename std::enable_if<d == 2>::type + setupInteractionRegions_(const Seed& seed, const FVElementGeometry& fvGeometry) + { + const auto& globalFvGeometry = problem_().model().globalFvGeometry(); + + if (regionUnique_) + { + interactionRegions_.reserve(1); + const auto& scvSeed1 = seed.scvSeed(0); + const auto& scvSeed2 = seed.outerScvSeed(0); + const auto& scvSeed3 = seed.outerScvSeed(1); + auto e1 = globalFvGeometry.element(scvSeed1.globalIndex()); + auto e2 = globalFvGeometry.element(scvSeed2.globalIndex()); + auto e3 = globalFvGeometry.element(scvSeed3.globalIndex()); + interactionRegions_.emplace_back(problem_(), fvGeometry, scvSeed1, scvSeed2, scvSeed3, e1, e2, e3); + + } + else + { + interactionRegions_.reserve(2); + const auto& scvSeed1 = seed.scvSeed(0); + const auto& scvSeed2 = seed.scvSeed(1); + const auto& outerScvSeed1 = seed.outerScvSeed(0); + const auto& outerScvSeed2 = seed.outerScvSeed(1); + auto e1 = globalFvGeometry.element(scvSeed1.globalIndex()); + auto e2 = globalFvGeometry.element(scvSeed2.globalIndex()); + auto e3 = globalFvGeometry.element(outerScvSeed1.globalIndex()); + auto e4 = globalFvGeometry.element(outerScvSeed2.globalIndex()); + + // scvSeed1 is the one the seed construction began at + if (scvSeed1.contiFaceLocalIdx() == 0) + { + interactionRegions_.emplace_back(problem_(), fvGeometry, scvSeed1, OuterScvSeedType(scvSeed2), outerScvSeed1, e1, e2, e3); + interactionRegions_.emplace_back(problem_(), fvGeometry, scvSeed2, outerScvSeed2, OuterScvSeedType(scvSeed1), e2, e4, e1); + } + else + { + interactionRegions_.emplace_back(problem_(), fvGeometry, scvSeed1, outerScvSeed1, OuterScvSeedType(scvSeed2), e1, e3, e2); + interactionRegions_.emplace_back(problem_(), fvGeometry, scvSeed2, OuterScvSeedType(scvSeed1), outerScvSeed2, e2, e1, e4); + } + } + } + + void postSolve_(const InteractionRegion& ir) + { + globalScvfIndices_.resize(2); + globalScvfIndices_[0] = ir.globalScvfs[0]; + globalScvfIndices_[1] = ir.globalScvfs[1]; + volVarsStencil_ = ir.scvIndices; + volVarsPositions_ = ir.scvCenters; + contiFaceLocalIdx_ = ir.contiFaceLocalIdx; + systemSolved_ = true; + } + + Scalar calculateOmega_(const GlobalPosition& normal, + const GlobalPosition& nu, + const Scalar detX, + const Tensor& T) const + { + GlobalPosition tmp; + T.mv(nu, tmp); + return (tmp*normal)/detX; + } + + Scalar calculateOmega_(const GlobalPosition& normal, + const GlobalPosition& nu, + const Scalar detX, + const Scalar t) const + { return (normal*nu)*t/detX; } + + //! Specialization for dim = 2 + template<int d = dim> + typename std::enable_if<d == 2, Scalar>::type + calculateXi_(const GlobalPosition& nu1, + const GlobalPosition& nu2, + const Scalar detX) const + { return crossProduct<Scalar>(nu1, nu2)/detX; } + + const Seed& seed_() const + { return seedPtr_; } + + const Problem& problem_() const + { return *problemPtr_; } + + const FVElementGeometry& fvGeometry_() const + { return *fvGeometryPtr_; } + + const ElementVolumeVariables& elemVolVars_() const + { return *elemVolVarsPtr_; } + + const Seed* seedPtr_; + const Problem* problemPtr_; + const FVElementGeometry* fvGeometryPtr_; + const ElementVolumeVariables* elemVolVarsPtr_; + + bool regionUnique_; + bool systemSolved_; + + LocalIndexType contiFaceLocalIdx_; + GlobalIndexSet globalScvfIndices_; + GlobalIndexSet volVarsStencil_; + PositionVector volVarsPositions_; + + std::vector<InteractionRegion> interactionRegions_; + + Matrix T_; +}; + +} // end namespace + +#endif diff --git a/dumux/discretization/cellcentered/mpfa/lmethod/interactionvolumeseed.hh b/dumux/discretization/cellcentered/mpfa/lmethod/interactionvolumeseed.hh new file mode 100644 index 0000000000000000000000000000000000000000..808abbece40e3feaf9b6c02aca04a6fbb26eb59f --- /dev/null +++ b/dumux/discretization/cellcentered/mpfa/lmethod/interactionvolumeseed.hh @@ -0,0 +1,98 @@ +// -*- 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 interaction volume seeds of mpfa methods. + */ +#ifndef DUMUX_DISCRETIZATION_CC_MPFA_L_INTERACTIONVOLUMESEED_HH +#define DUMUX_DISCRETIZATION_CC_MPFA_L_INTERACTIONVOLUMESEED_HH + +#include <dumux/implicit/cellcentered/mpfa/properties.hh> +#include "localsubcontrolentityseeds.hh" + +namespace Dumux +{ + +/*! + * \ingroup Mpfa + * \brief Base class for the interaction volume seed of mpfa methods + */ +template<typename G, typename L> +class CCMpfaLInteractionVolumeSeed +{ + using GlobalIndexSet = G; + using GlobalIndexType = typename G::value_type; + using LocalIndexSet = L; + using LocalIndexType = typename L::value_type; + +public: + using LocalScvSeed = CCMpfaLCentralLocalScvSeed<G, L>; + using LocalOuterScvSeed = CCMpfaLOuterLocalScvSeed<G>; + + CCMpfaLInteractionVolumeSeed(std::vector<LocalScvSeed>&& scvSeeds, + std::vector<LocalOuterScvSeed>&& outerScvSeeds, + std::vector<GlobalIndexType>&& globalScvfIndices) + : scvSeeds_(std::move(scvSeeds)), + outerScvSeeds_(std::move(outerScvSeeds)), + globalScvfIndices_(std::move(globalScvfIndices)) {} + + const std::vector<LocalScvSeed>& scvSeeds() const + { return scvSeeds_; } + + const LocalScvSeed& scvSeed(const LocalIndexType idx) const + { return scvSeeds_[idx]; } + + const std::vector<LocalOuterScvSeed>& outerScvSeeds() const + { return outerScvSeeds_; } + + const LocalOuterScvSeed& outerScvSeed(const LocalIndexType idx) const + { return outerScvSeeds_[idx]; } + + std::vector<GlobalIndexType> globalScvIndices() const + { + std::vector<GlobalIndexType> globalIndices; + globalIndices.reserve(scvSeeds().size() + outerScvSeeds().size()); + + for (auto&& localScvSeed : scvSeeds()) + globalIndices.push_back(localScvSeed.globalIndex()); + + for (auto&& localScvSeed : outerScvSeeds()) + globalIndices.push_back(localScvSeed.globalIndex()); + + // make the entries unique + std::sort(globalIndices.begin(), globalIndices.end()); + globalIndices.erase(std::unique(globalIndices.begin(), globalIndices.end()), globalIndices.end()); + + return globalIndices; + } + + const std::vector<GlobalIndexType>& globalScvfIndices() const + { return globalScvfIndices_; } + + bool isUnique() const + { return scvSeeds_.size() == 1; } + +private: + std::vector<LocalScvSeed> scvSeeds_; + std::vector<LocalOuterScvSeed> outerScvSeeds_; + std::vector<GlobalIndexType> globalScvfIndices_; +}; +} // end namespace + +#endif diff --git a/dumux/discretization/cellcentered/mpfa/lmethod/localsubcontrolentityseeds.hh b/dumux/discretization/cellcentered/mpfa/lmethod/localsubcontrolentityseeds.hh new file mode 100644 index 0000000000000000000000000000000000000000..739ed82e5fc17e2514a17334203d230ea91ecd78 --- /dev/null +++ b/dumux/discretization/cellcentered/mpfa/lmethod/localsubcontrolentityseeds.hh @@ -0,0 +1,98 @@ +// -*- 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 sub control entity seeds of the mpfa-o method. + */ +#ifndef DUMUX_DISCRETIZATION_CC_MPFA_L_LOCALSUBCONTROLENTITYSEEDS_HH +#define DUMUX_DISCRETIZATION_CC_MPFA_L_LOCALSUBCONTROLENTITYSEEDS_HH + +#include <dumux/implicit/cellcentered/mpfa/properties.hh> +#include <dumux/discretization/cellcentered/mpfa/facetypes.hh> + +namespace Dumux +{ +//! The scv seed class for scvs which could be the central scv in the actual interaction region +template<typename G, typename L> +class CCMpfaLCentralLocalScvSeed +{ + using GlobalIndexSet = G; + using LocalIndexSet = L; +public: + using GlobalIndexType = typename GlobalIndexSet::value_type; + using LocalIndexType = typename LocalIndexSet::value_type; + + //! constructor fully defining the scv seed + CCMpfaLCentralLocalScvSeed(GlobalIndexSet&& globalScvfIndices, + GlobalIndexType globalScvIndex, + LocalIndexType contiFaceLocalIdx) + : contiFaceLocalIdx_(contiFaceLocalIdx), + globalScvIndex_(globalScvIndex), + globalScvfIndices_(std::move(globalScvfIndices)) {} + + LocalIndexType contiFaceLocalIdx() const + { return contiFaceLocalIdx_; } + + GlobalIndexType globalIndex() const + { return globalScvIndex_; } + + const GlobalIndexSet& globalScvfIndices() const + { return globalScvfIndices_; } + +private: + LocalIndexType contiFaceLocalIdx_; + GlobalIndexType globalScvIndex_; + GlobalIndexSet globalScvfIndices_; +}; + +//! The scv seed class for scvs which could be the central scv in the actual interaction region +template<typename G> +class CCMpfaLOuterLocalScvSeed +{ + using GlobalIndexSet = G; +public: + using GlobalIndexType = typename G::value_type; + + //! Constructor fully initializing the members + CCMpfaLOuterLocalScvSeed(GlobalIndexType globalScvIndex, + GlobalIndexType globalScvfIndex) + : scvIndexGlobal_(globalScvIndex), + scvfIndexGlobal_(globalScvfIndex) {} + + //! Construct from central scv seed + template<typename GI, typename LI> + CCMpfaLOuterLocalScvSeed(const CCMpfaLCentralLocalScvSeed<GI, LI>& scvSeed) + : scvIndexGlobal_(scvSeed.globalIndex()), + scvfIndexGlobal_(scvSeed.globalScvfIndices()[scvSeed.contiFaceLocalIdx()]) {} + + + const GlobalIndexType globalIndex() const + { return scvIndexGlobal_; } + + const GlobalIndexType globalScvfIndex() const + { return scvfIndexGlobal_; } + +private: + GlobalIndexType scvIndexGlobal_; + GlobalIndexType scvfIndexGlobal_; +}; + +} // end namespace + +#endif diff --git a/dumux/discretization/cellcentered/mpfa/lmethod/subcontrolvolumeface.hh b/dumux/discretization/cellcentered/mpfa/lmethod/subcontrolvolumeface.hh new file mode 100644 index 0000000000000000000000000000000000000000..13e90f8edc57ffaaea75353a59fe54a100b7250d --- /dev/null +++ b/dumux/discretization/cellcentered/mpfa/lmethod/subcontrolvolumeface.hh @@ -0,0 +1,77 @@ +// -*- 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 Class for an mpfa-o sub control volume face + */ +#ifndef DUMUX_DISCRETIZATION_CC_MPFA_L_SUBCONTROLVOLUMEFACE_HH +#define DUMUX_DISCRETIZATION_CC_MPFA_L_SUBCONTROLVOLUMEFACE_HH + +#include <dumux/discretization/cellcentered/mpfa/methods.hh> +#include <dumux/discretization/cellcentered/mpfa/subcontrolvolumefacebase.hh> + +namespace Dumux +{ + +/*! + * \ingroup Discretization + * \brief Class for a sub control volume face in the mpfa-l method. We simply inherit from the base class here. + */ +template<class G, typename I> +class CCMpfaSubControlVolumeFace<MpfaMethods::lMethod, G, I> : public CCMpfaSubControlVolumeFaceBase<G, I> +{ + using ParentType = CCMpfaSubControlVolumeFaceBase<G, I>; + using Geometry = G; + using IndexType = I; + + using Scalar = typename Geometry::ctype; + static const int dim = Geometry::mydimension; + static const int dimworld = Geometry::coorddimension; + + using GlobalPosition = Dune::FieldVector<Scalar, dimworld>; + +public: + //! We do not use the localIndex variable. + //! It is here to satisfy the general mpfa scvf interface. + template<class MpfaHelper> + CCMpfaSubControlVolumeFace(const MpfaHelper& helper, + std::vector<GlobalPosition>&& corners, + GlobalPosition&& unitOuterNormal, + IndexType vertexIndex, + unsigned int localIndex, + IndexType scvfIndex, + IndexType insideScvIdx, + const std::vector<IndexType>& outsideScvIndices, + Scalar q, + bool boundary) + : ParentType(helper, + std::move(corners), + std::move(unitOuterNormal), + vertexIndex, + localIndex, + scvfIndex, + insideScvIdx, + outsideScvIndices, + 0.0, // q should be always zero for the mpfa-l method, + boundary) {} +}; + +} // end namespace + +#endif diff --git a/dumux/discretization/cellcentered/mpfa/methods.hh b/dumux/discretization/cellcentered/mpfa/methods.hh index 9309a2568b99b9ec74e1e7d6b9bf4c7e85216318..07d47b78ac6b716f63ff92e0601ee172008ec03f 100644 --- a/dumux/discretization/cellcentered/mpfa/methods.hh +++ b/dumux/discretization/cellcentered/mpfa/methods.hh @@ -27,6 +27,7 @@ namespace Dumux { enum class MpfaMethods : unsigned int { + lMethod, oMethod, oMethodFps }; diff --git a/dumux/discretization/cellcentered/mpfa/mpfageometryhelper.hh b/dumux/discretization/cellcentered/mpfa/mpfageometryhelper.hh deleted file mode 100644 index c98451ae93e5e595a02e684cfcb38252b503e824..0000000000000000000000000000000000000000 --- a/dumux/discretization/cellcentered/mpfa/mpfageometryhelper.hh +++ /dev/null @@ -1,236 +0,0 @@ -// -*- 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 finite volume geometry vector for box models - * This builds up the sub control volumes and sub control volume faces - * for each element. - */ -#ifndef DUMUX_DISCRETIZATION_CC_MPFA_GEOMETRYHELPER_HH -#define DUMUX_DISCRETIZATION_CC_MPFA_GEOMETRYHELPER_HH - -#include <dune/geometry/multilineargeometry.hh> -#include <dune/geometry/referenceelements.hh> - -#include <dumux/common/math.hh> - -namespace Dumux -{ -//! A class to create sub control volume face geometries per intersection -template <class GridView, int dim> -class MpfaGeometryHelper -{}; - -//! Specialization for dim == 2 -template <class GridView> -class MpfaGeometryHelper<GridView, 2> -{ -private: - using Scalar = typename GridView::ctype; - static const int dim = GridView::dimension; - static const int dimWorld = GridView::dimensionworld; - - using GlobalPosition = Dune::FieldVector<Scalar, dim>; - using Element = typename GridView::template Codim<0>::Entity; - using Intersection = typename GridView::Intersection; - - using FaceReferenceElements = typename Dune::ReferenceElements<Scalar, dim-1>; - -public: - using PointVector = std::vector<GlobalPosition>; - - MpfaGeometryHelper(const typename Element::Geometry& elemGeom) : gt(elemGeom.type()) {} - - //! get sub control volume face corners of an intersection for the given local index - static PointVector getScvfCorners(const typename Intersection::Geometry& geometry, - unsigned int indexOnIntersection) - { - if (indexOnIntersection == 0) - return PointVector({geometry.center(), geometry.corner(0)}); - else if (indexOnIntersection == 1) - return PointVector({geometry.center(), geometry.corner(1)}); - else - DUNE_THROW(Dune::InvalidStateException, "local index exceeds the number of corners of 2d intersections"); - } - - static GlobalPosition getScvfIntegrationPoint(const PointVector& scvfCorners, Scalar q) - { - auto d = scvfCorners[1]; - auto ip = scvfCorners[0]; - d -= ip; - d *= q; - ip += d; - - return ip; - } - - static Scalar getScvfArea(const PointVector& scvfCorners) - { - return (scvfCorners[1]-scvfCorners[0]).two_norm(); - } - - std::size_t getNumLocalScvfs() - { - Dune::GeometryType triangle, quadrilateral; - triangle.makeTriangle(); - quadrilateral.makeQuadrilateral(); - - if (gt == triangle) - return 6; - else if (gt == quadrilateral) - return 8; - else - DUNE_THROW(Dune::InvalidStateException, "unknown 2d geometry type " << gt); - } - -private: - const Dune::GeometryType gt; -}; - -//! Specialization for dim == 3 -template <class GridView> -class MpfaGeometryHelper<GridView, 3> -{ -private: - using Scalar = typename GridView::ctype; - static const int dim = GridView::dimension; - static const int dimWorld = GridView::dimensionworld; - - using GlobalPosition = Dune::FieldVector<Scalar, dim>; - using Element = typename GridView::template Codim<0>::Entity; - using Intersection = typename GridView::Intersection; - - using FaceReferenceElements = typename Dune::ReferenceElements<Scalar, dim-1>; - -public: - using PointVector = std::vector<GlobalPosition>; - - MpfaGeometryHelper(const typename Element::Geometry& elemGeom) : gt(elemGeom.type()) {} - - //! get sub control volume face corners of an intersection for the given local index - static PointVector getScvfCorners(const typename Intersection::Geometry& geometry, - unsigned int indexOnIntersection) - { - // extract the corners of the sub control volumes - const auto& referenceElement = FaceReferenceElements::general(geometry.type()); - - // maximum number of necessary points is 9 (for quadrilateral) - GlobalPosition p[9]; - auto corners = geometry.corners(); - - // the intersection center - p[0] = geometry.center(); - - // vertices - for (int i = 0; i < corners; ++i) - p[i+1] = geometry.corner(i); - - // edge midpoints - for (int i = 0; i < referenceElement.size(1); ++i) - p[i+corners+1] = geometry.global(referenceElement.position(i, 1)); - - // proceed according to number of corners - switch (corners) - { - case 3: // triangle - { - //! Only build the maps the first time we encounter a triangle - static const std::uint8_t vo = 1; //! vertex offset in point vector p - static const std::uint8_t eo = 4; //! edge offset in point vector p - static const std::uint8_t map[3][4] = - { - {0, eo+1, eo+0, vo+0}, - {0, eo+0, eo+2, vo+1}, - {0, eo+2, eo+1, vo+2} - }; - - return PointVector( {p[map[indexOnIntersection][0]], - p[map[indexOnIntersection][1]], - p[map[indexOnIntersection][2]], - p[map[indexOnIntersection][3]]} ); - } - case 4: // quadrilateral - { - //! Only build the maps the first time we encounter a quadrilateral - static const std::uint8_t vo = 1; //! vertex offset in point vector p - static const std::uint8_t eo = 5; //! face offset in point vector p - static const std::uint8_t map[4][4] = - { - {0, eo+0, eo+2, vo+0}, - {0, eo+2, eo+1, vo+1}, - {0, eo+3, eo+0, vo+2}, - {0, eo+1, eo+3, vo+3} - }; - - return PointVector( {p[map[indexOnIntersection][0]], - p[map[indexOnIntersection][1]], - p[map[indexOnIntersection][2]], - p[map[indexOnIntersection][3]]} ); - } - default: - DUNE_THROW(Dune::NotImplemented, "Mpfa scvf corners for dim=" << dim - << " dimWorld=" << dimWorld - << " corners=" << corners); - } - } - - static GlobalPosition getScvfIntegrationPoint(const PointVector& scvfCorners, Scalar q) - { - auto d = scvfCorners[3]; - auto ip = scvfCorners[0]; - d -= ip; - d *= q; - ip += d; - - return ip; - } - - static Scalar getScvfArea(const PointVector& scvfCorners) - { - // after Wolfram alpha quadrilateral area - return 0.5*Dumux::crossProduct(scvfCorners[3]-scvfCorners[0], scvfCorners[2]-scvfCorners[1]).two_norm(); - } - - std::size_t getNumLocalScvfs() - { - Dune::GeometryType tetrahedron, pyramid, prism, hexahedron; - tetrahedron.makeTetrahedron(); - pyramid.makePyramid(); - prism.makePrism(); - hexahedron.makeHexahedron(); - - if (gt == tetrahedron) - return 12; - else if (gt == pyramid) - return 16; - else if (gt == prism) - return 18; - else if (gt == hexahedron) - return 24; - else - DUNE_THROW(Dune::InvalidStateException, "unknown 3d geometry type " << gt); - } - -private: - const Dune::GeometryType gt; // the geometry type of the element -}; - -} // end namespace - -#endif \ No newline at end of file diff --git a/dumux/discretization/cellcentered/mpfa/omethod/globalfvgeometry.hh b/dumux/discretization/cellcentered/mpfa/omethod/globalfvgeometry.hh deleted file mode 100644 index 1e8288198553c7f29e4562c6a633f710bb90543e..0000000000000000000000000000000000000000 --- a/dumux/discretization/cellcentered/mpfa/omethod/globalfvgeometry.hh +++ /dev/null @@ -1,46 +0,0 @@ -// -*- 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 finite volume geometry vector for box models - * This builds up the sub control volumes and sub control volume faces - * for each element. - */ -#ifndef DUMUX_DISCRETIZATION_CC_MPFA_O_GLOBALFVGEOMETRY_HH -#define DUMUX_DISCRETIZATION_CC_MPFA_O_GLOBALFVGEOMETRY_HH - -#include <dumux/discretization/cellcentered/mpfa/globalfvgeometrybase.hh> -#include <dumux/discretization/cellcentered/mpfa/methods.hh> - -namespace Dumux -{ -//! Specialization for the mpfa-o scheme. -template<class TypeTag, bool EnableCache> -class CCMpfaGlobalFVGeometryImplementation<TypeTag, MpfaMethods::oMethod, EnableCache> : public CCMpfaGlobalFVGeometryBase<TypeTag, EnableCache> -{ - using ParentType = CCMpfaGlobalFVGeometryBase<TypeTag, EnableCache>; - using GridView = typename GET_PROP_TYPE(TypeTag, GridView); - -public: - CCMpfaGlobalFVGeometryImplementation(const GridView gridView) : ParentType(gridView) {} -}; - -} // end namespace - -#endif diff --git a/dumux/discretization/cellcentered/mpfa/omethod/helper.hh b/dumux/discretization/cellcentered/mpfa/omethod/helper.hh index 2de4395fcaaaeec48dcc61b8ee90f1855315a0aa..d12415c2da7aad1940094008b73b67bbc86b0980 100644 --- a/dumux/discretization/cellcentered/mpfa/omethod/helper.hh +++ b/dumux/discretization/cellcentered/mpfa/omethod/helper.hh @@ -35,38 +35,38 @@ namespace Dumux /*! * \ingroup Mpfa * \brief Helper class to get the required information on an interaction volume. - * Specialization for the Mpfa-O method in two dimensions. + * Specialization for the Mpfa-O method in two dimensions embedded in a 2d world. */ template<class TypeTag> -class MpfaHelperBase<TypeTag, MpfaMethods::oMethod, 2> +class MpfaMethodHelper<TypeTag, MpfaMethods::oMethod, /*dim*/2, /*dimWorld*/2> { using Implementation = typename GET_PROP_TYPE(TypeTag, MpfaHelper); + + static const int dim = 2; + static const int dimWorld = 2; + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); - using InteractionVolume = typename GET_PROP_TYPE(TypeTag, InteractionVolume); - using BoundaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, BoundaryInteractionVolume); - - static const int dim = GridView::dimension; - static const int dimWorld = GridView::dimensionworld; - using Element = typename GridView::template Codim<0>::Entity; - using GlobalIndexType = typename GridView::IndexSet::IndexType; - using LocalIndexType = typename InteractionVolume::LocalIndexType; + // The o method is used on the boundaries also for other methods. These will use this helper + // class to set up the seeds on the boundary. Therefore we extract all seed information from + // the boundary interaction volume here to be compatible with other mpfa methods where seed types + // most probably differ. We use the fact here, that for the o-method, boundary and interior + // interaction volumes are identical, which is always given. + using InteractionVolume = typename GET_PROP_TYPE(TypeTag, BoundaryInteractionVolume); using InteractionVolumeSeed = typename InteractionVolume::Seed; using ScvSeed = typename InteractionVolumeSeed::LocalScvSeed; using ScvfSeed = typename InteractionVolumeSeed::LocalScvfSeed; - using BoundaryInteractionVolumeSeed = typename BoundaryInteractionVolume::Seed; - using BoundaryScvSeed = typename BoundaryInteractionVolumeSeed::LocalScvSeed; - using BoundaryScvfSeed = typename BoundaryInteractionVolumeSeed::LocalScvfSeed; - - using GlobalIndexSet = std::vector<GlobalIndexType>; - using LocalIndexSet = std::vector<LocalIndexType>; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalIndexType = typename InteractionVolume::GlobalIndexType; + using LocalIndexType = typename InteractionVolume::LocalIndexType; - using DimVector = Dune::FieldVector<Scalar, dimWorld>; + using GlobalIndexSet = typename InteractionVolume::GlobalIndexSet; + using LocalIndexSet = typename InteractionVolume::LocalIndexSet; public: static InteractionVolumeSeed makeInnerInteractionVolumeSeed(const Problem& problem, const Element& element, @@ -89,13 +89,13 @@ public: return InteractionVolumeSeed(std::move(scvSeeds), std::move(scvfSeeds), false); } - static BoundaryInteractionVolumeSeed makeBoundaryInteractionVolumeSeed(const Problem& problem, - const Element& element, - const FVElementGeometry& fvGeometry, - const SubControlVolumeFace& scvf) + static InteractionVolumeSeed makeBoundaryInteractionVolumeSeed(const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const SubControlVolumeFace& scvf) { - std::vector<BoundaryScvSeed> scvSeeds; - std::vector<BoundaryScvfSeed> scvfSeeds; + std::vector<ScvSeed> scvSeeds; + std::vector<ScvfSeed> scvfSeeds; // reserve sufficient memory scvSeeds.reserve(20); @@ -107,13 +107,13 @@ public: scvSeeds.shrink_to_fit(); scvfSeeds.shrink_to_fit(); - return BoundaryInteractionVolumeSeed(std::move(scvSeeds), std::move(scvfSeeds), true); + return InteractionVolumeSeed(std::move(scvSeeds), std::move(scvfSeeds), true); } private: - template<typename ScvSeed, typename ScvfSeed> - static void fillEntitySeeds_(std::vector<ScvSeed>& scvSeeds, - std::vector<ScvfSeed>& scvfSeeds, + template<typename ScvSeedType, typename ScvfSeedType> + static void fillEntitySeeds_(std::vector<ScvSeedType>& scvSeeds, + std::vector<ScvfSeedType>& scvfSeeds, const Problem& problem, const Element& element, const FVElementGeometry& fvGeometry, @@ -140,23 +140,25 @@ private: performRotation_(problem, scvfVector, scvSeeds, scvfSeeds, scvIdx0, /*clockwise*/true); // Finish by creating the first scv - scvSeeds.emplace(scvSeeds.begin(), ScvSeed(GlobalIndexSet({scvfVector[0]->index(), scvfVector[1]->index()}), - LocalIndexSet({0, storeIdx}), - scvIdx0)); + scvSeeds.emplace(scvSeeds.begin(), GlobalIndexSet({scvfVector[0]->index(), scvfVector[1]->index()}), + LocalIndexSet({0, storeIdx}), + scvIdx0); } else // Finish by creating the first scv - scvSeeds.emplace(scvSeeds.begin(), ScvSeed(GlobalIndexSet({scvfVector[0]->index(), scvfVector[1]->index()}), - LocalIndexSet({0, static_cast<LocalIndexType>(scvfSeeds.size()-1)}), - scvIdx0)); + scvSeeds.emplace(scvSeeds.begin(), GlobalIndexSet({scvfVector[0]->index(), scvfVector[1]->index()}), + LocalIndexSet({0, static_cast<LocalIndexType>(scvfSeeds.size()-1)}), + scvIdx0); } - // clockwise rotation and construction of local scv & scv face entities - template<typename ScvSeed, typename ScvfSeed, typename ScvfPointerVector> + // in 2d we can make use of knowing the basis orientation (right hand system) + // that way we can find the interaction regions more rapidly by clockwise and + // counter clockwise rotation and construction of local scv & scv face entities + template<typename ScvSeedType, typename ScvfSeedType, typename ScvfPointerVector> static void performRotation_(const Problem& problem, const ScvfPointerVector& scvfVector, - std::vector<ScvSeed>& scvSeeds, - std::vector<ScvfSeed>& scvfSeeds, + std::vector<ScvSeedType>& scvSeeds, + std::vector<ScvfSeedType>& scvfSeeds, const GlobalIndexType scvIdx0, const bool clockWise = false) { @@ -176,23 +178,22 @@ private: while (!finished) { // Get some indices beforehand - GlobalIndexType globalScvfIdx = curScvf.index(); - GlobalIndexType insideGlobalScvIdx = curScvf.insideScvIdx(); GlobalIndexType outsideGlobalScvIdx = curScvf.outsideScvIdx(); LocalIndexType insideLocalScvIdx = firstIteration ? 0 : localScvIdx; // the current element inside of the scv face - auto insideElement = problem.model().globalFvGeometry().element(insideGlobalScvIdx); + auto insideElement = problem.model().globalFvGeometry().element(curScvf.insideScvIdx()); auto faceType = Implementation::getMpfaFaceType(problem, insideElement, curScvf); // if the face touches the boundary, create a boundary scvf entity if (curScvf.boundary()) { assert(faceType == MpfaFaceTypes::neumann || faceType == MpfaFaceTypes::dirichlet); - scvfSeeds.emplace_back(ScvfSeed(curScvf, - LocalIndexSet({insideLocalScvIdx}), - GlobalIndexSet({globalScvfIdx}), - faceType)); + scvfSeeds.emplace_back( curScvf, + insideLocalScvIdx, + LocalIndexSet(), + GlobalIndexSet(), + faceType ); // rotation loop is finished finished = true; return; } @@ -201,10 +202,11 @@ private: if (outsideGlobalScvIdx == scvIdx0) { // create scv face entity for the last face of the loop - scvfSeeds.emplace_back(ScvfSeed(curScvf, - LocalIndexSet({insideLocalScvIdx, 0}), - GlobalIndexSet({globalScvfIdx, scvfVector[1]->index()}), - faceType)); + scvfSeeds.emplace_back( curScvf, + insideLocalScvIdx, + LocalIndexSet({0}), + GlobalIndexSet({scvfVector[1]->index()}), + faceType ); // rotation loop is finished finished = true; return; @@ -222,13 +224,12 @@ private: auto& nextScvf = *outsideScvfVector[nextFaceCoordIdx]; // create local scv face entity of the current scvf - GlobalIndexType commonGlobalScvfIdx = commonScvf.index(); LocalIndexType outsideLocalScvIdx = localScvIdx+1; - - scvfSeeds.emplace_back(ScvfSeed(curScvf, - LocalIndexSet({insideLocalScvIdx, outsideLocalScvIdx}), - GlobalIndexSet({globalScvfIdx, commonGlobalScvfIdx}), - faceType)); + scvfSeeds.emplace_back( curScvf, + insideLocalScvIdx, + LocalIndexSet({outsideLocalScvIdx}), + GlobalIndexSet({commonScvf.index()}), + faceType ); localScvfIdx++; // create index set storing the two local scvf indices @@ -238,7 +239,7 @@ private: // create "outside" scv GlobalIndexSet globalScvfIndices({outsideScvfVector[0]->index(), outsideScvfVector[1]->index()}); - scvSeeds.emplace_back(ScvSeed(std::move(globalScvfIndices), std::move(localScvfs), curScvf.outsideScvIdx())); + scvSeeds.emplace_back(std::move(globalScvfIndices), std::move(localScvfs), outsideGlobalScvIdx); localScvIdx++; // create the next scvf in the following iteration @@ -251,38 +252,292 @@ private: /*! * \ingroup Mpfa * \brief Helper class to get the required information on an interaction volume. - * Specialization for the Mpfa-O method in three dimensions. + * Specialization for the Mpfa-O method in two dimensions embedded in a 3d world. */ template<class TypeTag> -class MpfaHelperBase<TypeTag, MpfaMethods::oMethod, 3> +class MpfaMethodHelper<TypeTag, MpfaMethods::oMethod, /*dim*/2, /*dimWorld*/3> { using Implementation = typename GET_PROP_TYPE(TypeTag, MpfaHelper); + + static const int dim = 2; + static const int dimWorld = 3; + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); - using InteractionVolume = typename GET_PROP_TYPE(TypeTag, InteractionVolume); - using BoundaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, BoundaryInteractionVolume); - static const int dim = GridView::dimension; - static const int dimWorld = GridView::dimensionworld; + // The o method is used on the boundaries also for other methods. These will use this helper + // class to set up the seeds on the boundary. Therefore we extract all seed information from + // the boundary interaction volume here to be compatible with other mpfa methods where seed types + // most probably differ. We use the fact here, that for the o-method, boundary and interior + // interaction volumes are identical, which is always given. + using InteractionVolume = typename GET_PROP_TYPE(TypeTag, BoundaryInteractionVolume); + using InteractionVolumeSeed = typename InteractionVolume::Seed; + using ScvSeed = typename InteractionVolumeSeed::LocalScvSeed; + using ScvfSeed = typename InteractionVolumeSeed::LocalScvfSeed; + using Element = typename GridView::template Codim<0>::Entity; - using GlobalIndexType = typename GridView::IndexSet::IndexType; + using GlobalIndexType = typename InteractionVolume::GlobalIndexType; using LocalIndexType = typename InteractionVolume::LocalIndexType; + using GlobalIndexSet = typename InteractionVolume::GlobalIndexSet; + using LocalIndexSet = typename InteractionVolume::LocalIndexSet; +public: + static InteractionVolumeSeed makeInnerInteractionVolumeSeed(const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const SubControlVolumeFace& scvf) + { + std::vector<ScvSeed> scvSeeds; + std::vector<ScvfSeed> scvfSeeds; + + // reserve sufficient memory + scvSeeds.reserve(20); + scvfSeeds.reserve(20); + + // The vertex index around which we construct the interaction volume + auto vIdxGlobal = scvf.vertexIndex(); + + // Get the two scv faces in the scv + auto scvfVector = Implementation::getScvFacesAtVertex(vIdxGlobal, element, fvGeometry); + + // fill the entity seed data + fillEntitySeeds_(scvSeeds, scvfSeeds, problem, element, fvGeometry, scvfVector, vIdxGlobal); + + // shrink containers to necessary size + scvSeeds.shrink_to_fit(); + scvfSeeds.shrink_to_fit(); + + for (auto& scvfSeed : scvfSeeds) + scvfSeed.makeOutsideDataUnique(); + + return InteractionVolumeSeed(std::move(scvSeeds), std::move(scvfSeeds), false); + } + + static InteractionVolumeSeed makeBoundaryInteractionVolumeSeed(const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const SubControlVolumeFace& scvf) + { + std::vector<ScvSeed> scvSeeds; + std::vector<ScvfSeed> scvfSeeds; + + // reserve sufficient memory + scvSeeds.reserve(20); + scvfSeeds.reserve(20); + + // The vertex index around which we construct the interaction volume + auto vIdxGlobal = scvf.vertexIndex(); + + // Get the two scv faces in the scv + auto scvfVector = Implementation::getScvFacesAtVertex(vIdxGlobal, element, fvGeometry); + + // fill the entity seed data + fillEntitySeeds_(scvSeeds, scvfSeeds, problem, element, fvGeometry, scvfVector, vIdxGlobal); + + // shrink containers to necessary size + scvSeeds.shrink_to_fit(); + scvfSeeds.shrink_to_fit(); + + for (auto& scvfSeed : scvfSeeds) + scvfSeed.makeOutsideDataUnique(); + + return InteractionVolumeSeed(std::move(scvSeeds), std::move(scvfSeeds), true); + } + +private: + template<class ScvSeedType, class ScvfSeedType, class ScvfVector> + static void fillEntitySeeds_(std::vector<ScvSeedType>& scvSeeds, + std::vector<ScvfSeedType>& scvfSeeds, + const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const ScvfVector& scvfVector, + const GlobalIndexType vIdxGlobal) + { + // make the scv without knowing the local scvf indices yet + // take the inside scv index from the first scvf (is the same for all) + scvSeeds.emplace_back( GlobalIndexSet({scvfVector[0]->index(), + scvfVector[1]->index()}), + scvfVector[0]->insideScvIdx()); + + // make the scvf seeds for the two scvfs connected to the scv + auto& actualScvSeed = scvSeeds.back(); + LocalIndexType actualLocalScvIdx = scvSeeds.size()-1; + + for (int coordDir = 0; coordDir < dim; ++coordDir) + { + const auto& actualScvf = *scvfVector[coordDir]; + + // if scvf is on a boundary, we create the scvfSeed and make no neighbor + if (actualScvf.boundary()) + { + // set the local scvfIndex of the face that is about to created + actualScvSeed.setLocalScvfIndex(coordDir, scvfSeeds.size()); + + // create the scvf seed + scvfSeeds.emplace_back( actualScvf, + actualLocalScvIdx, + LocalIndexSet(), + GlobalIndexSet(), + Implementation::getMpfaFaceType(problem, element, actualScvf) ); + } + else + { + // we loop over all neighbors of this face + for (auto outsideGlobalScvIdx : actualScvf.outsideScvIndices()) + { + // get outside element, fvgeometry etc. + auto outsideElement = problem.model().globalFvGeometry().element(outsideGlobalScvIdx); + auto outsideFvGeometry = localView(problem.model().globalFvGeometry()); + outsideFvGeometry.bindElement(outsideElement); + + // find scvf in outside corresponding to the actual scvf + auto outsideScvfVector = Implementation::getScvFacesAtVertex(vIdxGlobal, outsideElement, outsideFvGeometry); + auto commonFaceLocalIdx = Implementation::getCommonFaceLocalIndex(actualScvf, outsideScvfVector); + const auto& outsideScvf = *outsideScvfVector[commonFaceLocalIdx]; + const auto outsideScvfIdx = outsideScvf.index(); + + // check if the outside scv already exists and get its local index + bool outsideScvExists = false; + LocalIndexType outsideLocalScvIdx = 0; + for (auto&& scvSeed : scvSeeds) + { + if (scvSeed.globalIndex() == outsideGlobalScvIdx) + { + outsideScvExists = true; + break; + } + // keep track of local index + outsideLocalScvIdx++; + } + + // check if face already exists inside or in any neighbor + bool outsideScvfExists = false; + bool insideScvfExists = false; + LocalIndexType outsideLocalScvfIdx = 0; + LocalIndexType insideLocalScvfIdx = 0; + for (auto&& scvfSeed : scvfSeeds) + { + if (Implementation::contains(actualScvf.outsideScvIndices(), scvfSeed.insideGlobalScvIndex()) && + Implementation::contains(scvfSeed.outsideGlobalScvIndices(), actualScvf.insideScvIdx())) + outsideScvfExists = true; + else + if (!outsideScvfExists) + outsideLocalScvfIdx++; + + if (scvfSeed.insideGlobalScvIndex() == actualScvf.insideScvIdx() && + std::equal(scvfSeed.outsideGlobalScvIndices().begin(), scvfSeed.outsideGlobalScvIndices().end(), actualScvf.outsideScvIndices().begin())) + insideScvfExists = true; + else + if (!insideScvfExists) + insideLocalScvfIdx++; + } + + // we should never have two local scv faces existing inside and in an outside element + assert(!(outsideScvfExists && insideScvfExists) && "The scv face seems to exist twice!"); + + // outside scv has to be created + if (!outsideScvExists) + { + // The face does not yet exist + if (!insideScvfExists && !outsideScvfExists) + { + // set the local scvfIndex of the face that is about to created + actualScvSeed.setLocalScvfIndex(coordDir, scvfSeeds.size()); + + // create scvf seed + scvfSeeds.emplace_back(actualScvf, + actualLocalScvIdx, + Implementation::getMpfaFaceType(problem, element, actualScvf)); + + // pass the actual outside indices to the new scvf seed + scvfSeeds.back().addOutsideScvfIndex(outsideScvfIdx); + scvfSeeds.back().addOutsideLocalScvIndex(static_cast<LocalIndexType>(scvSeeds.size())); + } + else if (insideScvfExists && !outsideScvfExists) + { + // pass info on outside to the inside scvf seed + scvfSeeds[insideLocalScvfIdx].addOutsideScvfIndex(outsideScvfIdx); + scvfSeeds[insideLocalScvfIdx].addOutsideLocalScvIndex(static_cast<LocalIndexType>(scvSeeds.size())); + } + else if (!insideScvfExists && outsideScvfExists) + { + // pass info on outside to the inside scvf seed + scvfSeeds[outsideLocalScvfIdx].addOutsideScvfIndex(actualScvf.index()); + scvfSeeds[outsideLocalScvfIdx].addOutsideLocalScvIndex(static_cast<LocalIndexType>(scvSeeds.size())); + } + + // make outside scv by recursion + fillEntitySeeds_(scvSeeds, scvfSeeds, problem, outsideElement, outsideFvGeometry, outsideScvfVector, vIdxGlobal); + } + // outside scv exists, but no corresponding local scv face yet. Make it from inside. + else if (outsideScvExists && !outsideScvfExists && !insideScvfExists) + { + // set the local scvfIndex of the face that is about to created + actualScvSeed.setLocalScvfIndex(coordDir, scvfSeeds.size()); + + // create scvf seed + scvfSeeds.emplace_back(actualScvf, + actualLocalScvIdx, + Implementation::getMpfaFaceType(problem, element, actualScvf)); + + // pass the actual outside indices to the new scvf seed + scvfSeeds.back().addOutsideScvfIndex(outsideScvfIdx); + scvfSeeds.back().addOutsideLocalScvIndex(outsideLocalScvIdx); + } + else if (outsideScvExists && !insideScvfExists && outsideScvfExists) + { + // set the local scvfIndex of the found outside local scv face seed + actualScvSeed.setLocalScvfIndex(coordDir, outsideLocalScvfIdx); + + // pass info on outside to the inside found local scvf seed + scvfSeeds[outsideLocalScvfIdx].addOutsideScvfIndex(outsideScvfIdx); + scvfSeeds[outsideLocalScvfIdx].addOutsideLocalScvIndex(actualLocalScvIdx); + } + } + } + } + } +}; + +/*! + * \ingroup Mpfa + * \brief Helper class to get the required information on an interaction volume. + * Specialization for the Mpfa-O method in three dimensions. + */ +template<class TypeTag> +class MpfaMethodHelper<TypeTag, MpfaMethods::oMethod, /*dim*/3, /*dimWorld*/3> +{ + using Implementation = typename GET_PROP_TYPE(TypeTag, MpfaHelper); + + static const int dim = 3; + static const int dimWorld = 3; + + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); + using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); + + // The o method is used on the boundaries also for other methods. These will use this helper + // class to set up the seeds on the boundary. Therefore we extract all seed information from + // the boundary interaction volume here to be compatible with other mpfa methods where seed types + // most probably differ. We use the fact here, that for the o-method, boundary and interior + // interaction volumes are identical, which is always given. + using InteractionVolume = typename GET_PROP_TYPE(TypeTag, BoundaryInteractionVolume); using InteractionVolumeSeed = typename InteractionVolume::Seed; using ScvSeed = typename InteractionVolumeSeed::LocalScvSeed; using ScvfSeed = typename InteractionVolumeSeed::LocalScvfSeed; - using BoundaryInteractionVolumeSeed = typename BoundaryInteractionVolume::Seed; - using BoundaryScvSeed = typename BoundaryInteractionVolumeSeed::LocalScvSeed; - using BoundaryScvfSeed = typename BoundaryInteractionVolumeSeed::LocalScvfSeed; - - using GlobalIndexSet = std::vector<GlobalIndexType>; - using LocalIndexSet = std::vector<LocalIndexType>; + using Element = typename GridView::template Codim<0>::Entity; + using GlobalIndexType = typename InteractionVolume::GlobalIndexType; + using LocalIndexType = typename InteractionVolume::LocalIndexType; - using DimVector = Dune::FieldVector<Scalar, dimWorld>; + using GlobalIndexSet = typename InteractionVolume::GlobalIndexSet; + using LocalIndexSet = typename InteractionVolume::LocalIndexSet; public: static InteractionVolumeSeed makeInnerInteractionVolumeSeed(const Problem& problem, @@ -300,8 +555,11 @@ public: // The vertex index around which we construct the interaction volume auto vIdxGlobal = scvf.vertexIndex(); + // Get the three scv faces in the scv + auto scvfVector = Implementation::getScvFacesAtVertex(vIdxGlobal, element, fvGeometry); + // create the scv entity seeds - fillEntitySeeds_(scvSeeds, scvfSeeds, problem, element, fvGeometry, scvf, vIdxGlobal); + fillEntitySeeds_(scvSeeds, scvfSeeds, problem, element, fvGeometry, scvfVector, vIdxGlobal); // shrink containers to necessary size scvSeeds.shrink_to_fit(); @@ -310,13 +568,13 @@ public: return InteractionVolumeSeed(std::move(scvSeeds), std::move(scvfSeeds), false); } - static BoundaryInteractionVolumeSeed makeBoundaryInteractionVolumeSeed(const Problem& problem, - const Element& element, - const FVElementGeometry& fvGeometry, - const SubControlVolumeFace& scvf) + static InteractionVolumeSeed makeBoundaryInteractionVolumeSeed(const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const SubControlVolumeFace& scvf) { - std::vector<BoundaryScvSeed> scvSeeds; - std::vector<BoundaryScvfSeed> scvfSeeds; + std::vector<ScvSeed> scvSeeds; + std::vector<ScvfSeed> scvfSeeds; // reserve sufficient memory scvSeeds.reserve(50); @@ -325,37 +583,35 @@ public: // The vertex index around which we construct the interaction volume auto vIdxGlobal = scvf.vertexIndex(); + // Get the three scv faces in the scv + auto scvfVector = Implementation::getScvFacesAtVertex(vIdxGlobal, element, fvGeometry); + // create the scv entity seeds - fillEntitySeeds_(scvSeeds, scvfSeeds, problem, element, fvGeometry, scvf, vIdxGlobal); + fillEntitySeeds_(scvSeeds, scvfSeeds, problem, element, fvGeometry, scvfVector, vIdxGlobal); // shrink containers to necessary size scvSeeds.shrink_to_fit(); scvfSeeds.shrink_to_fit(); - return BoundaryInteractionVolumeSeed(std::move(scvSeeds), std::move(scvfSeeds), true); + return InteractionVolumeSeed(std::move(scvSeeds), std::move(scvfSeeds), true); } private: - template<typename ScvSeed, typename ScvfSeed> - static void fillEntitySeeds_(std::vector<ScvSeed>& scvSeeds, - std::vector<ScvfSeed>& scvfSeeds, + template<class ScvSeedType, class ScvfSeedType, class ScvfVector> + static void fillEntitySeeds_(std::vector<ScvSeedType>& scvSeeds, + std::vector<ScvfSeedType>& scvfSeeds, const Problem& problem, const Element& element, const FVElementGeometry& fvGeometry, - const SubControlVolumeFace& scvf, + const ScvfVector& scvfVector, const GlobalIndexType vIdxGlobal) { - // Get the three scv faces in the scv - auto scvfVector = Implementation::getScvFacesAtVertex(vIdxGlobal, element, fvGeometry); - - // global scvIdx and global scvf indices - auto globalScvIdx = scvf.insideScvIdx(); - GlobalIndexSet globalScvfIndices( {scvfVector[0]->index(), - scvfVector[1]->index(), - scvfVector[2]->index()} ); - - // make the scv - scvSeeds.emplace_back(ScvSeed(std::move(globalScvfIndices), globalScvIdx)); + // make the scv without knowing the local scvf indices yet + // take the inside scv index from the first scvf (is the same for all) + scvSeeds.emplace_back( GlobalIndexSet({scvfVector[0]->index(), + scvfVector[1]->index(), + scvfVector[2]->index()}), + scvfVector[0]->insideScvIdx()); // make the scvf seeds for the three scvfs connected to the scv auto& actualScvSeed = scvSeeds.back(); @@ -372,10 +628,11 @@ private: actualScvSeed.setLocalScvfIndex(coordDir, scvfSeeds.size()); // create the scvf seed - auto faceType = Implementation::getMpfaFaceType(problem, element, *scvfVector[coordDir]); + auto faceType = Implementation::getMpfaFaceType(problem, element, actualScvf); scvfSeeds.emplace_back( actualScvf, - LocalIndexSet({actualLocalScvIdx}), - GlobalIndexSet({scvfVector[coordDir]->index()}), + actualLocalScvIdx, + LocalIndexSet(), + GlobalIndexSet(), faceType ); } else @@ -410,17 +667,18 @@ private: // find scvf in outside corresponding to the actual scvf auto outsideScvfVector = Implementation::getScvFacesAtVertex(vIdxGlobal, outsideElement, outsideFvGeometry); - auto commonFaceLocalIdx = Implementation::getCommonFaceLocalIndex(*scvfVector[coordDir], outsideScvfVector); + auto commonFaceLocalIdx = Implementation::getCommonFaceLocalIndex(actualScvf, outsideScvfVector); const auto& outsideScvf = *outsideScvfVector[commonFaceLocalIdx]; // create scvf seed - auto faceType = Implementation::getMpfaFaceType(problem, element, actualScvf); - LocalIndexSet scvIndicesLocal({actualLocalScvIdx, static_cast<LocalIndexType>(scvSeeds.size())}); - GlobalIndexSet scvfIndicesGlobal({globalScvfIndex, outsideScvf.index()}); - scvfSeeds.emplace_back(actualScvf, std::move(scvIndicesLocal), std::move(scvfIndicesGlobal), faceType); + scvfSeeds.emplace_back(actualScvf, + actualLocalScvIdx, + LocalIndexSet( {static_cast<LocalIndexType>(scvSeeds.size())} ), + GlobalIndexSet( {outsideScvf.index()} ), + Implementation::getMpfaFaceType(problem, element, actualScvf)); // make outside scv by recursion - fillEntitySeeds_(scvSeeds, scvfSeeds, problem, outsideElement, outsideFvGeometry, outsideScvf, vIdxGlobal); + fillEntitySeeds_(scvSeeds, scvfSeeds, problem, outsideElement, outsideFvGeometry, outsideScvfVector, vIdxGlobal); } // we have to find out if it is necessary to create a new scvf else @@ -456,14 +714,15 @@ private: // find scvf in outside corresponding to the actual scvf auto outsideScvfVector = Implementation::getScvFacesAtVertex(vIdxGlobal, outsideElement, outsideFvGeometry); - auto commonFaceLocalIdx = Implementation::getCommonFaceLocalIndex(*scvfVector[coordDir], outsideScvfVector); + auto commonFaceLocalIdx = Implementation::getCommonFaceLocalIndex(actualScvf, outsideScvfVector); const auto& outsideScvf = *outsideScvfVector[commonFaceLocalIdx]; - // some data on the face - auto faceType = Implementation::getMpfaFaceType(problem, element, *scvfVector[coordDir]); - LocalIndexSet scvIndicesLocal( {actualLocalScvIdx, outsideLocalScvIdx} ); - GlobalIndexSet scvfIndicesGlobal( {globalScvfIndex, outsideScvf.index()} ); - scvfSeeds.emplace_back(*scvfVector[coordDir], std::move(scvIndicesLocal), std::move(scvfIndicesGlobal), faceType); + // make scv face seed + scvfSeeds.emplace_back(actualScvf, + actualLocalScvIdx, + LocalIndexSet( {outsideLocalScvIdx} ), + GlobalIndexSet( {outsideScvf.index()} ), + Implementation::getMpfaFaceType(problem, element, actualScvf)); } } } diff --git a/dumux/discretization/cellcentered/mpfa/omethod/interactionvolume.hh b/dumux/discretization/cellcentered/mpfa/omethod/interactionvolume.hh index dc55c25bf95eb127806203f7fbe181b977980c76..245ba09e53332a44c0935394ce600eb49a43c020 100644 --- a/dumux/discretization/cellcentered/mpfa/omethod/interactionvolume.hh +++ b/dumux/discretization/cellcentered/mpfa/omethod/interactionvolume.hh @@ -36,35 +36,28 @@ namespace Dumux { //! Specialization of the interaction volume traits class for the mpfa-o method template<class TypeTag> -class CCMpfaOInteractionVolumeTraits +class CCMpfaOInteractionVolumeTraits : public CCMpfaInteractionVolumeTraitsBase<TypeTag> { + using BaseTraits = CCMpfaInteractionVolumeTraitsBase<TypeTag>; using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); static const int dim = GridView::dimension; static const int dimWorld = GridView::dimensionworld; -public: - using BoundaryInteractionVolume = CCMpfaInteractionVolumeImplementation<TypeTag, MpfaMethods::oMethod>; - using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; - using DimVector = Dune::FieldVector<Scalar, dim>; - using Tensor = Dune::FieldMatrix<Scalar, dim, dim>; - using LocalIndexType = std::uint8_t; - using LocalIndexSet = std::vector<LocalIndexType>; - using LocalIndexPair = std::pair<LocalIndexType, bool>; - using GlobalIndexType = typename GridView::IndexSet::IndexType; - using GlobalIndexSet = std::vector<GlobalIndexType>; +public: + using BoundaryInteractionVolume = CCMpfaInteractionVolumeImplementation<TypeTag, MpfaMethods::oMethod>; + using PositionVector = std::vector<GlobalPosition>; using Matrix = Dune::DynamicMatrix<Scalar>; using Vector = typename Matrix::row_type; - using PositionVector = std::vector<GlobalPosition>; - using LocalScvType = CCMpfaOLocalScv<TypeTag>; using LocalScvfType = CCMpfaOLocalScvf<TypeTag>; - - using Seed = CCMpfaOInteractionVolumeSeed<GlobalIndexType, LocalIndexType>; + using typename BaseTraits::LocalIndexSet; + using typename BaseTraits::GlobalIndexSet; + using Seed = CCMpfaOInteractionVolumeSeed<GlobalIndexSet, LocalIndexSet, dim, dimWorld>; }; //! Forward declaration of the mpfa-o interaction volume @@ -101,7 +94,8 @@ public: template<class TypeTag, class Traits> class CCMpfaOInteractionVolume : public CCMpfaInteractionVolumeBase<TypeTag, Traits> { - // The interaction volume implementation has to be friend + // The interaction volume implementation has to be friend, + // because some methods use the mpfa-o interaction volume as base friend typename GET_PROP_TYPE(TypeTag, InteractionVolume); using ParentType = CCMpfaInteractionVolumeBase<TypeTag, Traits>; @@ -114,10 +108,11 @@ class CCMpfaOInteractionVolume : public CCMpfaInteractionVolumeBase<TypeTag, Tra using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables); static const int dim = GridView::dimension; + static const int dimWorld = GridView::dimensionworld; using Element = typename GridView::template Codim<0>::Entity; + using DimVector = Dune::FieldVector<Scalar, dim>; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; - using DimVector = typename Traits::DimVector; - using GlobalPosition = typename Traits::GlobalPosition; using DynamicVector = typename Traits::Vector; using DynamicMatrix = typename Traits::Matrix; using Tensor = typename Traits::Tensor; @@ -128,7 +123,7 @@ class CCMpfaOInteractionVolume : public CCMpfaInteractionVolumeBase<TypeTag, Tra public: using typename ParentType::LocalIndexType; using typename ParentType::LocalIndexSet; - using typename ParentType::LocalIndexPair; + using typename ParentType::LocalFaceData; using typename ParentType::GlobalIndexSet; using typename ParentType::PositionVector; using typename ParentType::Seed; @@ -238,7 +233,11 @@ public: } } - LocalIndexPair getLocalIndexPair(const SubControlVolumeFace& scvf) const + //! gets local data on a global scvf within the interaction volume + //! specialization for dim = dimWorld + template<int d = dim, int dw = dimWorld> + typename std::enable_if< (d == dw), LocalFaceData >::type + getLocalFaceData(const SubControlVolumeFace& scvf) const { auto scvfGlobalIdx = scvf.index(); @@ -246,10 +245,10 @@ public: for (const auto& localScvf : localScvfs_) { if (localScvf.insideGlobalScvfIndex() == scvfGlobalIdx) - return std::make_pair(localIdx, false); + return LocalFaceData(localIdx, localScvf.insideLocalScvIndex(), false); if (!localScvf.boundary() && localScvf.outsideGlobalScvfIndex() == scvfGlobalIdx) - return std::make_pair(localIdx, true); + return LocalFaceData(localIdx, localScvf.outsideLocalScvIndex(), true); localIdx++; } @@ -257,22 +256,115 @@ public: DUNE_THROW(Dune::InvalidStateException, "Could not find the local scv face in the interaction volume for the given scvf with index: " << scvf.index()); } - DynamicVector getTransmissibilities(const LocalIndexPair& localIndexPair) const + //! gets local data on a global scvf within the interaction volume + //! specialization for dim < dimWorld + template<int d = dim, int dw = dimWorld> + typename std::enable_if< (d < dw), LocalFaceData >::type + getLocalFaceData(const SubControlVolumeFace& scvf) const { - auto tij = T_[localIndexPair.first]; + const auto scvfGlobalIdx = scvf.index(); - if (localIndexPair.second) + LocalIndexType localFaceIdx = 0; + for (const auto& localScvf : localScvfs_) + { + if (localScvf.insideGlobalScvfIndex() == scvfGlobalIdx) + return LocalFaceData(localFaceIdx, localScvf.insideLocalScvIndex(), false); + + if (!localScvf.boundary() && MpfaHelper::contains(localScvf.outsideGlobalScvfIndices(), scvfGlobalIdx)) + { + if (localScvf.outsideLocalScvIndices().size() == 1) + return LocalFaceData(localFaceIdx, localScvf.outsideLocalScvIndex(), true); + + // Now we have to find wich local scv is the one the global scvf is embedded in + const auto scvGlobalIdx = scvf.insideScvIdx(); + for (auto localScvIdx : localScvf.outsideLocalScvIndices()) + if (localScv_(localScvIdx).globalIndex() == scvGlobalIdx) + return LocalFaceData(localFaceIdx, localScvIdx, true); + + DUNE_THROW(Dune::InvalidStateException, "Could not find the local scv in the interaction volume for the given scvf with index: " << scvfGlobalIdx); + } + + localFaceIdx++; + } + + DUNE_THROW(Dune::InvalidStateException, "Could not find the local scv face in the interaction volume for the given scvf with index: " << scvfGlobalIdx); + } + + //! gets the transmissibilities for a sub-control volume face within the interaction volume + //! specialization for dim == dimWorld + template<int d = dim, int dw = dimWorld> + typename std::enable_if< (d == dw), DynamicVector >::type + getTransmissibilities(const LocalFaceData& localFaceData) const + { + auto tij = T_[localFaceData.localScvfIndex]; + if (localFaceData.isOutside) tij *= -1.0; return tij; } - Scalar getNeumannFlux(const LocalIndexPair& localIndexPair) const + //! gets the transmissibilities for a sub-control volume face within the interaction volume + //! specialization for dim < dimWorld + template<int d = dim, int dw = dimWorld> + typename std::enable_if< (d < dw), DynamicVector >::type + getTransmissibilities(const LocalFaceData& localFaceData) const { - if (!onBoundary() || fluxScvfIndexSet_().size() == 0 || GET_PROP_VALUE(TypeTag, UseTpfaBoundary)) + // if we are not on a branching point, do the usual + if (localScvf_(localFaceData.localScvfIndex).outsideLocalScvIndices().size() == 1) + { + auto tij = T_[localFaceData.localScvfIndex]; + if (localFaceData.isOutside) + tij *= -1.0; + return tij; + } + + // This means we are on a branching point. If we come from the inside, simply return tij + if (!localFaceData.isOutside) + return T_[localFaceData.localScvfIndex]; + + // otherwise compute the outside transmissibilities + DynamicVector tij(volVarsStencil().size(), 0.0); + + // get the local scv and iterate over local coordinates + const std::size_t numLocalScvs = localScvs_.size(); + const auto& localScv = localScv_(localFaceData.localScvIndex); + const auto& localScvf = localScvf_(localFaceData.localScvfIndex); + + auto idxInOutside = this->findLocalIndex(localScvf.outsideLocalScvIndices(), localFaceData.localScvIndex); + const auto& wijk = wijk_[localFaceData.localScvfIndex][idxInOutside+1]; + for (int localDir = 0; localDir < dim; localDir++) + { + auto localScvfIdx = localScv.localScvfIndex(localDir); + const auto& localScvf = localScvf_(localScvfIdx); + + // add entries from the face unknowns + if (localScvf.faceType() != MpfaFaceTypes::dirichlet) + { + auto fluxFaceIndex = this->findLocalIndex(fluxScvfIndexSet_(), localScvfIdx); + auto tmp = AinvB_[fluxFaceIndex]; + tmp *= wijk[localDir]; + + tij += tmp; + } + else + { + auto idxInDiriFaces = this->findLocalIndex(dirichletScvfIndexSet_(), localScvfIdx); + tij[numLocalScvs + idxInDiriFaces] += wijk[localDir]; + } + + // add entry from the scv unknown + tij[localFaceData.localScvIndex] -= wijk[localDir]; + } + + return tij; + } + + Scalar getNeumannFlux(const LocalFaceData& localFaceData) const + { + if (!onBoundary() || GET_PROP_VALUE(TypeTag, UseTpfaBoundary) || fluxScvfIndexSet_().size() == 0 ) return 0.0; - auto flux = CAinv_[localIndexPair.first] * neumannFluxes_; - if (localIndexPair.second) + auto flux = CAinv_[localFaceData.localScvfIndex] * neumannFluxes_; + if (localFaceData.isOutside) return -1.0*flux; return flux; } @@ -339,6 +431,9 @@ private: { const std::size_t numLocalScvs = localScvs_.size(); + // reserve space for the omegas + wijk_.resize(localScvfs_.size()); + // loop over the local faces LocalIndexType rowIdx = 0; for (const auto& localScvf : localScvfs_) @@ -391,40 +486,49 @@ private: B[idxInFluxFaces][posLocalScvIdx] += posWijk[localDir]; } + // store the omegas + wijk_[rowIdx].emplace_back(std::move(posWijk)); + // If face is not on a boundary or an interior dirichlet face, add entries for the "negative" scv if (faceType == MpfaFaceTypes::interior) { - const auto negLocalScvIdx = localScvf.outsideLocalScvIndex(); - const auto& negLocalScv = localScv_(negLocalScvIdx); - const auto& negGlobalScv = fvGeometry_().scv(negLocalScv.globalIndex()); - auto negElement = localElement_(negLocalScvIdx);; - auto negTensor = getTensor(negElement, elemVolVars_()[negGlobalScv], negGlobalScv); - - // the omega factors of the "negative" sub volume - auto negWijk = calculateOmegas_(negLocalScv, localScvf, negTensor); - negWijk *= problem_().boxExtrusionFactor(negElement, negGlobalScv); - - // Check local directions of negative sub volume - for (int localDir = 0; localDir < dim; localDir++) + // loop over all the outside neighbors of this face and add entries + for (auto negLocalScvIdx : localScvf.outsideLocalScvIndices()) { - auto curLocalScvfIdx = negLocalScv.localScvfIndex(localDir); - const auto& curLocalScvf = localScvf_(curLocalScvfIdx); + const auto& negLocalScv = localScv_(negLocalScvIdx); + const auto& negGlobalScv = fvGeometry_().scv(negLocalScv.globalIndex()); + auto negElement = localElement_(negLocalScvIdx);; + auto negTensor = getTensor(negElement, elemVolVars_()[negGlobalScv], negGlobalScv); - if (curLocalScvf.faceType() != MpfaFaceTypes::dirichlet) - { - // we need the index of the current local scvf in the flux face indices - auto curIdxInFluxFaces = this->findLocalIndex(fluxScvfIndexSet_(), curLocalScvfIdx); - A[idxInFluxFaces][curIdxInFluxFaces] -= negWijk[localDir]; - } - else + // the omega factors of the "negative" sub volume + auto negWijk = calculateOmegas_(negLocalScv, localScvf, negTensor); + negWijk *= problem_().boxExtrusionFactor(negElement, negGlobalScv); + + // Check local directions of negative sub volume + for (int localDir = 0; localDir < dim; localDir++) { - // the current face is a Dirichlet face and creates entries in B - auto curIdxInDiriFaces = this->findLocalIndex(dirichletScvfIndexSet_(), curLocalScvfIdx); - B[idxInFluxFaces][numLocalScvs + curIdxInDiriFaces] += negWijk[localDir]; + auto curLocalScvfIdx = negLocalScv.localScvfIndex(localDir); + const auto& curLocalScvf = localScvf_(curLocalScvfIdx); + + if (curLocalScvf.faceType() != MpfaFaceTypes::dirichlet) + { + // we need the index of the current local scvf in the flux face indices + auto curIdxInFluxFaces = this->findLocalIndex(fluxScvfIndexSet_(), curLocalScvfIdx); + A[idxInFluxFaces][curIdxInFluxFaces] -= negWijk[localDir]; + } + else + { + // the current face is a Dirichlet face and creates entries in B + auto curIdxInDiriFaces = this->findLocalIndex(dirichletScvfIndexSet_(), curLocalScvfIdx); + B[idxInFluxFaces][numLocalScvs + curIdxInDiriFaces] += negWijk[localDir]; + } + + // add entries to matrix B + B[idxInFluxFaces][negLocalScvIdx] -= negWijk[localDir]; } - // add entries to matrix B - B[idxInFluxFaces][negLocalScvIdx] -= negWijk[localDir]; + // store the omegas + wijk_[rowIdx].emplace_back(std::move(negWijk)); } } // go to the next face @@ -444,6 +548,9 @@ private: AinvB_.resize(0, 0); CAinv_.resize(0, 0); + // resize the omegas + wijk_.resize(numFaces); + // Loop over all the faces, in this case these are all dirichlet boundaries LocalIndexType rowIdx = 0; for (const auto& localScvf : localScvfs_) @@ -468,6 +575,9 @@ private: T_[rowIdx][posLocalScvIdx] -= posWijk[localDir]; } + // store the omegas + wijk_[rowIdx].emplace_back(std::move(posWijk)); + // go to the next face rowIdx++; } @@ -477,7 +587,7 @@ private: const LocalScvfType& localScvf, const Tensor& T) const { - GlobalPosition wijk; + DimVector wijk; GlobalPosition tmp; for (int dir = 0; dir < dim; ++dir) { @@ -495,7 +605,7 @@ private: const LocalScvfType& localScvf, const Scalar t) const { - GlobalPosition wijk; + DimVector wijk; GlobalPosition tmp(localScvf.unitOuterNormal()); tmp *= t; @@ -522,7 +632,6 @@ private: const ElementVolumeVariables* elemVolVarsPtr_; bool onBoundary_; - LocalIndexType eqIdx_; std::vector<Element> localElements_; std::vector<LocalScvType> localScvs_; @@ -535,6 +644,8 @@ private: LocalIndexSet fluxFaceIndexSet_; LocalIndexSet dirichletFaceIndexSet_; + std::vector< std::vector< DimVector > > wijk_; + DynamicMatrix T_; DynamicMatrix AinvB_; DynamicMatrix CAinv_; diff --git a/dumux/discretization/cellcentered/mpfa/omethod/interactionvolumeseed.hh b/dumux/discretization/cellcentered/mpfa/omethod/interactionvolumeseed.hh index 0387ae55bc66e1415824cae0c479fe867e46ed97..bdcdc831fc42678eb57b8b7eca62ac20b8689857 100644 --- a/dumux/discretization/cellcentered/mpfa/omethod/interactionvolumeseed.hh +++ b/dumux/discretization/cellcentered/mpfa/omethod/interactionvolumeseed.hh @@ -31,12 +31,13 @@ namespace Dumux /*! * \ingroup Mpfa - * \brief Base class for the interaction volume seed of mpfa methods + * \brief Class for the interaction volume seed of the mpfa-o method */ -template<typename G, typename L> +template<typename G, typename L, int dim, int dimWorld> class CCMpfaOInteractionVolumeSeed { - using GlobalIndexType = G; + using GlobalIndexSet = G; + using GlobalIndexType = typename GlobalIndexSet::value_type; public: using LocalScvSeed = CCMpfaOLocalScvSeed<G, L>; @@ -47,8 +48,7 @@ public: bool onBoundary) : onBoundary_(onBoundary), scvSeeds_(std::move(scvSeeds)), - scvfSeeds_(std::move(scvfSeeds)) - {} + scvfSeeds_(std::move(scvfSeeds)) {} bool onBoundary() const { return onBoundary_; } @@ -76,8 +76,11 @@ public: globalIndices.reserve(scvfSeeds().size() * 2); for (auto&& localScvfSeed : scvfSeeds()) - for (auto scvfIdxGlobal : localScvfSeed.globalScvfIndices()) + { + globalIndices.push_back(localScvfSeed.insideGlobalScvfIndex()); + for (auto scvfIdxGlobal : localScvfSeed.outsideGlobalScvfIndices()) globalIndices.push_back(scvfIdxGlobal); + } globalIndices.shrink_to_fit(); return globalIndices; diff --git a/dumux/discretization/cellcentered/mpfa/omethod/localsubcontrolentities.hh b/dumux/discretization/cellcentered/mpfa/omethod/localsubcontrolentities.hh index 1e78961d0b3ddcd18445bcccef35ce507d42e19d..ce4046ae6e6afebaff9ebc1fefc1d98f6078f497 100644 --- a/dumux/discretization/cellcentered/mpfa/omethod/localsubcontrolentities.hh +++ b/dumux/discretization/cellcentered/mpfa/omethod/localsubcontrolentities.hh @@ -36,17 +36,18 @@ class CCMpfaOLocalScv using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using Helper = typename GET_PROP_TYPE(TypeTag, MpfaHelper); using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); - using InteractionVolume = typename GET_PROP_TYPE(TypeTag, InteractionVolume); using Element = typename GridView::template Codim<0>::Entity; - using LocalScvSeed = typename InteractionVolume::Seed::LocalScvSeed; - using GlobalIndexType = typename GridView::IndexSet::IndexType; + // we use the seed types of the boundary interaction volume to be compatible with other mpfa + // methods that use o-type interaction volumes on the boundary but differing ones inside the domain. + using InteractionVolume = typename GET_PROP_TYPE(TypeTag, BoundaryInteractionVolume); + using LocalScvSeed = typename InteractionVolume::Seed::LocalScvSeed; + using GlobalIndexType = typename InteractionVolume::GlobalIndexType; using LocalIndexType = typename InteractionVolume::LocalIndexType; static const int dim = GridView::dimension; static const int dimWorld = GridView::dimensionworld; - using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; using LocalBasis = std::array<GlobalPosition, dim>; @@ -113,15 +114,19 @@ struct CCMpfaOLocalScvf using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using Helper = typename GET_PROP_TYPE(TypeTag, MpfaHelper); - using InteractionVolume = typename GET_PROP_TYPE(TypeTag, InteractionVolume); using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); using Element = typename GridView::template Codim<0>::Entity; - using LocalScvfSeed = typename InteractionVolume::Seed::LocalScvfSeed; - using GlobalIndexType = typename GridView::IndexSet::IndexType; + // we use the seed types of the boundary interaction volume to be compatible with other mpfa + // methods that use o-type interaction volumes on the boundary but differing ones inside the domain. + using InteractionVolume = typename GET_PROP_TYPE(TypeTag, BoundaryInteractionVolume); + using LocalScvfSeed = typename InteractionVolume::Seed::LocalScvfSeed; + using GlobalIndexType = typename InteractionVolume::GlobalIndexType; + using GlobalIndexSet = typename InteractionVolume::GlobalIndexSet; using LocalIndexType = typename InteractionVolume::LocalIndexType; + using LocalIndexSet = typename InteractionVolume::LocalIndexSet; static const int dim = GridView::dimension; static const int dimWorld = GridView::dimensionworld; @@ -131,36 +136,50 @@ public: CCMpfaOLocalScvf(const LocalScvfSeed& scvfSeed, const SubControlVolumeFace& scvf) : seedPtr_(&scvfSeed), - center_(scvf.center()), ip_(scvf.ipGlobal()), normal_(scvf.unitOuterNormal()), area_(scvf.area()) {} - GlobalIndexType insideGlobalScvfIndex() const + const GlobalIndexType insideGlobalScvfIndex() const { return scvfSeed_().insideGlobalScvfIndex(); } - GlobalIndexType outsideGlobalScvfIndex() const - { return scvfSeed_().outsideGlobalScvfIndex(); } + const GlobalIndexSet& outsideGlobalScvfIndices() const + { return scvfSeed_().outsideGlobalScvfIndices(); } - LocalIndexType insideLocalScvIndex() const + const GlobalIndexType outsideGlobalScvfIndex() const + { + assert(scvfSeed_().outsideGlobalScvfIndices().size() == 1 && "outside scvf index not uniquely defined"); + return scvfSeed_().outsideGlobalScvfIndices()[0]; + } + + const LocalIndexType insideLocalScvIndex() const { return scvfSeed_().insideLocalScvIndex(); } - LocalIndexType outsideLocalScvIndex() const - { return scvfSeed_().outsideLocalScvIndex(); } + const LocalIndexSet& outsideLocalScvIndices() const + { return scvfSeed_().outsideLocalScvIndices(); } + + const LocalIndexType outsideLocalScvIndex() const + { + assert(scvfSeed_().outsideLocalScvIndices().size() == 1 && "outside local scv index not uniquely defined"); + return scvfSeed_().outsideLocalScvIndices()[0]; + } - GlobalIndexType insideGlobalScvIndex() const + const GlobalIndexType insideGlobalScvIndex() const { return scvfSeed_().insideGlobalScvIndex(); } - GlobalIndexType outsideGlobalScvIndex() const - { return scvfSeed_().outsideGlobalScvIndex();} + const GlobalIndexSet& outsideGlobalScvIndices() const + { return scvfSeed_().outsideGlobalScvIndices(); } + + const GlobalIndexType outsideGlobalScvIndex() const + { + assert(scvfSeed_().outsideGlobalScvIndices().size() == 1 && "outside scv index not uniquely defined"); + return scvfSeed_().outsideGlobalScvIndices()[0]; + } MpfaFaceTypes faceType() const { return scvfSeed_().faceType(); } - GlobalPosition center() const - { return center_; } - GlobalPosition ip() const { return ip_; } @@ -178,7 +197,6 @@ private: { return *seedPtr_; } const LocalScvfSeed* seedPtr_; - GlobalPosition center_; GlobalPosition ip_; GlobalPosition normal_; Scalar area_; diff --git a/dumux/discretization/cellcentered/mpfa/omethod/localsubcontrolentityseeds.hh b/dumux/discretization/cellcentered/mpfa/omethod/localsubcontrolentityseeds.hh index add3fdca34f91efeb85d1eb7de424a32621f50c7..49fbc1ad2b5e99c4d87108e7f1a6ca87b71243bf 100644 --- a/dumux/discretization/cellcentered/mpfa/omethod/localsubcontrolentityseeds.hh +++ b/dumux/discretization/cellcentered/mpfa/omethod/localsubcontrolentityseeds.hh @@ -32,10 +32,10 @@ template<typename G, typename L> class CCMpfaOLocalScvSeed { public: - using GlobalIndexType = G; - using LocalIndexType = L; - using GlobalIndexSet = std::vector<GlobalIndexType>; - using LocalIndexSet = std::vector<LocalIndexType>; + using GlobalIndexSet = G; + using LocalIndexSet = L; + using GlobalIndexType = typename GlobalIndexSet::value_type; + using LocalIndexType = typename LocalIndexSet::value_type; //! constructor fully defining the scv seed CCMpfaOLocalScvSeed(GlobalIndexSet&& globalScvfIndices, @@ -43,8 +43,7 @@ public: GlobalIndexType globalScvIndex) : globalScvIndex_(globalScvIndex), localScvfIndices_(std::move(localScvfIndices)), - globalScvfIndices_(std::move(globalScvfIndices)) - {} + globalScvfIndices_(std::move(globalScvfIndices)) {} //! Constructor when the local scvf indices are not known at this point CCMpfaOLocalScvSeed(GlobalIndexSet&& globalScvfIndices, @@ -75,60 +74,78 @@ private: GlobalIndexSet globalScvfIndices_; }; - +/*! + * \ingroup Mpfa + * \brief Class for a sub-control volume face seed of the mpfa-o method. + * + * \param G the global index set type + * \param L the local index set type + */ template<typename G, typename L> class CCMpfaOLocalScvfSeed { public: - using GlobalIndexType = G; - using LocalIndexType = L; - using GlobalIndexSet = std::vector<GlobalIndexType>; - using LocalIndexSet = std::vector<LocalIndexType>; + using GlobalIndexSet = G; + using LocalIndexSet = L; + using GlobalIndexType = typename GlobalIndexSet::value_type; + using LocalIndexType = typename LocalIndexSet::value_type; + //! constructor fully defining the scv face seed + template<class SubControlVolumeFace> + CCMpfaOLocalScvfSeed(const SubControlVolumeFace& scvf, + LocalIndexType insideLocalScvIndex, + LocalIndexSet&& outsideLocalScvIndices, + GlobalIndexSet&& outsideGlobalScvfIndices, + const MpfaFaceTypes faceType) + : boundary_(scvf.boundary()), + insideScvLocalIdx_(insideLocalScvIndex), + outsideLocalScvIndices_(std::move(outsideLocalScvIndices)), + insideScvGlobalIdx_(scvf.insideScvIdx()), + outsideGlobalScvIndices_(scvf.outsideScvIndices()), + insideScvfGlobalIdx_(scvf.index()), + outsideGlobalScvfIndices_(std::move(outsideGlobalScvfIndices)), + faceType_(faceType) {} + + //! Constructor when the outside indices are not known at this point template<class SubControlVolumeFace> CCMpfaOLocalScvfSeed(const SubControlVolumeFace& scvf, - LocalIndexSet&& scvIndicesLocal, - GlobalIndexSet&& scvfIndicesGlobal, + LocalIndexType insideLocalScvIndex, const MpfaFaceTypes faceType) : boundary_(scvf.boundary()), - scvIndicesGlobal_({scvf.insideScvIdx(), scvf.outsideScvIdx()}), - scvfIndicesGlobal_(std::move(scvfIndicesGlobal)), - scvIndicesLocal_(std::move(scvIndicesLocal)), + insideScvLocalIdx_(insideLocalScvIndex), + insideScvGlobalIdx_(scvf.insideScvIdx()), + outsideGlobalScvIndices_(scvf.outsideScvIndices()), + insideScvfGlobalIdx_(scvf.index()), faceType_(faceType) - {} - - const GlobalIndexSet& globalScvfIndices() const - { return scvfIndicesGlobal_; } - - const LocalIndexSet& localScvIndices() const - { return scvIndicesLocal_; } + { + auto size = outsideGlobalScvIndices_.size(); + outsideLocalScvIndices_.reserve(size); + outsideGlobalScvfIndices_.reserve(size); + } - const GlobalIndexSet& globalScvIndices() const - { return scvIndicesGlobal_; } + const GlobalIndexType insideGlobalScvfIndex() const + { return insideScvfGlobalIdx_; } - LocalIndexType insideLocalScvIndex() const - { return scvIndicesLocal_[0]; } + const GlobalIndexSet& outsideGlobalScvfIndices() const + { return outsideGlobalScvfIndices_; } - LocalIndexType outsideLocalScvIndex() const + const GlobalIndexType outsideGlobalScvfIndex() const { - assert(!boundary() && "There is no outside local scv for boundary scvfs"); - return scvIndicesLocal_[1]; + assert(outsideGlobalScvfIndices_.size() == 1 && "outside global scvf index not uniquely defined"); + return outsideGlobalScvfIndices_[0]; } - GlobalIndexType insideGlobalScvIndex() const - { return scvIndicesGlobal_[0]; } + const LocalIndexType insideLocalScvIndex() const + { return insideScvLocalIdx_; } - GlobalIndexType outsideGlobalScvIndex() const - { return scvIndicesGlobal_[1]; } + const LocalIndexSet& outsideLocalScvIndices() const + { return outsideLocalScvIndices_; } - GlobalIndexType insideGlobalScvfIndex() const - { return scvfIndicesGlobal_[0]; } + const GlobalIndexType insideGlobalScvIndex() const + { return insideScvGlobalIdx_; } - GlobalIndexType outsideGlobalScvfIndex() const - { - assert(!boundary() && "There is no outside global scvf for boundary scvfs"); - return scvfIndicesGlobal_[1]; - } + const GlobalIndexSet& outsideGlobalScvIndices() const + { return outsideGlobalScvIndices_; } MpfaFaceTypes faceType() const { return faceType_; } @@ -136,11 +153,37 @@ public: bool boundary() const { return boundary_; } + void addOutsideScvfIndex(const GlobalIndexType index) + { + outsideGlobalScvfIndices_.push_back(index); + } + + void addOutsideLocalScvIndex(const LocalIndexType index) + { + outsideLocalScvIndices_.push_back(index); + } + + void makeOutsideDataUnique() + { + std::sort(outsideGlobalScvfIndices_.begin(), outsideGlobalScvfIndices_.end()); + outsideGlobalScvfIndices_.erase(std::unique(outsideGlobalScvfIndices_.begin(), outsideGlobalScvfIndices_.end()), outsideGlobalScvfIndices_.end()); + + std::sort(outsideLocalScvIndices_.begin(), outsideLocalScvIndices_.end()); + outsideLocalScvIndices_.erase(std::unique(outsideLocalScvIndices_.begin(), outsideLocalScvIndices_.end()), outsideLocalScvIndices_.end()); + } + private: bool boundary_; - GlobalIndexSet scvIndicesGlobal_; - GlobalIndexSet scvfIndicesGlobal_; - LocalIndexSet scvIndicesLocal_; + + LocalIndexType insideScvLocalIdx_; + LocalIndexSet outsideLocalScvIndices_; + + GlobalIndexType insideScvGlobalIdx_; + GlobalIndexSet outsideGlobalScvIndices_; + + GlobalIndexType insideScvfGlobalIdx_; + GlobalIndexSet outsideGlobalScvfIndices_; + MpfaFaceTypes faceType_; }; } // end namespace diff --git a/dumux/discretization/cellcentered/mpfa/omethod/subcontrolvolumeface.hh b/dumux/discretization/cellcentered/mpfa/omethod/subcontrolvolumeface.hh index 5bc232954fcf6f677d402b67d1782482d3ae8bbf..992f3364269731a39cea040e4fd27df62d4a7bc4 100644 --- a/dumux/discretization/cellcentered/mpfa/omethod/subcontrolvolumeface.hh +++ b/dumux/discretization/cellcentered/mpfa/omethod/subcontrolvolumeface.hh @@ -47,7 +47,7 @@ class CCMpfaSubControlVolumeFace<MpfaMethods::oMethod, G, I> : public CCMpfaSubC using GlobalPosition = Dune::FieldVector<Scalar, dimworld>; public: - //! We do not use the vertexIndexInElement variable. + //! We do not use the localIndex variable. //! It is here to satisfy the general mpfa scvf interface. template<class MpfaGeometryHelper> CCMpfaSubControlVolumeFace(const MpfaGeometryHelper& geomHelper, @@ -56,19 +56,20 @@ public: IndexType vertexIndex, unsigned int localIndex, IndexType scvfIndex, - std::array<IndexType, 2>&& scvIndices, + IndexType insideScvIdx, + const std::vector<IndexType>& outsideScvIndices, Scalar q, bool boundary) : ParentType(geomHelper, - std::move(corners), - std::move(unitOuterNormal), + std::forward<std::vector<GlobalPosition>>(corners), + std::forward<GlobalPosition>(unitOuterNormal), vertexIndex, localIndex, scvfIndex, - std::move(scvIndices), + insideScvIdx, + outsideScvIndices, q, - boundary) - {} + boundary) {} }; } // end namespace diff --git a/dumux/discretization/cellcentered/mpfa/omethodfps/globalfvgeometry.hh b/dumux/discretization/cellcentered/mpfa/omethodfps/globalfvgeometry.hh deleted file mode 100644 index d7a01c290242302191e5d28e49487ec0b9fd890a..0000000000000000000000000000000000000000 --- a/dumux/discretization/cellcentered/mpfa/omethodfps/globalfvgeometry.hh +++ /dev/null @@ -1,46 +0,0 @@ -// -*- 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 finite volume geometry vector for box models - * This builds up the sub control volumes and sub control volume faces - * for each element. - */ -#ifndef DUMUX_DISCRETIZATION_CC_MPFA_O_FPS_GLOBALFVGEOMETRY_HH -#define DUMUX_DISCRETIZATION_CC_MPFA_O_FPS_GLOBALFVGEOMETRY_HH - -#include <dumux/discretization/cellcentered/mpfa/globalfvgeometrybase.hh> -#include <dumux/discretization/cellcentered/mpfa/methods.hh> - -namespace Dumux -{ -//! Specialization for the mpfa-o scheme with full pressure support. -template<class TypeTag, bool EnableCache> -class CCMpfaGlobalFVGeometryImplementation<TypeTag, MpfaMethods::oMethodFps, EnableCache> : public CCMpfaGlobalFVGeometryBase<TypeTag, EnableCache> -{ - using ParentType = CCMpfaGlobalFVGeometryBase<TypeTag, EnableCache>; - using GridView = typename GET_PROP_TYPE(TypeTag, GridView); - -public: - CCMpfaGlobalFVGeometryImplementation(const GridView gridView) : ParentType(gridView) {} -}; - -} // end namespace - -#endif diff --git a/dumux/discretization/cellcentered/mpfa/omethodfps/helper.hh b/dumux/discretization/cellcentered/mpfa/omethodfps/helper.hh index 3a00caf30fe06b5d025d05813921cc104b25370f..d0e6d3faa15dfac346e93162a9d7ee1b0aca231d 100644 --- a/dumux/discretization/cellcentered/mpfa/omethodfps/helper.hh +++ b/dumux/discretization/cellcentered/mpfa/omethodfps/helper.hh @@ -32,8 +32,8 @@ namespace Dumux * \brief Helper class to get the required information on an interaction volume. * Specialization for the Mpfa-O method in two dimensions. */ -template<class TypeTag, int dim> -class MpfaHelperBase<TypeTag, MpfaMethods::oMethodFps, dim> : public MpfaHelperBase<TypeTag, MpfaMethods::oMethod, dim> +template<class TypeTag, int dim, int dimWorld> +class MpfaMethodHelper<TypeTag, MpfaMethods::oMethodFps, dim, dimWorld> : public MpfaMethodHelper<TypeTag, MpfaMethods::oMethod, dim, dimWorld> {}; } // end namespace diff --git a/dumux/discretization/cellcentered/mpfa/omethodfps/interactionvolume.hh b/dumux/discretization/cellcentered/mpfa/omethodfps/interactionvolume.hh index 62fc0bc6ca112937068976dd0ef059a9f18d7c79..d5483f18d34b53dfd6fc1fc5ab13de2b2eb7641c 100644 --- a/dumux/discretization/cellcentered/mpfa/omethodfps/interactionvolume.hh +++ b/dumux/discretization/cellcentered/mpfa/omethodfps/interactionvolume.hh @@ -67,6 +67,7 @@ class CCMpfaInteractionVolumeImplementation<TypeTag, MpfaMethods::oMethodFps> : using LocalScvfType = typename Traits::LocalScvfType; static const int dim = GridView::dimension; + static const int dimWorld = GridView::dimensionworld; using CoordScalar = typename GridView::ctype; using FeCache = Dune::PQkLocalFiniteElementCache<CoordScalar, Scalar, dim, 1>; using FeLocalBasis = typename FeCache::FiniteElementType::Traits::LocalBasisType; @@ -74,8 +75,8 @@ class CCMpfaInteractionVolumeImplementation<TypeTag, MpfaMethods::oMethodFps> : using ShapeValue = typename Dune::FieldVector<Scalar, 1>; using JacobianInverseTransposed = typename LocalScvType::Geometry::JacobianInverseTransposed; - using GlobalPosition = typename Traits::GlobalPosition; using LocalPosition = typename LocalScvType::Geometry::LocalCoordinate; + using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>; using DynamicVector = typename Traits::Vector; using DynamicMatrix = typename Traits::Matrix; using Tensor = typename Traits::Tensor; @@ -101,7 +102,7 @@ class CCMpfaInteractionVolumeImplementation<TypeTag, MpfaMethods::oMethodFps> : public: using typename ParentType::LocalIndexType; - using typename ParentType::LocalIndexPair; + using typename ParentType::LocalFaceData; using typename ParentType::Seed; CCMpfaInteractionVolumeImplementation(const Seed& seed, @@ -234,34 +235,36 @@ private: LocalMatrixContainer& mc) { // get diffusion tensor in "negative" sub volume - const auto localScvIdx = localScvf.outsideLocalScvIndex(); - const auto& localScv = this->localScv_(localScvIdx); - const auto& globalScv = this->fvGeometry_().scv(localScv.globalIndex()); - const auto& element = this->localElement_(localScvIdx);; - auto D = makeTensor_(getTensor(element, this->elemVolVars_()[globalScv], globalScv)); + for (auto localScvIdx : localScvf.outsideLocalScvIndices()) + { + const auto& localScv = this->localScv_(localScvIdx); + const auto& globalScv = this->fvGeometry_().scv(localScv.globalIndex()); + const auto& element = this->localElement_(localScvIdx);; + auto D = makeTensor_(getTensor(element, this->elemVolVars_()[globalScv], globalScv)); - // the local finite element bases of the scvs - const auto& localBasis = feCache_.get(localScv.geometry().type()).localBasis(); + // the local finite element bases of the scvs + const auto& localBasis = feCache_.get(localScv.geometry().type()).localBasis(); - // the normal and local integration point - // On the ref element, normal vector points in the direction of local coordinate - auto normalDir = localScv.getScvfIdxInScv(localScvfIdx); - auto ipLocal = localScv.geometry().local(localScvf.ip()); + // the normal and local integration point + // On the ref element, normal vector points in the direction of local coordinate + auto normalDir = localScv.getScvfIdxInScv(localScvfIdx); + auto ipLocal = localScv.geometry().local(localScvf.ip()); - // find normals and integration points in the two scvs for condition of zero divergence - LocalIndexType divEqNormalDir = normalDir == 1 ? 0 : 1; - LocalPosition divEqIpLocal(0.0); - divEqIpLocal[divEqNormalDir] = divEqNormalDir == 1 ? c_ : 1.0 - (1.0-c_)*p_; - divEqIpLocal[normalDir] = divEqNormalDir == 1 ? c_ + (1.0-c_)*p_ : c_; + // find normals and integration points in the two scvs for condition of zero divergence + LocalIndexType divEqNormalDir = normalDir == 1 ? 0 : 1; + LocalPosition divEqIpLocal(0.0); + divEqIpLocal[divEqNormalDir] = divEqNormalDir == 1 ? c_ : 1.0 - (1.0-c_)*p_; + divEqIpLocal[normalDir] = divEqNormalDir == 1 ? c_ + (1.0-c_)*p_ : c_; - // does the face has an unknown associated with it? - bool isFluxFace = localScvf.faceType() != MpfaFaceTypes::dirichlet; + // does the face has an unknown associated with it? + bool isFluxFace = localScvf.faceType() != MpfaFaceTypes::dirichlet; - // assemble coefficients for the face fluxes - addFaceFluxCoefficients_(localScv, localBasis, D, localScvfIdx, ipLocal, normalDir, mc, isFluxFace, true); + // assemble coefficients for the face fluxes + addFaceFluxCoefficients_(localScv, localBasis, D, localScvfIdx, ipLocal, normalDir, mc, isFluxFace, true); - // assemble matrix entries for the condition of zero divergence - addDivEquationCoefficients_(localScv, localBasis, D, divEqIpLocal, divEqNormalDir, mc); + // assemble matrix entries for the condition of zero divergence + addDivEquationCoefficients_(localScv, localBasis, D, divEqIpLocal, divEqNormalDir, mc); + } } void addFaceFluxCoefficients_(const LocalScvType& localScv, diff --git a/dumux/discretization/cellcentered/mpfa/omethodfps/mpfafpsgeometryhelper.hh b/dumux/discretization/cellcentered/mpfa/omethodfps/mpfafpsgeometryhelper.hh index b993c7e22806172ba4fb2fd64622ba6719b1ced8..ba427928048b4a7ad2a7d3bad68111971d9e9055 100644 --- a/dumux/discretization/cellcentered/mpfa/omethodfps/mpfafpsgeometryhelper.hh +++ b/dumux/discretization/cellcentered/mpfa/omethodfps/mpfafpsgeometryhelper.hh @@ -34,11 +34,11 @@ namespace Dumux { -//! Create sub control volumes and sub control volume face geometries +//! A class to mainly extract the scv corners of the scvs inside an element template<class GridView, int dim> class MpfaFpsGeometryHelper; -//! A class to create sub control volume and sub control volume face geometries per element +//! Specialization for dim = 2 template <class GridView> class MpfaFpsGeometryHelper<GridView, 2> { @@ -134,121 +134,6 @@ private: std::size_t corners_; // number of element corners }; -//! A class to create sub control volume and sub control volume face geometries per element -template <class GridView> -class MpfaFpsGeometryHelper<GridView, 3> -{ - using Scalar = typename GridView::ctype; - static const int dim = GridView::dimension; - static const int dimWorld = GridView::dimensionworld; - - using ScvGeometry = Dune::CachedMultiLinearGeometry<Scalar, dim, dimWorld>; - using ScvfGeometry = Dune::CachedMultiLinearGeometry<Scalar, dim-1, dimWorld>; - - using GlobalPosition = typename ScvGeometry::GlobalCoordinate; - using PointVector = std::vector<GlobalPosition>; - - using Element = typename GridView::template Codim<0>::Entity; - using Intersection = typename GridView::Intersection; - - using ReferenceElements = typename Dune::ReferenceElements<Scalar, dim>; - using FaceReferenceElements = typename Dune::ReferenceElements<Scalar, dim-1>; - - //! the maximum number of helper points used to construct the geometries - //! Using a statically sized point array is much faster than dynamic allocation - static constexpr int maxPoints = 27; -public: - MpfaFpsGeometryHelper(const typename Element::Geometry& geometry) - : elementGeometry_(geometry), corners_(geometry.corners()) - { - // extract the corners of the sub control volumes - const auto& referenceElement = ReferenceElements::general(geometry.type()); - - // the element center - p[0] = geometry.center(); - - // vertices - for (int i = 0; i < corners_; ++i) - p[i+1] = geometry.corner(i); - - // edge midpoints - for (int i = 0; i < referenceElement.size(dim-1); ++i) - p[i+corners_+1] = geometry.global(referenceElement.position(i, dim-1)); - - // face midpoints - for (int i = 0; i < referenceElement.size(1); ++i) - p[i+corners_+1+referenceElement.size(dim-1)] = geometry.global(referenceElement.position(i, 1)); - } - - //! Create a vector with the scv corners - PointVector getScvCorners(unsigned int localScvIdx) const - { - // proceed according to number of corners of the element - switch (corners_) - { - case 4: // tetrahedron - { - //! Only build the maps the first time we encounter a tetrahedron - static const std::uint8_t vo = 1; //! vertex offset in point vector p - static const std::uint8_t eo = 5; //! edge offset in point vector p - static const std::uint8_t fo = 11; //! face offset in point vector p - static const std::uint8_t map[4][8] = - { - {vo+0, eo+0, eo+1, fo+0, eo+3, fo+1, fo+2, 0}, - {vo+1, eo+2, eo+0, fo+0, eo+4, fo+3, fo+1, 0}, - {vo+2, eo+1, eo+2, fo+0, eo+5, fo+2, fo+3, 0}, - {vo+3, eo+3, eo+5, fo+2, eo+4, fo+1, fo+3, 0} - }; - - return PointVector( {p[map[localScvIdx][0]], - p[map[localScvIdx][1]], - p[map[localScvIdx][2]], - p[map[localScvIdx][3]], - p[map[localScvIdx][4]], - p[map[localScvIdx][5]], - p[map[localScvIdx][6]], - p[map[localScvIdx][7]]} ); - } - case 8: // hexahedron - { - //! Only build the maps the first time we encounter a quadrilateral - static const std::uint8_t vo = 1; //! vertex offset in point vector p - static const std::uint8_t eo = 9; //! edge offset in point vector p - static const std::uint8_t fo = 21; //! face offset in point vector p - static const std::uint8_t map[8][8] = - { - {vo+0, eo+6, eo+4, fo+4, eo+0, fo+2, fo+0, 0}, - {vo+1, eo+5, eo+6, fo+4, eo+1, fo+1, fo+2, 0}, - {vo+2, eo+4, eo+7, fo+4, eo+2, fo+0, fo+3, 0}, - {vo+3, eo+7, eo+5, fo+4, eo+3, fo+3, fo+1, 0}, - {vo+4, eo+8, eo+10, fo+5, eo+0, fo+0, fo+2, 0}, - {vo+5, eo+10, eo+9, fo+5, eo+1, fo+2, fo+1, 0}, - {vo+6, eo+11, eo+8, fo+5, eo+2, fo+3, fo+0, 0}, - {vo+7, eo+9, eo+11, fo+5, eo+3, fo+1, fo+3, 0}, - }; - - return PointVector( {p[map[localScvIdx][0]], - p[map[localScvIdx][1]], - p[map[localScvIdx][2]], - p[map[localScvIdx][3]], - p[map[localScvIdx][4]], - p[map[localScvIdx][5]], - p[map[localScvIdx][6]], - p[map[localScvIdx][7]]} ); - } - default: - DUNE_THROW(Dune::NotImplemented, "Mpfa-Fps scv corners for dim=" << dim - << " dimWorld=" << dimWorld - << " corners=" << corners_); - } - } - -private: - const typename Element::Geometry& elementGeometry_; //! Reference to the element geometry - GlobalPosition p[maxPoints]; // the points needed for construction of the scv/scvf geometries - std::size_t corners_; // number of element corners -}; - } // end namespace Dumux #endif diff --git a/dumux/discretization/cellcentered/mpfa/omethodfps/subcontrolvolumeface.hh b/dumux/discretization/cellcentered/mpfa/omethodfps/subcontrolvolumeface.hh index f98927a6d6f6864abdd0d1fdf445f50036549f28..cffb6a13cca3bd57db01622c84c4de7125b55a64 100644 --- a/dumux/discretization/cellcentered/mpfa/omethodfps/subcontrolvolumeface.hh +++ b/dumux/discretization/cellcentered/mpfa/omethodfps/subcontrolvolumeface.hh @@ -24,7 +24,7 @@ #define DUMUX_DISCRETIZATION_CC_MPFA_O_FPS_SUBCONTROLVOLUMEFACE_HH #include <dumux/discretization/cellcentered/mpfa/methods.hh> -#include <dumux/discretization/cellcentered/mpfa/omethod/subcontrolvolumeface.hh> +#include <dumux/discretization/cellcentered/mpfa/subcontrolvolumefacebase.hh> namespace Dumux { @@ -34,9 +34,9 @@ namespace Dumux * \brief Class for a sub control volume face in the mpfao-fps method. */ template<class G, typename I> -class CCMpfaSubControlVolumeFace<MpfaMethods::oMethodFps, G, I> : public CCMpfaSubControlVolumeFace<MpfaMethods::oMethod, G, I> +class CCMpfaSubControlVolumeFace<MpfaMethods::oMethodFps, G, I> : public CCMpfaSubControlVolumeFaceBase<G, I> { - using ParentType = CCMpfaSubControlVolumeFace<MpfaMethods::oMethod, G, I>; + using ParentType = CCMpfaSubControlVolumeFaceBase<G, I>; using Geometry = G; using IndexType = I; @@ -47,23 +47,25 @@ class CCMpfaSubControlVolumeFace<MpfaMethods::oMethodFps, G, I> : public CCMpfaS using GlobalPosition = Dune::FieldVector<Scalar, dimworld>; public: - template<class MpfaGeometryHelper> - CCMpfaSubControlVolumeFace(const MpfaGeometryHelper& geomHelper, + template<class MpfaHelper> + CCMpfaSubControlVolumeFace(const MpfaHelper& helper, std::vector<GlobalPosition>&& corners, GlobalPosition&& unitOuterNormal, IndexType vIdxGlobal, unsigned int vIdxLocal, IndexType scvfIndex, - std::array<IndexType, 2>&& scvIndices, + IndexType insideScvIdx, + const std::vector<IndexType>& outsideScvIndices, Scalar q, bool boundary) - : ParentType(geomHelper, + : ParentType(helper, std::move(corners), std::move(unitOuterNormal), vIdxGlobal, vIdxLocal, scvfIndex, - std::move(scvIndices), + insideScvIdx, + outsideScvIndices, q, boundary), vIdxInElement_(vIdxLocal) diff --git a/dumux/discretization/cellcentered/mpfa/omethod/globalinteractionvolumeseeds.hh b/dumux/discretization/cellcentered/mpfa/stencils.hh similarity index 63% rename from dumux/discretization/cellcentered/mpfa/omethod/globalinteractionvolumeseeds.hh rename to dumux/discretization/cellcentered/mpfa/stencils.hh index 9ee05b9332fe9ab30eadca7884166befbb6c32db..734951864ea814c656c4bfad1d40b752c0e4e6a7 100644 --- a/dumux/discretization/cellcentered/mpfa/omethod/globalinteractionvolumeseeds.hh +++ b/dumux/discretization/cellcentered/mpfa/stencils.hh @@ -18,31 +18,24 @@ *****************************************************************************/ /*! * \file - * \brief Base class for the global interaction volume seeds of mpfa methods. + * \brief Implements the notion of stencils for cell-centered models */ -#ifndef DUMUX_DISCRETIZATION_MPFA_O_GLOBALINTERACTIONVOLUMESEEDS_HH -#define DUMUX_DISCRETIZATION_MPFA_O_GLOBALINTERACTIONVOLUMESEEDS_HH +#ifndef DUMUX_DISCRETIZATION_CC_MPFA_STENCILS_HH +#define DUMUX_DISCRETIZATION_CC_MPFA_STENCILS_HH -#include <dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseedsbase.hh> -#include <dumux/discretization/cellcentered/mpfa/methods.hh> +#include <dumux/discretization/cellcentered/stencils.hh> namespace Dumux { -/*! - * \ingroup Mpfa - * \brief Specialization of the class for the mpfa-o method. - */ -template<class TypeTag> -class CCMpfaGlobalInteractionVolumeSeedsImplementation<TypeTag, MpfaMethods::oMethod> : public CCMpfaGlobalInteractionVolumeSeedsBase<TypeTag> -{ - using ParentType = CCMpfaGlobalInteractionVolumeSeedsBase<TypeTag>; - using GridView = typename GET_PROP_TYPE(TypeTag, GridView); +//! Method-specific implementation of the stencils vector +//! By default, we inherit from the symmetric stencils vector class +//! For non-symmetric mpfa methods a corresponding specialization has to be provided below +template<class TypeTag, MpfaMethods method> +class CCMpfaStencilsVectorImplementation : public CCSymmetricStencilsVector<TypeTag> {}; -public: - CCMpfaGlobalInteractionVolumeSeedsImplementation(const GridView gridView) : ParentType(gridView) {} -}; +template<class TypeTag> +using CCMpfaStencilsVector = CCMpfaStencilsVectorImplementation<TypeTag, GET_PROP_VALUE(TypeTag, MpfaMethod)>; } // end namespace - #endif diff --git a/dumux/discretization/cellcentered/mpfa/subcontrolvolumeface.hh b/dumux/discretization/cellcentered/mpfa/subcontrolvolumeface.hh index 947df913a9dc69cbbe830be1d28613d369f4d2da..5d1f96917818b2e6cf6d2182e143b350158e367c 100644 --- a/dumux/discretization/cellcentered/mpfa/subcontrolvolumeface.hh +++ b/dumux/discretization/cellcentered/mpfa/subcontrolvolumeface.hh @@ -53,11 +53,10 @@ class CCMpfaSubControlVolumeFace : public CCMpfaSubControlVolumeFaceImplementati public: /*! - * \brief Constructor interface of an mpfa sub control volume face + * \brief Constructor * - * We define a general constructor for scv faces in mpfa methods. - * This is necessary so that the class instantiating scv faces can work - * method-independent. + * We do not use the localIndex here. Its meaning can vary depending on the + * implementation (i.e. mpfa method) and is handled by the implementation itself. * * \param geomHelper The mpfa geometry helper * \param corners The corners of the scv face @@ -65,26 +64,38 @@ public: * \param vIdxGlobal The global vertex index the scvf is connected to * \param localIndex Some element local index (the local vertex index in mpfao-fps) * \param scvfIndex The global index of this scv face - * \param scvIndices The inside and outside scv indices connected to this face + * \param insideScvIdx The inside scv index connected to this face + * \param outsideScvIndices The outside scv indices connected to this face * \param q The parameterization of the quadrature point on the scvf for flux calculation * \param boundary Boolean to specify whether or not the scvf is on a boundary */ - template<class MpfaGeometryHelper> - CCMpfaSubControlVolumeFace(const MpfaGeometryHelper& geomHelper, + template<class MpfaHelper> + CCMpfaSubControlVolumeFace(const MpfaHelper& helper, std::vector<GlobalPosition>&& corners, GlobalPosition&& unitOuterNormal, IndexType vIdxGlobal, unsigned int localIndex, IndexType scvfIndex, - std::array<IndexType, 2>&& scvIndices, + IndexType insideScvIdx, + const std::vector<IndexType>& outsideScvIndices, Scalar q, bool boundary) - : ParentType(geomHelper, corners, unitOuterNormal, vIdxGlobal, localIndex, scvfIndex, scvIndices, q, boundary) + : ParentType(helper, + std::forward<std::vector<GlobalPosition>>(corners), + std::forward<GlobalPosition>(unitOuterNormal), + vIdxGlobal, + localIndex, + scvfIndex, + insideScvIdx, + outsideScvIndices, + q, + boundary) {} }; } // end namespace //! The available implementations should be included here +#include <dumux/discretization/cellcentered/mpfa/lmethod/subcontrolvolumeface.hh> #include <dumux/discretization/cellcentered/mpfa/omethod/subcontrolvolumeface.hh> #include <dumux/discretization/cellcentered/mpfa/omethodfps/subcontrolvolumeface.hh> diff --git a/dumux/discretization/cellcentered/mpfa/subcontrolvolumefacebase.hh b/dumux/discretization/cellcentered/mpfa/subcontrolvolumefacebase.hh index 57dc6591b31ffb5867e5a16833bcb0101c6dcce0..ca4598fd8ee1c20fb2fa9fe9e93178611fbabd41 100644 --- a/dumux/discretization/cellcentered/mpfa/subcontrolvolumefacebase.hh +++ b/dumux/discretization/cellcentered/mpfa/subcontrolvolumefacebase.hh @@ -60,25 +60,28 @@ public: * \param vIdxGlobal The global vertex index the scvf is connected to * \param localIndex Some element local index (the local vertex index in mpfao-fps) * \param scvfIndex The global index of this scv face - * \param scvIndices The inside and outside scv indices connected to this face + * \param insideScvIdx The inside scv index connected to this face + * \param outsideScvIndices The outside scv indices connected to this face * \param q The parameterization of the quadrature point on the scvf for flux calculation * \param boundary Boolean to specify whether or not the scvf is on a boundary */ - template<class MpfaGeometryHelper> - CCMpfaSubControlVolumeFaceBase(const MpfaGeometryHelper& geomHelper, + template<class MpfaHelper> + CCMpfaSubControlVolumeFaceBase(const MpfaHelper& helper, std::vector<GlobalPosition>&& corners, GlobalPosition&& unitOuterNormal, IndexType vertexIndex, unsigned int localIndex, IndexType scvfIndex, - std::array<IndexType, 2>&& scvIndices, + IndexType insideScvIdx, + const std::vector<IndexType>& outsideScvIndices, Scalar q, bool boundary) : ParentType(), boundary_(boundary), vertexIndex_(vertexIndex), scvfIndex_(scvfIndex), - scvIndices_(std::move(scvIndices)), + insideScvIdx_(insideScvIdx), + outsideScvIndices_(outsideScvIndices), corners_(std::move(corners)), center_(0.0), unitOuterNormal_(std::move(unitOuterNormal)) @@ -86,8 +89,8 @@ public: for (const auto& corner : corners_) center_ += corner; center_ /= corners_.size(); - ipGlobal_ = geomHelper.getScvfIntegrationPoint(corners_, q); - area_ = geomHelper.getScvfArea(corners_); + ipGlobal_ = helper.getScvfIntegrationPoint(corners_, q); + area_ = helper.getScvfArea(corners_); } //! The center of the sub control volume face @@ -111,11 +114,20 @@ public: //! index of the inside sub control volume for spatial param evaluation IndexType insideScvIdx() const - { return scvIndices_[0]; } + { return insideScvIdx_; } //! index of the outside sub control volume for spatial param evaluation + //! returns in undefined behaviour is boundary is true + //! is not uniquely defined for grids where dim < dimWorld and shouldn't be used in that case. IndexType outsideScvIdx() const - { return scvIndices_[1]; } + { + assert(outsideScvIndices_.size() == 1 && "outside scv index not uniquely defined"); + return outsideScvIndices_[0]; + } + + //! returns the outside scv indices (can be more than one index for dim < dimWorld) + std::vector<IndexType> outsideScvIndices() const + { return outsideScvIndices_; } //! The global index of this sub control volume face IndexType index() const @@ -152,7 +164,8 @@ private: bool boundary_; IndexType vertexIndex_; IndexType scvfIndex_; - std::array<IndexType, 2> scvIndices_; + IndexType insideScvIdx_; + std::vector<IndexType> outsideScvIndices_; std::vector<GlobalPosition> corners_; GlobalPosition center_; diff --git a/dumux/discretization/cellcentered/mpfa/omethodfps/globalinteractionvolumeseeds.hh b/dumux/implicit/cellcentered/mpfa/assemblymap.hh similarity index 61% rename from dumux/discretization/cellcentered/mpfa/omethodfps/globalinteractionvolumeseeds.hh rename to dumux/implicit/cellcentered/mpfa/assemblymap.hh index cd1ce2fb5f7b1fb97682fae7a1fafa5c3780dc3e..cf9b374fba30f5b29447b9a43a31bcd151e5011d 100644 --- a/dumux/discretization/cellcentered/mpfa/omethodfps/globalinteractionvolumeseeds.hh +++ b/dumux/implicit/cellcentered/mpfa/assemblymap.hh @@ -18,31 +18,27 @@ *****************************************************************************/ /*! * \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_O_FPS_GLOBALINTERACTIONVOLUMESEEDS_HH -#define DUMUX_DISCRETIZATION_MPFA_O_FPS_GLOBALINTERACTIONVOLUMESEEDS_HH +#ifndef DUMUX_CC_MPFA_ASSEMBLY_MAP_HH +#define DUMUX_CC_MPFA_ASSEMBLY_MAP_HH -#include <dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseedsbase.hh> -#include <dumux/discretization/cellcentered/mpfa/methods.hh> +#include <dumux/implicit/cellcentered/assemblymap.hh> namespace Dumux { -/*! - * \ingroup Mpfa - * \brief Specialization of the class for the mpfa-o method with full pressure support. - */ -template<class TypeTag> -class CCMpfaGlobalInteractionVolumeSeedsImplementation<TypeTag, MpfaMethods::oMethodFps> : public CCMpfaGlobalInteractionVolumeSeedsBase<TypeTag> -{ - using ParentType = CCMpfaGlobalInteractionVolumeSeedsBase<TypeTag>; - using GridView = typename GET_PROP_TYPE(TypeTag, GridView); - -public: - CCMpfaGlobalInteractionVolumeSeedsImplementation(const GridView gridView) : ParentType(gridView) {} -}; +//! Forward declaration of method specific implementation of the assembly map +template<class TypeTag, MpfaMethods method> +class CCMpfaAssemblyMapImplementation; -} // end namespace +// //! The Assembly map for models using mpfa methods +template<class TypeTag> +using CCMpfaAssemblyMap = CCMpfaAssemblyMapImplementation<TypeTag, GET_PROP_VALUE(TypeTag, MpfaMethod)>; +//! The default is simply the CCAssemblyMap +template<class TypeTag, MpfaMethods method> +class CCMpfaAssemblyMapImplementation : public CCAssemblyMap<TypeTag> {}; +} #endif diff --git a/dumux/implicit/cellcentered/mpfa/propertydefaults.hh b/dumux/implicit/cellcentered/mpfa/propertydefaults.hh index fda0c3b10a71233bb9da282cad35d12cbac11748..9ff21ee167e7f90fa00b8933d457bc31eea491d6 100644 --- a/dumux/implicit/cellcentered/mpfa/propertydefaults.hh +++ b/dumux/implicit/cellcentered/mpfa/propertydefaults.hh @@ -39,6 +39,7 @@ #include <dumux/discretization/cellcentered/mpfa/elementfluxvariablescache.hh> #include <dumux/discretization/cellcentered/mpfa/subcontrolvolumeface.hh> #include <dumux/discretization/cellcentered/mpfa/helper.hh> +#include <dumux/discretization/cellcentered/mpfa/stencils.hh> #include <dumux/discretization/cellcentered/mpfa/interactionvolume.hh> #include <dumux/discretization/cellcentered/mpfa/globalinteractionvolumeseeds.hh> #include <dumux/implicit/cellcentered/mpfa/localresidual.hh> @@ -46,9 +47,6 @@ namespace Dumux { -// forward declarations -template<class TypeTag> class CCElementBoundaryTypes; - namespace Properties { //! Set the corresponding discretization method property SET_PROP(CCMpfaModel, DiscretizationMethod) @@ -56,7 +54,7 @@ SET_PROP(CCMpfaModel, DiscretizationMethod) static const DiscretizationMethods value = DiscretizationMethods::CCMpfa; }; -//! Set the BaseLocalResidual to CCLocalResidual +//! Set the BaseLocalResidual to CCMpfaLocalResidual SET_TYPE_PROP(CCMpfaModel, BaseLocalResidual, CCMpfaLocalResidual<TypeTag>); //! By default we set the o-method as the Mpfa method of choice @@ -66,7 +64,7 @@ SET_PROP(CCMpfaModel, MpfaMethod) }; //! The mpfa helper class -SET_TYPE_PROP(CCMpfaModel, MpfaHelper, MpfaHelper<TypeTag>); +SET_TYPE_PROP(CCMpfaModel, MpfaHelper, CCMpfaHelper<TypeTag>); //! The interaction volume class SET_TYPE_PROP(CCMpfaModel, InteractionVolume, CCMpfaInteractionVolume<TypeTag>); @@ -95,6 +93,9 @@ SET_TYPE_PROP(CCMpfaModel, ElementVolumeVariables, Dumux::CCMpfaElementVolumeVar //! The local flux variables cache vector class SET_TYPE_PROP(CCMpfaModel, ElementFluxVariablesCache, Dumux::CCMpfaElementFluxVariablesCache<TypeTag, GET_PROP_VALUE(TypeTag, EnableGlobalFluxVariablesCache)>); +//! The stencil container, some mpfa methods might lead to non-symmetric global systems +SET_TYPE_PROP(CCMpfaModel, StencilsVector, Dumux::CCMpfaStencilsVector<TypeTag>); + SET_PROP(CCMpfaModel, SubControlVolumeFace) { private: diff --git a/dumux/porousmediumflow/implicit/fluxvariablescache.hh b/dumux/porousmediumflow/implicit/fluxvariablescache.hh index 3e7de05f2ea6703e84a35d8fb17d97a6850854d5..6d6725745629b843350a39b5559d8b7b629b19c2 100644 --- a/dumux/porousmediumflow/implicit/fluxvariablescache.hh +++ b/dumux/porousmediumflow/implicit/fluxvariablescache.hh @@ -152,13 +152,13 @@ private: // forward declaration of the base class of the mpfa flux variables cache template<class TypeTag, bool EnableAdvection, bool EnableMolecularDiffusion, bool EnableEnergyBalance> -class PorousMediumMpfaFluxVariablesCache +class MpfaPorousMediumFluxVariablesCache {}; // specialization for cell centered mpfa methods template<class TypeTag> class PorousMediumFluxVariablesCacheImplementation<TypeTag, DiscretizationMethods::CCMpfa> - : public PorousMediumMpfaFluxVariablesCache<TypeTag, + : public MpfaPorousMediumFluxVariablesCache<TypeTag, GET_PROP_VALUE(TypeTag, EnableAdvection), GET_PROP_VALUE(TypeTag, EnableMolecularDiffusion), GET_PROP_VALUE(TypeTag, EnableEnergyBalance)> @@ -166,7 +166,7 @@ class PorousMediumFluxVariablesCacheImplementation<TypeTag, DiscretizationMethod // specialization for the case of pure advection template<class TypeTag> -class PorousMediumMpfaFluxVariablesCache<TypeTag, true, false, false> +class MpfaPorousMediumFluxVariablesCache<TypeTag, true, false, false> { using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using Problem = typename GET_PROP_TYPE(TypeTag, Problem); @@ -192,11 +192,11 @@ class PorousMediumMpfaFluxVariablesCache<TypeTag, true, false, false> public: // the constructor - PorousMediumMpfaFluxVariablesCache() : isUpdated_(false) + MpfaPorousMediumFluxVariablesCache() : isUpdated_(false) { - // We have to initialize to zero (for inner interaction volumes) - for (auto& nFlux : phaseNeumannFluxes_) - nFlux = 0.0; + // We have to initialize the neumann fluxes to zero (for inner interaction volumes) + for (auto& flux : phaseNeumannFluxes_) + flux = 0.0; } // update cached objects @@ -208,19 +208,10 @@ public: const SubControlVolumeFace &scvf, const InteractionVolume& interactionVolume) { - const auto& localIndexPair = interactionVolume.getLocalIndexPair(scvf); - const auto& volVarsStencil = interactionVolume.volVarsStencil(); - const auto& volVarsPositions = interactionVolume.volVarsPositions(); - - // the types coming from the inner interaction volumes might differ (thus, = assignment is not possible) - const auto numVolVars = volVarsStencil.size(); - volVarsStencil_.clear(); - volVarsStencil_.reserve(numVolVars); - volVarsPositions_.clear(); - volVarsPositions_.reserve(numVolVars); - volVarsStencil_.insert(volVarsStencil_.begin(), volVarsStencil.begin(), volVarsStencil.end()); - volVarsPositions_.insert(volVarsPositions_.begin(), volVarsPositions.begin(), volVarsPositions.end()); - tij_ = interactionVolume.getTransmissibilities(localIndexPair); + const auto& localFaceData = interactionVolume.getLocalFaceData(scvf); + volVarsStencil_ = interactionVolume.volVarsStencil(); + volVarsPositions_ = interactionVolume.volVarsPositions(); + tij_ = interactionVolume.getTransmissibilities(localFaceData); } template<typename InteractionVolume> @@ -232,8 +223,8 @@ public: const InteractionVolume& interactionVolume, const unsigned int phaseIdx) { - const auto& localIndexPair = interactionVolume.getLocalIndexPair(scvf); - phaseNeumannFluxes_[phaseIdx] = interactionVolume.getNeumannFlux(localIndexPair); + const auto& localFaceData = interactionVolume.getLocalFaceData(scvf); + phaseNeumannFluxes_[phaseIdx] = interactionVolume.getNeumannFlux(localFaceData); } const Stencil& advectionVolVarsStencil(const unsigned int phaseIdx) const @@ -251,14 +242,9 @@ public: bool isUpdated() const { return isUpdated_; } - void setUpdated() + void setUpdateStatus(const bool status) { - isUpdated_ = true; - } - - void setOutdated() - { - isUpdated_ = false; + isUpdated_ = status; } private: