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/utility/parameters/ParameterGroup.hpp>
namespace Opm
{
BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck,
const UnstructuredGrid& grid,
const bool use_spline)
const UnstructuredGrid& grid)
{
rock_.init(deck, grid);
pvt_.init(deck, use_spline);
// Unfortunate lack of pointer smartness here...
if (use_spline) {
SaturationPropsFromDeck<>* ptr = new SaturationPropsFromDeck<>();
ptr->init(deck, grid);
satprops_.reset(ptr);
} else {
SaturationPropsFromDeck<SatFuncSetNonuniform>* ptr
= new SaturationPropsFromDeck<SatFuncSetNonuniform>();
ptr->init(deck, grid);
satprops_.reset(ptr);
}
pvt_.init(deck, 1025);
SaturationPropsFromDeck<SatFuncStone2Uniform>* ptr
= new SaturationPropsFromDeck<SatFuncStone2Uniform>();
satprops_.reset(ptr);
ptr->init(deck, grid, 200);
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() << ").");
}
}
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()
{
}

View File

@ -26,6 +26,7 @@
#include <opm/core/fluid/blackoil/BlackoilPvtProperties.hpp>
#include <opm/core/fluid/SaturationPropsFromDeck.hpp>
#include <opm/core/eclipse/EclipseGridParser.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <boost/scoped_ptr.hpp>
struct UnstructuredGrid;
@ -39,13 +40,26 @@ namespace Opm
{
public:
/// Initialize from deck and grid.
/// \param deck Deck input parser
/// \param grid Grid to which property object applies, needed for the
/// \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.
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,
const UnstructuredGrid& grid,
const bool use_spline);
const parameter::ParameterGroup& param);
/// Destructor.
virtual ~BlackoilPropertiesFromDeck();

View File

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

View File

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

View File

@ -21,8 +21,11 @@
#define OPM_SATURATIONPROPSFROMDECK_HEADER_INCLUDED
#include <opm/core/fluid/SaturationPropsInterface.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <opm/core/eclipse/EclipseGridParser.hpp>
#include <opm/core/fluid/blackoil/BlackoilPhases.hpp>
#include <opm/core/fluid/SatFuncStone2.hpp>
#include <opm/core/fluid/SatFuncSimple.hpp>
#include <vector>
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.
/// Possible values for template argument:
/// SatFuncSetNonuniform,
/// SatFuncSetUniform (default).
template <class SatFuncSet = SatFuncSetUniform>
/// Possible values for template argument (for now):
/// SatFuncSetStone2Nonuniform,
/// SatFuncSetStone2Uniform.
/// SatFuncSetSimpleNonuniform,
/// SatFuncSetSimpleUniform.
template <class SatFuncSet>
class SaturationPropsFromDeck : public SaturationPropsInterface
{
public:
@ -53,12 +49,15 @@ namespace Opm
SaturationPropsFromDeck();
/// Initialize from deck and grid.
/// \param deck Deck input parser
/// \param grid Grid to which property object applies, needed for the
/// \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] 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,
const UnstructuredGrid& grid);
const UnstructuredGrid& grid,
const int samples);
/// \return P, the number of phases.
int numPhases() const;

View File

@ -29,57 +29,6 @@
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 ---------
@ -93,7 +42,8 @@ namespace Opm
/// Initialize from deck.
template <class SatFuncSet>
void SaturationPropsFromDeck<SatFuncSet>::init(const EclipseGridParser& deck,
const UnstructuredGrid& grid)
const UnstructuredGrid& grid,
const int samples)
{
phase_usage_ = phaseUsageFromDeck(deck);
@ -144,7 +94,7 @@ namespace Opm
// Initialize tables.
satfuncset_.resize(num_tables);
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();
/// 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.
int numPhases() const;

View File

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