// -*- 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 . * *****************************************************************************/ /*! * \file * \ingroup CCMpfaDiscretization * \brief A helper class to fill the flux variable caches used in the flux constitutive laws */ #ifndef DUMUX_DISCRETIZATION_CCMPFA_FLUXVARSCACHE_FILLER_HH #define DUMUX_DISCRETIZATION_CCMPFA_FLUXVARSCACHE_FILLER_HH #include #include #include #include #include namespace Dumux { /*! * \ingroup CCMpfaDiscretization * \brief Helper class to fill the flux variables caches within * the interaction volume around a given sub-control volume face. */ template class CCMpfaFluxVariablesCacheFiller { using Problem = typename GET_PROP_TYPE(TypeTag, Problem); using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using Element = typename GridView::template Codim<0>::Entity; using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables); using FluxVariablesCache = typename GET_PROP_TYPE(TypeTag, FluxVariablesCache); using PrimaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, PrimaryInteractionVolume); using PrimaryDataHandle = typename PrimaryInteractionVolume::Traits::DataHandle; using PrimaryLocalFaceData = typename PrimaryInteractionVolume::Traits::LocalFaceData; using SecondaryInteractionVolume = typename GET_PROP_TYPE(TypeTag, SecondaryInteractionVolume); using SecondaryDataHandle = typename SecondaryInteractionVolume::Traits::DataHandle; using SecondaryLocalFaceData = typename SecondaryInteractionVolume::Traits::LocalFaceData; static constexpr int dim = GridView::dimension; static constexpr int dimWorld = GridView::dimensionworld; static constexpr bool doAdvection = GET_PROP_VALUE(TypeTag, EnableAdvection); static constexpr bool doDiffusion = GET_PROP_VALUE(TypeTag, EnableMolecularDiffusion); static constexpr bool doHeatConduction = GET_PROP_VALUE(TypeTag, EnableEnergyBalance); static constexpr bool soldependentAdvection = GET_PROP_VALUE(TypeTag, SolutionDependentAdvection); static constexpr bool soldependentDiffusion = GET_PROP_VALUE(TypeTag, SolutionDependentMolecularDiffusion); static constexpr bool soldependentHeatConduction = GET_PROP_VALUE(TypeTag, SolutionDependentHeatConduction); public: //! This cache filler is always solution-dependent, as it updates the //! vectors of cell unknowns with which the transmissibilities have to be //! multiplied in order to obtain the fluxes. static constexpr bool isSolDependent = true; //! The constructor. Sets problem pointer. CCMpfaFluxVariablesCacheFiller(const Problem& problem) : problemPtr_(&problem) {} /*! * \brief function to fill the flux variables caches * * \param fluxVarsCacheContainer Either the element or global flux variables cache * \param scvfFluxVarsCache The flux var cache to be updated corresponding to the given scvf * \param element The finite element * \param fvGeometry The finite volume geometry * \param elemVolVars The element volume variables (primary/secondary variables) * \param scvf The corresponding sub-control volume face * \param forceUpdateAll if true, forces all caches to be updated (even the solution-independent ones) */ template void fill(FluxVariablesCacheContainer& fluxVarsCacheContainer, FluxVariablesCache& scvfFluxVarsCache, const Element& element, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const SubControlVolumeFace& scvf, bool forceUpdateAll = false) { // Set pointers elementPtr_ = &element; fvGeometryPtr_ = &fvGeometry; elemVolVarsPtr_ = &elemVolVars; // prepare interaction volume and fill caches of all the scvfs connected to it const auto& fvGridGeometry = fvGeometry.fvGridGeometry(); if (fvGridGeometry.vertexUsesSecondaryInteractionVolume(scvf.vertexIndex())) { if (forceUpdateAll) { // the local index of the interaction volume to be created in its container const auto ivIndexInContainer = fluxVarsCacheContainer.secondaryInteractionVolumes().size(); // prepare the locally cached boundary interaction volume const auto& indexSet = fvGridGeometry.gridInteractionVolumeIndexSets().secondaryIndexSet(scvf); fluxVarsCacheContainer.secondaryInteractionVolumes().emplace_back(); secondaryIv_ = &fluxVarsCacheContainer.secondaryInteractionVolumes().back(); secondaryIv_->setUpLocalScope(indexSet, problem(), fvGeometry); // prepare the corresponding data handle fluxVarsCacheContainer.secondaryDataHandles().emplace_back(); secondaryIvDataHandle_ = &fluxVarsCacheContainer.secondaryDataHandles().back(); secondaryIvDataHandle_->resize(*secondaryIv_); // fill the caches for all the scvfs in the interaction volume fillCachesInInteractionVolume_(fluxVarsCacheContainer, *secondaryIv_, *secondaryIvDataHandle_, ivIndexInContainer, true); } else { const auto ivIndexInContainer = scvfFluxVarsCache.ivIndexInContainer(); secondaryIv_ = &fluxVarsCacheContainer.secondaryInteractionVolumes()[ivIndexInContainer]; secondaryIvDataHandle_ = &fluxVarsCacheContainer.secondaryDataHandles()[ivIndexInContainer]; // fill the caches for all the scvfs in the interaction volume fillCachesInInteractionVolume_(fluxVarsCacheContainer, *secondaryIv_, *secondaryIvDataHandle_, ivIndexInContainer); } } else { if (forceUpdateAll) { // the local index of the interaction volume to be created in its container const auto ivIndexInContainer = fluxVarsCacheContainer.primaryInteractionVolumes().size(); // prepare the locally cached boundary interaction volume const auto& indexSet = fvGridGeometry.gridInteractionVolumeIndexSets().primaryIndexSet(scvf); fluxVarsCacheContainer.primaryInteractionVolumes().emplace_back(); primaryIv_ = &fluxVarsCacheContainer.primaryInteractionVolumes().back(); primaryIv_->setUpLocalScope(indexSet, problem(), fvGeometry); // prepare the corresponding data handle fluxVarsCacheContainer.primaryDataHandles().emplace_back(); primaryIvDataHandle_ = &fluxVarsCacheContainer.primaryDataHandles().back(); primaryIvDataHandle_->resize(*primaryIv_); // fill the caches for all the scvfs in the interaction volume fillCachesInInteractionVolume_(fluxVarsCacheContainer, *primaryIv_, *primaryIvDataHandle_, ivIndexInContainer, true); } else { const auto ivIndexInContainer = scvfFluxVarsCache.ivIndexInContainer(); primaryIv_ = &fluxVarsCacheContainer.primaryInteractionVolumes()[ivIndexInContainer]; primaryIvDataHandle_ = &fluxVarsCacheContainer.primaryDataHandles()[ivIndexInContainer]; // fill the caches for all the scvfs in the interaction volume fillCachesInInteractionVolume_(fluxVarsCacheContainer, *primaryIv_, *primaryIvDataHandle_, ivIndexInContainer); } } } //! returns the stored interaction volume pointer const PrimaryInteractionVolume& primaryInteractionVolume() const { return *primaryIv_; } //! returns the stored interaction volume pointer const SecondaryInteractionVolume& secondaryInteractionVolume() const { return *secondaryIv_; } //! returns the stored data handle pointer const PrimaryDataHandle& primaryIvDataHandle() const { return *primaryIvDataHandle_; } //! returns the stored data handle pointer const SecondaryDataHandle& secondaryIvDataHandle() const { return *secondaryIvDataHandle_; } //! returns the currently stored iv-local face data object const PrimaryLocalFaceData& primaryIvLocalFaceData() const { return *primaryLocalFaceData_; } //! returns the currently stored iv-local face data object const SecondaryLocalFaceData& secondaryIvLocalFaceData() const { return *secondaryLocalFaceData_; } private: const Problem& problem() const { return *problemPtr_; } const Element& element() const { return *elementPtr_; } const FVElementGeometry& fvGeometry() const { return *fvGeometryPtr_; } const ElementVolumeVariables& elemVolVars() const { return *elemVolVarsPtr_; } //! Method to fill the flux var caches within an interaction volume template void fillCachesInInteractionVolume_(FluxVarsCacheContainer& fluxVarsCacheContainer, InteractionVolume& iv, DataHandle& handle, unsigned int ivIndexInContainer, bool forceUpdateAll = false) { // First we upate data which are not dependent on the physical processes. // We store pointers to the other flux var caches, so that we have to obtain // this data only once and can use it again in the sub-cache fillers. const auto numGlobalScvfs = iv.localFaceData().size(); std::vector ivScvfs(numGlobalScvfs); std::vector ivFluxVarCaches(numGlobalScvfs); unsigned int i = 0; for (const auto& d : iv.localFaceData()) { // obtain the scvf const auto& scvfJ = fvGeometry().scvf(d.globalScvfIndex()); ivScvfs[i] = &scvfJ; ivFluxVarCaches[i] = &fluxVarsCacheContainer[scvfJ]; ivFluxVarCaches[i]->setIvIndexInContainer(ivIndexInContainer); ivFluxVarCaches[i]->setUpdateStatus(true); i++; } fillAdvection(iv, handle, ivScvfs, ivFluxVarCaches, forceUpdateAll); fillDiffusion(iv, handle, ivScvfs, ivFluxVarCaches, forceUpdateAll); fillHeatConduction(iv, handle, ivScvfs, ivFluxVarCaches, forceUpdateAll); } //! fills the advective quantities (enabled advection) template< class InteractionVolume, class DataHandle, bool enableAdvection = doAdvection, typename std::enable_if_t = 0 > void fillAdvection(InteractionVolume& iv, DataHandle& handle, const std::vector& ivScvfs, const std::vector& ivFluxVarCaches, bool forceUpdateAll = false) { using AdvectionType = typename GET_PROP_TYPE(TypeTag, AdvectionType); using AdvectionFiller = typename AdvectionType::Cache::Filler; static constexpr auto AdvectionMethod = AdvectionType::myDiscretizationMethod; using LambdaFactory = TensorLambdaFactory; // skip the following if advection doesn't use mpfa if (AdvectionMethod == DiscretizationMethods::CCMpfa) { // get instance of the interaction volume-local assembler using IvLocalAssembler = InteractionVolumeAssembler< InteractionVolume >; IvLocalAssembler localAssembler(problem(), fvGeometry(), elemVolVars()); // Use different assembly if gravity is enabled static const bool enableGravity = getParamFromGroup(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Problem.EnableGravity"); // Assemble T only if permeability is sol-dependent or if update is forced if (forceUpdateAll || soldependentAdvection) { // distinguish between normal/surface grids (optimized away by compiler) if (dim < dimWorld) { if (enableGravity) localAssembler.assembleWithGravity( handle.advectionTout(), handle.advectionT(), handle.gravityOutside(), handle.gravity(), handle.advectionCA(), handle.advectionA(), iv, LambdaFactory::getAdvectionLambda() ); else localAssembler.assemble( handle.advectionTout(), handle.advectionT(), iv, LambdaFactory::getAdvectionLambda() ); } // normal grids else { if (enableGravity) localAssembler.assembleWithGravity( handle.advectionT(), handle.gravity(), handle.advectionCA(), iv, LambdaFactory::getAdvectionLambda() ); else localAssembler.assemble( handle.advectionT(), iv, LambdaFactory::getAdvectionLambda() ); } } // (maybe) only reassemble gravity vector else if (enableGravity) { if (dim == dimWorld) localAssembler.assembleGravity( handle.gravity(), iv, handle.advectionCA(), LambdaFactory::getAdvectionLambda() ); else localAssembler.assembleGravity( handle.gravity(), handle.gravityOutside(), iv, handle.advectionCA(), handle.advectionA(), LambdaFactory::getAdvectionLambda() ); } // assemble pressure vectors for (unsigned int pIdx = 0; pIdx < GET_PROP_VALUE(TypeTag, NumPhases); ++pIdx) { const auto& evv = &elemVolVars(); auto getPressure = [&evv, pIdx] (auto volVarIdx) { return (evv->operator[](volVarIdx)).pressure(pIdx); }; localAssembler.assemble(handle.pressures(pIdx), iv, getPressure); } } // fill advection caches for (unsigned int i = 0; i < iv.localFaceData().size(); ++i) { // set pointer to current local face data object // ifs are evaluated at compile time and are optimized away if (std::is_same::value) { // we cannot make a disctinction, thus we set both pointers primaryLocalFaceData_ = &(iv.localFaceData()[i]); secondaryLocalFaceData_ = &(iv.localFaceData()[i]); } else if (std::is_same::value) primaryLocalFaceData_ = &(iv.localFaceData()[i]); else secondaryLocalFaceData_ = &(iv.localFaceData()[i]); // fill this scvfs cache AdvectionFiller::fill(*ivFluxVarCaches[i], problem(), iv.element(iv.localFaceData()[i].ivLocalInsideScvIndex()), fvGeometry(), elemVolVars(), *ivScvfs[i], *this); } } //! do nothing if advection is not enabled template< class InteractionVolume, class DataHandle, bool enableAdvection = doAdvection, typename std::enable_if_t = 0 > void fillAdvection(InteractionVolume& iv, DataHandle& handle, const std::vector& ivScvfs, const std::vector& ivFluxVarCaches, bool forceUpdateAll = false) {} //! fills the diffusive quantities (diffusion enabled) template< class InteractionVolume, class DataHandle, bool enableDiffusion = doDiffusion, typename std::enable_if_t = 0 > void fillDiffusion(InteractionVolume& iv, DataHandle& handle, const std::vector& ivScvfs, const std::vector& ivFluxVarCaches, bool forceUpdateAll = false) { using DiffusionType = typename GET_PROP_TYPE(TypeTag, MolecularDiffusionType); using DiffusionFiller = typename DiffusionType::Cache::Filler; static constexpr auto DiffusionMethod = DiffusionType::myDiscretizationMethod; using LambdaFactory = TensorLambdaFactory; static constexpr int numPhases = GET_PROP_VALUE(TypeTag, NumPhases); static constexpr int numComponents = GET_PROP_VALUE(TypeTag, NumComponents); // get instance of the interaction volume-local assembler using IvLocalAssembler = InteractionVolumeAssembler< InteractionVolume >; IvLocalAssembler localAssembler(problem(), fvGeometry(), elemVolVars()); for (unsigned int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { for (unsigned int compIdx = 0; compIdx < numComponents; ++compIdx) { if (phaseIdx == compIdx) continue; // solve the local system subject to the diffusion tensor (if uses mpfa) if (DiffusionMethod == DiscretizationMethods::CCMpfa) { // update the context in handle handle.setPhaseIndex(phaseIdx); handle.setComponentIndex(compIdx); // assemble T if (forceUpdateAll || soldependentDiffusion) { if (dim < dimWorld) localAssembler.assemble( handle.diffusionTout(), handle.diffusionT(), iv, LambdaFactory::getDiffusionLambda(phaseIdx, compIdx) ); else localAssembler. assemble( handle.diffusionT(), iv, LambdaFactory::getDiffusionLambda(phaseIdx, compIdx) ); } // assemble vector of mole fractions const auto& evv = &elemVolVars(); auto getMoleFraction = [&evv, phaseIdx, compIdx] (auto volVarIdx) { return (evv->operator[](volVarIdx)).moleFraction(phaseIdx, compIdx); }; localAssembler.assemble(handle.moleFractions(phaseIdx, compIdx), iv, getMoleFraction); } // fill diffusion caches for (unsigned int i = 0; i < iv.localFaceData().size(); ++i) { // set pointer to current local face data object // ifs are evaluated at compile time and are optimized away if (std::is_same::value) { // we cannot make a disctinction, thus we set both pointers primaryLocalFaceData_ = &(iv.localFaceData()[i]); secondaryLocalFaceData_ = &(iv.localFaceData()[i]); } else if (std::is_same::value) primaryLocalFaceData_ = &(iv.localFaceData()[i]); else secondaryLocalFaceData_ = &(iv.localFaceData()[i]); // fill this scvfs cache DiffusionFiller::fill(*ivFluxVarCaches[i], phaseIdx, compIdx, problem(), iv.element(iv.localFaceData()[i].ivLocalInsideScvIndex()), fvGeometry(), elemVolVars(), *ivScvfs[i], *this); } } } } //! do nothing if diffusion is not enabled template< class InteractionVolume, class DataHandle, bool enableDiffusion = doDiffusion, typename std::enable_if_t = 0 > void fillDiffusion(InteractionVolume& iv, DataHandle& handle, const std::vector& ivScvfs, const std::vector& ivFluxVarCaches, bool forceUpdateAll = false) {} //! fills the quantities related to heat conduction (heat conduction enabled) template< class InteractionVolume, class DataHandle, bool enableHeatConduction = doHeatConduction, typename std::enable_if_t = 0 > void fillHeatConduction(InteractionVolume& iv, DataHandle& handle, const std::vector& ivScvfs, const std::vector& ivFluxVarCaches, bool forceUpdateAll = false) { using HeatConductionType = typename GET_PROP_TYPE(TypeTag, HeatConductionType); using HeatConductionFiller = typename HeatConductionType::Cache::Filler; static constexpr auto HeatConductionMethod = HeatConductionType::myDiscretizationMethod; using LambdaFactory = TensorLambdaFactory; // maybe solve the local system subject to fourier coefficient if (HeatConductionMethod == DiscretizationMethods::CCMpfa) { // get instance of the interaction volume-local assembler using IvLocalAssembler = InteractionVolumeAssembler< InteractionVolume >; IvLocalAssembler localAssembler(problem(), fvGeometry(), elemVolVars()); if (forceUpdateAll || soldependentAdvection) { if (dim < dimWorld) localAssembler.assemble( handle.heatConductionTout(), handle.heatConductionT(), iv, LambdaFactory::getHeatConductionLambda() ); else localAssembler.assemble( handle.heatConductionT(), iv, LambdaFactory::getHeatConductionLambda() ); } // assemble vector of temperatures const auto& evv = &elemVolVars(); auto getMoleFraction = [&evv] (auto volVarIdx) { return (evv->operator[](volVarIdx)).temperature(); }; localAssembler.assemble(handle.temperatures(), iv, getMoleFraction); } // fill heat conduction caches for (unsigned int i = 0; i < iv.localFaceData().size(); ++i) { // set pointer to current local face data object // ifs are evaluated at compile time and are optimized away if (std::is_same::value) { // we cannot make a disctinction, thus we set both pointers primaryLocalFaceData_ = &(iv.localFaceData()[i]); secondaryLocalFaceData_ = &(iv.localFaceData()[i]); } else if (std::is_same::value) primaryLocalFaceData_ = &(iv.localFaceData()[i]); else secondaryLocalFaceData_ = &(iv.localFaceData()[i]); // fill this scvfs cache HeatConductionFiller::fill(*ivFluxVarCaches[i], problem(), iv.element(iv.localFaceData()[i].ivLocalInsideScvIndex()), fvGeometry(), elemVolVars(), *ivScvfs[i], *this); } } //! do nothing if heat conduction is disabled template< class InteractionVolume, class DataHandle, bool enableHeatConduction = doHeatConduction, typename std::enable_if_t = 0 > void fillHeatConduction(InteractionVolume& iv, DataHandle& handle, const std::vector& ivScvfs, const std::vector& ivFluxVarCaches, bool forceUpdateAll = false) {} const Problem* problemPtr_; const Element* elementPtr_; const FVElementGeometry* fvGeometryPtr_; const ElementVolumeVariables* elemVolVarsPtr_; // We store pointers to an inner and a boundary interaction volume. // These are updated during the filling of the caches and the // physics-related caches have access to them PrimaryInteractionVolume* primaryIv_; SecondaryInteractionVolume* secondaryIv_; // pointer to the current interaction volume data handle PrimaryDataHandle* primaryIvDataHandle_; SecondaryDataHandle* secondaryIvDataHandle_; // We do an interaction volume-wise filling of the caches // While filling, we store a pointer to the current localScvf // face data object of the IV so that the individual caches // can access it and don't have to retrieve it again const PrimaryLocalFaceData* primaryLocalFaceData_; const SecondaryLocalFaceData* secondaryLocalFaceData_; }; } // end namespace Dumux #endif