// -*- 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 * * \brief Contains the classes required to extend the black-oil model by solvent component. * For details, refer: * [*] T.H. Sandve, O. Sævareid and I. Aavatsmark: “Improved Extended Blackoil Formulation * for CO2 EOR Simulations.” in ECMOR XVII – The 17th European Conference on the * Mathematics of Oil Recovery, September 2020. */ #ifndef EWOMS_BLACK_OIL_EXTBO_MODULE_HH #define EWOMS_BLACK_OIL_EXTBO_MODULE_HH #include "blackoilproperties.hh" #include //#include //TODO: Missing ... #if HAVE_ECL_INPUT #include #include #include #include #include #include #include #include #include #endif #include #include #include #include #include namespace Opm { /*! * \ingroup BlackOil * \brief Contains the high level supplements required to extend the black oil * model. */ template ()> class BlackOilExtboModule { using Scalar = GetPropType; using Evaluation = GetPropType; using PrimaryVariables = GetPropType; using IntensiveQuantities = GetPropType; using ExtensiveQuantities = GetPropType; using ElementContext = GetPropType; using FluidSystem = GetPropType; using Model = GetPropType; using Simulator = GetPropType; using EqVector = GetPropType; using RateVector = GetPropType; using Indices = GetPropType; using Toolbox = MathToolbox; using TabulatedFunction = typename BlackOilExtboParams::TabulatedFunction; using Tabulated2DFunction = typename BlackOilExtboParams::Tabulated2DFunction; static constexpr unsigned zFractionIdx = Indices::zFractionIdx; static constexpr unsigned contiZfracEqIdx = Indices::contiZfracEqIdx; static constexpr unsigned enableExtbo = enableExtboV; static constexpr unsigned numEq = getPropValue(); static constexpr unsigned numPhases = FluidSystem::numPhases; static constexpr unsigned gasPhaseIdx = FluidSystem::gasPhaseIdx; static constexpr unsigned oilPhaseIdx = FluidSystem::oilPhaseIdx; static constexpr unsigned waterPhaseIdx = FluidSystem::waterPhaseIdx; static constexpr bool blackoilConserveSurfaceVolume = getPropValue(); public: #if HAVE_ECL_INPUT /*! * \brief Initialize all internal data structures needed by the solvent module */ static void initFromState(const EclipseState& eclState) { // some sanity checks: if extended BO is enabled, the PVTSOL keyword must be // present, if extended BO is disabled the keyword must not be present. if (enableExtbo && !eclState.runspec().phases().active(Phase::ZFRACTION)) throw std::runtime_error("Extended black oil treatment requested at compile " "time, but the deck does not contain the PVTSOL keyword"); else if (!enableExtbo && eclState.runspec().phases().active(Phase::ZFRACTION)) throw std::runtime_error("Extended black oil treatment disabled at compile time, but the deck " "contains the PVTSOL keyword"); if (!eclState.runspec().phases().active(Phase::ZFRACTION)) return; // solvent treatment is supposed to be disabled // pvt properties from kw PVTSOL: const auto& tableManager = eclState.getTableManager(); const auto& pvtsolTables = tableManager.getPvtsolTables(); size_t numPvtRegions = pvtsolTables.size(); params_.BO_.resize(numPvtRegions, Tabulated2DFunction{Tabulated2DFunction::InterpolationPolicy::LeftExtreme}); params_.BG_.resize(numPvtRegions, Tabulated2DFunction{Tabulated2DFunction::InterpolationPolicy::LeftExtreme}); params_.RS_.resize(numPvtRegions, Tabulated2DFunction{Tabulated2DFunction::InterpolationPolicy::LeftExtreme}); params_.RV_.resize(numPvtRegions, Tabulated2DFunction{Tabulated2DFunction::InterpolationPolicy::LeftExtreme}); params_.X_.resize(numPvtRegions, Tabulated2DFunction{Tabulated2DFunction::InterpolationPolicy::LeftExtreme}); params_.Y_.resize(numPvtRegions, Tabulated2DFunction{Tabulated2DFunction::InterpolationPolicy::LeftExtreme}); params_.VISCO_.resize(numPvtRegions, Tabulated2DFunction{Tabulated2DFunction::InterpolationPolicy::LeftExtreme}); params_.VISCG_.resize(numPvtRegions, Tabulated2DFunction{Tabulated2DFunction::InterpolationPolicy::LeftExtreme}); params_.PBUB_RS_.resize(numPvtRegions, Tabulated2DFunction{Tabulated2DFunction::InterpolationPolicy::LeftExtreme}); params_.PBUB_RV_.resize(numPvtRegions, Tabulated2DFunction{Tabulated2DFunction::InterpolationPolicy::LeftExtreme}); params_.zLim_.resize(numPvtRegions); const bool extractCmpFromPvt = true; //: Default values used in [*] params_.oilCmp_.resize(numPvtRegions); params_.gasCmp_.resize(numPvtRegions); for (unsigned regionIdx = 0; regionIdx < numPvtRegions; ++ regionIdx) { const auto& pvtsolTable = pvtsolTables[regionIdx]; const auto& saturatedTable = pvtsolTable.getSaturatedTable(); assert(saturatedTable.numRows() > 1); std::vector oilCmp(saturatedTable.numRows(), -4.0e-9); //Default values used in [*] std::vector gasCmp(saturatedTable.numRows(), -0.08); //-------------"------------- params_.zLim_[regionIdx] = 0.7; //-------------"------------- std::vector zArg(saturatedTable.numRows(), 0.0); for (unsigned outerIdx = 0; outerIdx < saturatedTable.numRows(); ++ outerIdx) { Scalar ZCO2 = saturatedTable.get("ZCO2", outerIdx); zArg[outerIdx] = ZCO2; params_.BO_[regionIdx].appendXPos(ZCO2); params_.BG_[regionIdx].appendXPos(ZCO2); params_.RS_[regionIdx].appendXPos(ZCO2); params_.RV_[regionIdx].appendXPos(ZCO2); params_.X_[regionIdx].appendXPos(ZCO2); params_.Y_[regionIdx].appendXPos(ZCO2); params_.VISCO_[regionIdx].appendXPos(ZCO2); params_.VISCG_[regionIdx].appendXPos(ZCO2); params_.PBUB_RS_[regionIdx].appendXPos(ZCO2); params_.PBUB_RV_[regionIdx].appendXPos(ZCO2); const auto& underSaturatedTable = pvtsolTable.getUnderSaturatedTable(outerIdx); size_t numRows = underSaturatedTable.numRows(); Scalar bo0=0.0; Scalar po0=0.0; for (unsigned innerIdx = 0; innerIdx < numRows; ++ innerIdx) { Scalar po = underSaturatedTable.get("P", innerIdx); Scalar bo = underSaturatedTable.get("B_O", innerIdx); Scalar bg = underSaturatedTable.get("B_G", innerIdx); Scalar rs = underSaturatedTable.get("RS", innerIdx)+innerIdx*1.0e-10; Scalar rv = underSaturatedTable.get("RV", innerIdx)+innerIdx*1.0e-10; Scalar xv = underSaturatedTable.get("XVOL", innerIdx); Scalar yv = underSaturatedTable.get("YVOL", innerIdx); Scalar mo = underSaturatedTable.get("MU_O", innerIdx); Scalar mg = underSaturatedTable.get("MU_G", innerIdx); if (bo0 > bo) { // This is undersaturated oil-phase for ZCO2 <= zLim ... // Here we assume tabulated bo to decay beyond boiling point if (extractCmpFromPvt) { Scalar cmpFactor = (bo-bo0)/(po-po0); oilCmp[outerIdx] = cmpFactor; params_.zLim_[regionIdx] = ZCO2; //std::cout << "### cmpFactorOil: " << cmpFactor << " zLim: " << zLim_[regionIdx] << std::endl; } break; } else if (bo0 == bo) { // This is undersaturated gas-phase for ZCO2 > zLim ... // Here we assume tabulated bo to be constant extrapolated beyond dew point if (innerIdx+1 < numRows && ZCO2<1.0 && extractCmpFromPvt) { Scalar rvNxt = underSaturatedTable.get("RV", innerIdx+1)+innerIdx*1.0e-10; Scalar bgNxt = underSaturatedTable.get("B_G", innerIdx+1); Scalar cmpFactor = (bgNxt-bg)/(rvNxt-rv); gasCmp[outerIdx] = cmpFactor; //std::cout << "### cmpFactorGas: " << cmpFactor << " zLim: " << zLim_[regionIdx] << std::endl; } params_.BO_[regionIdx].appendSamplePoint(outerIdx,po,bo); params_.BG_[regionIdx].appendSamplePoint(outerIdx,po,bg); params_.RS_[regionIdx].appendSamplePoint(outerIdx,po,rs); params_.RV_[regionIdx].appendSamplePoint(outerIdx,po,rv); params_.X_[regionIdx].appendSamplePoint(outerIdx,po,xv); params_.Y_[regionIdx].appendSamplePoint(outerIdx,po,yv); params_.VISCO_[regionIdx].appendSamplePoint(outerIdx,po,mo); params_.VISCG_[regionIdx].appendSamplePoint(outerIdx,po,mg); break; } bo0=bo; po0=po; params_.BO_[regionIdx].appendSamplePoint(outerIdx,po,bo); params_.BG_[regionIdx].appendSamplePoint(outerIdx,po,bg); params_.RS_[regionIdx].appendSamplePoint(outerIdx,po,rs); params_.RV_[regionIdx].appendSamplePoint(outerIdx,po,rv); params_.X_[regionIdx].appendSamplePoint(outerIdx,po,xv); params_.Y_[regionIdx].appendSamplePoint(outerIdx,po,yv); params_.VISCO_[regionIdx].appendSamplePoint(outerIdx,po,mo); params_.VISCG_[regionIdx].appendSamplePoint(outerIdx,po,mg); // rs,rv -> pressure params_.PBUB_RS_[regionIdx].appendSamplePoint(outerIdx, rs, po); params_.PBUB_RV_[regionIdx].appendSamplePoint(outerIdx, rv, po); } } params_.oilCmp_[regionIdx].setXYContainers(zArg, oilCmp, /*sortInput=*/false); params_.gasCmp_[regionIdx].setXYContainers(zArg, gasCmp, /*sortInput=*/false); } // Reference density for pure z-component taken from kw SDENSITY const auto& sdensityTables = eclState.getTableManager().getSolventDensityTables(); if (sdensityTables.size() == numPvtRegions) { params_.zReferenceDensity_.resize(numPvtRegions); for (unsigned regionIdx = 0; regionIdx < numPvtRegions; ++ regionIdx) { Scalar rhoRefS = sdensityTables[regionIdx].getSolventDensityColumn().front(); params_.zReferenceDensity_[regionIdx]=rhoRefS; } } else throw std::runtime_error("Extbo: kw SDENSITY is missing or not aligned with NTPVT\n"); } #endif /*! * \brief Register all run-time parameters for the black-oil solvent module. */ static void registerParameters() { } /*! * \brief Register all solvent specific VTK and ECL output modules. */ static void registerOutputModules(Model&, Simulator&) { } static bool primaryVarApplies(unsigned pvIdx) { if constexpr (enableExtbo) return pvIdx == zFractionIdx; else return false; } static std::string primaryVarName([[maybe_unused]] unsigned pvIdx) { assert(primaryVarApplies(pvIdx)); return "z_fraction"; } static Scalar primaryVarWeight([[maybe_unused]] unsigned pvIdx) { assert(primaryVarApplies(pvIdx)); // TODO: it may be beneficial to chose this differently. return static_cast(1.0); } static bool eqApplies(unsigned eqIdx) { if constexpr (enableExtbo) return eqIdx == contiZfracEqIdx; else return false; } static std::string eqName([[maybe_unused]] unsigned eqIdx) { assert(eqApplies(eqIdx)); return "conti^solvent"; } static Scalar eqWeight([[maybe_unused]] unsigned eqIdx) { assert(eqApplies(eqIdx)); // TODO: it may be beneficial to chose this differently. return static_cast(1.0); } template static void addStorage(Dune::FieldVector& storage, const IntensiveQuantities& intQuants) { if constexpr (enableExtbo) { if constexpr (blackoilConserveSurfaceVolume) { storage[contiZfracEqIdx] = Toolbox::template decay(intQuants.porosity()) * Toolbox::template decay(intQuants.yVolume()) * Toolbox::template decay(intQuants.fluidState().saturation(gasPhaseIdx)) * Toolbox::template decay(intQuants.fluidState().invB(gasPhaseIdx)); if (FluidSystem::enableDissolvedGas()) { // account for dissolved z in oil phase storage[contiZfracEqIdx] += Toolbox::template decay(intQuants.porosity()) * Toolbox::template decay(intQuants.xVolume()) * Toolbox::template decay(intQuants.fluidState().Rs()) * Toolbox::template decay(intQuants.fluidState().saturation(oilPhaseIdx)) * Toolbox::template decay(intQuants.fluidState().invB(oilPhaseIdx)); } // Reg. terms: Preliminary attempt to avoid singular behaviour when solvent is invading a pure water // region. Results seems insensitive to the weighting factor. // TODO: Further investigations ... const Scalar regWghtFactor = 1.0e-6; storage[contiZfracEqIdx] += regWghtFactor*(1.0-Toolbox::template decay(intQuants.zFraction())) + regWghtFactor*Toolbox::template decay(intQuants.porosity()) * Toolbox::template decay(intQuants.fluidState().saturation(gasPhaseIdx)) * Toolbox::template decay(intQuants.fluidState().invB(gasPhaseIdx)); storage[contiZfracEqIdx-1] += regWghtFactor*Toolbox::template decay(intQuants.zFraction()); } else { throw std::runtime_error("Only component conservation in terms of surface volumes is implemented. "); } } } static void computeFlux([[maybe_unused]] RateVector& flux, [[maybe_unused]] const ElementContext& elemCtx, [[maybe_unused]] unsigned scvfIdx, [[maybe_unused]] unsigned timeIdx) { if constexpr (enableExtbo) { const auto& extQuants = elemCtx.extensiveQuantities(scvfIdx, timeIdx); if constexpr (blackoilConserveSurfaceVolume) { unsigned inIdx = extQuants.interiorIndex(); unsigned upIdxGas = static_cast(extQuants.upstreamIndex(gasPhaseIdx)); const auto& upGas = elemCtx.intensiveQuantities(upIdxGas, timeIdx); const auto& fsGas = upGas.fluidState(); if (upIdxGas == inIdx) { flux[contiZfracEqIdx] = extQuants.volumeFlux(gasPhaseIdx) * (upGas.yVolume()) * fsGas.invB(gasPhaseIdx); } else { flux[contiZfracEqIdx] = extQuants.volumeFlux(gasPhaseIdx) * (decay(upGas.yVolume())) * decay(fsGas.invB(gasPhaseIdx)); } if (FluidSystem::enableDissolvedGas()) { // account for dissolved z in oil phase unsigned upIdxOil = static_cast(extQuants.upstreamIndex(oilPhaseIdx)); const auto& upOil = elemCtx.intensiveQuantities(upIdxOil, timeIdx); const auto& fsOil = upOil.fluidState(); if (upIdxOil == inIdx) { flux[contiZfracEqIdx] += extQuants.volumeFlux(oilPhaseIdx) * upOil.xVolume() * fsOil.Rs() * fsOil.invB(oilPhaseIdx); } else { flux[contiZfracEqIdx] += extQuants.volumeFlux(oilPhaseIdx) * decay(upOil.xVolume()) * decay(fsOil.Rs()) * decay(fsOil.invB(oilPhaseIdx)); } } } else { throw std::runtime_error("Only component conservation in terms of surface volumes is implemented. "); } } } /*! * \brief Assign the solvent specific primary variables to a PrimaryVariables object */ static void assignPrimaryVars(PrimaryVariables& priVars, Scalar zFraction) { if constexpr (enableExtbo) priVars[zFractionIdx] = zFraction; } /*! * \brief Do a Newton-Raphson update the primary variables of the solvents. */ static void updatePrimaryVars(PrimaryVariables& newPv, const PrimaryVariables& oldPv, const EqVector& delta) { if constexpr (enableExtbo) // do a plain unchopped Newton update newPv[zFractionIdx] = oldPv[zFractionIdx] - delta[zFractionIdx]; } /*! * \brief Return how much a Newton-Raphson update is considered an error */ static Scalar computeUpdateError(const PrimaryVariables&, const EqVector&) { // do not consider consider the cange of solvent primary variables for // convergence // TODO: maybe this should be changed return static_cast(0.0); } /*! * \brief Return how much a residual is considered an error */ static Scalar computeResidualError(const EqVector& resid) { // do not weight the residual of solvents when it comes to convergence return std::abs(Toolbox::scalarValue(resid[contiZfracEqIdx])); } template static void serializeEntity(const Model& model, std::ostream& outstream, const DofEntity& dof) { if constexpr (enableExtbo) { unsigned dofIdx = model.dofMapper().index(dof); const PrimaryVariables& priVars = model.solution(/*timeIdx=*/0)[dofIdx]; outstream << priVars[zFractionIdx]; } } template static void deserializeEntity(Model& model, std::istream& instream, const DofEntity& dof) { if constexpr (enableExtbo) { unsigned dofIdx = model.dofMapper().index(dof); PrimaryVariables& priVars0 = model.solution(/*timeIdx=*/0)[dofIdx]; PrimaryVariables& priVars1 = model.solution(/*timeIdx=*/1)[dofIdx]; instream >> priVars0[zFractionIdx]; // set the primary variables for the beginning of the current time step. priVars1 = priVars0[zFractionIdx]; } } template static Value xVolume(unsigned pvtRegionIdx, const Value& pressure, const Value& z) { return params_.X_[pvtRegionIdx].eval(z, pressure, true); } template static Value yVolume(unsigned pvtRegionIdx, const Value& pressure, const Value& z) { return params_.Y_[pvtRegionIdx].eval(z, pressure, true); } template static Value pbubRs(unsigned pvtRegionIdx, const Value& z, const Value& rs) { return params_.PBUB_RS_[pvtRegionIdx].eval(z, rs, true); } template static Value pbubRv(unsigned pvtRegionIdx, const Value& z, const Value& rv) { return params_.PBUB_RV_[pvtRegionIdx].eval(z, rv, true); } template static Value oilViscosity(unsigned pvtRegionIdx, const Value& pressure, const Value& z) { return params_.VISCO_[pvtRegionIdx].eval(z, pressure, true); } template static Value gasViscosity(unsigned pvtRegionIdx, const Value& pressure, const Value& z) { return params_.VISCG_[pvtRegionIdx].eval(z, pressure, true); } template static Value bo(unsigned pvtRegionIdx, const Value& pressure, const Value& z) { return params_.BO_[pvtRegionIdx].eval(z, pressure, true); } template static Value bg(unsigned pvtRegionIdx, const Value& pressure, const Value& z) { return params_.BG_[pvtRegionIdx].eval(z, pressure, true); } template static Value rs(unsigned pvtRegionIdx, const Value& pressure, const Value& z) { return params_.RS_[pvtRegionIdx].eval(z, pressure, true); } template static Value rv(unsigned pvtRegionIdx, const Value& pressure, const Value& z) { return params_.RV_[pvtRegionIdx].eval(z, pressure, true); } static Scalar referenceDensity(unsigned regionIdx) { return params_.zReferenceDensity_[regionIdx]; } static Scalar zLim(unsigned regionIdx) { return params_.zLim_[regionIdx]; } template static Value oilCmp(unsigned pvtRegionIdx, const Value& z) { return params_.oilCmp_[pvtRegionIdx].eval(z, true); } template static Value gasCmp(unsigned pvtRegionIdx, const Value& z) { return params_.gasCmp_[pvtRegionIdx].eval(z, true); } private: static BlackOilExtboParams params_; }; template BlackOilExtboParams::Scalar> BlackOilExtboModule::params_; /*! * \ingroup BlackOil * \class Opm::BlackOilExtboIntensiveQuantities * * \brief Provides the volumetric quantities required for the equations needed by the * solvents extension of the black-oil model. */ template ()> class BlackOilExtboIntensiveQuantities { using Implementation = GetPropType; using Scalar = GetPropType; using Evaluation = GetPropType; using PrimaryVariables = GetPropType; using FluidSystem = GetPropType; using MaterialLaw = GetPropType; using Indices = GetPropType; using ElementContext = GetPropType; using ExtboModule = BlackOilExtboModule; enum { numPhases = getPropValue() }; static constexpr int zFractionIdx = Indices::zFractionIdx; static constexpr int oilPhaseIdx = FluidSystem::oilPhaseIdx; static constexpr int gasPhaseIdx = FluidSystem::gasPhaseIdx; static constexpr int waterPhaseIdx = FluidSystem::waterPhaseIdx; static constexpr double cutOff = 1e-12; public: /*! * \brief Compute extended pvt properties from table lookups. * * At this point the pressures of the fluid state are correct. */ void zFractionUpdate_(const ElementContext& elemCtx, unsigned dofIdx, unsigned timeIdx) { const PrimaryVariables& priVars = elemCtx.primaryVars(dofIdx, timeIdx); unsigned pvtRegionIdx = priVars.pvtRegionIndex(); auto& fs = asImp_().fluidState_; zFraction_ = priVars.makeEvaluation(zFractionIdx, timeIdx); oilViscosity_ = ExtboModule::oilViscosity(pvtRegionIdx, fs.pressure(oilPhaseIdx), zFraction_); gasViscosity_ = ExtboModule::gasViscosity(pvtRegionIdx, fs.pressure(gasPhaseIdx), zFraction_); bo_ = ExtboModule::bo(pvtRegionIdx, fs.pressure(oilPhaseIdx), zFraction_); bg_ = ExtboModule::bg(pvtRegionIdx, fs.pressure(gasPhaseIdx), zFraction_); bz_ = ExtboModule::bg(pvtRegionIdx, fs.pressure(oilPhaseIdx), Evaluation{0.99}); if (FluidSystem::enableDissolvedGas()) rs_ = ExtboModule::rs(pvtRegionIdx, fs.pressure(oilPhaseIdx), zFraction_); else rs_ = 0.0; if (FluidSystem::enableVaporizedOil()) rv_ = ExtboModule::rv(pvtRegionIdx, fs.pressure(gasPhaseIdx), zFraction_); else rv_ = 0.0; xVolume_ = ExtboModule::xVolume(pvtRegionIdx, fs.pressure(oilPhaseIdx), zFraction_); yVolume_ = ExtboModule::yVolume(pvtRegionIdx, fs.pressure(oilPhaseIdx), zFraction_); Evaluation pbub = fs.pressure(oilPhaseIdx); if (priVars.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw) { static const Scalar thresholdWaterFilledCell = 1.0 - 1e-6; Scalar sw = priVars.makeEvaluation(Indices::waterSwitchIdx, timeIdx).value(); if (sw >= thresholdWaterFilledCell) rs_ = 0.0; // water only, zero rs_ ... } if (priVars.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rs) { rs_ = priVars.makeEvaluation(Indices::compositionSwitchIdx, timeIdx); const Evaluation zLim = ExtboModule::zLim(pvtRegionIdx); if (zFraction_ > zLim) { pbub = ExtboModule::pbubRs(pvtRegionIdx, zLim, rs_); } else { pbub = ExtboModule::pbubRs(pvtRegionIdx, zFraction_, rs_); } bo_ = ExtboModule::bo(pvtRegionIdx, pbub, zFraction_) + ExtboModule::oilCmp(pvtRegionIdx, zFraction_)*(fs.pressure(oilPhaseIdx)-pbub); xVolume_ = ExtboModule::xVolume(pvtRegionIdx, pbub, zFraction_); } if (priVars.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rv) { rv_ = priVars.makeEvaluation(Indices::compositionSwitchIdx, timeIdx); Evaluation rvsat = ExtboModule::rv(pvtRegionIdx, pbub, zFraction_); bg_ = ExtboModule::bg(pvtRegionIdx, pbub, zFraction_) + ExtboModule::gasCmp(pvtRegionIdx, zFraction_)*(rv_-rvsat); yVolume_ = ExtboModule::yVolume(pvtRegionIdx, pbub, zFraction_); } } /*! * \brief Re-compute face densities to account for zFraction dependency. * * At this point the pressures and saturations of the fluid state are correct. */ void zPvtUpdate_() { const auto& iq = asImp_(); auto& fs = asImp_().fluidState_; unsigned pvtRegionIdx = iq.pvtRegionIndex(); zRefDensity_ = ExtboModule::referenceDensity(pvtRegionIdx); fs.setInvB(oilPhaseIdx, 1.0/bo_); fs.setInvB(gasPhaseIdx, 1.0/bg_); fs.setDensity(oilPhaseIdx, fs.invB(oilPhaseIdx) *(FluidSystem::referenceDensity(oilPhaseIdx, pvtRegionIdx) + (1.0-xVolume_)*fs.Rs()*FluidSystem::referenceDensity(gasPhaseIdx, pvtRegionIdx) + xVolume_*fs.Rs()*zRefDensity_ )); fs.setDensity(gasPhaseIdx, fs.invB(gasPhaseIdx) *(FluidSystem::referenceDensity(gasPhaseIdx, pvtRegionIdx)*(1.0-yVolume_)+yVolume_*zRefDensity_ + FluidSystem::referenceDensity(oilPhaseIdx, pvtRegionIdx)*fs.Rv())); } const Evaluation& zFraction() const { return zFraction_; } const Evaluation& xVolume() const { return xVolume_; } const Evaluation& yVolume() const { return yVolume_; } const Evaluation& oilViscosity() const { return oilViscosity_; } const Evaluation& gasViscosity() const { return gasViscosity_; } const Evaluation& bo() const { return bo_; } const Evaluation& bg() const { return bg_; } const Evaluation& rs() const { return rs_; } const Evaluation& rv() const { return rv_; } const Evaluation zPureInvFormationVolumeFactor() const { return 1.0/bz_; } const Scalar& zRefDensity() const { return zRefDensity_; } private: protected: Implementation& asImp_() { return *static_cast(this); } // Abstract "mass fraction" accounting for the solvent component. The relation between this // quantity and the actual mass fraction of solvent, is implicitly defined from the specific // pvt measurements as provided by kw PVTSOL. Evaluation zFraction_; // The solvent component is assumed gas at surface conditions Evaluation xVolume_; // Solvent volume fraction of Rs Evaluation yVolume_; // Solvent volume fraction of Sg/Bg // Standard black oil parameters modified for presence of solvent Evaluation oilViscosity_; Evaluation gasViscosity_; Evaluation bo_; Evaluation bg_; Evaluation rs_; Evaluation rv_; // Properties of pure solvent Evaluation bz_; Scalar zRefDensity_; }; template class BlackOilExtboIntensiveQuantities { using Evaluation = GetPropType; using ElementContext = GetPropType; using Scalar = GetPropType; public: void zPvtUpdate_() { } void zFractionUpdate_(const ElementContext&, unsigned, unsigned) { } const Evaluation& xVolume() const { throw std::runtime_error("xVolume() called but extbo is disabled"); } const Evaluation& yVolume() const { throw std::runtime_error("yVolume() called but extbo is disabled"); } const Evaluation& oilViscosity() const { throw std::runtime_error("oilViscosity() called but extbo is disabled"); } const Evaluation& gasViscosity() const { throw std::runtime_error("gasViscosity() called but extbo is disabled"); } const Evaluation& rs() const { throw std::runtime_error("rs() called but extbo is disabled"); } const Evaluation& rv() const { throw std::runtime_error("rv() called but extbo is disabled"); } const Evaluation& zPureInvFormationVolumeFactor() const { throw std::runtime_error("zPureInvFormationVolumeFactor() called but extbo is disabled"); } const Evaluation& zFraction() const { throw std::runtime_error("zFraction() called but extbo is disabled"); } const Evaluation& zInverseFormationVolumeFactor() const { throw std::runtime_error("zInverseFormationVolumeFactor() called but extbo is disabled"); } const Scalar& zRefDensity() const { throw std::runtime_error("zRefDensity() called but extbo is disabled"); } }; /*! * \ingroup BlackOil * \class Opm::BlackOilExtboExtensiveQuantities * * \brief Provides the solvent specific extensive quantities to the generic black-oil * module's extensive quantities. */ template ()> class BlackOilExtboExtensiveQuantities { using Implementation = GetPropType; using Scalar = GetPropType; using Evaluation = GetPropType; using ElementContext = GetPropType; using IntensiveQuantities = GetPropType; using ExtensiveQuantities = GetPropType; using FluidSystem = GetPropType; using GridView = GetPropType; using Toolbox = MathToolbox; static constexpr unsigned gasPhaseIdx = FluidSystem::gasPhaseIdx; static constexpr int dimWorld = GridView::dimensionworld; typedef Dune::FieldVector DimVector; typedef Dune::FieldVector DimEvalVector; public: private: Implementation& asImp_() { return *static_cast(this); } }; template class BlackOilExtboExtensiveQuantities { using ElementContext = GetPropType; using Evaluation = GetPropType; public: }; } // namespace Opm #endif