Merge branch 'master' into nonuniform_fluid_tables

Conflicts:
	Makefile.am
	opm/core/fluid/BlackoilPropertiesFromDeck.hpp
	opm/core/fluid/SaturationPropsFromDeck.cpp
	opm/core/fluid/SaturationPropsFromDeck.hpp
	opm/core/fluid/blackoil/BlackoilPvtProperties.cpp
	opm/core/fluid/blackoil/BlackoilPvtProperties.hpp
	opm/core/fluid/blackoil/SinglePvtDead.cpp

This merge combines three more-or-less orthogonal features
for saturation tables: the option to use StoneII or Simple
three-phase behaviour, the option to fit a spline or not,
and finally setting the number of samples used (if spline
fitting).

Interfaces have changed, the most top-level one being
that BlackoilPropertiesFromDeck::init() now also takes
a ParameterGroup argument.
This commit is contained in:
Atgeirr Flø Rasmussen 2012-09-04 11:42:31 +02:00
commit 03f6f43160
9 changed files with 296 additions and 262 deletions

View File

@ -18,33 +18,71 @@
*/ */
#include <opm/core/fluid/BlackoilPropertiesFromDeck.hpp> #include <opm/core/fluid/BlackoilPropertiesFromDeck.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
namespace Opm namespace Opm
{ {
BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck, BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck,
const UnstructuredGrid& grid, const UnstructuredGrid& grid)
const bool use_spline)
{ {
rock_.init(deck, grid); rock_.init(deck, grid);
pvt_.init(deck, use_spline); pvt_.init(deck, 1025);
// Unfortunate lack of pointer smartness here... SaturationPropsFromDeck<SatFuncStone2Uniform>* ptr
if (use_spline) { = new SaturationPropsFromDeck<SatFuncStone2Uniform>();
SaturationPropsFromDeck<>* ptr = new SaturationPropsFromDeck<>(); satprops_.reset(ptr);
ptr->init(deck, grid); ptr->init(deck, grid, 200);
satprops_.reset(ptr);
} else {
SaturationPropsFromDeck<SatFuncSetNonuniform>* ptr
= new SaturationPropsFromDeck<SatFuncSetNonuniform>();
ptr->init(deck, grid);
satprops_.reset(ptr);
}
if (pvt_.numPhases() != satprops_->numPhases()) { if (pvt_.numPhases() != satprops_->numPhases()) {
THROW("BlackoilPropertiesBasic::BlackoilPropertiesBasic() - Inconsistent number of phases in pvt data (" THROW("BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck() - Inconsistent number of phases in pvt data ("
<< pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_->numPhases() << ")."); << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_->numPhases() << ").");
} }
} }
BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck,
const UnstructuredGrid& grid,
const parameter::ParameterGroup& param)
{
rock_.init(deck, grid);
const int pvt_samples = param.getDefault("pvt_tab_size", 200);
pvt_.init(deck, pvt_samples);
// Unfortunate lack of pointer smartness here...
const int sat_samples = param.getDefault("sat_tab_size", 200);
std::string threephase_model = param.getDefault<std::string>("threephase_model", "simple");
bool use_stone2 = (threephase_model == "stone2");
if (sat_samples > 1) {
if (use_stone2) {
SaturationPropsFromDeck<SatFuncStone2Uniform>* ptr
= new SaturationPropsFromDeck<SatFuncStone2Uniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, sat_samples);
} else {
SaturationPropsFromDeck<SatFuncSimpleUniform>* ptr
= new SaturationPropsFromDeck<SatFuncSimpleUniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, sat_samples);
}
} else {
if (use_stone2) {
SaturationPropsFromDeck<SatFuncStone2Nonuniform>* ptr
= new SaturationPropsFromDeck<SatFuncStone2Nonuniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, sat_samples);
} else {
SaturationPropsFromDeck<SatFuncSimpleNonuniform>* ptr
= new SaturationPropsFromDeck<SatFuncSimpleNonuniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, sat_samples);
}
}
if (pvt_.numPhases() != satprops_->numPhases()) {
THROW("BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck() - Inconsistent number of phases in pvt data ("
<< pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_->numPhases() << ").");
}
}
BlackoilPropertiesFromDeck::~BlackoilPropertiesFromDeck() BlackoilPropertiesFromDeck::~BlackoilPropertiesFromDeck()
{ {
} }

View File

@ -26,6 +26,7 @@
#include <opm/core/fluid/blackoil/BlackoilPvtProperties.hpp> #include <opm/core/fluid/blackoil/BlackoilPvtProperties.hpp>
#include <opm/core/fluid/SaturationPropsFromDeck.hpp> #include <opm/core/fluid/SaturationPropsFromDeck.hpp>
#include <opm/core/eclipse/EclipseGridParser.hpp> #include <opm/core/eclipse/EclipseGridParser.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
struct UnstructuredGrid; struct UnstructuredGrid;
@ -39,13 +40,26 @@ namespace Opm
{ {
public: public:
/// Initialize from deck and grid. /// Initialize from deck and grid.
/// \param deck Deck input parser /// \param[in] deck Deck input parser
/// \param grid Grid to which property object applies, needed for the /// \param[in] grid Grid to which property object applies, needed for the
/// mapping from cell indices (typically from a processed grid) /// mapping from cell indices (typically from a processed grid)
/// to logical cartesian indices consistent with the deck. /// to logical cartesian indices consistent with the deck.
BlackoilPropertiesFromDeck(const EclipseGridParser& deck,
const UnstructuredGrid& grid);
/// Initialize from deck, grid and parameters.
/// \param[in] deck Deck input parser
/// \param[in] grid Grid to which property object applies, needed for the
/// mapping from cell indices (typically from a processed grid)
/// to logical cartesian indices consistent with the deck.
/// \param[in] param Parameters. Accepted parameters include:
/// dead_tab_size (1025) number of uniform sample points for dead-oil pvt tables.
/// tab_size_kr (200) number of uniform sample points for saturation tables.
/// For both parameters, a 0 or negative value indicates that no spline fitting is to
/// be done, and the input fluid data used directly for linear interpolation.
BlackoilPropertiesFromDeck(const EclipseGridParser& deck, BlackoilPropertiesFromDeck(const EclipseGridParser& deck,
const UnstructuredGrid& grid, const UnstructuredGrid& grid,
const bool use_spline); const parameter::ParameterGroup& param);
/// Destructor. /// Destructor.
virtual ~BlackoilPropertiesFromDeck(); virtual ~BlackoilPropertiesFromDeck();

View File

@ -31,7 +31,7 @@ namespace Opm
{ {
rock_.init(deck, grid); rock_.init(deck, grid);
pvt_.init(deck); pvt_.init(deck);
satprops_.init(deck, grid); satprops_.init(deck, grid, 200);
if (pvt_.numPhases() != satprops_.numPhases()) { if (pvt_.numPhases() != satprops_.numPhases()) {
THROW("IncompPropertiesFromDeck::IncompPropertiesFromDeck() - Inconsistent number of phases in pvt data (" THROW("IncompPropertiesFromDeck::IncompPropertiesFromDeck() - Inconsistent number of phases in pvt data ("
<< pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ")."); << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_.numPhases() << ").");

View File

@ -135,7 +135,7 @@ namespace Opm
private: private:
RockFromDeck rock_; RockFromDeck rock_;
PvtPropertiesIncompFromDeck pvt_; PvtPropertiesIncompFromDeck pvt_;
SaturationPropsFromDeck<> satprops_; SaturationPropsFromDeck<SatFuncStone2Uniform> satprops_;
}; };

View File

@ -21,8 +21,11 @@
#define OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED #define OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED
#include <opm/core/fluid/SaturationPropsInterface.hpp> #include <opm/core/fluid/SaturationPropsInterface.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <opm/core/eclipse/EclipseGridParser.hpp> #include <opm/core/eclipse/EclipseGridParser.hpp>
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp> #include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
#include <opm/core/fluid/SatFuncStone2.hpp>
#include <opm/core/fluid/SatFuncSimple.hpp>
#include <vector> #include <vector>
struct UnstructuredGrid; struct UnstructuredGrid;
@ -31,21 +34,14 @@ namespace Opm
{ {
/// Class storing saturation functions in a uniform table,
/// densely sampled from a monotone spline,
/// using linear interpolation.
class SatFuncSetUniform;
/// Class storing saturation functions in a nonuniform table
/// using linear interpolation.
class SatFuncSetNonuniform;
/// Interface to saturation functions from deck. /// Interface to saturation functions from deck.
/// Possible values for template argument: /// Possible values for template argument (for now):
/// SatFuncSetNonuniform, /// SatFuncSetStone2Nonuniform,
/// SatFuncSetUniform (default). /// SatFuncSetStone2Uniform.
template <class SatFuncSet = SatFuncSetUniform> /// SatFuncSetSimpleNonuniform,
/// SatFuncSetSimpleUniform.
template <class SatFuncSet>
class SaturationPropsFromDeck : public SaturationPropsInterface class SaturationPropsFromDeck : public SaturationPropsInterface
{ {
public: public:
@ -53,12 +49,15 @@ namespace Opm
SaturationPropsFromDeck(); SaturationPropsFromDeck();
/// Initialize from deck and grid. /// Initialize from deck and grid.
/// \param deck Deck input parser /// \param[in] deck Deck input parser
/// \param grid Grid to which property object applies, needed for the /// \param[in] grid Grid to which property object applies, needed for the
/// mapping from cell indices (typically from a processed grid) /// mapping from cell indices (typically from a processed grid)
/// to logical cartesian indices consistent with the deck. /// to logical cartesian indices consistent with the deck.
/// \param[in] samples Number of uniform sample points for saturation tables.
/// NOTE: samples will only be used with the SatFuncSetUniform template argument.
void init(const EclipseGridParser& deck, void init(const EclipseGridParser& deck,
const UnstructuredGrid& grid); const UnstructuredGrid& grid,
const int samples);
/// \return P, the number of phases. /// \return P, the number of phases.
int numPhases() const; int numPhases() const;

View File

@ -29,57 +29,6 @@
namespace Opm namespace Opm
{ {
/// Class storing saturation functions in a uniform table,
/// densely sampled from a monotone spline,
/// using linear interpolation.
class SatFuncSetUniform : public BlackoilPhases
{
public:
void init(const EclipseGridParser& deck, const int table_num, PhaseUsage phase_usg);
void evalKr(const double* s, double* kr) const;
void evalKrDeriv(const double* s, double* kr, double* dkrds) const;
void evalPc(const double* s, double* pc) const;
void evalPcDeriv(const double* s, double* pc, double* dpcds) const;
double smin_[PhaseUsage::MaxNumPhases];
double smax_[PhaseUsage::MaxNumPhases];
private:
PhaseUsage phase_usage; // A copy of the outer class' phase_usage_.
UniformTableLinear<double> krw_;
UniformTableLinear<double> krow_;
UniformTableLinear<double> pcow_;
UniformTableLinear<double> krg_;
UniformTableLinear<double> krog_;
UniformTableLinear<double> pcog_;
double krocw_; // = krow_(s_wc)
};
/// Class storing saturation functions in a nonuniform table
/// using linear interpolation.
class SatFuncSetNonuniform : public BlackoilPhases
{
public:
void init(const EclipseGridParser& deck, const int table_num, PhaseUsage phase_usg);
void evalKr(const double* s, double* kr) const;
void evalKrDeriv(const double* s, double* kr, double* dkrds) const;
void evalPc(const double* s, double* pc) const;
void evalPcDeriv(const double* s, double* pc, double* dpcds) const;
double smin_[PhaseUsage::MaxNumPhases];
double smax_[PhaseUsage::MaxNumPhases];
private:
PhaseUsage phase_usage; // A copy of the outer class' phase_usage_.
NonuniformTableLinear<double> krw_;
NonuniformTableLinear<double> krow_;
NonuniformTableLinear<double> pcow_;
NonuniformTableLinear<double> krg_;
NonuniformTableLinear<double> krog_;
NonuniformTableLinear<double> pcog_;
double krocw_; // = krow_(s_wc)
};
// ----------- Methods of SaturationPropsFromDeck --------- // ----------- Methods of SaturationPropsFromDeck ---------
@ -93,7 +42,8 @@ namespace Opm
/// Initialize from deck. /// Initialize from deck.
template <class SatFuncSet> template <class SatFuncSet>
void SaturationPropsFromDeck<SatFuncSet>::init(const EclipseGridParser& deck, void SaturationPropsFromDeck<SatFuncSet>::init(const EclipseGridParser& deck,
const UnstructuredGrid& grid) const UnstructuredGrid& grid,
const int samples)
{ {
phase_usage_ = phaseUsageFromDeck(deck); phase_usage_ = phaseUsageFromDeck(deck);
@ -144,7 +94,7 @@ namespace Opm
// Initialize tables. // Initialize tables.
satfuncset_.resize(num_tables); satfuncset_.resize(num_tables);
for (int table = 0; table < num_tables; ++table) { for (int table = 0; table < num_tables; ++table) {
satfuncset_[table].init(deck, table, phase_usage_); satfuncset_[table].init(deck, table, phase_usage_, samples);
} }
} }

View File

@ -47,7 +47,13 @@ namespace Opm
BlackoilPvtProperties(); BlackoilPvtProperties();
/// Initialize from deck. /// Initialize from deck.
void init(const EclipseGridParser& deck, const bool use_spline); /// \param deck An input deck.
/// \param samples If greater than zero, indicates the number of
/// uniform samples to be taken from monotone spline
/// curves interpolating the fluid data.
/// Otherwise, interpolate linearly in the original
/// data without fitting a spline.
void init(const EclipseGridParser& deck, const int samples);
/// Number of active phases. /// Number of active phases.
int numPhases() const; int numPhases() const;

View File

@ -512,11 +512,11 @@ namespace Opm
State& state) State& state)
{ {
const int num_phases = props.numPhases(); const int num_phases = props.numPhases();
if (num_phases != 2) {
THROW("initStateFromDeck(): currently handling only two-phase scenarios.");
}
state.init(grid, num_phases); state.init(grid, num_phases);
if (deck.hasField("EQUIL")) { if (deck.hasField("EQUIL")) {
if (num_phases != 2) {
THROW("initStateFromDeck(): EQUIL-based init currently handling only two-phase scenarios.");
}
// Set saturations depending on oil-water contact. // Set saturations depending on oil-water contact.
const EQUIL& equil= deck.getEQUIL(); const EQUIL& equil= deck.getEQUIL();
if (equil.equil.size() != 1) { if (equil.equil.size() != 1) {
@ -535,11 +535,27 @@ namespace Opm
const std::vector<double>& sw_deck = deck.getFloatingPointValue("SWAT"); const std::vector<double>& sw_deck = deck.getFloatingPointValue("SWAT");
const std::vector<double>& p_deck = deck.getFloatingPointValue("PRESSURE"); const std::vector<double>& p_deck = deck.getFloatingPointValue("PRESSURE");
const int num_cells = grid.number_of_cells; const int num_cells = grid.number_of_cells;
for (int c = 0; c < num_cells; ++c) { if (num_phases == 2) {
int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c]; for (int c = 0; c < num_cells; ++c) {
s[2*c] = sw_deck[c_deck]; int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c];
s[2*c + 1] = 1.0 - s[2*c]; s[2*c] = sw_deck[c_deck];
p[c] = p_deck[c_deck]; s[2*c + 1] = 1.0 - s[2*c];
p[c] = p_deck[c_deck];
}
} else if (num_phases == 3) {
if (!deck.hasField("SGAS")) {
THROW("initStateFromDeck(): missing SGAS keyword in 3-phase init (only SWAT and PRESSURE found).");
}
const std::vector<double>& sg_deck = deck.getFloatingPointValue("SGAS");
for (int c = 0; c < num_cells; ++c) {
int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c];
s[2*c] = sw_deck[c_deck];
s[2*c + 1] = 1.0 - (sw_deck[c_deck] + sg_deck[c_deck]);
s[2*c + 2] = sg_deck[c_deck];
p[c] = p_deck[c_deck];
}
} else {
THROW("initStateFromDeck(): init with SWAT etc. only available with 2 or 3 phases.");
} }
} else { } else {
THROW("initStateFromDeck(): we must either have EQUIL, or both SWAT and PRESSURE."); THROW("initStateFromDeck(): we must either have EQUIL, or both SWAT and PRESSURE.");

View File

@ -40,14 +40,14 @@ namespace Opm
/// @param[out] porevol the pore volume by cell. /// @param[out] porevol the pore volume by cell.
void computePorevolume(const UnstructuredGrid& grid, void computePorevolume(const UnstructuredGrid& grid,
const double* porosity, const double* porosity,
std::vector<double>& porevol) std::vector<double>& porevol)
{ {
int num_cells = grid.number_of_cells; int num_cells = grid.number_of_cells;
porevol.resize(num_cells); porevol.resize(num_cells);
std::transform(porosity, porosity + num_cells, std::transform(porosity, porosity + num_cells,
grid.cell_volumes, grid.cell_volumes,
porevol.begin(), porevol.begin(),
std::multiplies<double>()); std::multiplies<double>());
} }
@ -98,20 +98,20 @@ namespace Opm
/// For each phase p, we compute /// For each phase p, we compute
/// sat_vol_p = sum_i s_p_i pv_i /// sat_vol_p = sum_i s_p_i pv_i
void computeSaturatedVol(const std::vector<double>& pv, void computeSaturatedVol(const std::vector<double>& pv,
const std::vector<double>& s, const std::vector<double>& s,
double* sat_vol) double* sat_vol)
{ {
const int num_cells = pv.size(); const int num_cells = pv.size();
const int np = s.size()/pv.size(); const int np = s.size()/pv.size();
if (int(s.size()) != num_cells*np) { if (int(s.size()) != num_cells*np) {
THROW("Sizes of s and pv vectors do not match."); THROW("Sizes of s and pv vectors do not match.");
} }
std::fill(sat_vol, sat_vol + np, 0.0); std::fill(sat_vol, sat_vol + np, 0.0);
for (int c = 0; c < num_cells; ++c) { for (int c = 0; c < num_cells; ++c) {
for (int p = 0; p < np; ++p) { for (int p = 0; p < np; ++p) {
sat_vol[p] += pv[c]*s[np*c + p]; sat_vol[p] += pv[c]*s[np*c + p];
} }
} }
} }
@ -123,28 +123,28 @@ namespace Opm
/// For each phase p, we compute /// For each phase p, we compute
/// aver_sat_p = (sum_i s_p_i pv_i) / (sum_i pv_i). /// aver_sat_p = (sum_i s_p_i pv_i) / (sum_i pv_i).
void computeAverageSat(const std::vector<double>& pv, void computeAverageSat(const std::vector<double>& pv,
const std::vector<double>& s, const std::vector<double>& s,
double* aver_sat) double* aver_sat)
{ {
const int num_cells = pv.size(); const int num_cells = pv.size();
const int np = s.size()/pv.size(); const int np = s.size()/pv.size();
if (int(s.size()) != num_cells*np) { if (int(s.size()) != num_cells*np) {
THROW("Sizes of s and pv vectors do not match."); THROW("Sizes of s and pv vectors do not match.");
} }
double tot_pv = 0.0; double tot_pv = 0.0;
// Note that we abuse the output array to accumulate the // Note that we abuse the output array to accumulate the
// saturated pore volumes. // saturated pore volumes.
std::fill(aver_sat, aver_sat + np, 0.0); std::fill(aver_sat, aver_sat + np, 0.0);
for (int c = 0; c < num_cells; ++c) { for (int c = 0; c < num_cells; ++c) {
tot_pv += pv[c]; tot_pv += pv[c];
for (int p = 0; p < np; ++p) { for (int p = 0; p < np; ++p) {
aver_sat[p] += pv[c]*s[np*c + p]; aver_sat[p] += pv[c]*s[np*c + p];
} }
} }
// Must divide by pore volumes to get saturations. // Must divide by pore volumes to get saturations.
for (int p = 0; p < np; ++p) { for (int p = 0; p < np; ++p) {
aver_sat[p] /= tot_pv; aver_sat[p] /= tot_pv;
} }
} }
@ -161,38 +161,38 @@ namespace Opm
/// where P = s.size()/src.size(). /// where P = s.size()/src.size().
/// @param[out] produced must also point to a valid array with P elements. /// @param[out] produced must also point to a valid array with P elements.
void computeInjectedProduced(const IncompPropertiesInterface& props, void computeInjectedProduced(const IncompPropertiesInterface& props,
const std::vector<double>& s, const std::vector<double>& s,
const std::vector<double>& src, const std::vector<double>& src,
const double dt, const double dt,
double* injected, double* injected,
double* produced) double* produced)
{ {
const int num_cells = src.size(); const int num_cells = src.size();
const int np = s.size()/src.size(); const int np = s.size()/src.size();
if (int(s.size()) != num_cells*np) { if (int(s.size()) != num_cells*np) {
THROW("Sizes of s and src vectors do not match."); THROW("Sizes of s and src vectors do not match.");
} }
std::fill(injected, injected + np, 0.0); std::fill(injected, injected + np, 0.0);
std::fill(produced, produced + np, 0.0); std::fill(produced, produced + np, 0.0);
const double* visc = props.viscosity(); const double* visc = props.viscosity();
std::vector<double> mob(np); std::vector<double> mob(np);
for (int c = 0; c < num_cells; ++c) { for (int c = 0; c < num_cells; ++c) {
if (src[c] > 0.0) { if (src[c] > 0.0) {
injected[0] += src[c]*dt; injected[0] += src[c]*dt;
} else if (src[c] < 0.0) { } else if (src[c] < 0.0) {
const double flux = -src[c]*dt; const double flux = -src[c]*dt;
const double* sat = &s[np*c]; const double* sat = &s[np*c];
props.relperm(1, sat, &c, &mob[0], 0); props.relperm(1, sat, &c, &mob[0], 0);
double totmob = 0.0; double totmob = 0.0;
for (int p = 0; p < np; ++p) { for (int p = 0; p < np; ++p) {
mob[p] /= visc[p]; mob[p] /= visc[p];
totmob += mob[p]; totmob += mob[p];
} }
for (int p = 0; p < np; ++p) { for (int p = 0; p < np; ++p) {
produced[p] += (mob[p]/totmob)*flux; produced[p] += (mob[p]/totmob)*flux;
} }
} }
} }
} }
@ -203,9 +203,9 @@ namespace Opm
/// @param[in] s saturation values (for all phases) /// @param[in] s saturation values (for all phases)
/// @param[out] totmob total mobilities. /// @param[out] totmob total mobilities.
void computeTotalMobility(const Opm::IncompPropertiesInterface& props, void computeTotalMobility(const Opm::IncompPropertiesInterface& props,
const std::vector<int>& cells, const std::vector<int>& cells,
const std::vector<double>& s, const std::vector<double>& s,
std::vector<double>& totmob) std::vector<double>& totmob)
{ {
std::vector<double> pmobc; std::vector<double> pmobc;
@ -231,10 +231,10 @@ namespace Opm
/// @param[out] totmob total mobility /// @param[out] totmob total mobility
/// @param[out] omega fractional-flow weighted fluid densities. /// @param[out] omega fractional-flow weighted fluid densities.
void computeTotalMobilityOmega(const Opm::IncompPropertiesInterface& props, void computeTotalMobilityOmega(const Opm::IncompPropertiesInterface& props,
const std::vector<int>& cells, const std::vector<int>& cells,
const std::vector<double>& s, const std::vector<double>& s,
std::vector<double>& totmob, std::vector<double>& totmob,
std::vector<double>& omega) std::vector<double>& omega)
{ {
std::vector<double> pmobc; std::vector<double> pmobc;
@ -331,33 +331,33 @@ namespace Opm
/// (+) positive inflow of first phase (water) /// (+) positive inflow of first phase (water)
/// (-) negative total outflow of both phases /// (-) negative total outflow of both phases
void computeTransportSource(const UnstructuredGrid& grid, void computeTransportSource(const UnstructuredGrid& grid,
const std::vector<double>& src, const std::vector<double>& src,
const std::vector<double>& faceflux, const std::vector<double>& faceflux,
const double inflow_frac, const double inflow_frac,
const Wells* wells, const Wells* wells,
const std::vector<double>& well_perfrates, const std::vector<double>& well_perfrates,
std::vector<double>& transport_src) std::vector<double>& transport_src)
{ {
int nc = grid.number_of_cells; int nc = grid.number_of_cells;
transport_src.resize(nc); transport_src.resize(nc);
// Source term and boundary contributions. // Source term and boundary contributions.
for (int c = 0; c < nc; ++c) { for (int c = 0; c < nc; ++c) {
transport_src[c] = 0.0; transport_src[c] = 0.0;
transport_src[c] += src[c] > 0.0 ? inflow_frac*src[c] : src[c]; transport_src[c] += src[c] > 0.0 ? inflow_frac*src[c] : src[c];
for (int hf = grid.cell_facepos[c]; hf < grid.cell_facepos[c + 1]; ++hf) { for (int hf = grid.cell_facepos[c]; hf < grid.cell_facepos[c + 1]; ++hf) {
int f = grid.cell_faces[hf]; int f = grid.cell_faces[hf];
const int* f2c = &grid.face_cells[2*f]; const int* f2c = &grid.face_cells[2*f];
double bdy_influx = 0.0; double bdy_influx = 0.0;
if (f2c[0] == c && f2c[1] == -1) { if (f2c[0] == c && f2c[1] == -1) {
bdy_influx = -faceflux[f]; bdy_influx = -faceflux[f];
} else if (f2c[0] == -1 && f2c[1] == c) { } else if (f2c[0] == -1 && f2c[1] == c) {
bdy_influx = faceflux[f]; bdy_influx = faceflux[f];
} }
if (bdy_influx != 0.0) { if (bdy_influx != 0.0) {
transport_src[c] += bdy_influx > 0.0 ? inflow_frac*bdy_influx : bdy_influx; transport_src[c] += bdy_influx > 0.0 ? inflow_frac*bdy_influx : bdy_influx;
} }
} }
} }
// Well contributions. // Well contributions.
if (wells) { if (wells) {
@ -392,52 +392,52 @@ namespace Opm
/// @param[in] face_flux signed per-face fluxes /// @param[in] face_flux signed per-face fluxes
/// @param[out] cell_velocity the estimated velocities. /// @param[out] cell_velocity the estimated velocities.
void estimateCellVelocity(const UnstructuredGrid& grid, void estimateCellVelocity(const UnstructuredGrid& grid,
const std::vector<double>& face_flux, const std::vector<double>& face_flux,
std::vector<double>& cell_velocity) std::vector<double>& cell_velocity)
{ {
const int dim = grid.dimensions; const int dim = grid.dimensions;
cell_velocity.clear(); cell_velocity.clear();
cell_velocity.resize(grid.number_of_cells*dim, 0.0); cell_velocity.resize(grid.number_of_cells*dim, 0.0);
for (int face = 0; face < grid.number_of_faces; ++face) { for (int face = 0; face < grid.number_of_faces; ++face) {
int c[2] = { grid.face_cells[2*face], grid.face_cells[2*face + 1] }; int c[2] = { grid.face_cells[2*face], grid.face_cells[2*face + 1] };
const double* fc = &grid.face_centroids[face*dim]; const double* fc = &grid.face_centroids[face*dim];
double flux = face_flux[face]; double flux = face_flux[face];
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
if (c[i] >= 0) { if (c[i] >= 0) {
const double* cc = &grid.cell_centroids[c[i]*dim]; const double* cc = &grid.cell_centroids[c[i]*dim];
for (int d = 0; d < dim; ++d) { for (int d = 0; d < dim; ++d) {
double v_contrib = fc[d] - cc[d]; double v_contrib = fc[d] - cc[d];
v_contrib *= flux/grid.cell_volumes[c[i]]; v_contrib *= flux/grid.cell_volumes[c[i]];
cell_velocity[c[i]*dim + d] += (i == 0) ? v_contrib : -v_contrib; cell_velocity[c[i]*dim + d] += (i == 0) ? v_contrib : -v_contrib;
} }
} }
} }
} }
} }
/// Extract a vector of water saturations from a vector of /// Extract a vector of water saturations from a vector of
/// interleaved water and oil saturations. /// interleaved water and oil saturations.
void toWaterSat(const std::vector<double>& sboth, void toWaterSat(const std::vector<double>& sboth,
std::vector<double>& sw) std::vector<double>& sw)
{ {
int num = sboth.size()/2; int num = sboth.size()/2;
sw.resize(num); sw.resize(num);
for (int i = 0; i < num; ++i) { for (int i = 0; i < num; ++i) {
sw[i] = sboth[2*i]; sw[i] = sboth[2*i];
} }
} }
/// Make a a vector of interleaved water and oil saturations from /// Make a a vector of interleaved water and oil saturations from
/// a vector of water saturations. /// a vector of water saturations.
void toBothSat(const std::vector<double>& sw, void toBothSat(const std::vector<double>& sw,
std::vector<double>& sboth) std::vector<double>& sboth)
{ {
int num = sw.size(); int num = sw.size();
sboth.resize(2*num); sboth.resize(2*num);
for (int i = 0; i < num; ++i) { for (int i = 0; i < num; ++i) {
sboth[2*i] = sw[i]; sboth[2*i] = sw[i];
sboth[2*i + 1] = 1.0 - sw[i]; sboth[2*i + 1] = 1.0 - sw[i];
} }
} }
@ -450,30 +450,30 @@ namespace Opm
if (np != 2) { if (np != 2) {
THROW("wellsToSrc() requires a 2 phase case."); THROW("wellsToSrc() requires a 2 phase case.");
} }
src.resize(num_cells); src.resize(num_cells);
for (int w = 0; w < wells.number_of_wells; ++w) { for (int w = 0; w < wells.number_of_wells; ++w) {
const int cur = wells.ctrls[w]->current; const int cur = wells.ctrls[w]->current;
if (wells.ctrls[w]->num != 1) { if (wells.ctrls[w]->num != 1) {
MESSAGE("In wellsToSrc(): well has more than one control, all but current control will be ignored."); MESSAGE("In wellsToSrc(): well has more than one control, all but current control will be ignored.");
} }
if (wells.ctrls[w]->type[cur] != RESERVOIR_RATE) { if (wells.ctrls[w]->type[cur] != RESERVOIR_RATE) {
THROW("In wellsToSrc(): well is something other than RESERVOIR_RATE."); THROW("In wellsToSrc(): well is something other than RESERVOIR_RATE.");
} }
if (wells.well_connpos[w+1] - wells.well_connpos[w] != 1) { if (wells.well_connpos[w+1] - wells.well_connpos[w] != 1) {
THROW("In wellsToSrc(): well has multiple perforations."); THROW("In wellsToSrc(): well has multiple perforations.");
} }
for (int p = 0; p < np; ++p) { for (int p = 0; p < np; ++p) {
if (wells.ctrls[w]->distr[np*cur + p] != 1.0) { if (wells.ctrls[w]->distr[np*cur + p] != 1.0) {
THROW("In wellsToSrc(): well not controlled on total rate."); THROW("In wellsToSrc(): well not controlled on total rate.");
} }
} }
double flow = wells.ctrls[w]->target[cur]; double flow = wells.ctrls[w]->target[cur];
if (wells.type[w] == INJECTOR) { if (wells.type[w] == INJECTOR) {
flow *= wells.comp_frac[np*w + 0]; // Obtaining water rate for inflow source. flow *= wells.comp_frac[np*w + 0]; // Obtaining water rate for inflow source.
} }
const double cell = wells.well_cells[wells.well_connpos[w]]; const double cell = wells.well_cells[wells.well_connpos[w]];
src[cell] = flow; src[cell] = flow;
} }
} }
@ -593,8 +593,10 @@ namespace Opm
{ {
int nw = well_bhp.size(); int nw = well_bhp.size();
ASSERT(nw == wells.number_of_wells); ASSERT(nw == wells.number_of_wells);
if (props.numPhases() != 2) { int np = props.numPhases();
THROW("WellReport for now assumes two phase flow."); const int max_np = 3;
if (np > max_np) {
THROW("WellReport for now assumes #phases <= " << max_np);
} }
const double* visc = props.viscosity(); const double* visc = props.viscosity();
std::vector<double> data_now; std::vector<double> data_now;
@ -605,7 +607,8 @@ namespace Opm
double well_rate_total = 0.0; double well_rate_total = 0.0;
double well_rate_water = 0.0; double well_rate_water = 0.0;
for (int perf = wells.well_connpos[w]; perf < wells.well_connpos[w + 1]; ++perf) { for (int perf = wells.well_connpos[w]; perf < wells.well_connpos[w + 1]; ++perf) {
const double perf_rate = well_perfrates[perf]*(unit::day/unit::second); const double perf_rate = unit::convert::to(well_perfrates[perf],
unit::cubic(unit::meter)/unit::day);
well_rate_total += perf_rate; well_rate_total += perf_rate;
if (perf_rate > 0.0) { if (perf_rate > 0.0) {
// Injection. // Injection.
@ -613,11 +616,14 @@ namespace Opm
} else { } else {
// Production. // Production.
const int cell = wells.well_cells[perf]; const int cell = wells.well_cells[perf];
double mob[2]; double mob[max_np];
props.relperm(1, &saturation[2*cell], &cell, mob, 0); props.relperm(1, &saturation[2*cell], &cell, mob, 0);
mob[0] /= visc[0]; double tmob = 0;
mob[1] /= visc[1]; for(int i = 0; i < np; ++i) {
const double fracflow = mob[0]/(mob[0] + mob[1]); mob[i] /= visc[i];
tmob += mob[i];
}
const double fracflow = mob[0]/tmob;
well_rate_water += perf_rate*fracflow; well_rate_water += perf_rate*fracflow;
} }
} }
@ -646,8 +652,10 @@ namespace Opm
// TODO: refactor, since this is almost identical to the other push(). // TODO: refactor, since this is almost identical to the other push().
int nw = well_bhp.size(); int nw = well_bhp.size();
ASSERT(nw == wells.number_of_wells); ASSERT(nw == wells.number_of_wells);
if (props.numPhases() != 2) { int np = props.numPhases();
THROW("WellReport for now assumes two phase flow."); const int max_np = 3;
if (np > max_np) {
THROW("WellReport for now assumes #phases <= " << max_np);
} }
std::vector<double> data_now; std::vector<double> data_now;
data_now.reserve(1 + 3*nw); data_now.reserve(1 + 3*nw);
@ -665,13 +673,16 @@ namespace Opm
} else { } else {
// Production. // Production.
const int cell = wells.well_cells[perf]; const int cell = wells.well_cells[perf];
double mob[2]; double mob[max_np];
props.relperm(1, &s[2*cell], &cell, mob, 0); props.relperm(1, &s[2*cell], &cell, mob, 0);
double visc[2]; double visc[max_np];
props.viscosity(1, &p[cell], &z[2*cell], &cell, visc, 0); props.viscosity(1, &p[cell], &z[2*cell], &cell, visc, 0);
mob[0] /= visc[0]; double tmob = 0;
mob[1] /= visc[1]; for(int i = 0; i < np; ++i) {
const double fracflow = mob[0]/(mob[0] + mob[1]); mob[i] /= visc[i];
tmob += mob[i];
}
const double fracflow = mob[0]/(tmob);
well_rate_water += perf_rate*fracflow; well_rate_water += perf_rate*fracflow;
} }
} }