// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- // vi: set et ts=4 sw=4 sts=4: /* This file is part of the Open Porous Media project (OPM). OPM 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. OPM 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 OPM. If not, see . Consult the COPYING file in the top-level source directory of this module for the precise wording of the license and the list of copyright holders. */ /*! * \file * \copydoc Opm::EclMaterialLawManager */ #if ! HAVE_ECL_INPUT #error "Eclipse input support in opm-common is required to use the ECL material manager!" #endif #ifndef OPM_ECL_MATERIAL_LAW_MANAGER_HPP #define OPM_ECL_MATERIAL_LAW_MANAGER_HPP #include #include #include #include #include #include #include #include #include #include #if HAVE_OPM_COMMON #include #endif #include #include #include #include #include namespace Opm { /*! * \ingroup fluidmatrixinteractions * * \brief Provides an simple way to create and manage the material law objects * for a complete ECL deck. */ template class EclMaterialLawManager { private: typedef TraitsT Traits; typedef typename Traits::Scalar Scalar; enum { waterPhaseIdx = Traits::wettingPhaseIdx }; enum { oilPhaseIdx = Traits::nonWettingPhaseIdx }; enum { gasPhaseIdx = Traits::gasPhaseIdx }; enum { numPhases = Traits::numPhases }; typedef TwoPhaseMaterialTraits GasOilTraits; typedef TwoPhaseMaterialTraits OilWaterTraits; // the two-phase material law which is defined on effective (unscaled) saturations typedef PiecewiseLinearTwoPhaseMaterial GasOilEffectiveTwoPhaseLaw; typedef PiecewiseLinearTwoPhaseMaterial OilWaterEffectiveTwoPhaseLaw; typedef typename GasOilEffectiveTwoPhaseLaw::Params GasOilEffectiveTwoPhaseParams; typedef typename OilWaterEffectiveTwoPhaseLaw::Params OilWaterEffectiveTwoPhaseParams; // the two-phase material law which is defined on absolute (scaled) saturations typedef EclEpsTwoPhaseLaw GasOilEpsTwoPhaseLaw; typedef EclEpsTwoPhaseLaw OilWaterEpsTwoPhaseLaw; typedef typename GasOilEpsTwoPhaseLaw::Params GasOilEpsTwoPhaseParams; typedef typename OilWaterEpsTwoPhaseLaw::Params OilWaterEpsTwoPhaseParams; // the scaled two-phase material laws with hystersis typedef EclHysteresisTwoPhaseLaw GasOilTwoPhaseLaw; typedef EclHysteresisTwoPhaseLaw OilWaterTwoPhaseLaw; typedef typename GasOilTwoPhaseLaw::Params GasOilTwoPhaseHystParams; typedef typename OilWaterTwoPhaseLaw::Params OilWaterTwoPhaseHystParams; public: // the three-phase material law used by the simulation typedef EclMultiplexerMaterial MaterialLaw; typedef typename MaterialLaw::Params MaterialLawParams; private: // internal typedefs typedef std::vector > GasOilEffectiveParamVector; typedef std::vector > OilWaterEffectiveParamVector; typedef std::vector > > GasOilScalingPointsVector; typedef std::vector > > OilWaterScalingPointsVector; typedef std::vector > > GasOilScalingInfoVector; typedef std::vector > > OilWaterScalingInfoVector; typedef std::vector > GasOilParamVector; typedef std::vector > OilWaterParamVector; typedef std::vector > MaterialLawParamsVector; public: EclMaterialLawManager() {} void initFromDeck(const Opm::Deck& deck, const Opm::EclipseState& eclState, const std::vector& compressedToCartesianElemIdx) { // get the number of saturation regions and the number of cells in the deck const size_t numSatRegions = eclState.runspec().tabdims().getNumSatTables(); // copy the SATNUM grid property. in some cases this is not necessary, but it // should not require much memory anyway... #ifdef ENABLE_3DPROPS_TESTING size_t numCompressedElems = compressedToCartesianElemIdx.size(); satnumRegionArray_.resize(numCompressedElems); if (eclState.fieldProps().has("SATNUM")) { const auto& satnumRawData = eclState.fieldProps().get_global("SATNUM"); for (unsigned elemIdx = 0; elemIdx < numCompressedElems; ++elemIdx) { unsigned cartesianElemIdx = static_cast(compressedToCartesianElemIdx[elemIdx]); satnumRegionArray_[elemIdx] = satnumRawData[cartesianElemIdx] - 1; } } else std::fill(satnumRegionArray_.begin(), satnumRegionArray_.end(), 0); // create the information for the imbibition region (IMBNUM). By default this is // the same as the saturation region (SATNUM) imbnumRegionArray_ = satnumRegionArray_; if (eclState.fieldProps().has("IMBNUM")) { const auto& imbnumRawData = eclState.fieldProps().get_global("IMBNUM"); for (unsigned elemIdx = 0; elemIdx < numCompressedElems; ++elemIdx) { int cartesianElemIdx = compressedToCartesianElemIdx[elemIdx]; imbnumRegionArray_[elemIdx] = imbnumRawData[cartesianElemIdx] - 1; } } #else size_t numCompressedElems = compressedToCartesianElemIdx.size(); satnumRegionArray_.resize(numCompressedElems); if (eclState.get3DProperties().hasDeckIntGridProperty("SATNUM")) { const auto& satnumRawData = eclState.get3DProperties().getIntGridProperty("SATNUM").getData(); for (unsigned elemIdx = 0; elemIdx < numCompressedElems; ++elemIdx) { unsigned cartesianElemIdx = static_cast(compressedToCartesianElemIdx[elemIdx]); satnumRegionArray_[elemIdx] = satnumRawData[cartesianElemIdx] - 1; } } else std::fill(satnumRegionArray_.begin(), satnumRegionArray_.end(), 0); // create the information for the imbibition region (IMBNUM). By default this is // the same as the saturation region (SATNUM) imbnumRegionArray_ = satnumRegionArray_; if (eclState.get3DProperties().hasDeckIntGridProperty("IMBNUM")) { const auto& imbnumRawData = eclState.get3DProperties().getIntGridProperty("IMBNUM").getData(); for (unsigned elemIdx = 0; elemIdx < numCompressedElems; ++elemIdx) { int cartesianElemIdx = compressedToCartesianElemIdx[elemIdx]; imbnumRegionArray_[elemIdx] = imbnumRawData[cartesianElemIdx] - 1; } } #endif readGlobalEpsOptions_(deck, eclState); readGlobalHysteresisOptions_(deck); readGlobalThreePhaseOptions_(deck); unscaledEpsInfo_.resize(numSatRegions); for (unsigned satRegionIdx = 0; satRegionIdx < numSatRegions; ++satRegionIdx) unscaledEpsInfo_[satRegionIdx].extractUnscaled(deck, eclState, satRegionIdx); initParamsForElements_(deck, eclState, compressedToCartesianElemIdx, satnumRegionArray_, imbnumRegionArray_); } /*! * \brief Modify the initial condition according to the SWATINIT keyword. * * The method returns the water saturation which yields a givenn capillary * pressure. The reason this method is not folded directly into initFromDeck() is * that the capillary pressure given depends on the particuars of how the simulator * calculates its initial condition. */ Scalar applySwatinit(unsigned elemIdx, Scalar pcow, Scalar Sw) { auto& elemScaledEpsInfo = *oilWaterScaledEpsInfoDrainage_[elemIdx]; // TODO: Mixed wettability systems - see ecl kw OPTIONS switch 74 if (pcow < 0.0) Sw = elemScaledEpsInfo.Swu; else { if (Sw <= elemScaledEpsInfo.Swl) Sw = elemScaledEpsInfo.Swl; // specify a fluid state which only stores the saturations typedef Opm::SimpleModularFluidState don't care */ /*storePressure=*/false, /*storeTemperature=*/false, /*storeComposition=*/false, /*storeFugacity=*/false, /*storeSaturation=*/true, /*storeDensity=*/false, /*storeViscosity=*/false, /*storeEnthalpy=*/false> FluidState; FluidState fs; fs.setSaturation(waterPhaseIdx, Sw); fs.setSaturation(gasPhaseIdx, 0); fs.setSaturation(oilPhaseIdx, 0); Scalar pc[numPhases] = { 0 }; MaterialLaw::capillaryPressures(pc, materialLawParams(elemIdx), fs); Scalar pcowAtSw = pc[oilPhaseIdx] - pc[waterPhaseIdx]; const Scalar pcowAtSwThreshold = 1.0; //Pascal // avoid divison by very small number if (std::abs(pcowAtSw) > pcowAtSwThreshold) { elemScaledEpsInfo.maxPcow *= pcow/pcowAtSw; auto& elemEclEpsScalingPoints = oilWaterScaledEpsPointsDrainage(elemIdx); elemEclEpsScalingPoints.init(elemScaledEpsInfo, *oilWaterEclEpsConfig_, Opm::EclOilWaterSystem); } } return Sw; } bool enableEndPointScaling() const { return enableEndPointScaling_; } bool enableHysteresis() const { return hysteresisConfig_->enableHysteresis(); } MaterialLawParams& materialLawParams(unsigned elemIdx) { assert(0 <= elemIdx && elemIdx < materialLawParams_.size()); return *materialLawParams_[elemIdx]; } const MaterialLawParams& materialLawParams(unsigned elemIdx) const { assert(0 <= elemIdx && elemIdx < materialLawParams_.size()); return *materialLawParams_[elemIdx]; } /*! * \brief Returns a material parameter object for a given element and saturation region. * * This method changes the saturation table idx in the original material law parameter object. * In the context of ECL reservoir simulators, this is required to properly handle * wells with its own saturation table idx. In order to reset the saturation table idx * in the materialLawparams_ call the method with the cells satRegionIdx */ const MaterialLawParams& connectionMaterialLawParams(unsigned satRegionIdx, unsigned elemIdx) const { MaterialLawParams& mlp = *materialLawParams_[elemIdx]; #if HAVE_OPM_COMMON if (enableHysteresis()) OpmLog::warning("Warning: Using non-default satnum regions for conenction is not tested in combination with hysteresis"); #endif // Currently we don't support COMPIMP. I.e. use the same table lookup for the hysteresis curves. // unsigned impRegionIdx = satRegionIdx; // change the sat table it points to. switch (mlp.approach()) { case EclStone1Approach: { auto& realParams = mlp.template getRealParams(); realParams.oilWaterParams().drainageParams().setUnscaledPoints(oilWaterUnscaledPointsVector_[satRegionIdx]); realParams.oilWaterParams().drainageParams().setEffectiveLawParams(oilWaterEffectiveParamVector_[satRegionIdx]); realParams.gasOilParams().drainageParams().setUnscaledPoints(gasOilUnscaledPointsVector_[satRegionIdx]); realParams.gasOilParams().drainageParams().setEffectiveLawParams(gasOilEffectiveParamVector_[satRegionIdx]); // if (enableHysteresis()) { // realParams.oilWaterParams().imbibitionParams().setUnscaledPoints(oilWaterUnscaledPointsVector_[impRegionIdx]); // realParams.oilWaterParams().imbibitionParams().setEffectiveLawParams(oilWaterEffectiveParamVector_[impRegionIdx]); // realParams.gasOilParams().imbibitionParams().setUnscaledPoints(gasOilUnscaledPointsVector_[impRegionIdx]); // realParams.gasOilParams().imbibitionParams().setEffectiveLawParams(gasOilEffectiveParamVector_[impRegionIdx]); // } } break; case EclStone2Approach: { auto& realParams = mlp.template getRealParams(); realParams.oilWaterParams().drainageParams().setUnscaledPoints(oilWaterUnscaledPointsVector_[satRegionIdx]); realParams.oilWaterParams().drainageParams().setEffectiveLawParams(oilWaterEffectiveParamVector_[satRegionIdx]); realParams.gasOilParams().drainageParams().setUnscaledPoints(gasOilUnscaledPointsVector_[satRegionIdx]); realParams.gasOilParams().drainageParams().setEffectiveLawParams(gasOilEffectiveParamVector_[satRegionIdx]); // if (enableHysteresis()) { // realParams.oilWaterParams().imbibitionParams().setUnscaledPoints(oilWaterUnscaledPointsVector_[impRegionIdx]); // realParams.oilWaterParams().imbibitionParams().setEffectiveLawParams(oilWaterEffectiveParamVector_[impRegionIdx]); // realParams.gasOilParams().imbibitionParams().setUnscaledPoints(gasOilUnscaledPointsVector_[impRegionIdx]); // realParams.gasOilParams().imbibitionParams().setEffectiveLawParams(gasOilEffectiveParamVector_[impRegionIdx]); // } } break; case EclDefaultApproach: { auto& realParams = mlp.template getRealParams(); realParams.oilWaterParams().drainageParams().setUnscaledPoints(oilWaterUnscaledPointsVector_[satRegionIdx]); realParams.oilWaterParams().drainageParams().setEffectiveLawParams(oilWaterEffectiveParamVector_[satRegionIdx]); realParams.gasOilParams().drainageParams().setUnscaledPoints(gasOilUnscaledPointsVector_[satRegionIdx]); realParams.gasOilParams().drainageParams().setEffectiveLawParams(gasOilEffectiveParamVector_[satRegionIdx]); // if (enableHysteresis()) { // realParams.oilWaterParams().imbibitionParams().setUnscaledPoints(oilWaterUnscaledPointsVector_[impRegionIdx]); // realParams.oilWaterParams().imbibitionParams().setEffectiveLawParams(oilWaterEffectiveParamVector_[impRegionIdx]); // realParams.gasOilParams().imbibitionParams().setUnscaledPoints(gasOilUnscaledPointsVector_[impRegionIdx]); // realParams.gasOilParams().imbibitionParams().setEffectiveLawParams(gasOilEffectiveParamVector_[impRegionIdx]); // } } break; case EclTwoPhaseApproach: { auto& realParams = mlp.template getRealParams(); realParams.oilWaterParams().drainageParams().setUnscaledPoints(oilWaterUnscaledPointsVector_[satRegionIdx]); realParams.oilWaterParams().drainageParams().setEffectiveLawParams(oilWaterEffectiveParamVector_[satRegionIdx]); realParams.gasOilParams().drainageParams().setUnscaledPoints(gasOilUnscaledPointsVector_[satRegionIdx]); realParams.gasOilParams().drainageParams().setEffectiveLawParams(gasOilEffectiveParamVector_[satRegionIdx]); // if (enableHysteresis()) { // realParams.oilWaterParams().imbibitionParams().setUnscaledPoints(oilWaterUnscaledPointsVector_[impRegionIdx]); // realParams.oilWaterParams().imbibitionParams().setEffectiveLawParams(oilWaterEffectiveParamVector_[impRegionIdx]); // realParams.gasOilParams().imbibitionParams().setUnscaledPoints(gasOilUnscaledPointsVector_[impRegionIdx]); // realParams.gasOilParams().imbibitionParams().setEffectiveLawParams(gasOilEffectiveParamVector_[impRegionIdx]); // } } break; default: throw std::logic_error("Enum value for material approach unknown!"); } return mlp; } int satnumRegionIdx(unsigned elemIdx) const { return satnumRegionArray_[elemIdx]; } int imbnumRegionIdx(unsigned elemIdx) const { return imbnumRegionArray_[elemIdx]; } std::shared_ptr& materialLawParamsPointerReferenceHack(unsigned elemIdx) { assert(0 <= elemIdx && elemIdx < materialLawParams_.size()); return materialLawParams_[elemIdx]; } template void updateHysteresis(const FluidState& fluidState, unsigned elemIdx) { if (!enableHysteresis()) return; auto threePhaseParams = materialLawParams_[elemIdx]; MaterialLaw::updateHysteresis(*threePhaseParams, fluidState); } void oilWaterHysteresisParams(Scalar& pcSwMdc, Scalar& krnSwMdc, unsigned elemIdx) const { if (!enableHysteresis()) throw std::runtime_error("Cannot get hysteresis parameters if hysteresis not enabled."); const auto& params = materialLawParams(elemIdx); MaterialLaw::oilWaterHysteresisParams(pcSwMdc, krnSwMdc, params); } void setOilWaterHysteresisParams(const Scalar& pcSwMdc, const Scalar& krnSwMdc, unsigned elemIdx) { if (!enableHysteresis()) throw std::runtime_error("Cannot set hysteresis parameters if hysteresis not enabled."); auto& params = materialLawParams(elemIdx); MaterialLaw::setOilWaterHysteresisParams(pcSwMdc, krnSwMdc, params); } void gasOilHysteresisParams(Scalar& pcSwMdc, Scalar& krnSwMdc, unsigned elemIdx) const { if (!enableHysteresis()) throw std::runtime_error("Cannot get hysteresis parameters if hysteresis not enabled."); const auto& params = materialLawParams(elemIdx); MaterialLaw::gasOilHysteresisParams(pcSwMdc, krnSwMdc, params); } void setGasOilHysteresisParams(const Scalar& pcSwMdc, const Scalar& krnSwMdc, unsigned elemIdx) { if (!enableHysteresis()) throw std::runtime_error("Cannot set hysteresis parameters if hysteresis not enabled."); auto& params = materialLawParams(elemIdx); MaterialLaw::setGasOilHysteresisParams(pcSwMdc, krnSwMdc, params); } EclEpsScalingPoints& oilWaterScaledEpsPointsDrainage(unsigned elemIdx) { auto& materialParams = *materialLawParams_[elemIdx]; switch (materialParams.approach()) { case EclStone1Approach: { auto& realParams = materialParams.template getRealParams(); return realParams.oilWaterParams().drainageParams().scaledPoints(); } case EclStone2Approach: { auto& realParams = materialParams.template getRealParams(); return realParams.oilWaterParams().drainageParams().scaledPoints(); } case EclDefaultApproach: { auto& realParams = materialParams.template getRealParams(); return realParams.oilWaterParams().drainageParams().scaledPoints(); } case EclTwoPhaseApproach: { auto& realParams = materialParams.template getRealParams(); return realParams.oilWaterParams().drainageParams().scaledPoints(); } default: throw std::logic_error("Enum value for material approach unknown!"); } } const Opm::EclEpsScalingPointsInfo& oilWaterScaledEpsInfoDrainage(size_t elemIdx) const { return *oilWaterScaledEpsInfoDrainage_[elemIdx]; } std::shared_ptr >& oilWaterScaledEpsInfoDrainagePointerReferenceHack(unsigned elemIdx) { return oilWaterScaledEpsInfoDrainage_[elemIdx]; } private: void readGlobalEpsOptions_(const Opm::Deck& deck, const Opm::EclipseState& eclState) { oilWaterEclEpsConfig_ = std::make_shared(); oilWaterEclEpsConfig_-> initFromDeck(deck, eclState, Opm::EclOilWaterSystem); enableEndPointScaling_ = deck.hasKeyword("ENDSCALE"); } void readGlobalHysteresisOptions_(const Opm::Deck& deck) { hysteresisConfig_ = std::make_shared(); hysteresisConfig_->initFromDeck(deck); } void readGlobalThreePhaseOptions_(const Opm::Deck& deck) { bool gasEnabled = deck.hasKeyword("GAS"); bool oilEnabled = deck.hasKeyword("OIL"); bool waterEnabled = deck.hasKeyword("WATER"); int numEnabled = (gasEnabled?1:0) + (oilEnabled?1:0) + (waterEnabled?1:0); if (numEnabled == 0) { throw std::runtime_error("At least one fluid phase must be enabled. (Is: "+std::to_string(numEnabled)+")"); } else if (numEnabled == 1) { threePhaseApproach_ = Opm::EclMultiplexerApproach::EclOnePhaseApproach; } else if ( numEnabled == 2) { threePhaseApproach_ = Opm::EclTwoPhaseApproach; if (!gasEnabled) twoPhaseApproach_ = Opm::EclTwoPhaseOilWater; else if (!oilEnabled) twoPhaseApproach_ = Opm::EclTwoPhaseGasWater; else if (!waterEnabled) twoPhaseApproach_ = Opm::EclTwoPhaseGasOil; } else { assert(numEnabled == 3); threePhaseApproach_ = Opm::EclDefaultApproach; if (deck.hasKeyword("STONE") || deck.hasKeyword("STONE2")) threePhaseApproach_ = Opm::EclStone2Approach; else if (deck.hasKeyword("STONE1")) threePhaseApproach_ = Opm::EclStone1Approach; } } void initParamsForElements_(const Deck& deck, const EclipseState& eclState, const std::vector& compressedToCartesianElemIdx, const std::vector& satnumRegionArray, const std::vector& imbnumRegionArray) { const size_t numSatRegions = eclState.runspec().tabdims().getNumSatTables(); unsigned numCompressedElems = static_cast(compressedToCartesianElemIdx.size()); // read the end point scaling configuration. this needs to be done only once per // deck. auto gasOilConfig = std::make_shared(); auto oilWaterConfig = std::make_shared(); gasOilConfig->initFromDeck(deck, eclState, Opm::EclGasOilSystem); oilWaterConfig->initFromDeck(deck, eclState, Opm::EclOilWaterSystem); // read the saturation region specific parameters from the deck gasOilUnscaledPointsVector_.resize(numSatRegions); oilWaterUnscaledPointsVector_.resize(numSatRegions); gasOilEffectiveParamVector_.resize(numSatRegions); oilWaterEffectiveParamVector_.resize(numSatRegions); for (unsigned satRegionIdx = 0; satRegionIdx < numSatRegions; ++satRegionIdx) { // unscaled points for end-point scaling readGasOilUnscaledPoints_(gasOilUnscaledPointsVector_, gasOilConfig, deck, eclState, satRegionIdx); readOilWaterUnscaledPoints_(oilWaterUnscaledPointsVector_, oilWaterConfig, deck, eclState, satRegionIdx); // the parameters for the effective two-phase matererial laws readGasOilEffectiveParameters_(gasOilEffectiveParamVector_, deck, eclState, satRegionIdx); readOilWaterEffectiveParameters_(oilWaterEffectiveParamVector_, deck, eclState, satRegionIdx); // read the end point scaling info for the saturation region unscaledEpsInfo_[satRegionIdx].extractUnscaled(deck, eclState, satRegionIdx); } // read the scaled end point scaling parameters which are specific for each // element GasOilScalingInfoVector gasOilScaledInfoVector(numCompressedElems); oilWaterScaledEpsInfoDrainage_.resize(numCompressedElems); GasOilScalingInfoVector gasOilScaledImbInfoVector; OilWaterScalingInfoVector oilWaterScaledImbInfoVector; GasOilScalingPointsVector gasOilScaledPointsVector(numCompressedElems); GasOilScalingPointsVector oilWaterScaledEpsPointsDrainage(numCompressedElems); GasOilScalingPointsVector gasOilScaledImbPointsVector; OilWaterScalingPointsVector oilWaterScaledImbPointsVector; if (enableHysteresis()) { gasOilScaledImbInfoVector.resize(numCompressedElems); gasOilScaledImbPointsVector.resize(numCompressedElems); oilWaterScaledImbInfoVector.resize(numCompressedElems); oilWaterScaledImbPointsVector.resize(numCompressedElems); } EclEpsGridProperties epsGridProperties(eclState, false, compressedToCartesianElemIdx); for (unsigned elemIdx = 0; elemIdx < numCompressedElems; ++elemIdx) { readGasOilScaledPoints_(gasOilScaledInfoVector, gasOilScaledPointsVector, gasOilConfig, eclState, epsGridProperties, elemIdx); readOilWaterScaledPoints_(oilWaterScaledEpsInfoDrainage_, oilWaterScaledEpsPointsDrainage, oilWaterConfig, eclState, epsGridProperties, elemIdx); } if (enableHysteresis()) { EclEpsGridProperties epsImbGridProperties(eclState, true, compressedToCartesianElemIdx); for (unsigned elemIdx = 0; elemIdx < numCompressedElems; ++elemIdx) { readGasOilScaledPoints_(gasOilScaledImbInfoVector, gasOilScaledImbPointsVector, gasOilConfig, eclState, epsImbGridProperties, elemIdx); readOilWaterScaledPoints_(oilWaterScaledImbInfoVector, oilWaterScaledImbPointsVector, oilWaterConfig, eclState, epsImbGridProperties, elemIdx); } } // create the parameter objects for the two-phase laws GasOilParamVector gasOilParams(numCompressedElems); OilWaterParamVector oilWaterParams(numCompressedElems); GasOilParamVector gasOilImbParams; OilWaterParamVector oilWaterImbParams; if (enableHysteresis()) { gasOilImbParams.resize(numCompressedElems); oilWaterImbParams.resize(numCompressedElems); } bool hasGas = deck.hasKeyword("GAS"); bool hasOil = deck.hasKeyword("OIL"); bool hasWater = deck.hasKeyword("WATER"); assert(numCompressedElems == satnumRegionArray.size()); assert(!enableHysteresis() || numCompressedElems == imbnumRegionArray.size()); for (unsigned elemIdx = 0; elemIdx < numCompressedElems; ++elemIdx) { unsigned satRegionIdx = static_cast(satnumRegionArray[elemIdx]); gasOilParams[elemIdx] = std::make_shared(); oilWaterParams[elemIdx] = std::make_shared(); gasOilParams[elemIdx]->setConfig(hysteresisConfig_); oilWaterParams[elemIdx]->setConfig(hysteresisConfig_); if (hasGas && hasOil) { auto gasOilDrainParams = std::make_shared(); gasOilDrainParams->setConfig(gasOilConfig); gasOilDrainParams->setUnscaledPoints(gasOilUnscaledPointsVector_[satRegionIdx]); gasOilDrainParams->setScaledPoints(gasOilScaledPointsVector[elemIdx]); gasOilDrainParams->setEffectiveLawParams(gasOilEffectiveParamVector_[satRegionIdx]); gasOilDrainParams->finalize(); gasOilParams[elemIdx]->setDrainageParams(gasOilDrainParams, *gasOilScaledInfoVector[elemIdx], EclGasOilSystem); } if (hasOil && hasWater) { auto oilWaterDrainParams = std::make_shared(); oilWaterDrainParams->setConfig(oilWaterConfig); oilWaterDrainParams->setUnscaledPoints(oilWaterUnscaledPointsVector_[satRegionIdx]); oilWaterDrainParams->setScaledPoints(oilWaterScaledEpsPointsDrainage[elemIdx]); oilWaterDrainParams->setEffectiveLawParams(oilWaterEffectiveParamVector_[satRegionIdx]); oilWaterDrainParams->finalize(); oilWaterParams[elemIdx]->setDrainageParams(oilWaterDrainParams, *oilWaterScaledEpsInfoDrainage_[elemIdx], EclOilWaterSystem); } if (enableHysteresis()) { unsigned imbRegionIdx = imbnumRegionArray[elemIdx]; if (hasGas && hasOil) { auto gasOilImbParamsHyst = std::make_shared(); gasOilImbParamsHyst->setConfig(gasOilConfig); gasOilImbParamsHyst->setUnscaledPoints(gasOilUnscaledPointsVector_[imbRegionIdx]); gasOilImbParamsHyst->setScaledPoints(gasOilScaledImbPointsVector[elemIdx]); gasOilImbParamsHyst->setEffectiveLawParams(gasOilEffectiveParamVector_[imbRegionIdx]); gasOilImbParamsHyst->finalize(); gasOilParams[elemIdx]->setImbibitionParams(gasOilImbParamsHyst, *gasOilScaledImbInfoVector[elemIdx], EclGasOilSystem); } if (hasOil && hasWater) { auto oilWaterImbParamsHyst = std::make_shared(); oilWaterImbParamsHyst->setConfig(oilWaterConfig); oilWaterImbParamsHyst->setUnscaledPoints(oilWaterUnscaledPointsVector_[imbRegionIdx]); oilWaterImbParamsHyst->setScaledPoints(oilWaterScaledImbPointsVector[elemIdx]); oilWaterImbParamsHyst->setEffectiveLawParams(oilWaterEffectiveParamVector_[imbRegionIdx]); oilWaterImbParamsHyst->finalize(); oilWaterParams[elemIdx]->setImbibitionParams(oilWaterImbParamsHyst, *gasOilScaledImbInfoVector[elemIdx], EclGasOilSystem); } } if (hasGas && hasOil) gasOilParams[elemIdx]->finalize(); if (hasOil && hasWater) oilWaterParams[elemIdx]->finalize(); } // create the parameter objects for the three-phase law materialLawParams_.resize(numCompressedElems); for (unsigned elemIdx = 0; elemIdx < numCompressedElems; ++elemIdx) { materialLawParams_[elemIdx] = std::make_shared(); unsigned satRegionIdx = static_cast(satnumRegionArray[elemIdx]); initThreePhaseParams_(deck, eclState, *materialLawParams_[elemIdx], satRegionIdx, *oilWaterScaledEpsInfoDrainage_[elemIdx], oilWaterParams[elemIdx], gasOilParams[elemIdx]); materialLawParams_[elemIdx]->finalize(); } } // The saturation function family. // If SWOF and SGOF are specified in the deck it return FamilyI // If SWFN, SGFN and SOF3 are specified in the deck it return FamilyII // If keywords are missing or mixed, an error is given. enum SaturationFunctionFamily { noFamily, FamilyI, FamilyII }; SaturationFunctionFamily getSaturationFunctionFamily(const Opm::Deck& deck, const Opm::EclipseState& eclState) const { const auto& tableManager = eclState.getTableManager(); const TableContainer& swofTables = tableManager.getSwofTables(); const TableContainer& slgofTables= tableManager.getSlgofTables(); const TableContainer& sgofTables = tableManager.getSgofTables(); const TableContainer& swfnTables = tableManager.getSwfnTables(); const TableContainer& sgfnTables = tableManager.getSgfnTables(); const TableContainer& sof3Tables = tableManager.getSof3Tables(); const TableContainer& sof2Tables = tableManager.getSof2Tables(); bool hasGas = deck.hasKeyword("GAS"); bool hasOil = deck.hasKeyword("OIL"); bool hasWater = deck.hasKeyword("WATER"); bool family1 = false; bool family2 = false; if (!hasGas) { // oil-water case family1 = !swofTables.empty(); family2 = !swfnTables.empty() && !sof2Tables.empty(); } else if (!hasWater) { // oil-gas case family1 = !sgofTables.empty(); family2 = !sgfnTables.empty() && !sof2Tables.empty(); } else if (!hasOil) { // water-gas case throw std::runtime_error("water-gas two-phase simulations are currently not supported"); } else { // three-phase case family1 = (!sgofTables.empty() || !slgofTables.empty()) && !swofTables.empty(); family2 = !swfnTables.empty() && !sgfnTables.empty() && !sof3Tables.empty(); } if (family1 && family2) throw std::invalid_argument("Saturation families should not be mixed \n" "Use either SGOF and SWOF or SGFN, SWFN and SOF3"); if (!family1 && !family2) throw std::invalid_argument("Saturations function must be specified using either " "family 1 or family 2 keywords \n" "Use either SGOF and SWOF or SGFN, SWFN and SOF3" ); if (family1 && !family2) return SaturationFunctionFamily::FamilyI; else if (family2 && !family1) return SaturationFunctionFamily::FamilyII; return SaturationFunctionFamily::noFamily; // no family or two families } template void readGasOilEffectiveParameters_(Container& dest, const Opm::Deck& deck, const Opm::EclipseState& eclState, unsigned satRegionIdx) { bool hasGas = deck.hasKeyword("GAS"); bool hasOil = deck.hasKeyword("OIL"); if (!hasGas || !hasOil) // we don't read anything if either the gas or the oil phase is not active return; dest[satRegionIdx] = std::make_shared(); auto& effParams = *dest[satRegionIdx]; // the situation for the gas phase is complicated that all saturations are // shifted by the connate water saturation. Scalar Swco = unscaledEpsInfo_[satRegionIdx].Swl; const auto& tableManager = eclState.getTableManager(); switch (getSaturationFunctionFamily(deck, eclState)) { case FamilyI: { const TableContainer& sgofTables = tableManager.getSgofTables(); const TableContainer& slgofTables = tableManager.getSlgofTables(); if (!sgofTables.empty()) readGasOilEffectiveParametersSgof_(effParams, Swco, sgofTables.getTable(satRegionIdx)); else if (!slgofTables.empty()) readGasOilEffectiveParametersSlgof_(effParams, Swco, slgofTables.getTable(satRegionIdx)); break; } case FamilyII: { const SgfnTable& sgfnTable = tableManager.getSgfnTables().getTable( satRegionIdx ); bool hasWater = deck.hasKeyword("WATER"); if (!hasWater) { // oil and gas case const Sof2Table& sof2Table = tableManager.getSof2Tables().getTable( satRegionIdx ); readGasOilEffectiveParametersFamily2_(effParams, Swco, sof2Table, sgfnTable); } else { const Sof3Table& sof3Table = tableManager.getSof3Tables().getTable( satRegionIdx ); readGasOilEffectiveParametersFamily2_(effParams, Swco, sof3Table, sgfnTable); } break; } //default: case noFamily: throw std::domain_error("No valid saturation keyword family specified"); } } void readGasOilEffectiveParametersSgof_(GasOilEffectiveTwoPhaseParams& effParams, Scalar Swco, const Opm::SgofTable& sgofTable) { // convert the saturations of the SGOF keyword from gas to oil saturations std::vector SoSamples(sgofTable.numRows()); std::vector SoKroSamples(sgofTable.numRows()); for (size_t sampleIdx = 0; sampleIdx < sgofTable.numRows(); ++ sampleIdx) { SoSamples[sampleIdx] = 1 - sgofTable.get("SG", sampleIdx); SoKroSamples[sampleIdx] = SoSamples[sampleIdx] - Swco; } effParams.setKrwSamples(SoKroSamples, sgofTable.getColumn("KROG").vectorCopy()); effParams.setKrnSamples(SoSamples, sgofTable.getColumn("KRG").vectorCopy()); effParams.setPcnwSamples(SoSamples, sgofTable.getColumn("PCOG").vectorCopy()); effParams.finalize(); } void readGasOilEffectiveParametersSlgof_(GasOilEffectiveTwoPhaseParams& effParams, Scalar Swco, const Opm::SlgofTable& slgofTable) { // convert the saturations of the SLGOF keyword from "liquid" to oil saturations std::vector SoSamples(slgofTable.numRows()); std::vector SoKroSamples(slgofTable.numRows()); for (size_t sampleIdx = 0; sampleIdx < slgofTable.numRows(); ++ sampleIdx) { SoSamples[sampleIdx] = slgofTable.get("SL", sampleIdx); SoKroSamples[sampleIdx] = slgofTable.get("SL", sampleIdx) - Swco; } effParams.setKrwSamples(SoKroSamples, slgofTable.getColumn("KROG").vectorCopy()); effParams.setKrnSamples(SoSamples, slgofTable.getColumn("KRG").vectorCopy()); effParams.setPcnwSamples(SoSamples, slgofTable.getColumn("PCOG").vectorCopy()); effParams.finalize(); } void readGasOilEffectiveParametersFamily2_(GasOilEffectiveTwoPhaseParams& effParams, Scalar /* Swco */, const Opm::Sof3Table& sof3Table, const Opm::SgfnTable& sgfnTable) { // convert the saturations of the SGFN keyword from gas to oil saturations std::vector SoSamples(sgfnTable.numRows()); std::vector SoColumn = sof3Table.getColumn("SO").vectorCopy(); for (size_t sampleIdx = 0; sampleIdx < sgfnTable.numRows(); ++ sampleIdx) { SoSamples[sampleIdx] = 1 - sgfnTable.get("SG", sampleIdx); } effParams.setKrwSamples(SoColumn, sof3Table.getColumn("KROG").vectorCopy()); effParams.setKrnSamples(SoSamples, sgfnTable.getColumn("KRG").vectorCopy()); effParams.setPcnwSamples(SoSamples, sgfnTable.getColumn("PCOG").vectorCopy()); effParams.finalize(); } void readGasOilEffectiveParametersFamily2_(GasOilEffectiveTwoPhaseParams& effParams, Scalar /* Swco */, const Opm::Sof2Table& sof2Table, const Opm::SgfnTable& sgfnTable) { // convert the saturations of the SGFN keyword from gas to oil saturations std::vector SoSamples(sgfnTable.numRows()); std::vector SoColumn = sof2Table.getColumn("SO").vectorCopy(); for (size_t sampleIdx = 0; sampleIdx < sgfnTable.numRows(); ++ sampleIdx) { SoSamples[sampleIdx] = 1 - sgfnTable.get("SG", sampleIdx); } effParams.setKrwSamples(SoColumn, sof2Table.getColumn("KRO").vectorCopy()); effParams.setKrnSamples(SoSamples, sgfnTable.getColumn("KRG").vectorCopy()); effParams.setPcnwSamples(SoSamples, sgfnTable.getColumn("PCOG").vectorCopy()); effParams.finalize(); } template void readOilWaterEffectiveParameters_(Container& dest, const Opm::Deck& deck, const Opm::EclipseState& eclState, unsigned satRegionIdx) { bool hasWater = deck.hasKeyword("WATER"); bool hasOil = deck.hasKeyword("OIL"); if (!hasOil || !hasWater) // we don't read anything if either the water or the oil phase is not active return; dest[satRegionIdx] = std::make_shared(); const auto& tableManager = eclState.getTableManager(); auto& effParams = *dest[satRegionIdx]; switch (getSaturationFunctionFamily(deck, eclState)) { case FamilyI: { const auto& swofTable = tableManager.getSwofTables().getTable(satRegionIdx); std::vector SwColumn = swofTable.getColumn("SW").vectorCopy(); effParams.setKrwSamples(SwColumn, swofTable.getColumn("KRW").vectorCopy()); effParams.setKrnSamples(SwColumn, swofTable.getColumn("KROW").vectorCopy()); effParams.setPcnwSamples(SwColumn, swofTable.getColumn("PCOW").vectorCopy()); effParams.finalize(); break; } case FamilyII: { const auto& swfnTable = tableManager.getSwfnTables().getTable(satRegionIdx); const auto& sof3Table = tableManager.getSof3Tables().getTable(satRegionIdx); std::vector SwColumn = swfnTable.getColumn("SW").vectorCopy(); // convert the saturations of the SOF3 keyword from oil to water saturations std::vector SwSamples(sof3Table.numRows()); for (size_t sampleIdx = 0; sampleIdx < sof3Table.numRows(); ++ sampleIdx) SwSamples[sampleIdx] = 1 - sof3Table.get("SO", sampleIdx); effParams.setKrwSamples(SwColumn, swfnTable.getColumn("KRW").vectorCopy()); effParams.setKrnSamples(SwSamples, sof3Table.getColumn("KROW").vectorCopy()); effParams.setPcnwSamples(SwColumn, swfnTable.getColumn("PCOW").vectorCopy()); effParams.finalize(); break; } case noFamily: //default: throw std::domain_error("No valid saturation keyword family specified"); } } template void readGasOilUnscaledPoints_(Container& dest, std::shared_ptr config, const Opm::Deck& deck, const Opm::EclipseState& /* eclState */, unsigned satRegionIdx) { bool hasGas = deck.hasKeyword("GAS"); bool hasOil = deck.hasKeyword("OIL"); if (!hasGas || !hasOil) // we don't read anything if either the gas or the oil phase is not active return; dest[satRegionIdx] = std::make_shared >(); dest[satRegionIdx]->init(unscaledEpsInfo_[satRegionIdx], *config, EclGasOilSystem); } template void readOilWaterUnscaledPoints_(Container& dest, std::shared_ptr config, const Opm::Deck& deck, const Opm::EclipseState& /* eclState */, unsigned satRegionIdx) { bool hasWater = deck.hasKeyword("WATER"); bool hasOil = deck.hasKeyword("OIL"); if (!hasOil || !hasWater) // we don't read anything if either the water or the oil phase is not active return; dest[satRegionIdx] = std::make_shared >(); dest[satRegionIdx]->init(unscaledEpsInfo_[satRegionIdx], *config, EclOilWaterSystem); } template void readGasOilScaledPoints_(InfoContainer& destInfo, PointsContainer& destPoints, std::shared_ptr config, const Opm::EclipseState& eclState, const EclEpsGridProperties& epsGridProperties, unsigned elemIdx) { unsigned satRegionIdx = epsGridProperties.satRegion( elemIdx ); destInfo[elemIdx] = std::make_shared >(unscaledEpsInfo_[satRegionIdx]); destInfo[elemIdx]->extractScaled(eclState, epsGridProperties, elemIdx); destPoints[elemIdx] = std::make_shared >(); destPoints[elemIdx]->init(*destInfo[elemIdx], *config, EclGasOilSystem); } template void readOilWaterScaledPoints_(InfoContainer& destInfo, PointsContainer& destPoints, std::shared_ptr config, const Opm::EclipseState& eclState, const EclEpsGridProperties& epsGridProperties, unsigned elemIdx) { unsigned satRegionIdx = epsGridProperties.satRegion( elemIdx ); destInfo[elemIdx] = std::make_shared >(unscaledEpsInfo_[satRegionIdx]); destInfo[elemIdx]->extractScaled(eclState, epsGridProperties, elemIdx); destPoints[elemIdx] = std::make_shared >(); destPoints[elemIdx]->init(*destInfo[elemIdx], *config, EclOilWaterSystem); } void initThreePhaseParams_(const Opm::Deck& deck, const Opm::EclipseState& /* eclState */, MaterialLawParams& materialParams, unsigned satRegionIdx, const EclEpsScalingPointsInfo& epsInfo, std::shared_ptr oilWaterParams, std::shared_ptr gasOilParams) { materialParams.setApproach(threePhaseApproach_); switch (materialParams.approach()) { case EclStone1Approach: { auto& realParams = materialParams.template getRealParams(); realParams.setGasOilParams(gasOilParams); realParams.setOilWaterParams(oilWaterParams); realParams.setSwl(epsInfo.Swl); if (deck.hasKeyword("STONE1EX")) { Scalar eta = deck.getKeyword("STONE1EX").getRecord(satRegionIdx).getItem(0).getSIDouble(0); realParams.setEta(eta); } else realParams.setEta(1.0); realParams.finalize(); break; } case EclStone2Approach: { auto& realParams = materialParams.template getRealParams(); realParams.setGasOilParams(gasOilParams); realParams.setOilWaterParams(oilWaterParams); realParams.setSwl(epsInfo.Swl); realParams.finalize(); break; } case EclDefaultApproach: { auto& realParams = materialParams.template getRealParams(); realParams.setGasOilParams(gasOilParams); realParams.setOilWaterParams(oilWaterParams); realParams.setSwl(epsInfo.Swl); realParams.finalize(); break; } case EclTwoPhaseApproach: { auto& realParams = materialParams.template getRealParams(); realParams.setGasOilParams(gasOilParams); realParams.setOilWaterParams(oilWaterParams); realParams.setApproach(twoPhaseApproach_); realParams.finalize(); break; } case EclOnePhaseApproach: { // Nothing to do, no parameters. break; } } } bool enableEndPointScaling_; std::shared_ptr hysteresisConfig_; std::shared_ptr oilWaterEclEpsConfig_; std::vector> unscaledEpsInfo_; OilWaterScalingInfoVector oilWaterScaledEpsInfoDrainage_; GasOilScalingPointsVector gasOilUnscaledPointsVector_; OilWaterScalingPointsVector oilWaterUnscaledPointsVector_; GasOilEffectiveParamVector gasOilEffectiveParamVector_; OilWaterEffectiveParamVector oilWaterEffectiveParamVector_; Opm::EclMultiplexerApproach threePhaseApproach_; // this attribute only makes sense for twophase simulations! enum EclTwoPhaseApproach twoPhaseApproach_; std::vector > materialLawParams_; std::vector satnumRegionArray_; std::vector imbnumRegionArray_; }; } // namespace Opm #endif