diff --git a/dumux/porenetwork/2p/gridfluxvariablescache.hh b/dumux/porenetwork/2p/gridfluxvariablescache.hh index af5d4afa8805c910009d0b34556fd7302026a36b..a6a1969aa39ec9a7224baa0851f62ac3807bec44 100644 --- a/dumux/porenetwork/2p/gridfluxvariablescache.hh +++ b/dumux/porenetwork/2p/gridfluxvariablescache.hh @@ -36,13 +36,12 @@ namespace Dumux::PoreNetwork { * \ingroup PNMTwoPModel * \brief Flux variable caches traits */ -template<class P, class FVC, class I, class L> +template<class P, class FVC, class IS = TwoPInvasionState<P>> struct PNMTwoPDefaultGridFVCTraits { using Problem = P; using FluxVariablesCache = FVC; - using Indices = I; - using Labels = L; + using InvasionState = IS; template<class GridFluxVariablesCache, bool cachingEnabled> using LocalView = PNMTwoPElementFluxVariablesCache<GridFluxVariablesCache, cachingEnabled>; @@ -69,9 +68,9 @@ class PNMTwoPGridFluxVariablesCache<P, FVC, true, Traits> { using Problem = typename Traits::Problem; using ThisType = PNMTwoPGridFluxVariablesCache<P, FVC, true, Traits>; - using InvasionState = TwoPInvasionState<Problem>; + using InvasionState = typename Traits::InvasionState; - public: +public: //! export the flux variable cache type using FluxVariablesCache = typename Traits::FluxVariablesCache; @@ -140,7 +139,7 @@ class PNMTwoPGridFluxVariablesCache<P, FVC, false, Traits> { using Problem = typename Traits::Problem; using ThisType = PNMTwoPGridFluxVariablesCache<P, FVC, false, Traits>; - using InvasionState = TwoPInvasionState<Problem>; + using InvasionState = typename Traits::InvasionState; public: //! export the flux variable cache type diff --git a/dumux/porenetwork/2p/invasionstate.hh b/dumux/porenetwork/2p/invasionstate.hh index e6fce845b847e3f6482120d1682d872156fba694..8100a46bb500d937ab2e32423a4309173bbca493 100644 --- a/dumux/porenetwork/2p/invasionstate.hh +++ b/dumux/porenetwork/2p/invasionstate.hh @@ -19,42 +19,37 @@ /*! * \file * \ingroup PNMTwoPModel - * \brief Global flux variable cache + * \brief Invasion state class for the two-phase PNM. */ #ifndef DUMUX_PNM_2P_INVASIONSTATE_HH #define DUMUX_PNM_2P_INVASIONSTATE_HH #include <vector> #include <type_traits> +#include <dune/common/std/type_traits.hh> #include <dumux/common/parameters.hh> -#include <dumux/common/typetraits/isvalid.hh> #include <dumux/porenetwork/common/labels.hh> namespace Dumux::PoreNetwork { -#ifndef DOXYGEN -namespace Detail { -// helper struct detecting if the user-defined problem params class has a globalCapillaryPressure function -struct hasGlobalCapillaryPressure -{ - template<class Problem> - auto operator()(const Problem& p) - -> decltype(p.globalCapillaryPressure()) - {} -}; -} // end namespace Detail -#endif - /*! * \ingroup PNMTwoPModel - * \brief The grid flux variables cache for the two-phase PNM hodling the invasion state of the throats - * \note The flux caches of the gridview are stored which is memory intensive but faster + * \brief This class updates the invasion state for the two-phase PNM. */ template<class P> class TwoPInvasionState { using Problem = P; + template <class T> + using GlobalCapillaryPressureDetector = decltype(std::declval<T>().globalCapillaryPressure()); + + template<class T> + static constexpr bool hasGlobalCapillaryPressure() + { return Dune::Std::is_detected<GlobalCapillaryPressureDetector, T>::value; } + + enum class EventType {invasion, snapOff, none}; + public: TwoPInvasionState(const Problem& problem) : problem_(problem) @@ -71,10 +66,10 @@ public: } numThroatsInvaded_ = std::count(invadedCurrentIteration_.begin(), invadedCurrentIteration_.end(), true); - verbose_ = getParamFromGroup<bool>(problem.paramGroup(), "InvasionState.InvasionStateVerbosity", true); + verbose_ = getParamFromGroup<bool>(problem.paramGroup(), "InvasionState.Verbosity", true); restrictToGlobalCapillaryPressure_ = getParamFromGroup<bool>(problem.paramGroup(), "InvasionState.RestrictInvasionToGlobalCapillaryPressure", false); - if (decltype(isValid(Detail::hasGlobalCapillaryPressure())(problem)){}) + if constexpr (hasGlobalCapillaryPressure<Problem>()) { if (restrictToGlobalCapillaryPressure_) std::cout << "\n *** Invasion behavior is restricted by a global capillary pressure defined in the problem! *** \n" << std::endl; @@ -115,11 +110,14 @@ public: for (auto&& scvf : scvfs(fvGeometry)) { // checks if invasion or snap-off occured after Newton iteration step - if (invasionSwitch_(element, elemVolVars, elemFluxVarsCache[scvf])) + if (const auto invasionResult = invasionSwitch_(element, elemVolVars, elemFluxVarsCache[scvf]); invasionResult) { hasChangedInCurrentIteration_ = true; - static constexpr auto cachingEnabled = std::integral_constant<bool, GridFluxVariablesCache::cachingEnabled>{}; - updateChangedFluxVarCache_(element, fvGeometry, gridFluxVarsCache, elemVolVars, scvf, cachingEnabled); + if constexpr (GridFluxVariablesCache::cachingEnabled) + { + const auto eIdx = problem_.gridGeometry().elementMapper().index(element); + gridFluxVarsCache.cache(eIdx, scvf.index()).update(problem_, element, fvGeometry, elemVolVars, scvf, invadedCurrentIteration_[eIdx]); + } } } } @@ -194,36 +192,46 @@ private: //! The switch for determining the invasion state of a pore throat. Called at the end of each Newton step. template<class Element, class ElementVolumeVariables, class FluxVariablesCache> - bool invasionSwitch_(const Element& element, + auto invasionSwitch_(const Element& element, const ElementVolumeVariables& elemVolVars, const FluxVariablesCache& fluxVarsCache) { + using Scalar = typename ElementVolumeVariables::VolumeVariables::PrimaryVariables::value_type; const auto& gridGeometry = problem_.gridGeometry(); const auto& spatialParams = problem_.spatialParams(); const auto eIdx = gridGeometry.elementMapper().index(element); bool invadedBeforeSwitch = invadedCurrentIteration_[eIdx]; bool invadedAfterSwitch = invadedBeforeSwitch; - const auto wPhaseIdx = spatialParams.template wettingPhase<typename ElementVolumeVariables::VolumeVariables::FluidSystem>(element, elemVolVars); + // Result type, containing the local scv index of the pore from which the invasion/snap-off occurred + // Evaluates to 'false' if no invasion/snap-off occurred + struct Result + { + std::uint8_t localScvIdxWithCriticalPc; + Scalar criticalPc; + EventType event = EventType::none; + + operator bool() const + { return event != EventType::none; } + }; // Block non-wetting phase flux out of the outlet - static const bool blockOutlet = getParamFromGroup<bool>(problem_.paramGroup(), "InvasionState.BlockOutletForNonwettingPhase", true); - if (blockOutlet && gridGeometry.throatLabel(eIdx) == Labels::outlet) + static const auto blockNonwettingPhase = getParamFromGroup<std::vector<int>>(problem_.paramGroup(), "InvasionState.BlockNonwettingPhaseAtThroatLabel", std::vector<int>{Labels::outlet}); + if (!blockNonwettingPhase.empty() && std::find(blockNonwettingPhase.begin(), blockNonwettingPhase.end(), gridGeometry.throatLabel(eIdx)) != blockNonwettingPhase.end()) { invadedCurrentIteration_[eIdx] = false; - return false; + return Result{}; // nothing happened } //Determine whether throat gets invaded or snap-off occurs - using Scalar = typename ElementVolumeVariables::VolumeVariables::PrimaryVariables::value_type; const std::array<Scalar, 2> pc = { elemVolVars[0].capillaryPressure(), elemVolVars[1].capillaryPressure() }; const auto pcMax = std::max_element(pc.begin(), pc.end()); const Scalar pcEntry = fluxVarsCache.pcEntry(); const Scalar pcSnapoff = fluxVarsCache.pcSnapoff(); // check if there is a user-specified global capillary pressure which needs to be obeyed - if (maybeRestrictToGlobalCapillaryPressure_(pcEntry, decltype(isValid(Detail::hasGlobalCapillaryPressure())(problem_)){})) + if (maybeRestrictToGlobalCapillaryPressure_(pcEntry)) { if (*pcMax > pcEntry) { @@ -232,7 +240,7 @@ private: } invadedCurrentIteration_[eIdx] = false; - return false; + return Result{}; //nothing happened } if (*pcMax > pcEntry) @@ -242,68 +250,51 @@ private: invadedCurrentIteration_[eIdx] = invadedAfterSwitch; - if (invadedAfterSwitch != invadedBeforeSwitch && verbose_) + if (invadedBeforeSwitch == invadedAfterSwitch) + return Result{}; // nothing happened + else { - const std::array<Scalar, 2> sw = { elemVolVars[0].saturation(wPhaseIdx), elemVolVars[1].saturation(wPhaseIdx) }; - const auto scvIdx = pcMax - pc.begin(); - const auto vIdx = gridGeometry.gridView().indexSet().subIndex(element, scvIdx, 1); - if (!invadedBeforeSwitch && invadedAfterSwitch) - { - std::cout << "Throat " << eIdx << " was invaded from pore " << vIdx << " :"; - std::cout << " pc: " << *pcMax; - std::cout << ", pcEntry: " << spatialParams.pcEntry(element, elemVolVars); - std::cout << ", sw: " << sw[scvIdx] << std::endl; - } - else if (invadedBeforeSwitch && !invadedAfterSwitch) - { - std::cout << "Snap-off occured at: " << eIdx << " from pore " << vIdx << " :"; - std::cout << " pc: " << *pcMax; - std::cout << ", pcSnapoff: " << spatialParams.pcSnapoff(element, elemVolVars); - std::cout << ", sw: " << sw[scvIdx] << std::endl; - } - else - DUNE_THROW(Dune::InvalidStateException, "Invalid Process "); - } - return invadedBeforeSwitch != invadedAfterSwitch; - } + Result result; + result.localScvIdxWithCriticalPc = std::distance(pc.begin(), pcMax); + result.criticalPc = *pcMax; + result.event = !invadedBeforeSwitch && invadedAfterSwitch ? EventType::invasion : EventType::snapOff; - //! Update the fluxVarsCache after an invasion/snap-off occured (only for caching enabled) - template<class Element, class FVElementGeometry, class GridFluxVariablesCache, - class ElementVolumeVariables, class SubControlVolumeFace> - void updateChangedFluxVarCache_(const Element& element, - const FVElementGeometry& fvGeometry, - GridFluxVariablesCache& gridFluxVarsCache, - const ElementVolumeVariables& elemVolVars, - const SubControlVolumeFace& scvf, - std::true_type) - { - const auto eIdx = problem_.gridGeometry().elementMapper().index(element); - gridFluxVarsCache.cache(eIdx, scvf.index()).update(problem_, element, fvGeometry, elemVolVars, scvf, invaded(element)); - } + if (verbose_) + { + const auto wPhaseIdx = spatialParams.template wettingPhase<typename ElementVolumeVariables::VolumeVariables::FluidSystem>(element, elemVolVars); + const std::array sw = { elemVolVars[0].saturation(wPhaseIdx), elemVolVars[1].saturation(wPhaseIdx) }; + const auto vIdx = gridGeometry.gridView().indexSet().subIndex(element, result.localScvIdxWithCriticalPc, 1); + if (result.event == EventType::invasion) + { + std::cout << "Throat " << eIdx << " was invaded from pore " << vIdx << " :"; + std::cout << " pc: " << *pcMax; + std::cout << ", pcEntry: " << spatialParams.pcEntry(element, elemVolVars); + std::cout << ", sw: " << sw[result.localScvIdxWithCriticalPc] << std::endl; + } + else + { + std::cout << "Snap-off occured at throat " << eIdx << " from pore " << vIdx << " :"; + std::cout << " pc: " << *pcMax; + std::cout << ", pcSnapoff: " << spatialParams.pcSnapoff(element, elemVolVars); + std::cout << ", sw: " << sw[result.localScvIdxWithCriticalPc] << std::endl; + } + } - //! Do nothing if the fluxVarCaches are not cached globally - template<class Element, class FVElementGeometry, class GridFluxVariablesCache, - class ElementVolumeVariables, class SubControlVolumeFace> - void updateChangedFluxVarCache_(const Element& element, - const FVElementGeometry& fvGeometry, - GridFluxVariablesCache& gridFluxVarsCache, - const ElementVolumeVariables& elemVolVars, - const SubControlVolumeFace& scvf, - std::false_type) {} + return result; + } + } //! If the user has specified a global capillary pressure, check if it is lower than the given entry capillary pressure. //! This may be needed to exactly reproduce pc-S curves given by static network models. template<class Scalar> - bool maybeRestrictToGlobalCapillaryPressure_(const Scalar pcEntry, std::true_type) const + bool maybeRestrictToGlobalCapillaryPressure_(const Scalar pcEntry) const { - return restrictToGlobalCapillaryPressure_ && (pcEntry > problem_.globalCapillaryPressure()); + if constexpr (hasGlobalCapillaryPressure<Problem>()) + return restrictToGlobalCapillaryPressure_ && (pcEntry > problem_.globalCapillaryPressure()); + else + return false; } - //! Do nothing here. - template<class Scalar> - bool maybeRestrictToGlobalCapillaryPressure_(const Scalar pc, std::false_type) const - { return false; } - std::vector<bool> invadedCurrentIteration_; std::vector<bool> invadedPreviousTimeStep_; bool hasChangedInCurrentIteration_ = false; diff --git a/dumux/porenetwork/2p/model.hh b/dumux/porenetwork/2p/model.hh index 8d676cf2f0e81b14cff8683c430b6961fd2bfcda..16591c003b4b0813a75e0586e7f5a3993b7dffeb 100644 --- a/dumux/porenetwork/2p/model.hh +++ b/dumux/porenetwork/2p/model.hh @@ -130,10 +130,8 @@ struct GridFluxVariablesCache<TypeTag, TTag::PNMTwoP> private: static constexpr bool enableCache = getPropValue<TypeTag, Properties::EnableGridFluxVariablesCache>(); using Problem = GetPropType<TypeTag, Properties::Problem>; - using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices; - using Labels = GetPropType<TypeTag, Properties::Labels>; using FluxVariablesCache = GetPropType<TypeTag, Properties::FluxVariablesCache>; - using Traits = Dumux::PoreNetwork::PNMTwoPDefaultGridFVCTraits<Problem, FluxVariablesCache, Indices, Labels>; + using Traits = Dumux::PoreNetwork::PNMTwoPDefaultGridFVCTraits<Problem, FluxVariablesCache>; public: using type = Dumux::PoreNetwork::PNMTwoPGridFluxVariablesCache<Problem, FluxVariablesCache, enableCache, Traits>; };