move the hydrostatic equilibrium code to its proper location and make it compile

this just moves the hydrostatic equilibrium code from its historc
location at opm/core to ebos/equil and adds minimal changes to make it
compile. this allows to clean up that code without disturbing the
legacy simulators.
This commit is contained in:
Andreas Lauser
2018-01-02 12:43:56 +01:00
parent f6f2a78a6c
commit 6871e1cf88
6 changed files with 105 additions and 96 deletions

View File

@@ -28,17 +28,13 @@
#ifndef EWOMS_ECL_EQUIL_INITIALIZER_HH
#define EWOMS_ECL_EQUIL_INITIALIZER_HH
#include "equil/initStateEquil.hpp"
#include <ewoms/common/propertysystem.hh>
#include <opm/material/fluidstates/BlackOilFluidState.hpp>
#include <opm/material/fluidmatrixinteractions/EclMaterialLawManager.hpp>
// the ordering of these includes matters. do not touch it if you're not prepared to deal
// with some trouble!
#include <dune/grid/cpgrid/GridHelpers.hpp>
#include <opm/core/simulator/initStateEquil.hpp>
#include <vector>
namespace Ewoms {
@@ -97,10 +93,10 @@ public:
unsigned numCartesianElems = gridManager.cartesianSize();
typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem;
Opm::EQUIL::DeckDependent::InitialStateComputer<FluidSystem> initialState(materialLawManager,
gridManager.eclState(),
gridManager.grid(),
simulator.problem().gravity()[dimWorld - 1]);
EQUIL::DeckDependent::InitialStateComputer<FluidSystem> initialState(materialLawManager,
gridManager.eclState(),
gridManager.grid(),
simulator.problem().gravity()[dimWorld - 1]);
// copy the result into the array of initial fluid states
initialFluidStates_.resize(numCartesianElems);

View File

@@ -48,7 +48,7 @@
#if EBOS_USE_ALUGRID
#include "eclalugridmanager.hh"
#else
#include "eclpolyhedralgridmanager.hh"
//#include "eclpolyhedralgridmanager.hh"
#include "eclcpgridmanager.hh"
#endif
#include "eclwellmanager.hh"

View File

@@ -0,0 +1,804 @@
// -*- 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 3 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/>.
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 Auxiliary routines that to solve the ODEs that emerge from the hydrostatic
* equilibrium problem
*/
#ifndef EWOMS_EQUILIBRATIONHELPERS_HEADER_INCLUDED
#define EWOMS_EQUILIBRATIONHELPERS_HEADER_INCLUDED
#include <opm/core/utility/linearInterpolation.hpp>
#include <opm/core/utility/RegionMapping.hpp>
#include <opm/core/utility/RootFinders.hpp>
#include <opm/parser/eclipse/EclipseState/InitConfig/Equil.hpp>
#include <opm/material/fluidsystems/BlackOilFluidSystem.hpp>
#include <opm/material/fluidstates/SimpleModularFluidState.hpp>
#include <opm/material/fluidmatrixinteractions/EclMaterialLawManager.hpp>
#include <memory>
/*
---- synopsis of EquilibrationHelpers.hpp ----
namespace Opm
{
namespace EQUIL {
namespace Miscibility {
class RsFunction;
class NoMixing;
template <class FluidSystem>
class RsVD;
template <class FluidSystem>
class RsSatAtContact;
}
class EquilReg;
template <class FluidSystem, class MaterialLaw, class MaterialLawManager>
struct PcEq;
template <class FluidSystem, class MaterialLaw, class MaterialLawManager >
inline double satFromPc(const MaterialLawManager& materialLawManager,
const int phase,
const int cell,
const double target_pc,
const bool increasing = false)
template <class FluidSystem, class MaterialLaw, class MaterialLawManager>
inline double satFromSumOfPcs(const MaterialLawManager& materialLawManager,
const int phase1,
const int phase2,
const int cell,
const double target_pc)
} // namespace Equil
} // namespace Opm
---- end of synopsis of EquilibrationHelpers.hpp ----
*/
namespace Ewoms
{
/**
* Types and routines that collectively implement a basic
* ECLIPSE-style equilibration-based initialisation scheme.
*
* This namespace is intentionally nested to avoid name clashes
* with other parts of OPM.
*/
namespace EQUIL {
typedef Opm::FluidSystems::BlackOil<double> FluidSystemSimple;
// Adjust oil pressure according to gas saturation and cap pressure
typedef Opm::SimpleModularFluidState<double,
/*numPhases=*/3,
/*numComponents=*/3,
FluidSystemSimple,
/*storePressure=*/false,
/*storeTemperature=*/false,
/*storeComposition=*/false,
/*storeFugacity=*/false,
/*storeSaturation=*/true,
/*storeDensity=*/false,
/*storeViscosity=*/false,
/*storeEnthalpy=*/false> SatOnlyFluidState;
/**
* Types and routines relating to phase mixing in
* equilibration calculations.
*/
namespace Miscibility {
/**
* Base class for phase mixing functions.
*/
class RsFunction
{
public:
/**
* Function call operator.
*
* \param[in] depth Depth at which to calculate RS
* value.
*
* \param[in] press Pressure at which to calculate RS
* value.
*
* \param[in] temp Temperature at which to calculate RS
* value.
*
* \return Dissolved gas-oil ratio (RS) at depth @c
* depth and pressure @c press.
*/
virtual double operator()(const double depth,
const double press,
const double temp,
const double sat = 0.0) const = 0;
};
/**
* Type that implements "no phase mixing" policy.
*/
class NoMixing : public RsFunction {
public:
/**
* Function call.
*
* \param[in] depth Depth at which to calculate RS
* value.
*
* \param[in] press Pressure at which to calculate RS
* value.
*
* \param[in] temp Temperature at which to calculate RS
* value.
*
* \return Dissolved gas-oil ratio (RS) at depth @c
* depth and pressure @c press. In "no mixing
* policy", this is identically zero.
*/
double
operator()(const double /* depth */,
const double /* press */,
const double /* temp */,
const double /* sat */ = 0.0) const
{
return 0.0;
}
};
/**
* Type that implements "dissolved gas-oil ratio"
* tabulated as a function of depth policy. Data
* typically taken from keyword 'RSVD'.
*/
template <class FluidSystem>
class RsVD : public RsFunction {
public:
/**
* Constructor.
*
* \param[in] pvtRegionIdx The pvt region index
* \param[in] depth Depth nodes.
* \param[in] rs Dissolved gas-oil ratio at @c depth.
*/
RsVD(const int pvtRegionIdx,
const std::vector<double>& depth,
const std::vector<double>& rs)
: pvtRegionIdx_(pvtRegionIdx)
, depth_(depth)
, rs_(rs)
{
}
/**
* Function call.
*
* \param[in] depth Depth at which to calculate RS
* value.
*
* \param[in] press Pressure at which to calculate RS
* value.
*
* \param[in] temp Temperature at which to calculate RS
* value.
*
* \return Dissolved gas-oil ratio (RS) at depth @c
* depth and pressure @c press.
*/
double
operator()(const double depth,
const double press,
const double temp,
const double sat_gas = 0.0) const
{
if (sat_gas > 0.0) {
return satRs(press, temp);
} else {
return std::min(satRs(press, temp), Opm::linearInterpolationNoExtrapolation(depth_, rs_, depth));
}
}
private:
const int pvtRegionIdx_;
std::vector<double> depth_; /**< Depth nodes */
std::vector<double> rs_; /**< Dissolved gas-oil ratio */
double satRs(const double press, const double temp) const
{
return FluidSystem::oilPvt().saturatedGasDissolutionFactor(pvtRegionIdx_, temp, press);
}
};
/**
* Type that implements "vaporized oil-gas ratio"
* tabulated as a function of depth policy. Data
* typically taken from keyword 'RVVD'.
*/
template <class FluidSystem>
class RvVD : public RsFunction {
public:
/**
* Constructor.
*
* \param[in] pvtRegionIdx The pvt region index
* \param[in] depth Depth nodes.
* \param[in] rv Dissolved gas-oil ratio at @c depth.
*/
RvVD(const int pvtRegionIdx,
const std::vector<double>& depth,
const std::vector<double>& rv)
: pvtRegionIdx_(pvtRegionIdx)
, depth_(depth)
, rv_(rv)
{
}
/**
* Function call.
*
* \param[in] depth Depth at which to calculate RV
* value.
*
* \param[in] press Pressure at which to calculate RV
* value.
*
* \param[in] temp Temperature at which to calculate RV
* value.
*
* \return Vaporized oil-gas ratio (RV) at depth @c
* depth and pressure @c press.
*/
double
operator()(const double depth,
const double press,
const double temp,
const double sat_oil = 0.0 ) const
{
if (std::abs(sat_oil) > 1e-16) {
return satRv(press, temp);
} else {
return std::min(satRv(press, temp), Opm::linearInterpolationNoExtrapolation(depth_, rv_, depth));
}
}
private:
const int pvtRegionIdx_;
std::vector<double> depth_; /**< Depth nodes */
std::vector<double> rv_; /**< Vaporized oil-gas ratio */
double satRv(const double press, const double temp) const
{
return FluidSystem::gasPvt().saturatedOilVaporizationFactor(pvtRegionIdx_, temp, press);
}
};
/**
* Class that implements "dissolved gas-oil ratio" (Rs)
* as function of depth and pressure as follows:
*
* 1. The Rs at the gas-oil contact is equal to the
* saturated Rs value, Rs_sat_contact.
*
* 2. The Rs elsewhere is equal to Rs_sat_contact, but
* constrained to the saturated value as given by the
* local pressure.
*
* This should yield Rs-values that are constant below the
* contact, and decreasing above the contact.
*/
template <class FluidSystem>
class RsSatAtContact : public RsFunction {
public:
/**
* Constructor.
*
* \param[in] pvtRegionIdx The pvt region index
* \param[in] p_contact oil pressure at the contact
* \param[in] T_contact temperature at the contact
*/
RsSatAtContact(const int pvtRegionIdx, const double p_contact, const double T_contact)
: pvtRegionIdx_(pvtRegionIdx)
{
rs_sat_contact_ = satRs(p_contact, T_contact);
}
/**
* Function call.
*
* \param[in] depth Depth at which to calculate RS
* value.
*
* \param[in] press Pressure at which to calculate RS
* value.
*
* \param[in] temp Temperature at which to calculate RS
* value.
*
* \return Dissolved gas-oil ratio (RS) at depth @c
* depth and pressure @c press.
*/
double
operator()(const double /* depth */,
const double press,
const double temp,
const double sat_gas = 0.0) const
{
if (sat_gas > 0.0) {
return satRs(press, temp);
} else {
return std::min(satRs(press, temp), rs_sat_contact_);
}
}
private:
const int pvtRegionIdx_;
double rs_sat_contact_;
double satRs(const double press, const double temp) const
{
return FluidSystem::oilPvt().saturatedGasDissolutionFactor(pvtRegionIdx_, temp, press);
}
};
/**
* Class that implements "vaporized oil-gas ratio" (Rv)
* as function of depth and pressure as follows:
*
* 1. The Rv at the gas-oil contact is equal to the
* saturated Rv value, Rv_sat_contact.
*
* 2. The Rv elsewhere is equal to Rv_sat_contact, but
* constrained to the saturated value as given by the
* local pressure.
*
* This should yield Rv-values that are constant below the
* contact, and decreasing above the contact.
*/
template <class FluidSystem>
class RvSatAtContact : public RsFunction {
public:
/**
* Constructor.
*
* \param[in] pvtRegionIdx The pvt region index
* \param[in] p_contact oil pressure at the contact
* \param[in] T_contact temperature at the contact
*/
RvSatAtContact(const int pvtRegionIdx, const double p_contact, const double T_contact)
:pvtRegionIdx_(pvtRegionIdx)
{
rv_sat_contact_ = satRv(p_contact, T_contact);
}
/**
* Function call.
*
* \param[in] depth Depth at which to calculate RV
* value.
*
* \param[in] press Pressure at which to calculate RV
* value.
*
* \param[in] temp Temperature at which to calculate RV
* value.
*
* \return Dissolved oil-gas ratio (RV) at depth @c
* depth and pressure @c press.
*/
double
operator()(const double /*depth*/,
const double press,
const double temp,
const double sat_oil = 0.0) const
{
if (sat_oil > 0.0) {
return satRv(press, temp);
} else {
return std::min(satRv(press, temp), rv_sat_contact_);
}
}
private:
const int pvtRegionIdx_;
double rv_sat_contact_;
double satRv(const double press, const double temp) const
{
return FluidSystem::gasPvt().saturatedOilVaporizationFactor(pvtRegionIdx_, temp, press);;
}
};
} // namespace Miscibility
/**
* Aggregate information base of an equilibration region.
*
* Provides inquiry methods for retrieving depths of contacs
* and pressure values as well as a means of calculating fluid
* densities, dissolved gas-oil ratio and vapourised oil-gas
* ratios.
*
* \tparam DensCalc Type that provides access to a phase
* density calculation facility. Must implement an operator()
* declared as
* <CODE>
* std::vector<double>
* operator()(const double press,
* const std::vector<double>& svol )
* </CODE>
* that calculates the phase densities of all phases in @c
* svol at fluid pressure @c press.
*/
class EquilReg {
public:
/**
* Constructor.
*
* \param[in] rec Equilibration data of current region.
* \param[in] rs Calculator of dissolved gas-oil ratio.
* \param[in] rv Calculator of vapourised oil-gas ratio.
* \param[in] pvtRegionIdx The pvt region index
*/
EquilReg(const Opm::EquilRecord& rec,
std::shared_ptr<Miscibility::RsFunction> rs,
std::shared_ptr<Miscibility::RsFunction> rv,
const int pvtIdx)
: rec_ (rec)
, rs_ (rs)
, rv_ (rv)
, pvtIdx_ (pvtIdx)
{
}
/**
* Type of dissolved gas-oil ratio calculator.
*/
typedef Miscibility::RsFunction CalcDissolution;
/**
* Type of vapourised oil-gas ratio calculator.
*/
typedef Miscibility::RsFunction CalcEvaporation;
/**
* Datum depth in current region
*/
double datum() const { return this->rec_.datumDepth(); }
/**
* Pressure at datum depth in current region.
*/
double pressure() const { return this->rec_.datumDepthPressure(); }
/**
* Depth of water-oil contact.
*/
double zwoc() const { return this->rec_.waterOilContactDepth(); }
/**
* water-oil capillary pressure at water-oil contact.
*
* \return P_o - P_w at WOC.
*/
double pcow_woc() const { return this->rec_.waterOilContactCapillaryPressure(); }
/**
* Depth of gas-oil contact.
*/
double zgoc() const { return this->rec_.gasOilContactDepth(); }
/**
* Gas-oil capillary pressure at gas-oil contact.
*
* \return P_g - P_o at GOC.
*/
double pcgo_goc() const { return this->rec_.gasOilContactCapillaryPressure(); }
/**
* Retrieve dissolved gas-oil ratio calculator of current
* region.
*/
const CalcDissolution&
dissolutionCalculator() const { return *this->rs_; }
/**
* Retrieve vapourised oil-gas ratio calculator of current
* region.
*/
const CalcEvaporation&
evaporationCalculator() const { return *this->rv_; }
/**
* Retrieve pvtIdx of the region.
*/
int pvtIdx() const { return this->pvtIdx_; }
private:
Opm::EquilRecord rec_; /**< Equilibration data */
std::shared_ptr<Miscibility::RsFunction> rs_; /**< RS calculator */
std::shared_ptr<Miscibility::RsFunction> rv_; /**< RV calculator */
const int pvtIdx_;
};
/// Functor for inverting capillary pressure function.
/// Function represented is
/// f(s) = pc(s) - target_pc
template <class FluidSystem, class MaterialLaw, class MaterialLawManager>
struct PcEq
{
PcEq(const MaterialLawManager& materialLawManager,
const int phase,
const int cell,
const double target_pc)
: materialLawManager_(materialLawManager),
phase_(phase),
cell_(cell),
target_pc_(target_pc)
{
}
double operator()(double s) const
{
const auto& matParams = materialLawManager_.materialLawParams(cell_);
SatOnlyFluidState fluidState;
fluidState.setSaturation(FluidSystem::waterPhaseIdx, 0.0);
fluidState.setSaturation(FluidSystem::oilPhaseIdx, 0.0);
fluidState.setSaturation(FluidSystem::gasPhaseIdx, 0.0);
fluidState.setSaturation(phase_, s);
double pc[FluidSystem::numPhases];
std::fill(pc, pc + FluidSystem::numPhases, 0.0);
MaterialLaw::capillaryPressures(pc, matParams, fluidState);
double sign = (phase_ == FluidSystem::waterPhaseIdx)? -1.0 : 1.0;
double pcPhase = pc[FluidSystem::oilPhaseIdx] + sign * pc[phase_];
return pcPhase - target_pc_;
}
private:
const MaterialLawManager& materialLawManager_;
const int phase_;
const int cell_;
const double target_pc_;
};
template <class FluidSystem, class MaterialLawManager>
double minSaturations(const MaterialLawManager& materialLawManager, const int phase, const int cell) {
const auto& scaledDrainageInfo =
materialLawManager.oilWaterScaledEpsInfoDrainage(cell);
// Find minimum and maximum saturations.
switch(phase) {
case FluidSystem::waterPhaseIdx :
{
return scaledDrainageInfo.Swl;
break;
}
case FluidSystem::gasPhaseIdx :
{
return scaledDrainageInfo.Sgl;
break;
}
case FluidSystem::oilPhaseIdx :
{
OPM_THROW(std::runtime_error, "Min saturation not implemented for oil phase.");
break;
}
default: OPM_THROW(std::runtime_error, "Unknown phaseIdx .");
}
return -1.0;
}
template <class FluidSystem, class MaterialLawManager>
double maxSaturations(const MaterialLawManager& materialLawManager, const int phase, const int cell) {
const auto& scaledDrainageInfo =
materialLawManager.oilWaterScaledEpsInfoDrainage(cell);
// Find minimum and maximum saturations.
switch(phase) {
case FluidSystem::waterPhaseIdx :
{
return scaledDrainageInfo.Swu;
break;
}
case FluidSystem::gasPhaseIdx :
{
return scaledDrainageInfo.Sgu;
break;
}
case FluidSystem::oilPhaseIdx :
{
OPM_THROW(std::runtime_error, "Max saturation not implemented for oil phase.");
break;
}
default: OPM_THROW(std::runtime_error, "Unknown phaseIdx .");
}
return -1.0;
}
/// Compute saturation of some phase corresponding to a given
/// capillary pressure.
template <class FluidSystem, class MaterialLaw, class MaterialLawManager >
inline double satFromPc(const MaterialLawManager& materialLawManager,
const int phase,
const int cell,
const double target_pc,
const bool increasing = false)
{
// Find minimum and maximum saturations.
const double s0 = increasing ? maxSaturations<FluidSystem>(materialLawManager, phase, cell) : minSaturations<FluidSystem>(materialLawManager, phase, cell);
const double s1 = increasing ? minSaturations<FluidSystem>(materialLawManager, phase, cell) : maxSaturations<FluidSystem>(materialLawManager, phase, cell);
// Create the equation f(s) = pc(s) - target_pc
const PcEq<FluidSystem, MaterialLaw, MaterialLawManager> f(materialLawManager, phase, cell, target_pc);
const double f0 = f(s0);
const double f1 = f(s1);
if (f0 <= 0.0) {
return s0;
} else if (f1 > 0.0) {
return s1;
} else {
const int max_iter = 60;
const double tol = 1e-6;
int iter_used = -1;
typedef Opm::RegulaFalsi<Opm::ThrowOnError> ScalarSolver;
const double sol = ScalarSolver::solve(f, std::min(s0, s1), std::max(s0, s1), max_iter, tol, iter_used);
return sol;
}
}
/// Functor for inverting a sum of capillary pressure functions.
/// Function represented is
/// f(s) = pc1(s) + pc2(1 - s) - target_pc
template <class FluidSystem, class MaterialLaw, class MaterialLawManager>
struct PcEqSum
{
PcEqSum(const MaterialLawManager& materialLawManager,
const int phase1,
const int phase2,
const int cell,
const double target_pc)
: materialLawManager_(materialLawManager),
phase1_(phase1),
phase2_(phase2),
cell_(cell),
target_pc_(target_pc)
{
}
double operator()(double s) const
{
const auto& matParams = materialLawManager_.materialLawParams(cell_);
SatOnlyFluidState fluidState;
fluidState.setSaturation(FluidSystem::waterPhaseIdx, 0.0);
fluidState.setSaturation(FluidSystem::oilPhaseIdx, 0.0);
fluidState.setSaturation(FluidSystem::gasPhaseIdx, 0.0);
fluidState.setSaturation(phase1_, s);
fluidState.setSaturation(phase2_, 1.0 - s);
double pc[FluidSystem::numPhases];
std::fill(pc, pc + FluidSystem::numPhases, 0.0);
MaterialLaw::capillaryPressures(pc, matParams, fluidState);
double sign1 = (phase1_ == FluidSystem::waterPhaseIdx)? -1.0 : 1.0;
double pc1 = pc[FluidSystem::oilPhaseIdx] + sign1 * pc[phase1_];
double sign2 = (phase2_ == FluidSystem::waterPhaseIdx)? -1.0 : 1.0;
double pc2 = pc[FluidSystem::oilPhaseIdx] + sign2 * pc[phase2_];
return pc1 + pc2 - target_pc_;
}
private:
const MaterialLawManager& materialLawManager_;
const int phase1_;
const int phase2_;
const int cell_;
const double target_pc_;
};
/// Compute saturation of some phase corresponding to a given
/// capillary pressure, where the capillary pressure function
/// is given as a sum of two other functions.
template <class FluidSystem, class MaterialLaw, class MaterialLawManager>
inline double satFromSumOfPcs(const MaterialLawManager& materialLawManager,
const int phase1,
const int phase2,
const int cell,
const double target_pc)
{
// Find minimum and maximum saturations.
const double smin = minSaturations<FluidSystem>(materialLawManager, phase1, cell);
const double smax = maxSaturations<FluidSystem>(materialLawManager, phase1, cell);
// Create the equation f(s) = pc1(s) + pc2(1-s) - target_pc
const PcEqSum<FluidSystem, MaterialLaw, MaterialLawManager> f(materialLawManager, phase1, phase2, cell, target_pc);
const double f0 = f(smin);
const double f1 = f(smax);
if (f0 <= 0.0) {
return smin;
} else if (f1 > 0.0) {
return smax;
} else {
const int max_iter = 30;
const double tol = 1e-6;
int iter_used = -1;
typedef Opm::RegulaFalsi<Opm::ThrowOnError> ScalarSolver;
const double sol = ScalarSolver::solve(f, smin, smax, max_iter, tol, iter_used);
return sol;
}
}
/// Compute saturation from depth. Used for constant capillary pressure function
template <class FluidSystem, class MaterialLaw, class MaterialLawManager>
inline double satFromDepth(const MaterialLawManager& materialLawManager,
const double cellDepth,
const double contactDepth,
const int phase,
const int cell,
const bool increasing = false)
{
const double s0 = increasing ? maxSaturations<FluidSystem>(materialLawManager, phase, cell) : minSaturations<FluidSystem>(materialLawManager, phase, cell);
const double s1 = increasing ? minSaturations<FluidSystem>(materialLawManager, phase, cell) : maxSaturations<FluidSystem>(materialLawManager, phase, cell);
if (cellDepth < contactDepth){
return s0;
} else {
return s1;
}
}
/// Return true if capillary pressure function is constant
template <class FluidSystem, class MaterialLaw, class MaterialLawManager>
inline bool isConstPc(const MaterialLawManager& materialLawManager,
const int phase,
const int cell)
{
// Create the equation f(s) = pc(s);
const PcEq<FluidSystem, MaterialLaw, MaterialLawManager> f(materialLawManager, phase, cell, 0);
const double f0 = f(minSaturations<FluidSystem>(materialLawManager, phase, cell));
const double f1 = f(maxSaturations<FluidSystem>(materialLawManager, phase, cell));
return std::abs(f0 - f1) < std::numeric_limits<double>::epsilon();
}
} // namespace Equil
} // namespace Ewoms
#endif // EWOMS_EQUILIBRATIONHELPERS_HEADER_INCLUDED

View File

@@ -0,0 +1,184 @@
// -*- 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 3 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/>.
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.
*/
#ifndef OPM_REGIONMAPPING_HH
#define OPM_REGIONMAPPING_HH
#include <boost/range.hpp>
#include <unordered_map>
#include <vector>
namespace Ewoms
{
/**
* Forward and reverse mappings between cells and
* regions/partitions (e.g., the ECLIPSE-style 'SATNUM',
* 'PVTNUM', or 'EQUILNUM' arrays).
*
* \tparam Region Type of a forward region mapping. Expected
* to provide indexed access through
* operator[]() as well as inner types
* 'value_type', 'size_type', and
* 'const_iterator'.
*/
template < class Region = std::vector<int> >
class RegionMapping {
public:
/**
* Constructor.
*
* \param[in] reg Forward region mapping, restricted to
* active cells only.
*/
explicit
RegionMapping(const Region& reg)
: reg_(reg)
{
rev_.init(reg_);
}
/**
* Type of forward (cell-to-region) mapping result.
* Expected to be an integer.
*/
typedef typename Region::value_type RegionId;
/**
* Type of reverse (region-to-cell) mapping (element)
* result.
*/
typedef typename Region::size_type CellId;
/**
* Type of reverse region-to-cell range bounds and
* iterators.
*/
typedef typename std::vector<CellId>::const_iterator CellIter;
typedef boost::iterator_range<CellIter> Range;
/**
* Compute region number of given active cell.
*
* \param[in] c Active cell
* \return Region to which @c c belongs.
*/
RegionId
region(const CellId c) const { return reg_[c]; }
const std::vector<RegionId>&
activeRegions() const
{
return rev_.active;
}
/**
* Extract active cells in particular region.
*
* \param[in] r Region number
*
* \return Range of active cells in region @c r. Empty if @c r is
* not an active region.
*/
Range
cells(const RegionId r) const {
const auto id = rev_.binid.find(r);
if (id == rev_.binid.end()) {
// Region 'r' not an active region. Return empty.
return Range(rev_.c.end(), rev_.c.end());
}
const auto i = id->second;
return Range(rev_.c.begin() + rev_.p[i + 0],
rev_.c.begin() + rev_.p[i + 1]);
}
private:
/**
* Copy of forward region mapping (cell-to-region).
*/
Region reg_;
/**
* Reverse mapping (region-to-cell).
*/
struct {
typedef typename std::vector<CellId>::size_type Pos;
std::unordered_map<RegionId, Pos> binid;
std::vector<RegionId> active;
std::vector<Pos> p; /**< Region start pointers */
std::vector<CellId> c; /**< Region cells */
/**
* Compute reverse mapping. Standard linear insertion
* sort algorithm.
*/
void
init(const Region& reg)
{
binid.clear();
for (const auto& r : reg) {
++binid[r];
}
p .clear(); p.emplace_back(0);
active.clear();
{
Pos n = 0;
for (auto& id : binid) {
active.push_back(id.first);
p .push_back(id.second);
id.second = n++;
}
}
for (decltype(p.size()) i = 1, sz = p.size(); i < sz; ++i) {
p[0] += p[i];
p[i] = p[0] - p[i];
}
assert (p[0] == static_cast<Pos>(reg.size()));
c.resize(reg.size());
{
CellId i = 0;
for (const auto& r : reg) {
auto& pos = p[ binid[r] + 1 ];
c[ pos++ ] = i++;
}
}
p[0] = 0;
}
} rev_; /**< Reverse mapping instance */
};
} // namespace Opm
#endif // OPM_REGIONMAPPING_HEADER_INCLUDED

View File

@@ -0,0 +1,446 @@
// -*- 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 3 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/>.
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 Routines that actually solve the ODEs that emerge from the hydrostatic
* equilibrium problem
*/
#ifndef EWOMS_INITSTATEEQUIL_HEADER_INCLUDED
#define EWOMS_INITSTATEEQUIL_HEADER_INCLUDED
#include "EquilibrationHelpers.hpp"
#include "RegionMapping.hpp"
#include <opm/core/utility/extractPvtTableIndex.hpp>
#include <dune/grid/cpgrid/GridHelpers.hpp>
#include <opm/parser/eclipse/Units/Units.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/GridProperty.hpp>
#include <opm/parser/eclipse/EclipseState/InitConfig/Equil.hpp>
#include <opm/parser/eclipse/EclipseState/InitConfig/InitConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Tables/TableContainer.hpp>
#include <opm/parser/eclipse/EclipseState/Tables/TableManager.hpp>
#include <opm/parser/eclipse/EclipseState/Tables/RsvdTable.hpp>
#include <opm/parser/eclipse/EclipseState/Tables/RvvdTable.hpp>
#include <opm/common/OpmLog/OpmLog.hpp>
#include <opm/common/data/SimulationDataContainer.hpp>
#include <opm/material/fluidsystems/BlackOilFluidSystem.hpp>
#include <opm/material/fluidstates/SimpleModularFluidState.hpp>
#include <opm/material/fluidmatrixinteractions/EclMaterialLawManager.hpp>
#include <array>
#include <cassert>
#include <utility>
#include <vector>
namespace Ewoms
{
/**
* Types and routines that collectively implement a basic
* ECLIPSE-style equilibration-based initialisation scheme.
*
* This namespace is intentionally nested to avoid name clashes
* with other parts of OPM.
*/
namespace EQUIL {
/**
* Compute initial phase pressures by means of equilibration.
*
* This function uses the information contained in an
* equilibration record (i.e., depths and pressurs) as well as
* a density calculator and related data to vertically
* integrate the phase pressure ODE
* \f[
* \frac{\mathrm{d}p_{\alpha}}{\mathrm{d}z} =
* \rho_{\alpha}(z,p_{\alpha})\cdot g
* \f]
* in which \f$\rho_{\alpha}$ denotes the fluid density of
* fluid phase \f$\alpha\f$, \f$p_{\alpha}\f$ is the
* corresponding phase pressure, \f$z\f$ is the depth and
* \f$g\f$ is the acceleration due to gravity (assumed
* directed downwords, in the positive \f$z\f$ direction).
*
* \tparam Region Type of an equilibration region information
* base. Typically an instance of the EquilReg
* class template.
*
* \tparam CellRange Type of cell range that demarcates the
* cells pertaining to the current
* equilibration region. Must implement
* methods begin() and end() to bound the range
* as well as provide an inner type,
* const_iterator, to traverse the range.
*
* \param[in] G Grid.
* \param[in] reg Current equilibration region.
* \param[in] cells Range that spans the cells of the current
* equilibration region.
* \param[in] grav Acceleration of gravity.
*
* \return Phase pressures, one vector for each active phase,
* of pressure values in each cell in the current
* equilibration region.
*/
template <class Grid, class Region, class CellRange, class FluidSystem>
std::vector< std::vector<double> >
phasePressures(const Grid& G,
const Region& reg,
const CellRange& cells,
const double grav = Opm::unit::gravity);
/**
* Compute initial phase saturations by means of equilibration.
*
* \tparam FluidSystem The FluidSystem from opm-material
* Must be initialized before used.
*
* \tparam Grid Type of the grid
*
* \tparam Region Type of an equilibration region information
* base. Typically an instance of the EquilReg
* class template.
*
* \tparam CellRange Type of cell range that demarcates the
* cells pertaining to the current
* equilibration region. Must implement
* methods begin() and end() to bound the range
* as well as provide an inner type,
* const_iterator, to traverse the range.
*
* \tparam MaterialLawManager The MaterialLawManager from opm-material
*
* \param[in] G Grid.
* \param[in] reg Current equilibration region.
* \param[in] cells Range that spans the cells of the current
* equilibration region.
* \param[in] materialLawManager The MaterialLawManager from opm-material
* \param[in] swat_init A vector of initial water saturations.
* The capillary pressure is scaled to fit these values
* \param[in] phase_pressures Phase pressures, one vector for each active phase,
* of pressure values in each cell in the current
* equilibration region.
* \return Phase saturations, one vector for each phase, each containing
* one saturation value per cell in the region.
*/
template <class FluidSystem, class Grid, class Region, class CellRange, class MaterialLawManager>
std::vector< std::vector<double> >
phaseSaturations(const Grid& grid,
const Region& reg,
const CellRange& cells,
MaterialLawManager& materialLawManager,
const std::vector<double> swat_init,
std::vector< std::vector<double> >& phase_pressures);
/**
* Compute initial Rs values.
*
* \tparam CellRangeType Type of cell range that demarcates the
* cells pertaining to the current
* equilibration region. Must implement
* methods begin() and end() to bound the range
* as well as provide an inner type,
* const_iterator, to traverse the range.
*
* \param[in] grid Grid.
* \param[in] cells Range that spans the cells of the current
* equilibration region.
* \param[in] oil_pressure Oil pressure for each cell in range.
* \param[in] temperature Temperature for each cell in range.
* \param[in] rs_func Rs as function of pressure and depth.
* \return Rs values, one for each cell in the 'cells' range.
*/
template <class Grid, class CellRangeType>
std::vector<double> computeRs(const Grid& grid,
const CellRangeType& cells,
const std::vector<double> oil_pressure,
const std::vector<double>& temperature,
const Miscibility::RsFunction& rs_func,
const std::vector<double> gas_saturation);
namespace DeckDependent {
inline
std::vector<Opm::EquilRecord>
getEquil(const Opm::EclipseState& state)
{
const auto& init = state.getInitConfig();
if( !init.hasEquil() ) {
OPM_THROW(std::domain_error, "Deck does not provide equilibration data.");
}
const auto& equil = init.getEquil();
return { equil.begin(), equil.end() };
}
template<class Grid>
inline
std::vector<int>
equilnum(const Opm::EclipseState& eclipseState,
const Grid& G )
{
std::vector<int> eqlnum;
if (eclipseState.get3DProperties().hasDeckIntGridProperty("EQLNUM")) {
const int nc = Opm::UgGridHelpers::numCells(G);
eqlnum.resize(nc);
const std::vector<int>& e =
eclipseState.get3DProperties().getIntGridProperty("EQLNUM").getData();
const int* gc = Opm::UgGridHelpers::globalCell(G);
for (int cell = 0; cell < nc; ++cell) {
const int deck_pos = (gc == NULL) ? cell : gc[cell];
eqlnum[cell] = e[deck_pos] - 1;
}
}
else {
// No explicit equilibration region.
// All cells in region zero.
eqlnum.assign(Opm::UgGridHelpers::numCells(G), 0);
}
return eqlnum;
}
template<class FluidSystem>
class InitialStateComputer {
public:
template<class MaterialLawManager, class Grid>
InitialStateComputer(MaterialLawManager& materialLawManager,
const Opm::EclipseState& eclipseState,
const Grid& G ,
const double grav = Opm::unit::gravity,
const bool applySwatInit = true
)
: pp_(FluidSystem::numPhases,
std::vector<double>(Opm::UgGridHelpers::numCells(G))),
sat_(FluidSystem::numPhases,
std::vector<double>(Opm::UgGridHelpers::numCells(G))),
rs_(Opm::UgGridHelpers::numCells(G)),
rv_(Opm::UgGridHelpers::numCells(G))
{
//Check for presence of kw SWATINIT
if (eclipseState.get3DProperties().hasDeckDoubleGridProperty("SWATINIT") && applySwatInit) {
const std::vector<double>& swat_init_ecl = eclipseState.
get3DProperties().getDoubleGridProperty("SWATINIT").getData();
const int nc = Opm::UgGridHelpers::numCells(G);
swat_init_.resize(nc);
const int* gc = Opm::UgGridHelpers::globalCell(G);
for (int c = 0; c < nc; ++c) {
const int deck_pos = (gc == NULL) ? c : gc[c];
swat_init_[c] = swat_init_ecl[deck_pos];
}
}
// Get the equilibration records.
const std::vector<Opm::EquilRecord> rec = getEquil(eclipseState);
const auto& tables = eclipseState.getTableManager();
// Create (inverse) region mapping.
const Ewoms::RegionMapping<> eqlmap(equilnum(eclipseState, grid));
const int invalidRegion = -1;
regionPvtIdx_.resize(rec.size(), invalidRegion);
setRegionPvtIdx(G, eclipseState, eqlmap);
// Create Rs functions.
rs_func_.reserve(rec.size());
if (FluidSystem::enableDissolvedGas()) {
const Opm::TableContainer& rsvdTables = tables.getRsvdTables();
for (size_t i = 0; i < rec.size(); ++i) {
if (eqlmap.cells(i).empty())
{
rs_func_.push_back(std::shared_ptr<Miscibility::RsVD<FluidSystem>>());
continue;
}
const int pvtIdx = regionPvtIdx_[i];
if (!rec[i].liveOilInitConstantRs()) {
if (rsvdTables.size() <= 0 ) {
OPM_THROW(std::runtime_error, "Cannot initialise: RSVD table not available.");
}
const Opm::RsvdTable& rsvdTable = rsvdTables.getTable<Opm::RsvdTable>(i);
std::vector<double> depthColumn = rsvdTable.getColumn("DEPTH").vectorCopy();
std::vector<double> rsColumn = rsvdTable.getColumn("RS").vectorCopy();
rs_func_.push_back(std::make_shared<Miscibility::RsVD<FluidSystem>>(pvtIdx,
depthColumn , rsColumn));
} else {
if (rec[i].gasOilContactDepth() != rec[i].datumDepth()) {
OPM_THROW(std::runtime_error,
"Cannot initialise: when no explicit RSVD table is given, \n"
"datum depth must be at the gas-oil-contact. "
"In EQUIL region " << (i + 1) << " (counting from 1), this does not hold.");
}
const double p_contact = rec[i].datumDepthPressure();
const double T_contact = 273.15 + 20; // standard temperature for now
rs_func_.push_back(std::make_shared<Miscibility::RsSatAtContact<FluidSystem>>(pvtIdx, p_contact, T_contact));
}
}
} else {
for (size_t i = 0; i < rec.size(); ++i) {
rs_func_.push_back(std::make_shared<Miscibility::NoMixing>());
}
}
rv_func_.reserve(rec.size());
if (FluidSystem::enableVaporizedOil()) {
const Opm::TableContainer& rvvdTables = tables.getRvvdTables();
for (size_t i = 0; i < rec.size(); ++i) {
if (eqlmap.cells(i).empty())
{
rv_func_.push_back(std::shared_ptr<Miscibility::RvVD<FluidSystem>>());
continue;
}
const int pvtIdx = regionPvtIdx_[i];
if (!rec[i].wetGasInitConstantRv()) {
if (rvvdTables.size() <= 0) {
OPM_THROW(std::runtime_error, "Cannot initialise: RVVD table not available.");
}
const Opm::RvvdTable& rvvdTable = rvvdTables.getTable<Opm::RvvdTable>(i);
std::vector<double> depthColumn = rvvdTable.getColumn("DEPTH").vectorCopy();
std::vector<double> rvColumn = rvvdTable.getColumn("RV").vectorCopy();
rv_func_.push_back(std::make_shared<Miscibility::RvVD<FluidSystem>>(pvtIdx,
depthColumn , rvColumn));
} else {
if (rec[i].gasOilContactDepth() != rec[i].datumDepth()) {
OPM_THROW(std::runtime_error,
"Cannot initialise: when no explicit RVVD table is given, \n"
"datum depth must be at the gas-oil-contact. "
"In EQUIL region " << (i + 1) << " (counting from 1), this does not hold.");
}
const double p_contact = rec[i].datumDepthPressure() + rec[i].gasOilContactCapillaryPressure();
const double T_contact = 273.15 + 20; // standard temperature for now
rv_func_.push_back(std::make_shared<Miscibility::RvSatAtContact<FluidSystem>>(pvtIdx ,p_contact, T_contact));
}
}
} else {
for (size_t i = 0; i < rec.size(); ++i) {
rv_func_.push_back(std::make_shared<Miscibility::NoMixing>());
}
}
// Compute pressures, saturations, rs and rv factors.
calcPressSatRsRv(eqlmap, rec, materialLawManager, G, grav);
// Modify oil pressure in no-oil regions so that the pressures of present phases can
// be recovered from the oil pressure and capillary relations.
}
typedef std::vector<double> Vec;
typedef std::vector<Vec> PVec; // One per phase.
const PVec& press() const { return pp_; }
const PVec& saturation() const { return sat_; }
const Vec& rs() const { return rs_; }
const Vec& rv() const { return rv_; }
private:
typedef EquilReg EqReg;
std::vector< std::shared_ptr<Miscibility::RsFunction> > rs_func_;
std::vector< std::shared_ptr<Miscibility::RsFunction> > rv_func_;
std::vector<int> regionPvtIdx_;
PVec pp_;
PVec sat_;
Vec rs_;
Vec rv_;
Vec swat_init_;
template<class Grid, class RMap>
void setRegionPvtIdx(const Grid& G, const Opm::EclipseState& eclipseState, const RMap& reg) {
std::vector<int> cellPvtRegionIdx;
extractPvtTableIndex(cellPvtRegionIdx, eclipseState, Opm::UgGridHelpers::numCells(G), Opm::UgGridHelpers::globalCell(G));
for (const auto& r : reg.activeRegions()) {
const auto& cells = reg.cells(r);
const int cell = *(cells.begin());
regionPvtIdx_[r] = cellPvtRegionIdx[cell];
}
}
template <class RMap, class MaterialLawManager, class Grid>
void
calcPressSatRsRv(const RMap& reg ,
const std::vector< Opm::EquilRecord >& rec ,
MaterialLawManager& materialLawManager,
const Grid& G ,
const double grav)
{
for (const auto& r : reg.activeRegions()) {
const auto& cells = reg.cells(r);
if (cells.empty())
{
Opm::OpmLog::warning("Equilibration region " + std::to_string(r + 1)
+ " has no active cells");
continue;
}
const EqReg eqreg(rec[r], rs_func_[r], rv_func_[r], regionPvtIdx_[r]);
PVec pressures = phasePressures<FluidSystem>(G, eqreg, cells, grav);
const std::vector<double>& temp = temperature(G, eqreg, cells);
const PVec sat = phaseSaturations<FluidSystem>(G, eqreg, cells, materialLawManager, swat_init_, pressures);
const int np = FluidSystem::numPhases;
for (int p = 0; p < np; ++p) {
copyFromRegion(pressures[p], cells, pp_[p]);
copyFromRegion(sat[p], cells, sat_[p]);
}
const bool oil = FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx);
const bool gas = FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx);
if (oil && gas) {
const int oilpos = FluidSystem::oilPhaseIdx;
const int gaspos = FluidSystem::gasPhaseIdx;
const Vec rs_vals = computeRs(G, cells, pressures[oilpos], temp, *(rs_func_[r]), sat[gaspos]);
const Vec rv_vals = computeRs(G, cells, pressures[gaspos], temp, *(rv_func_[r]), sat[oilpos]);
copyFromRegion(rs_vals, cells, rs_);
copyFromRegion(rv_vals, cells, rv_);
}
}
}
template <class CellRangeType>
void copyFromRegion(const Vec& source,
const CellRangeType& cells,
Vec& destination)
{
auto s = source.begin();
auto c = cells.begin();
const auto e = cells.end();
for (; c != e; ++c, ++s) {
destination[*c] = *s;
}
}
};
} // namespace DeckDependent
} // namespace EQUIL
} // namespace Opm
#include "initStateEquil_impl.hpp"
#endif // OPM_INITSTATEEQUIL_HEADER_INCLUDED

View File

@@ -0,0 +1,796 @@
// -*- 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 3 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/>.
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 Routines that actually solve the ODEs that emerge from the hydrostatic
* equilibrium problem
*/
#ifndef EWOMS_INITSTATEEQUIL_IMPL_HEADER_INCLUDED
#define EWOMS_INITSTATEEQUIL_IMPL_HEADER_INCLUDED
#include <opm/core/grid.h>
#include <opm/material/fluidsystems/BlackOilFluidSystem.hpp>
#include <cassert>
#include <cmath>
#include <functional>
#include <vector>
namespace Ewoms
{
namespace Details {
template <class RHS>
class RK4IVP {
public:
RK4IVP(const RHS& f ,
const std::array<double,2>& span,
const double y0 ,
const int N )
: N_(N)
, span_(span)
{
const double h = stepsize();
const double h2 = h / 2;
const double h6 = h / 6;
y_.reserve(N + 1);
f_.reserve(N + 1);
y_.push_back(y0);
f_.push_back(f(span_[0], y0));
for (int i = 0; i < N; ++i) {
const double x = span_[0] + i*h;
const double y = y_.back();
const double k1 = f_[i];
const double k2 = f(x + h2, y + h2*k1);
const double k3 = f(x + h2, y + h2*k2);
const double k4 = f(x + h , y + h*k3);
y_.push_back(y + h6*(k1 + 2*(k2 + k3) + k4));
f_.push_back(f(x + h, y_.back()));
}
assert (y_.size() == std::vector<double>::size_type(N + 1));
}
double
operator()(const double x) const
{
// Dense output (O(h**3)) according to Shampine
// (Hermite interpolation)
const double h = stepsize();
int i = (x - span_[0]) / h;
const double t = (x - (span_[0] + i*h)) / h;
// Crude handling of evaluation point outside "span_";
if (i < 0) { i = 0; }
if (N_ <= i) { i = N_ - 1; }
const double y0 = y_[i], y1 = y_[i + 1];
const double f0 = f_[i], f1 = f_[i + 1];
double u = (1 - 2*t) * (y1 - y0);
u += h * ((t - 1)*f0 + t*f1);
u *= t * (t - 1);
u += (1 - t)*y0 + t*y1;
return u;
}
private:
int N_;
std::array<double,2> span_;
std::vector<double> y_;
std::vector<double> f_;
double
stepsize() const { return (span_[1] - span_[0]) / N_; }
};
namespace PhasePressODE {
template <class FluidSystem>
class Water {
public:
Water(const double temp,
const int pvtRegionIdx,
const double norm_grav)
: temp_(temp)
, pvtRegionIdx_(pvtRegionIdx)
, g_(norm_grav)
{
}
double
operator()(const double /* depth */,
const double press) const
{
return this->density(press) * g_;
}
private:
const double temp_;
const int pvtRegionIdx_;
const double g_;
double
density(const double press) const
{
double rho = FluidSystem::waterPvt().inverseFormationVolumeFactor(pvtRegionIdx_, temp_, press);
rho *= FluidSystem::referenceDensity(FluidSystem::waterPhaseIdx, pvtRegionIdx_);
return rho;
}
};
template <class FluidSystem, class RS>
class Oil {
public:
Oil(const double temp,
const RS& rs,
const int pvtRegionIdx,
const double norm_grav)
: temp_(temp)
, rs_(rs)
, pvtRegionIdx_(pvtRegionIdx)
, g_(norm_grav)
{
}
double
operator()(const double depth,
const double press) const
{
return this->density(depth, press) * g_;
}
private:
const double temp_;
const RS& rs_;
const int pvtRegionIdx_;
const double g_;
double
density(const double depth,
const double press) const
{
double rs = rs_(depth, press, temp_);
double bOil = 0.0;
if ( !FluidSystem::enableDissolvedGas() || rs >= FluidSystem::oilPvt().saturatedGasDissolutionFactor(pvtRegionIdx_, temp_, press) ) {
bOil = FluidSystem::oilPvt().saturatedInverseFormationVolumeFactor(pvtRegionIdx_, temp_, press);
} else {
bOil = FluidSystem::oilPvt().inverseFormationVolumeFactor(pvtRegionIdx_, temp_, press, rs);
}
double rho = bOil * FluidSystem::referenceDensity(FluidSystem::oilPhaseIdx, pvtRegionIdx_);
if (FluidSystem::enableDissolvedGas()) {
rho += rs * bOil * FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, pvtRegionIdx_);
}
return rho;
}
};
template <class FluidSystem, class RV>
class Gas {
public:
Gas(const double temp,
const RV& rv,
const int pvtRegionIdx,
const double norm_grav)
: temp_(temp)
, rv_(rv)
, pvtRegionIdx_(pvtRegionIdx)
, g_(norm_grav)
{
}
double
operator()(const double depth,
const double press) const
{
return this->density(depth, press) * g_;
}
private:
const double temp_;
const RV& rv_;
const int pvtRegionIdx_;
const double g_;
double
density(const double depth,
const double press) const
{
double rv = rv_(depth, press, temp_);
double bGas = 0.0;
if ( !FluidSystem::enableVaporizedOil() || rv >= FluidSystem::gasPvt().saturatedOilVaporizationFactor(pvtRegionIdx_, temp_, press) ) {
bGas = FluidSystem::gasPvt().saturatedInverseFormationVolumeFactor(pvtRegionIdx_, temp_, press);
} else {
bGas = FluidSystem::gasPvt().inverseFormationVolumeFactor(pvtRegionIdx_, temp_, press, rv);
}
double rho = bGas * FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, pvtRegionIdx_);
if (FluidSystem::enableVaporizedOil()) {
rho += rv * bGas * FluidSystem::referenceDensity(FluidSystem::oilPhaseIdx, pvtRegionIdx_);
}
return rho;
}
};
} // namespace PhasePressODE
namespace PhasePressure {
template <class Grid,
class PressFunction,
class CellRange>
void
assign(const Grid& G ,
const std::array<PressFunction, 2>& f ,
const double split,
const CellRange& cells,
std::vector<double>& p )
{
enum { up = 0, down = 1 };
std::vector<double>::size_type c = 0;
for (typename CellRange::const_iterator
ci = cells.begin(), ce = cells.end();
ci != ce; ++ci, ++c)
{
assert (c < p.size());
const double z = Opm::UgGridHelpers::cellCenterDepth(G, *ci);
p[c] = (z < split) ? f[up](z) : f[down](z);
}
}
template < class FluidSystem,
class Grid,
class Region,
class CellRange>
void
water(const Grid& G ,
const Region& reg ,
const std::array<double,2>& span ,
const double grav ,
double& po_woc,
const CellRange& cells ,
std::vector<double>& press )
{
using PhasePressODE::Water;
typedef Water<FluidSystem> ODE;
const double T = 273.15 + 20; // standard temperature for now
ODE drho(T, reg.pvtIdx() , grav);
double z0;
double p0;
if (reg.datum() > reg.zwoc()) {//Datum in water zone
z0 = reg.datum();
p0 = reg.pressure();
} else {
z0 = reg.zwoc();
p0 = po_woc - reg.pcow_woc(); // Water pressure at contact
}
std::array<double,2> up = {{ z0, span[0] }};
std::array<double,2> down = {{ z0, span[1] }};
typedef Details::RK4IVP<ODE> WPress;
std::array<WPress,2> wpress = {
{
WPress(drho, up , p0, 2000)
,
WPress(drho, down, p0, 2000)
}
};
assign(G, wpress, z0, cells, press);
if (reg.datum() > reg.zwoc()) {
// Return oil pressure at contact
po_woc = wpress[0](reg.zwoc()) + reg.pcow_woc();
}
}
template <class FluidSystem,
class Grid,
class Region,
class CellRange>
void
oil(const Grid& G ,
const Region& reg ,
const std::array<double,2>& span ,
const double grav ,
const CellRange& cells ,
std::vector<double>& press ,
double& po_woc,
double& po_goc)
{
using PhasePressODE::Oil;
typedef Oil<FluidSystem, typename Region::CalcDissolution> ODE;
const double T = 273.15 + 20; // standard temperature for now
ODE drho(T, reg.dissolutionCalculator(),
reg.pvtIdx(), grav);
double z0;
double p0;
if (reg.datum() > reg.zwoc()) {//Datum in water zone, po_woc given
z0 = reg.zwoc();
p0 = po_woc;
} else if (reg.datum() < reg.zgoc()) {//Datum in gas zone, po_goc given
z0 = reg.zgoc();
p0 = po_goc;
} else { //Datum in oil zone
z0 = reg.datum();
p0 = reg.pressure();
}
std::array<double,2> up = {{ z0, span[0] }};
std::array<double,2> down = {{ z0, span[1] }};
typedef Details::RK4IVP<ODE> OPress;
std::array<OPress,2> opress = {
{
OPress(drho, up , p0, 2000)
,
OPress(drho, down, p0, 2000)
}
};
assign(G, opress, z0, cells, press);
const double woc = reg.zwoc();
if (z0 > woc) { po_woc = opress[0](woc); } // WOC above datum
else if (z0 < woc) { po_woc = opress[1](woc); } // WOC below datum
else { po_woc = p0; } // WOC *at* datum
const double goc = reg.zgoc();
if (z0 > goc) { po_goc = opress[0](goc); } // GOC above datum
else if (z0 < goc) { po_goc = opress[1](goc); } // GOC below datum
else { po_goc = p0; } // GOC *at* datum
}
template <class FluidSystem,
class Grid,
class Region,
class CellRange>
void
gas(const Grid& G ,
const Region& reg ,
const std::array<double,2>& span ,
const double grav ,
double& po_goc,
const CellRange& cells ,
std::vector<double>& press )
{
using PhasePressODE::Gas;
typedef Gas<FluidSystem, typename Region::CalcEvaporation> ODE;
const double T = 273.15 + 20; // standard temperature for now
ODE drho(T, reg.evaporationCalculator(),
reg.pvtIdx(), grav);
double z0;
double p0;
if (reg.datum() < reg.zgoc()) {//Datum in gas zone
z0 = reg.datum();
p0 = reg.pressure();
} else {
z0 = reg.zgoc();
p0 = po_goc + reg.pcgo_goc(); // Gas pressure at contact
}
std::array<double,2> up = {{ z0, span[0] }};
std::array<double,2> down = {{ z0, span[1] }};
typedef Details::RK4IVP<ODE> GPress;
std::array<GPress,2> gpress = {
{
GPress(drho, up , p0, 2000)
,
GPress(drho, down, p0, 2000)
}
};
assign(G, gpress, z0, cells, press);
if (reg.datum() < reg.zgoc()) {
// Return oil pressure at contact
po_goc = gpress[1](reg.zgoc()) - reg.pcgo_goc();
}
}
} // namespace PhasePressure
template <class FluidSystem,
class Grid,
class Region,
class CellRange>
void
equilibrateOWG(const Grid& G,
const Region& reg,
const double grav,
const std::array<double,2>& span,
const CellRange& cells,
std::vector< std::vector<double> >& press)
{
const bool water = FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx);
const bool oil = FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx);
const bool gas = FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx);
const int oilpos = FluidSystem::oilPhaseIdx;
const int waterpos = FluidSystem::waterPhaseIdx;
const int gaspos = FluidSystem::gasPhaseIdx;
if (reg.datum() > reg.zwoc()) { // Datum in water zone
double po_woc = -1;
double po_goc = -1;
if (water) {
PhasePressure::water<FluidSystem>(G, reg, span, grav, po_woc,
cells, press[ waterpos ]);
}
if (oil) {
PhasePressure::oil<FluidSystem>(G, reg, span, grav, cells,
press[ oilpos ], po_woc, po_goc);
}
if (gas) {
PhasePressure::gas<FluidSystem>(G, reg, span, grav, po_goc,
cells, press[ gaspos ]);
}
} else if (reg.datum() < reg.zgoc()) { // Datum in gas zone
double po_woc = -1;
double po_goc = -1;
if (gas) {
PhasePressure::gas<FluidSystem>(G, reg, span, grav, po_goc,
cells, press[ gaspos ]);
}
if (oil) {
PhasePressure::oil<FluidSystem>(G, reg, span, grav, cells,
press[ oilpos ], po_woc, po_goc);
}
if (water) {
PhasePressure::water<FluidSystem>(G, reg, span, grav, po_woc,
cells, press[ waterpos ]);
}
} else { // Datum in oil zone
double po_woc = -1;
double po_goc = -1;
if (oil) {
PhasePressure::oil<FluidSystem>(G, reg, span, grav, cells,
press[ oilpos ], po_woc, po_goc);
}
if (water) {
PhasePressure::water<FluidSystem>(G, reg, span, grav, po_woc,
cells, press[ waterpos ]);
}
if (gas) {
PhasePressure::gas<FluidSystem>(G, reg, span, grav, po_goc,
cells, press[ gaspos ]);
}
}
}
} // namespace Details
namespace EQUIL {
template <class FluidSystem,
class Grid,
class Region,
class CellRange>
std::vector< std::vector<double> >
phasePressures(const Grid& G,
const Region& reg,
const CellRange& cells,
const double grav)
{
std::array<double,2> span =
{{ std::numeric_limits<double>::max() ,
-std::numeric_limits<double>::max() }}; // Symm. about 0.
int ncell = 0;
{
// This code is only supported in three space dimensions
assert (Opm::UgGridHelpers::dimensions(G) == 3);
const int nd = Opm::UgGridHelpers::dimensions(G);
// Define vertical span as
//
// [minimum(node depth(cells)), maximum(node depth(cells))]
//
// Note: We use a sledgehammer approach--looping all
// the nodes of all the faces of all the 'cells'--to
// compute those bounds. This necessarily entails
// visiting some nodes (and faces) multiple times.
//
// Note: The implementation of 'RK4IVP<>' implicitly
// imposes the requirement that cell centroids are all
// within this vertical span. That requirement is not
// checked.
auto cell2Faces = Opm::UgGridHelpers::cell2Faces(G);
auto faceVertices = Opm::UgGridHelpers::face2Vertices(G);
for (typename CellRange::const_iterator
ci = cells.begin(), ce = cells.end();
ci != ce; ++ci, ++ncell)
{
for (auto fi=cell2Faces[*ci].begin(),
fe=cell2Faces[*ci].end();
fi != fe;
++fi)
{
for (auto i = faceVertices[*fi].begin(), e = faceVertices[*fi].end();
i != e; ++i)
{
const double z = Opm::UgGridHelpers::vertexCoordinates(G, *i)[nd-1];
if (z < span[0]) { span[0] = z; }
if (z > span[1]) { span[1] = z; }
}
}
}
}
const int np = FluidSystem::numPhases; //reg.phaseUsage().num_phases;
typedef std::vector<double> pval;
std::vector<pval> press(np, pval(ncell, 0.0));
const double zwoc = reg.zwoc ();
const double zgoc = reg.zgoc ();
// make sure goc and woc is within the span for the phase pressure calculation
span[0] = std::min(span[0],zgoc);
span[1] = std::max(span[1],zwoc);
Details::equilibrateOWG<FluidSystem>(G, reg, grav, span, cells, press);
return press;
}
template <class Grid,
class Region,
class CellRange>
std::vector<double>
temperature(const Grid& /* G */,
const Region& /* reg */,
const CellRange& cells)
{
// use the standard temperature for everything for now
return std::vector<double>(cells.size(), 273.15 + 20.0);
}
template <class FluidSystem, class Grid, class Region, class CellRange, class MaterialLawManager>
std::vector< std::vector<double> >
phaseSaturations(const Grid& G,
const Region& reg,
const CellRange& cells,
MaterialLawManager& materialLawManager,
const std::vector<double> swat_init,
std::vector< std::vector<double> >& phase_pressures)
{
if (!FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
OPM_THROW(std::runtime_error, "Cannot initialise: not handling water-gas cases.");
}
std::vector< std::vector<double> > phase_saturations = phase_pressures; // Just to get the right size.
// Adjust oil pressure according to gas saturation and cap pressure
typedef Opm::SimpleModularFluidState<double,
/*numPhases=*/3,
/*numComponents=*/3,
FluidSystem,
/*storePressure=*/false,
/*storeTemperature=*/false,
/*storeComposition=*/false,
/*storeFugacity=*/false,
/*storeSaturation=*/true,
/*storeDensity=*/false,
/*storeViscosity=*/false,
/*storeEnthalpy=*/false> SatOnlyFluidState;
SatOnlyFluidState fluidState;
typedef typename MaterialLawManager::MaterialLaw MaterialLaw;
const bool water = FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx);
const bool gas = FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx);
const int oilpos = FluidSystem::oilPhaseIdx;
const int waterpos = FluidSystem::waterPhaseIdx;
const int gaspos = FluidSystem::gasPhaseIdx;
std::vector<double>::size_type local_index = 0;
for (typename CellRange::const_iterator ci = cells.begin(); ci != cells.end(); ++ci, ++local_index) {
const int cell = *ci;
const auto& scaledDrainageInfo =
materialLawManager.oilWaterScaledEpsInfoDrainage(cell);
const auto& matParams = materialLawManager.materialLawParams(cell);
// Find saturations from pressure differences by
// inverting capillary pressure functions.
double sw = 0.0;
if (water) {
if (isConstPc<FluidSystem, MaterialLaw, MaterialLawManager>(materialLawManager,FluidSystem::waterPhaseIdx, cell)){
const double cellDepth = Opm::UgGridHelpers::cellCenterDepth(G,
cell);
sw = satFromDepth<FluidSystem, MaterialLaw, MaterialLawManager>(materialLawManager,cellDepth,reg.zwoc(),waterpos,cell,false);
phase_saturations[waterpos][local_index] = sw;
}
else{
const double pcov = phase_pressures[oilpos][local_index] - phase_pressures[waterpos][local_index];
if (swat_init.empty()) { // Invert Pc to find sw
sw = satFromPc<FluidSystem, MaterialLaw, MaterialLawManager>(materialLawManager, waterpos, cell, pcov);
phase_saturations[waterpos][local_index] = sw;
} else { // Scale Pc to reflect imposed sw
sw = swat_init[cell];
sw = materialLawManager.applySwatinit(cell, pcov, sw);
phase_saturations[waterpos][local_index] = sw;
}
}
}
double sg = 0.0;
if (gas) {
if (isConstPc<FluidSystem, MaterialLaw, MaterialLawManager>(materialLawManager,FluidSystem::gasPhaseIdx,cell)){
const double cellDepth = Opm::UgGridHelpers::cellCenterDepth(G,
cell);
sg = satFromDepth<FluidSystem, MaterialLaw, MaterialLawManager>(materialLawManager,cellDepth,reg.zgoc(),gaspos,cell,true);
phase_saturations[gaspos][local_index] = sg;
}
else{
// Note that pcog is defined to be (pg - po), not (po - pg).
const double pcog = phase_pressures[gaspos][local_index] - phase_pressures[oilpos][local_index];
const double increasing = true; // pcog(sg) expected to be increasing function
sg = satFromPc<FluidSystem, MaterialLaw, MaterialLawManager>(materialLawManager, gaspos, cell, pcog, increasing);
phase_saturations[gaspos][local_index] = sg;
}
}
if (gas && water && (sg + sw > 1.0)) {
// Overlapping gas-oil and oil-water transition
// zones can lead to unphysical saturations when
// treated as above. Must recalculate using gas-water
// capillary pressure.
const double pcgw = phase_pressures[gaspos][local_index] - phase_pressures[waterpos][local_index];
if (! swat_init.empty()) {
// Re-scale Pc to reflect imposed sw for vanishing oil phase.
// This seems consistent with ecl, and fails to honour
// swat_init in case of non-trivial gas-oil cap pressure.
sw = materialLawManager.applySwatinit(cell, pcgw, sw);
}
sw = satFromSumOfPcs<FluidSystem, MaterialLaw, MaterialLawManager>(materialLawManager, waterpos, gaspos, cell, pcgw);
sg = 1.0 - sw;
phase_saturations[waterpos][local_index] = sw;
phase_saturations[gaspos][local_index] = sg;
if ( water ) {
fluidState.setSaturation(FluidSystem::waterPhaseIdx, sw);
}
else {
fluidState.setSaturation(FluidSystem::waterPhaseIdx, 0.0);
}
fluidState.setSaturation(FluidSystem::oilPhaseIdx, 1.0 - sw - sg);
fluidState.setSaturation(FluidSystem::gasPhaseIdx, sg);
double pC[/*numPhases=*/3] = { 0.0, 0.0, 0.0 };
MaterialLaw::capillaryPressures(pC, matParams, fluidState);
double pcGas = pC[FluidSystem::oilPhaseIdx] + pC[FluidSystem::gasPhaseIdx];
phase_pressures[oilpos][local_index] = phase_pressures[gaspos][local_index] - pcGas;
}
phase_saturations[oilpos][local_index] = 1.0 - sw - sg;
// Adjust phase pressures for max and min saturation ...
double threshold_sat = 1.0e-6;
double so = 1.0;
double pC[FluidSystem::numPhases] = { 0.0, 0.0, 0.0 };
if (water) {
double swu = scaledDrainageInfo.Swu;
fluidState.setSaturation(FluidSystem::waterPhaseIdx, swu);
so -= swu;
}
if (gas) {
double sgu = scaledDrainageInfo.Sgu;
fluidState.setSaturation(FluidSystem::gasPhaseIdx, sgu);
so-= sgu;
}
fluidState.setSaturation(FluidSystem::oilPhaseIdx, so);
if (water && sw > scaledDrainageInfo.Swu-threshold_sat ) {
fluidState.setSaturation(FluidSystem::waterPhaseIdx, scaledDrainageInfo.Swu);
MaterialLaw::capillaryPressures(pC, matParams, fluidState);
double pcWat = pC[FluidSystem::oilPhaseIdx] - pC[FluidSystem::waterPhaseIdx];
phase_pressures[oilpos][local_index] = phase_pressures[waterpos][local_index] + pcWat;
} else if (gas && sg > scaledDrainageInfo.Sgu-threshold_sat) {
fluidState.setSaturation(FluidSystem::gasPhaseIdx, scaledDrainageInfo.Sgu);
MaterialLaw::capillaryPressures(pC, matParams, fluidState);
double pcGas = pC[FluidSystem::oilPhaseIdx] + pC[FluidSystem::gasPhaseIdx];
phase_pressures[oilpos][local_index] = phase_pressures[gaspos][local_index] - pcGas;
}
if (gas && sg < scaledDrainageInfo.Sgl+threshold_sat) {
fluidState.setSaturation(FluidSystem::gasPhaseIdx, scaledDrainageInfo.Sgl);
MaterialLaw::capillaryPressures(pC, matParams, fluidState);
double pcGas = pC[FluidSystem::oilPhaseIdx] + pC[FluidSystem::gasPhaseIdx];
phase_pressures[gaspos][local_index] = phase_pressures[oilpos][local_index] + pcGas;
}
if (water && sw < scaledDrainageInfo.Swl+threshold_sat) {
fluidState.setSaturation(FluidSystem::waterPhaseIdx, scaledDrainageInfo.Swl);
MaterialLaw::capillaryPressures(pC, matParams, fluidState);
double pcWat = pC[FluidSystem::oilPhaseIdx] - pC[FluidSystem::waterPhaseIdx];
phase_pressures[waterpos][local_index] = phase_pressures[oilpos][local_index] - pcWat;
}
}
return phase_saturations;
}
/**
* Compute initial Rs values.
*
* \tparam CellRangeType Type of cell range that demarcates the
* cells pertaining to the current
* equilibration region. Must implement
* methods begin() and end() to bound the range
* as well as provide an inner type,
* const_iterator, to traverse the range.
*
* \param[in] grid Grid.
* \param[in] cells Range that spans the cells of the current
* equilibration region.
* \param[in] oil_pressure Oil pressure for each cell in range.
* \param[in] temperature Temperature for each cell in range.
* \param[in] rs_func Rs as function of pressure and depth.
* \return Rs values, one for each cell in the 'cells' range.
*/
template <class Grid, class CellRangeType>
std::vector<double> computeRs(const Grid& grid,
const CellRangeType& cells,
const std::vector<double> oil_pressure,
const std::vector<double>& temperature,
const Miscibility::RsFunction& rs_func,
const std::vector<double> gas_saturation)
{
assert(Opm::UgGridHelpers::dimensions(grid) == 3);
std::vector<double> rs(cells.size());
int count = 0;
for (auto it = cells.begin(); it != cells.end(); ++it, ++count) {
const double depth = Opm::UgGridHelpers::cellCenterDepth(grid, *it);
rs[count] = rs_func(depth, oil_pressure[count], temperature[count], gas_saturation[count]);
}
return rs;
}
} // namespace Equil
} // namespace Opm
#endif // OPM_INITSTATEEQUIL_IMPL_HEADER_INCLUDED