Files
opm-common/opm/material/fluidsystems/BlackOilFluidSystem.hpp
Andreas Lauser 1108f24c33 black oil fluid system: fix build on GCC 4.4
this compiler is for some reason not able to determine the number of
phases for externally defined attributes the way it was done. this is
most likely a compiler bug (or it is a left-over ideosyncrasy from
c++-2003 which was not yet removed for GCC 4.4...)
2014-08-06 11:26:25 +02:00

991 lines
36 KiB
C++

/*
Copyright (C) 2011-2013 by Andreas Lauser
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 <http://www.gnu.org/licenses/>.
*/
/*!
* \file
* \copydoc Opm::FluidSystems::BlackOil
*/
#ifndef OPM_BLACK_OIL_FLUID_SYSTEM_HPP
#define OPM_BLACK_OIL_FLUID_SYSTEM_HPP
#include <opm/core/utility/Exceptions.hpp>
#include <opm/core/utility/ErrorMacros.hpp>
#include <opm/core/utility/Spline.hpp>
#include <opm/material/IdealGas.hpp>
#include <opm/material/Constants.hpp>
#include <opm/material/components/H2O.hpp>
#include <opm/material/fluidsystems/BaseFluidSystem.hpp>
#include <opm/material/fluidsystems/NullParameterCache.hpp>
#include <opm/material/UniformXTabulated2DFunction.hpp>
#include <opm/parser/eclipse/Utility/PvtoTable.hpp>
#include <opm/parser/eclipse/Utility/PvtwTable.hpp>
#include <opm/parser/eclipse/Utility/PvdgTable.hpp>
#include <array>
#include <vector>
namespace Opm {
namespace FluidSystems {
/*!
* \brief A fluid system which uses the black-oil parameters
* to calculate termodynamically meaningful quantities.
*/
template <class Scalar>
class BlackOil
: public BaseFluidSystem<Scalar, BlackOil<Scalar> >
{
typedef Opm::Spline<Scalar> Spline;
typedef std::vector<std::pair<Scalar, Scalar> > SplineSamplingPoints;
typedef Opm::UniformXTabulated2DFunction<Scalar> TabulatedFunction;
public:
//! \copydoc BaseFluidSystem::ParameterCache
class ParameterCache : public Opm::NullParameterCache
{
public:
ParameterCache(int regionIdx=0)
{ regionIdx_ = 0; }
/*!
* \brief Return the index of the region which should be used to determine the
* thermodynamic properties
*/
int regionIndex() const
{ return regionIdx_; }
/*!
* \brief Set the index of the region which should be used to determine the
* thermodynamic properties
*/
void setRegionIndex(int val)
{ regionIdx_ = val; }
private:
int regionIdx_;
};
/****************************************
* Fluid phase parameters
****************************************/
//! \copydoc BaseFluidSystem::numPhases
static const int numPhases = 3;
//! Index of the oil phase
static const int oilPhaseIdx = 0;
//! Index of the water phase
static const int waterPhaseIdx = 1;
//! Index of the gas phase
static const int gasPhaseIdx = 2;
/*!
* \copydoc BaseFluidSystem::init
*
* \attention For this fluid system, this method just throws a
* <tt>std::logic_error</tt> as there is no
* way to generically calculate the required black oil
* parameters. Instead of this method, use
* \code
* FluidSystem::initBegin();
* // set the black oil parameters
* FluidSystem::initEnd();
* \endcode
*/
static void init()
{
OPM_THROW(std::logic_error,
"There is no generic init() method for this fluid system. The "
<< "black-oil fluid system must be initialized using:\n"
<< " FluidSystem::initBegin()\n"
<< " // set black oil parameters\n"
<< " FluidSystem::initEnd()\n");
}
/*!
* \brief Begin the initialization of the black oil fluid system.
*
* After calling this method the surface densities, all formation
* and volume factors, the oil bubble pressure, all viscosities
* and the water compressibility must be set. Before the fluid
* system can be used, initEnd() must be called to finalize the
* initialization.
*/
static void initBegin()
{}
/*!
* \brief Sets the pressure-dependent oil viscosity, density and
* gas content using a table stemming from the Eclipse PVTO
* keyword.
*
* When calling this method, the surface densities of the fluids _must_ have already
* been set!
*/
static void setPvtoTable(const PvtoTable &pvtoTable, int regionIdx=0)
{
const auto saturatedTable = pvtoTable.getOuterTable();
assert(saturatedTable->numRows() > 1);
resizeArrays_(regionIdx);
auto& oilViscosity = oilViscosity_[regionIdx];
auto& oilFormationVolumeFactor = oilFormationVolumeFactor_[regionIdx];
auto& gasDissolutionFactorSpline = gasDissolutionFactorSpline_[regionIdx];
gasDissolutionFactorSpline.setXYArrays(saturatedTable->numRows(),
saturatedTable->getPressureColumn(),
saturatedTable->getGasSolubilityColumn(),
/*type=*/Spline::Monotonic);
updateSaturationPressureSpline_(regionIdx);
Scalar rhooRef = surfaceDensity(oilPhaseIdx, regionIdx);
Scalar rhogRef = surfaceDensity(gasPhaseIdx, regionIdx);
// extract the table for the oil formation factor
for (int outerIdx = 0; outerIdx < saturatedTable->numRows(); ++ outerIdx) {
Scalar Rs = saturatedTable->getGasSolubilityColumn()[outerIdx];
Scalar XoG = Rs/(rhooRef/rhogRef + Rs);
oilFormationVolumeFactor.appendXPos(XoG);
oilViscosity.appendXPos(XoG);
assert(oilFormationVolumeFactor.numX() == outerIdx + 1);
assert(oilViscosity.numX() == outerIdx + 1);
const auto underSaturatedTable = pvtoTable.getInnerTable(outerIdx);
for (int innerIdx = underSaturatedTable->numRows() - 1; innerIdx >= 0; -- innerIdx) {
Scalar po = underSaturatedTable->getPressureColumn()[innerIdx];
Scalar Bo = underSaturatedTable->getOilFormationFactorColumn()[innerIdx];
Scalar muo = underSaturatedTable->getOilViscosityColumn()[innerIdx];
oilFormationVolumeFactor.appendSamplePoint(outerIdx, po, Bo);
oilViscosity.appendSamplePoint(outerIdx, po, muo);
}
}
// we only need this spline to be able to calculate the reference oil formation
// factor without preconditions (oilFormationVolumeFactor.eval() may throw if
// there are X values for which only one pressure is defined.)
Spline BoSpline;
BoSpline.setXYArrays(saturatedTable->numRows(),
saturatedTable->getPressureColumn(),
saturatedTable->getOilFormationFactorColumn(),
/*type=*/Spline::Monotonic);
referenceFormationVolumeFactor_[regionIdx][oilPhaseIdx] =
BoSpline.eval(surfacePressure_, /*extrapolate=*/true);
// make sure to have at least two sample points per mole fraction
for (int xIdx = 0; xIdx < oilFormationVolumeFactor.numX(); ++xIdx) {
// a single sample point is definitely needed
assert(oilFormationVolumeFactor.numY(xIdx) > 0);
if (oilFormationVolumeFactor.numY(xIdx) > 1)
continue;
// assume oil with a constant compressibility and viscosity
Scalar BoRef = referenceFormationVolumeFactor_[regionIdx][oilPhaseIdx];
Scalar poSat = saturatedTable->getPressureColumn()[0];
Scalar po = poSat * 2;
Scalar BoSat = saturatedTable->getOilFormationFactorColumn()[0] / BoRef;
Scalar drhoo_dp = (1.1200 - 1.1189)/((5000 - 4000)*6894.76);
Scalar rhoo = surfaceDensity_[regionIdx][oilPhaseIdx]/BoSat*(1 + drhoo_dp*(po - poSat));
Scalar Bo = surfaceDensity(oilPhaseIdx, regionIdx)/rhoo;
oilFormationVolumeFactor.appendSamplePoint(xIdx, po, Bo*BoRef);
Scalar muo = saturatedTable->getOilViscosityColumn()[0];
oilViscosity.appendSamplePoint(xIdx, po, muo);
}
}
/*!
* \brief Sets the pressure-dependent water viscosity and density
* using a table stemming from the Eclipse PVTW keyword.
*
* This function also sets the surface viscosity and the surface
* density of water, but these can be overwritten using setSurface*().
*/
static void setPvtwTable(const PvtwTable &pvtwTable, int regionIdx=0)
{
assert(pvtwTable.numRows() > 0);
resizeArrays_(regionIdx);
waterCompressibilityScalar_[regionIdx] = pvtwTable.getCompressibilityColumn()[0];
waterViscosityScalar_[regionIdx] = pvtwTable.getViscosityColumn()[0];
}
/*!
* \brief Sets the pressure-dependent viscosity and density of dry
* gas using a table stemming from the Eclipse PVDG
* keyword.
*
* This function also sets the surface viscosity and the surface
* density of gas, but these can be overwritten using
* setSurface*().
*/
static void setPvdgTable(const PvdgTable &pvdgTable, int regionIdx=0)
{
int numSamples = pvdgTable.numRows();
assert(numSamples > 1);
resizeArrays_(regionIdx);
gasFormationVolumeFactorSpline_[regionIdx].setXYArrays(numSamples,
pvdgTable.getPressureColumn(),
pvdgTable.getFormationFactorColumn(),
/*type=*/Spline::Monotonic);
gasViscositySpline_[regionIdx].setXYArrays(numSamples,
pvdgTable.getPressureColumn(),
pvdgTable.getFormationFactorColumn(),
/*type=*/Spline::Monotonic);
referenceFormationVolumeFactor_[regionIdx][gasPhaseIdx] =
gasFormationVolumeFactorSpline_[regionIdx].eval(surfacePressure_, /*extrapolate=*/true);
}
/*!
* \brief Initialize the values of the surface densities
*
* \param rhoOil The surface density of (gas saturated) oil phase.
* \param rhoWater The surface density of the water phase.
* \param rhoGas The surface density of the gas phase.
*/
static void setSurfaceDensities(Scalar rhoOil,
Scalar rhoWater,
Scalar rhoGas,
int regionIdx=0)
{
resizeArrays_(regionIdx);
surfaceDensity_[regionIdx][oilPhaseIdx] = rhoOil;
surfaceDensity_[regionIdx][waterPhaseIdx] = rhoWater;
surfaceDensity_[regionIdx][gasPhaseIdx] = rhoGas;
}
/*!
* \brief Initialize the spline for the gas dissolution factor \f$R_s\f$
*
* \param samplePoints A container of (x,y) values which is suitable to be passed to a spline.
*/
static void setSaturatedOilGasDissolutionFactor(const SplineSamplingPoints &samplePoints,
int regionIdx=0)
{
resizeArrays_(regionIdx);
gasDissolutionFactorSpline_[regionIdx].setContainerOfTuples(samplePoints,
/*type=*/Spline::Monotonic);
assert(gasDissolutionFactorSpline_[regionIdx].monotonic());
updateSaturationPressureSpline_(regionIdx);
}
/*!
* \brief Initialize the spline for the oil formation volume factor
*
* The oil density/volume factor is a function of (p_o, X_o^G), but this method here
* only requires the volume factor of gas-saturated oil (which only depends on
* pressure) while the dependence on the gas mass fraction is estimated...
*/
static void setSaturatedOilFormationVolumeFactor(const SplineSamplingPoints &samplePoints,
int regionIdx=0)
{
resizeArrays_(regionIdx);
auto& oilFormationVolumeFactor = oilFormationVolumeFactor_[regionIdx];
auto &RsSpline = gasDissolutionFactorSpline_[regionIdx];
Scalar XoGMin = 0.0;
Scalar RsMax = RsSpline.eval(gasDissolutionFactorSpline_[regionIdx].xMax());
Scalar XoGMax =
RsMax*surfaceDensity(gasPhaseIdx, regionIdx)
/ (RsMax*surfaceDensity(gasPhaseIdx, regionIdx)
+ surfaceDensity(oilPhaseIdx, regionIdx));
Scalar poMin = samplePoints.front().first;
Scalar poMax = samplePoints.back().first;
size_t nX = 20;
size_t nP = samplePoints.size()*2;
Spline oilFormationVolumeFactorSpline;
oilFormationVolumeFactorSpline.setContainerOfTuples(samplePoints, /*type=*/Spline::Monotonic);
// calculate a table of estimated densities depending on pressure and gas mass
// fraction
for (size_t XIdx = 0; XIdx < nX; ++XIdx) {
Scalar XoG = XoGMin + (XoGMax - XoGMin)*XIdx/nX;
oilFormationVolumeFactor.appendXPos(XoG);
for (size_t pIdx = 0; pIdx < nP; ++pIdx) {
Scalar po = poMin + (poMax - poMin)*pIdx/nP;
Scalar poSat = oilSaturationPressure(XoG, regionIdx);
Scalar BoSat = oilFormationVolumeFactorSpline.eval(poSat, /*extrapolate=*/true);
Scalar drhoo_dp = (1.1200 - 1.1189)/((5000 - 4000)*6894.76);
Scalar rhoo = surfaceDensity(oilPhaseIdx, regionIdx)/BoSat*(1 + drhoo_dp*(po - poSat));
Scalar Bo = surfaceDensity(oilPhaseIdx, regionIdx)/rhoo;
oilFormationVolumeFactor.appendSamplePoint(XIdx, po, Bo);
}
}
referenceFormationVolumeFactor_[regionIdx][oilPhaseIdx] =
oilFormationVolumeFactor.eval(/*XoG=*/0, surfacePressure_, /*extrapolate=*/true);
}
/*!
* \brief Initialize the spline for the oil formation volume factor
*
* This is a function of (Rs, po)...
*/
static void setOilFormationVolumeFactor(const TabulatedFunction &Bo, int regionIdx=0)
{
resizeArrays_(regionIdx);
oilFormationVolumeFactor_[regionIdx] = Bo;
referenceFormationVolumeFactor_[oilPhaseIdx] =
oilFormationVolumeFactor.eval(/*XoG=*/0, surfacePressure_, /*extrapolate=*/true);
}
/*!
* \brief Initialize the spline for the viscosity of gas-saturated
* oil.
*
* This is a function of (Rs, po)...
*/
static void setOilViscosity(const TabulatedFunction &muo, int regionIdx=0)
{
resizeArrays_(regionIdx);
oilViscosity_[regionIdx] = muo;
}
/*!
* \brief Initialize the oil viscosity
*
* The oil viscosity is a function of (p_o, X_o^G), but this method here only requires
* the viscosity of gas-saturated oil (which only depends on pressure) while the
* dependence on the gas mass fraction is assumed to be zero...
*/
static void setSaturatedOilViscosity(const SplineSamplingPoints &samplePoints, int regionIdx=0)
{
resizeArrays_(regionIdx);
auto& gasDissolutionFactorSpline = gasDissolutionFactorSpline_[regionIdx];
Scalar XoGMin = 0.0;
Scalar RsMax = gasDissolutionFactorSpline.eval(gasDissolutionFactorSpline_[regionIdx].xMax());
Scalar XoGMax =
RsMax*surfaceDensity(gasPhaseIdx, regionIdx)
/(RsMax*surfaceDensity(gasPhaseIdx, regionIdx)
+ surfaceDensity(oilPhaseIdx, regionIdx));
Scalar poMin = samplePoints.front().first;
Scalar poMax = samplePoints.back().first;
size_t nX = 20;
size_t nP = samplePoints.size()*2;
auto& oilViscosity = oilViscosity_[regionIdx];
Spline muoSpline;
muoSpline.setContainerOfTuples(samplePoints, /*type=*/Spline::Monotonic);
// calculate a table of estimated densities depending on pressure and gas mass
// fraction
for (size_t XIdx = 0; XIdx < nX; ++XIdx) {
Scalar XoG = XoGMin + (XoGMax - XoGMin)*XIdx/nX;
oilViscosity.appendXPos(XoG);
for (size_t pIdx = 0; pIdx < nP; ++pIdx) {
Scalar po = poMin + (poMax - poMin)*pIdx/nP;
Scalar muo = muoSpline.eval(po);
oilViscosity.appendSamplePoint(XIdx, po, muo);
}
}
}
/*!
* \brief Initialize the spline for the formation volume factor of dry gas
*
* \param samplePoints A container of (x,y) values which is suitable to be passed to a spline.
*/
static void setGasFormationVolumeFactor(const SplineSamplingPoints &samplePoints,
int regionIdx=0)
{
resizeArrays_(regionIdx);
gasFormationVolumeFactorSpline_[regionIdx].setContainerOfTuples(samplePoints,
/*type=*/Spline::Monotonic);
assert(gasFormationVolumeFactorSpline_[regionIdx].monotonic());
referenceFormationVolumeFactor_[regionIdx][gasPhaseIdx] =
gasFormationVolumeFactorSpline_[regionIdx].eval(surfacePressure_, /*extrapolate=*/true);
}
/*!
* \brief Initialize the spline for the viscosity of dry gas
*
* \param samplePoints A container of (x,y) values which is suitable to be passed to a spline.
*/
static void setGasViscosity(const SplineSamplingPoints &samplePoints, int regionIdx=0)
{
resizeArrays_(regionIdx);
gasViscositySpline_[regionIdx].setContainerOfTuples(samplePoints,
/*type=*/Spline::Monotonic);
assert(gasViscositySpline_[regionIdx].monotonic());
}
/*!
* \brief Set the water viscosity [Pa s]
*
* \param muWater The dynamic viscosity of the water phase.
*/
static void setWaterViscosity(Scalar muWater, int regionIdx=0)
{
resizeArrays_(regionIdx);
waterViscosityScalar_[regionIdx] = muWater;
}
/*!
* \brief Set the water compressibility [1 / Pa]
*
* \param cWater The compressibility of the water phase.
*/
static void setWaterCompressibility(Scalar cWater, int regionIdx=0)
{
resizeArrays_(regionIdx);
waterCompressibilityScalar_[regionIdx] = cWater;
}
/*!
* \brief Finish initializing the black oil fluid system.
*/
static void initEnd()
{
// calculate molar masses
// water is simple: 18 g/mol
molarMass_[waterCompIdx] = 18e-3;
// for gas, we take the density at standard conditions and assume it to be ideal
Scalar p = surfacePressure_;
Scalar rho_g = surfaceDensity_[/*regionIdx=*/0][gasPhaseIdx];
Scalar T = 297.15;
molarMass_[gasCompIdx] = Opm::Constants<Scalar>::R*T*rho_g / p;
// finally, for oil phase, we take the molar mass from the
// spe9 paper
molarMass_[oilCompIdx] = 175e-3; // kg/mol
}
//! \copydoc BaseFluidSystem::phaseName
static const char *phaseName(const int phaseIdx)
{
static const char *name[] = { "o", "w", "g" };
assert(0 <= phaseIdx && phaseIdx < numPhases + 1);
return name[phaseIdx];
}
//! \copydoc BaseFluidSystem::isLiquid
static bool isLiquid(const int phaseIdx)
{
assert(0 <= phaseIdx && phaseIdx < numPhases);
return phaseIdx != gasPhaseIdx;
}
/****************************************
* Component related parameters
****************************************/
//! \copydoc BaseFluidSystem::numComponents
static const int numComponents = 3;
//! Index of the oil component
static const int oilCompIdx = 0;
//! Index of the water component
static const int waterCompIdx = 1;
//! Index of the gas component
static const int gasCompIdx = 2;
//! \copydoc BaseFluidSystem::componentName
static const char *componentName(const int compIdx)
{
static const char *name[] = { "O", "W", "G" };
assert(0 <= compIdx && compIdx < numComponents);
return name[compIdx];
}
//! \copydoc BaseFluidSystem::molarMass
static Scalar molarMass(const int compIdx)
{ return molarMass_[compIdx]; }
//! \copydoc BaseFluidSystem::isIdealMixture
static bool isIdealMixture(int phaseIdx)
{
// fugacity coefficients are only pressure dependent -> we
// have an ideal mixture
return true;
}
//! \copydoc BaseFluidSystem::isCompressible
static bool isCompressible(int phaseIdx)
{ return true; /* all phases are compressible */ }
//! \copydoc BaseFluidSystem::isIdealGas
static bool isIdealGas(int phaseIdx)
{ return false; }
/****************************************
* thermodynamic relations
****************************************/
//! \copydoc BaseFluidSystem::density
template <class FluidState>
static Scalar density(const FluidState &fluidState,
ParameterCache &paramCache,
const int phaseIdx)
{
assert(0 <= phaseIdx && phaseIdx <= numPhases);
Scalar p = fluidState.pressure(phaseIdx);
int regionIdx = paramCache.regionIndex();
switch (phaseIdx) {
case waterPhaseIdx: return waterDensity_(p, regionIdx);
case gasPhaseIdx: return gasDensity_(p, regionIdx);
case oilPhaseIdx: return oilDensity(p,
fluidState.massFraction(oilPhaseIdx, gasCompIdx),
regionIdx);
}
OPM_THROW(std::logic_error, "Unhandled phase index " << phaseIdx);
}
//! \copydoc BaseFluidSystem::fugacityCoefficient
template <class FluidState>
static Scalar fugacityCoefficient(const FluidState &fluidState,
const ParameterCache &paramCache,
int phaseIdx,
int compIdx)
{
assert(0 <= phaseIdx && phaseIdx <= numPhases);
assert(0 <= compIdx && compIdx <= numComponents);
Scalar p = fluidState.pressure(phaseIdx);
int regionIdx = paramCache.regionIndex();
switch (phaseIdx) {
case waterPhaseIdx: return fugCoefficientInWater(compIdx, p, regionIdx);
case gasPhaseIdx: return fugCoefficientInGas(compIdx, p, regionIdx);
case oilPhaseIdx: return fugCoefficientInOil(compIdx, p, regionIdx);
}
OPM_THROW(std::logic_error, "Unhandled phase or component index");
}
//! \copydoc BaseFluidSystem::viscosity
template <class FluidState>
static Scalar viscosity(const FluidState &fluidState,
const ParameterCache &paramCache,
int phaseIdx)
{
assert(0 <= phaseIdx && phaseIdx <= numPhases);
Scalar p = fluidState.pressure(phaseIdx);
int regionIdx = paramCache.regionIndex();
switch (phaseIdx) {
case oilPhaseIdx: {
Scalar XoG = fluidState.massFraction(oilPhaseIdx, gasCompIdx);
// ATTENTION: XoG is represented by the _first_ axis!
return oilViscosity_[regionIdx].eval(XoG, p);
}
case waterPhaseIdx: return waterViscosity_(p, regionIdx);
case gasPhaseIdx: return gasViscosity_(p, regionIdx);
}
OPM_THROW(std::logic_error, "Unhandled phase index " << phaseIdx);
}
/*!
* \brief Returns the density of a fluid phase at surface pressure [kg/m^3]
*
* \copydoc Doxygen::phaseIdxParam
*/
static Scalar surfaceDensity(int phaseIdx, int regionIdx=0)
{ return surfaceDensity_[regionIdx][phaseIdx]; }
/*!
* \brief Returns the oil formation volume factor \f$B_o\f$ of saturated oil for a given pressure
*
* \param pressure The pressure of interest [Pa]
*/
static Scalar saturatedOilFormationVolumeFactor(Scalar pressure, int regionIdx=0)
{
Valgrind::CheckDefined(pressure);
// calculate the mass fractions of gas and oil
Scalar XoG = saturatedOilGasMassFraction(pressure, regionIdx);
// ATTENTION: XoG is represented by the _first_ axis!
return oilFormationVolumeFactor_[regionIdx].eval(XoG, pressure);
}
/*!
* \brief Return the normalized formation volume factor of gas.
*
* "Normalized" means "1 at surface pressure".
*/
static Scalar gasFormationVolumeFactor(Scalar pressure, int regionIdx=0)
{
Scalar BgRaw = gasFormationVolumeFactorSpline_[regionIdx].eval(pressure, /*extrapolate=*/true);
return BgRaw/referenceFormationVolumeFactor_[regionIdx][gasPhaseIdx];
}
/*!
* \brief Returns the gas formation factor \f$R_s\f$ for a given pressure
*
* \param pressure The pressure of interest [Pa]
*/
static Scalar gasDissolutionFactor(Scalar pressure, int regionIdx=0)
{ return gasDissolutionFactorSpline_[regionIdx].eval(pressure, /*extrapolate=*/true); }
/*!
* \brief Returns the fugacity coefficient of a given component in the water phase
*
* \param compIdx The index of the component of interest
* \param pressure The pressure of interest [Pa]
*/
static Scalar fugCoefficientInWater(int compIdx, Scalar pressure, int regionIdx=0)
{
// set the affinity of the gas and oil components to the water
// phase to be 6 orders of magnitute smaller than that of the
// water component. for this we use a pseudo-realistic vapor
// pressure of water as a starting point. (we just set it to
// 30 kPa to ease interpreting the results.)
const Scalar pvWater = 30e3;
if (compIdx == oilCompIdx)
return 1e3*pvWater / pressure;
else if (compIdx == gasCompIdx)
return 1e6*pvWater / pressure;
return pvWater / pressure;
}
/*!
* \brief Returns the fugacity coefficient of a given component in the gas phase
*
* \param compIdx The index of the component of interest
* \param pressure The pressure of interest [Pa]
*/
static Scalar fugCoefficientInGas(int compIdx, Scalar pressure, int regionIdx=0)
{
// assume an ideal gas
return 1.0;
}
/*!
* \brief Returns the fugacity coefficient of a given component in the oil phase
*
* \param compIdx The index of the component of interest
* \param pressure The pressure of interest [Pa]
*/
static Scalar fugCoefficientInOil(int compIdx, Scalar pressure, int regionIdx=0)
{
// set the oil component fugacity coefficient in oil phase
// arbitrarily. we use some pseudo-realistic value for the vapor
// pressure to ease physical interpretation of the results
Scalar phi_oO = 20e3/pressure;
if (compIdx == oilCompIdx)
return phi_oO;
else if (compIdx == waterCompIdx)
// assume that the affinity of the water component to the
// oil phase is one million times smaller than that of the
// oil component
return 1e6*phi_oO;
/////////////
// the rest of this method determines the fugacity coefficient
// of the gas component:
//
// first, retrieve the mole fraction of gas a saturated oil
// would exhibit at the given pressure
Scalar x_oGf = saturatedOilGasMoleFraction(pressure, regionIdx);
// then, scale the gas component's gas phase fugacity
// coefficient, so that the oil phase ends up at the right
// composition if we were doing a flash experiment
Scalar phi_gG = fugCoefficientInGas(gasCompIdx, pressure, regionIdx);
return phi_gG / x_oGf;
}
/*!
* \brief Returns the saturation pressure of the oil phase [Pa]
* depending on its mass fraction of the gas component
*
* \param X_oG The mass fraction of the gas component in the oil phase [-]
*/
static Scalar oilSaturationPressure(Scalar X_oG, int regionIdx=0)
{
// use the saturation pressure spline to get a pretty good
// initial value
Scalar pSat = saturationPressureSpline_[regionIdx].eval(X_oG, /*extrapolate=*/true);
// Newton method to do the remaining work. If the initial
// value is good, this should only take two to three
// iterations...
for (int i = 0; i < 20; ++i) {
Scalar f = saturatedOilGasMassFraction(pSat, regionIdx) - X_oG;
Scalar eps = pSat*1e-11;
Scalar fPrime = ((saturatedOilGasMassFraction(pSat + eps, regionIdx) - X_oG) - f)/eps;
Scalar delta = f/fPrime;
pSat -= delta;
if (std::abs(delta) < pSat * 1e-10)
return pSat;
}
OPM_THROW(NumericalProblem, "Could find the oil saturation pressure for X_o^g = " << X_oG);
}
// the mass fraction of the gas component in the oil phase in a
// flash experiment
static Scalar saturatedOilGasMassFraction(Scalar pressure, int regionIdx=0)
{
Scalar rho_gRef = surfaceDensity(gasPhaseIdx, regionIdx);
// calculate the mass of the gas component [kg/m^3] in the oil phase. This is
// equivalent to the gas formation factor [m^3/m^3] at current pressure times the
// gas density [kg/m^3] at standard pressure
Scalar rho_oG = gasDissolutionFactor(pressure, regionIdx) * rho_gRef;
// we now have the total density of saturated oil and the partial density of the
// gas component within it. The gas mass fraction is the ratio of these two.
return rho_oG/(surfaceDensity(oilPhaseIdx, regionIdx) + rho_oG);
}
// the mole fraction of the gas component of a gas-saturated oil phase
static Scalar saturatedOilGasMoleFraction(Scalar pressure, int regionIdx=0)
{
// calculate the mass fractions of gas and oil
Scalar XoG = saturatedOilGasMassFraction(pressure, regionIdx);
// which can be converted to mole fractions, given the
// components' molar masses
Scalar MG = molarMass(gasCompIdx);
Scalar MO = molarMass(oilCompIdx);
Scalar avgMolarMass = MO/(1 + XoG*(MO/MG - 1));
Scalar xoG = XoG*avgMolarMass/MG;
return xoG;
}
/*!
* \brief Return the normalized formation volume factor of (potentially)
* under-saturated oil.
*
* "Normalized" means "1 at surface pressure".
*/
static Scalar oilFormationVolumeFactor(Scalar oilPressure, Scalar XoG, int regionIdx=0)
{
// ATTENTION: XoG is represented by the _first_ axis!
Scalar BoRaw = oilFormationVolumeFactor_[regionIdx].eval(XoG, oilPressure);
return BoRaw/referenceFormationVolumeFactor_[regionIdx][oilPhaseIdx];
}
/*!
* \brief Return the density of (potentially) under-saturated oil.
*/
static Scalar oilDensity(Scalar oilPressure, Scalar XoG, int regionIdx=0)
{
Scalar Bo = oilFormationVolumeFactor(oilPressure, XoG, regionIdx);
return surfaceDensity_[regionIdx][oilPhaseIdx]/Bo;
}
/*!
* \brief Return the density of gas-saturated oil.
*/
static Scalar saturatedOilDensity(Scalar pressure, int regionIdx=0)
{
// mass fraction of gas-saturated oil
Scalar XoG = saturatedOilGasMassFraction(pressure, regionIdx);
return oilDensity(pressure, XoG, regionIdx);
}
private:
static void resizeArrays_(int regionIdx)
{
if (static_cast<int>(oilViscosity_.size()) <= regionIdx) {
int numRegions = regionIdx + 1;
oilFormationVolumeFactor_.resize(numRegions);
gasFormationVolumeFactorSpline_.resize(numRegions);
oilViscosity_.resize(numRegions);
gasViscositySpline_.resize(numRegions);
waterViscosityScalar_.resize(numRegions);
gasDissolutionFactorSpline_.resize(numRegions);
saturationPressureSpline_.resize(numRegions);
waterCompressibilityScalar_.resize(numRegions);
surfaceDensity_.resize(numRegions);
referenceFormationVolumeFactor_.resize(numRegions);
}
}
static void updateSaturationPressureSpline_(int regionIdx)
{
resizeArrays_(regionIdx);
auto& gasDissolutionFactorSpline = gasDissolutionFactorSpline_[regionIdx];
// create the spline representing saturation pressure
// depending of the mass fraction in gas
int n = gasDissolutionFactorSpline.numSamples()*5;
int delta =
(gasDissolutionFactorSpline.xMax() - gasDissolutionFactorSpline.xMin())/(n + 1);
SplineSamplingPoints pSatSamplePoints;
Scalar X_oG = 0;
for (int i=0; i <= n; ++ i) {
Scalar pSat = gasDissolutionFactorSpline.xMin() + i*delta;
X_oG = saturatedOilGasMassFraction(pSat, regionIdx);
std::pair<Scalar, Scalar> val(X_oG, pSat);
pSatSamplePoints.push_back(val);
}
saturationPressureSpline_[regionIdx].setContainerOfTuples(pSatSamplePoints, /*type=*/Spline::Monotonic);
}
static Scalar gasDensity_(Scalar pressure, int regionIdx)
{
// gas formation volume factor at reservoir pressure
Scalar Bg = gasFormationVolumeFactor(pressure, regionIdx);
return surfaceDensity_[regionIdx][gasPhaseIdx]/Bg;
}
static Scalar waterDensity_(Scalar pressure, int regionIdx)
{
// compressibility of water times standard density
Scalar rhoRef = surfaceDensity_[regionIdx][waterPhaseIdx];
return rhoRef*(1 + waterCompressibilityScalar_[regionIdx]*(pressure - 1.0135e5));
}
static Scalar gasViscosity_(Scalar pressure, int regionIdx)
{
return gasViscositySpline_[regionIdx].eval(pressure,
/*extrapolate=*/true);
}
static Scalar waterViscosity_(Scalar pressure, int regionIdx)
{ return waterViscosityScalar_[regionIdx]; }
static std::vector<TabulatedFunction> oilFormationVolumeFactor_;
static std::vector<TabulatedFunction> oilViscosity_;
static std::vector<Spline> gasDissolutionFactorSpline_;
static std::vector<Spline> saturationPressureSpline_;
static std::vector<Spline> gasFormationVolumeFactorSpline_;
static std::vector<Spline> gasViscositySpline_;
static std::vector<Scalar> waterCompressibilityScalar_;
static std::vector<Scalar> waterViscosityScalar_;
static const Scalar surfacePressure_;
// HACK for GCC 4.4: the array size has to be specified using the literal value '3'
// here, because GCC 4.4 seems to be unable to determine the number of phases from
// the BlackOil fluid system in the attribute declaration below...
static std::vector<std::array<Scalar, /*numPhases=*/3> > surfaceDensity_;
static std::vector<std::array<Scalar, /*numPhases=*/3>> referenceFormationVolumeFactor_;
static Scalar molarMass_[numComponents];
};
template <class Scalar>
std::vector<typename BlackOil<Scalar>::TabulatedFunction>
BlackOil<Scalar>::oilFormationVolumeFactor_;
template <class Scalar>
std::vector<typename BlackOil<Scalar>::TabulatedFunction>
BlackOil<Scalar>::oilViscosity_;
template <class Scalar>
std::vector<typename BlackOil<Scalar>::Spline>
BlackOil<Scalar>::gasDissolutionFactorSpline_;
template <class Scalar>
std::vector<typename BlackOil<Scalar>::Spline>
BlackOil<Scalar>::gasFormationVolumeFactorSpline_;
template <class Scalar>
std::vector<typename BlackOil<Scalar>::Spline>
BlackOil<Scalar>::saturationPressureSpline_;
template <class Scalar>
std::vector<typename BlackOil<Scalar>::Spline>
BlackOil<Scalar>::gasViscositySpline_;
template <class Scalar>
const Scalar
BlackOil<Scalar>::surfacePressure_ = 101325.0; // [Pa]
template <class Scalar>
std::vector<std::array<Scalar, 3> >
BlackOil<Scalar>::surfaceDensity_;
template <class Scalar>
std::vector<std::array<Scalar, 3> >
BlackOil<Scalar>::referenceFormationVolumeFactor_;
template <class Scalar>
Scalar
BlackOil<Scalar>::molarMass_[BlackOil<Scalar>::numComponents];
template <class Scalar>
std::vector<Scalar>
BlackOil<Scalar>::waterCompressibilityScalar_;
template <class Scalar>
std::vector<Scalar>
BlackOil<Scalar>::waterViscosityScalar_;
} // namespace FluidSystems
} // namespace Opm
#endif