This commit is contained in:
Kjetil Olsen Lye
2012-05-16 17:00:09 +02:00
19 changed files with 1198 additions and 73 deletions

View File

@@ -32,38 +32,44 @@ namespace Opm
{
public:
void init(const UnstructuredGrid& g)
void init(const UnstructuredGrid& g, int num_phases)
{
num_phases_ = num_phases;
press_.resize(g.number_of_cells, 0.0);
fpress_.resize(g.number_of_faces, 0.0);
flux_.resize(g.number_of_faces, 0.0);
sat_.resize(2 * g.number_of_cells, 0.0);
sat_.resize(num_phases_ * g.number_of_cells, 0.0);
for (int cell = 0; cell < g.number_of_cells; ++cell) {
sat_[2*cell + 1] = 1.0; // Defaulting oil saturations to 1.0.
// Defaulting the second saturation to 1.0.
// This will usually be oil in a water-oil case,
// gas in an oil-gas case.
// For proper initialization, one should not rely on this,
// but use available phase information instead.
sat_[num_phases_*cell + 1] = 1.0;
}
}
enum ExtremalSat { MinSat, MaxSat };
void setWaterSat(const std::vector<int>& cells,
void setFirstSat(const std::vector<int>& cells,
const Opm::IncompPropertiesInterface& props,
ExtremalSat es)
{
const int n = cells.size();
std::vector<double> smin(2*n);
std::vector<double> smax(2*n);
std::vector<double> smin(num_phases_*n);
std::vector<double> smax(num_phases_*n);
props.satRange(n, &cells[0], &smin[0], &smax[0]);
const double* svals = (es == MinSat) ? &smin[0] : &smax[0];
for (int ci = 0; ci < n; ++ci) {
const int cell = cells[ci];
sat_[2*cell] = svals[2*ci];
sat_[2*cell + 1] = 1.0 - sat_[2*cell];
sat_[num_phases_*cell] = svals[num_phases_*ci];
sat_[num_phases_*cell + 1] = 1.0 - sat_[num_phases_*cell];
}
}
int numPhases() const
{
return 2;
return num_phases_;
}
std::vector<double>& pressure () { return press_ ; }
@@ -77,6 +83,7 @@ namespace Opm
const std::vector<double>& saturation () const { return sat_ ; }
private:
int num_phases_;
std::vector<double> press_ ;
std::vector<double> fpress_;
std::vector<double> flux_ ;

53
opm/core/WellState.hpp Normal file
View File

@@ -0,0 +1,53 @@
/*
Copyright 2012 SINTEF ICT, Applied Mathematics.
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/>.
*/
#ifndef OPM_WELLSTATE_HEADER_INCLUDED
#define OPM_WELLSTATE_HEADER_INCLUDED
#include <opm/core/newwells.h>
#include <vector>
namespace Opm
{
class WellState
{
public:
void init(const Wells* wells)
{
if (wells) {
bhp_.resize(wells->number_of_wells);
perfrates_.resize(wells->well_connpos[wells->number_of_wells]);
}
}
std::vector<double>& bhp() { return bhp_; }
const std::vector<double>& bhp() const { return bhp_; }
std::vector<double>& perfRates() { return perfrates_; }
const std::vector<double>& perfRates() const { return perfrates_; }
private:
std::vector<double> bhp_;
std::vector<double> perfrates_;
};
} // namespace Opm
#endif // OPM_WELLSTATE_HEADER_INCLUDED

View File

@@ -962,15 +962,15 @@ namespace Opm
namespace
{
InjectionSpecification::InjectorType toInjectorType(std::string type)
InjectionSpecification::InjectorType toInjectorType(const std::string& type)
{
if (type == "OIL") {
if (type[0] == 'O') {
return InjectionSpecification::OIL;
}
if (type == "WATER") {
if (type[0] == 'W') {
return InjectionSpecification::WATER;
}
if (type == "GAS") {
if (type[0] == 'G') {
return InjectionSpecification::GAS;
}
THROW("Unknown type " << type << ", could not convert to SurfaceComponent");
@@ -1076,11 +1076,6 @@ namespace Opm
if (deck.hasField("WCONPROD")) {
WCONPROD wconprod = deck.getWCONPROD();
#if THIS_STATEMENT_IS_REALLY_NEEDED
std::cout << wconprod.wconprod.size() << std::endl;
#endif
for (size_t i = 0; i < wconprod.wconprod.size(); i++) {
if (wconprod.wconprod[i].well_ == name) {
WconprodLine line = wconprod.wconprod[i];

View File

@@ -250,8 +250,16 @@ namespace Opm
const int* global_cell = grid.global_cell;
const int* cpgdim = grid.cartdims;
std::map<int,int> cartesian_to_compressed;
for (int i = 0; i < grid.number_of_cells; ++i) {
cartesian_to_compressed.insert(std::make_pair(global_cell[i], i));
if (global_cell) {
for (int i = 0; i < grid.number_of_cells; ++i) {
cartesian_to_compressed.insert(std::make_pair(global_cell[i], i));
}
}
else {
for (int i = 0; i < grid.number_of_cells; ++i) {
cartesian_to_compressed.insert(std::make_pair(i, i));
}
}
// Get COMPDAT data
@@ -459,17 +467,17 @@ namespace Opm
// Set well component fraction.
double cf[3] = { 0.0, 0.0, 0.0 };
if (wci_line.injector_type_ == "WATER") {
if (wci_line.injector_type_[0] == 'W') {
if (!pu.phase_used[BlackoilPhases::Aqua]) {
THROW("Water phase not used, yet found water-injecting well.");
}
cf[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0;
} else if (wci_line.injector_type_ == "OIL") {
} else if (wci_line.injector_type_[0] == 'O') {
if (!pu.phase_used[BlackoilPhases::Liquid]) {
THROW("Oil phase not used, yet found oil-injecting well.");
}
cf[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0;
} else if (wci_line.injector_type_ == "GAS") {
} else if (wci_line.injector_type_[0] == 'G') {
if (!pu.phase_used[BlackoilPhases::Vapour]) {
THROW("Water phase not used, yet found water-injecting well.");
}

View File

@@ -214,6 +214,21 @@ namespace Opm
}
/// Obtain the range of allowable saturation values.
/// In cell cells[i], saturation of phase p is allowed to be
/// in the interval [smin[i*P + p], smax[i*P + p]].
/// \param[in] n Number of data points.
/// \param[in] cells Array of n cell indices.
/// \param[out] smin Array of nP minimum s values, array must be valid before calling.
/// \param[out] smax Array of nP maximum s values, array must be valid before calling.
void BlackoilPropertiesBasic::satRange(const int n,
const int* /*cells*/,
double* smin,
double* smax) const
{
satprops_.satRange(n, smin, smax);
}
} // namespace Opm

View File

@@ -150,6 +150,19 @@ namespace Opm
double* pc,
double* dpcds) const;
/// Obtain the range of allowable saturation values.
/// In cell cells[i], saturation of phase p is allowed to be
/// in the interval [smin[i*P + p], smax[i*P + p]].
/// \param[in] n Number of data points.
/// \param[in] cells Array of n cell indices.
/// \param[out] smin Array of nP minimum s values, array must be valid before calling.
/// \param[out] smax Array of nP maximum s values, array must be valid before calling.
virtual void satRange(const int n,
const int* cells,
double* smin,
double* smax) const;
private:
RockBasic rock_;
PvtPropertiesBasic pvt_;

View File

@@ -258,6 +258,21 @@ namespace Opm
}
/// Obtain the range of allowable saturation values.
/// In cell cells[i], saturation of phase p is allowed to be
/// in the interval [smin[i*P + p], smax[i*P + p]].
/// \param[in] n Number of data points.
/// \param[in] cells Array of n cell indices.
/// \param[out] smin Array of nP minimum s values, array must be valid before calling.
/// \param[out] smax Array of nP maximum s values, array must be valid before calling.
void BlackoilPropertiesFromDeck::satRange(const int n,
const int* cells,
double* smin,
double* smax) const
{
satprops_.satRange(n, cells, smin, smax);
}
} // namespace Opm

View File

@@ -146,6 +146,19 @@ namespace Opm
double* pc,
double* dpcds) const;
/// Obtain the range of allowable saturation values.
/// In cell cells[i], saturation of phase p is allowed to be
/// in the interval [smin[i*P + p], smax[i*P + p]].
/// \param[in] n Number of data points.
/// \param[in] cells Array of n cell indices.
/// \param[out] smin Array of nP minimum s values, array must be valid before calling.
/// \param[out] smax Array of nP maximum s values, array must be valid before calling.
virtual void satRange(const int n,
const int* cells,
double* smin,
double* smax) const;
private:
RockFromDeck rock_;
BlackoilPvtProperties pvt_;

View File

@@ -136,6 +136,19 @@ namespace Opm
const int* cells,
double* pc,
double* dpcds) const = 0;
/// Obtain the range of allowable saturation values.
/// In cell cells[i], saturation of phase p is allowed to be
/// in the interval [smin[i*P + p], smax[i*P + p]].
/// \param[in] n Number of data points.
/// \param[in] cells Array of n cell indices.
/// \param[out] smin Array of nP minimum s values, array must be valid before calling.
/// \param[out] smax Array of nP maximum s values, array must be valid before calling.
virtual void satRange(const int n,
const int* cells,
double* smin,
double* smax) const = 0;
};

View File

@@ -19,7 +19,8 @@
#include <opm/core/fluid/RockFromDeck.hpp>
#include <opm/core/eclipse/EclipseGridInspector.hpp>
#include <tr1/array>
namespace Opm
{
@@ -35,6 +36,8 @@ namespace Opm
PermeabilityKind fillTensor(const EclipseGridParser& parser,
std::vector<const std::vector<double>*>& tensor,
std::tr1::array<int,9>& kmap);
int numGlobalCells(const EclipseGridParser& parser);
} // anonymous namespace
@@ -82,10 +85,8 @@ namespace Opm
const std::vector<int>& global_cell,
double perm_threshold)
{
const int dim = 3;
EclipseGridInspector insp(parser);
std::tr1::array<int, 3> dims = insp.gridSize();
int num_global_cells = dims[0]*dims[1]*dims[2];
const int dim = 3;
const int num_global_cells = numGlobalCells(parser);
ASSERT (num_global_cells > 0);
permeability_.assign(dim * dim * global_cell.size(), 0.0);
@@ -330,6 +331,26 @@ namespace Opm
return kind;
}
int numGlobalCells(const EclipseGridParser& parser)
{
int ngc = -1;
if (parser.hasField("DIMENS")) {
const std::vector<int>&
dims = parser.getIntegerValue("DIMENS");
ngc = dims[0] * dims[1] * dims[2];
}
else if (parser.hasField("SPECGRID")) {
const SPECGRID& sgr = parser.getSPECGRID();
ngc = sgr.dimensions[ 0 ];
ngc *= sgr.dimensions[ 1 ];
ngc *= sgr.dimensions[ 2 ];
}
return ngc;
}
} // anonymous namespace
} // namespace Opm

View File

@@ -0,0 +1,202 @@
/*
Copyright 2012 SINTEF ICT, Applied Mathematics.
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/>.
*/
#include <opm/core/pressure/CompressibleTpfa.hpp>
#include <opm/core/pressure/tpfa/cfs_tpfa_residual.h>
#include <opm/core/pressure/tpfa/compr_quant_general.h>
#include <opm/core/pressure/tpfa/compr_source.h>
#include <opm/core/pressure/tpfa/trans_tpfa.h>
#include <opm/core/linalg/LinearSolverInterface.hpp>
#include <opm/core/linalg/sparse_sys.h>
#include <opm/core/utility/ErrorMacros.hpp>
#include <opm/core/newwells.h>
#include <opm/core/BlackoilState.hpp>
#include <opm/core/WellState.hpp>
#include <algorithm>
namespace Opm
{
/// Construct solver.
/// \param[in] g A 2d or 3d grid.
/// \param[in] permeability Array of permeability tensors, the array
/// should have size N*D^2, if D == g.dimensions
/// and N == g.number_of_cells.
/// \param[in] gravity Gravity vector. If nonzero, the array should
/// have D elements.
/// \param[in] wells The wells argument. Will be used in solution,
/// is ignored if NULL
CompressibleTpfa::CompressibleTpfa(const UnstructuredGrid& g,
const double* permeability,
const double* porevol,
const double* /* gravity */, // ???
const LinearSolverInterface& linsolver,
const struct Wells* wells,
const int num_phases)
: grid_(g),
porevol_(porevol),
linsolver_(linsolver),
htrans_(g.cell_facepos[ g.number_of_cells ]),
trans_ (g.number_of_faces),
wells_(wells)
{
if (wells_ && (wells_->number_of_phases != num_phases)) {
THROW("Inconsistent number of phases specified: "
<< wells_->number_of_phases << " != " << num_phases);
}
const int num_dofs = g.number_of_cells + (wells ? wells->number_of_wells : 0);
pressure_increment_.resize(num_dofs);
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
tpfa_htrans_compute(gg, permeability, &htrans_[0]);
tpfa_trans_compute(gg, &htrans_[0], &trans_[0]);
cfs_tpfa_res_wells w;
w.W = const_cast<struct Wells*>(wells_);
w.data = NULL;
h_ = cfs_tpfa_res_construct(gg, &w, num_phases);
}
/// Destructor.
CompressibleTpfa::~CompressibleTpfa()
{
cfs_tpfa_res_destroy(h_);
}
/// Solve pressure equation, by Newton iterations.
void CompressibleTpfa::solve(const double dt,
BlackoilState& state,
WellState& well_state)
{
// Set up dynamic data.
computePerSolveDynamicData();
computePerIterationDynamicData();
// Assemble J and F.
assemble(dt, state, well_state);
bool residual_ok = false; // Replace with tolerance check.
while (!residual_ok) {
// Solve for increment in Newton method:
// incr = x_{n+1} - x_{n} = -J^{-1}F
// (J is Jacobian matrix, F is residual)
solveIncrement();
// Update pressure vars with increment.
// Set up dynamic data.
computePerIterationDynamicData();
// Assemble J and F.
assemble(dt, state, well_state);
// Check for convergence.
// Include both tolerance check for residual
// and solution change.
}
// Write to output parameters.
// computeResults(...);
}
/// Compute per-solve dynamic properties.
void CompressibleTpfa::computePerSolveDynamicData()
{
}
/// Compute per-iteration dynamic properties.
void CompressibleTpfa::computePerIterationDynamicData()
{
}
/// Compute the residual and Jacobian.
void CompressibleTpfa::assemble(const double dt,
const BlackoilState& state,
const WellState& well_state)
{
const double* z = &state.surfacevol()[0];
const double* cell_press = &state.pressure()[0];
const double* well_bhp = well_state.bhp().empty() ? NULL : &well_state.bhp()[0];
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
CompletionData completion_data;
completion_data.gpot = &wellperf_gpot_[0];
completion_data.A = &wellperf_A_[0];
completion_data.phasemob = &wellperf_phasemob_[0];
cfs_tpfa_res_wells wells_tmp;
wells_tmp.W = const_cast<Wells*>(wells_);
wells_tmp.data = &completion_data;
cfs_tpfa_res_forces forces;
forces.wells = &wells_tmp;
forces.src = NULL; // Check if it is legal to leave it as NULL.
compr_quantities_gen cq;
cq.Ac = &cell_A_[0];
cq.dAc = &cell_dA_[0];
cq.Af = &face_A_[0];
cq.phasemobf = &face_phasemob_[0];
cq.voldiscr = &cell_voldisc_[0];
cfs_tpfa_res_assemble(gg, dt, &forces, z, &cq, &trans_[0],
&face_gravcap_[0], cell_press, well_bhp,
porevol_, h_);
}
/// Computes pressure_increment_.
void CompressibleTpfa::solveIncrement()
{
// Increment is equal to -J^{-1}F
linsolver_.solve(h_->J, h_->F, &pressure_increment_[0]);
std::transform(pressure_increment_.begin(), pressure_increment_.end(),
pressure_increment_.begin(), std::negate<double>());
}
/// Compute the output.
void CompressibleTpfa::computeResults(std::vector<double>& // pressure
,
std::vector<double>& // faceflux
,
std::vector<double>& // well_bhp
,
std::vector<double>& // well_rate
)
{
}
} // namespace Opm

View File

@@ -0,0 +1,119 @@
/*
Copyright 2012 SINTEF ICT, Applied Mathematics.
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/>.
*/
#ifndef OPM_COMPRESSIBLETPFA_HEADER_INCLUDED
#define OPM_COMPRESSIBLETPFA_HEADER_INCLUDED
#include <vector>
struct UnstructuredGrid;
struct cfs_tpfa_res_data;
struct Wells;
struct FlowBoundaryConditions;
namespace Opm
{
class BlackoilState;
class LinearSolverInterface;
class WellState;
/// Encapsulating a tpfa pressure solver for the compressible-fluid case.
/// Supports gravity, wells and simple sources as driving forces.
/// Below we use the shortcuts D for the number of dimensions, N
/// for the number of cells and F for the number of faces.
class CompressibleTpfa
{
public:
/// Construct solver.
/// \param[in] g A 2d or 3d grid.
/// \param[in] permeability Array of permeability tensors, the array
/// should have size N*D^2, if D == g.dimensions
/// and N == g.number_of_cells.
/// \param[in] gravity Gravity vector. If nonzero, the array should
/// have D elements.
/// \param[in] wells The wells argument. Will be used in solution,
/// is ignored if NULL
/// \param[in] num_phases Must be 2 or 3.
CompressibleTpfa(const UnstructuredGrid& g,
const double* porevol,
const double* permeability,
const double* gravity,
const LinearSolverInterface& linsolver,
const struct Wells* wells,
const int num_phases);
/// Destructor.
~CompressibleTpfa();
void solve(const double dt,
BlackoilState& state,
WellState& well_state);
private:
void computePerSolveDynamicData();
void computePerIterationDynamicData();
void assemble(const double dt,
const BlackoilState& state,
const WellState& well_state);
void solveIncrement();
void computeResults(std::vector<double>& pressure,
std::vector<double>& faceflux,
std::vector<double>& well_bhp,
std::vector<double>& well_rate);
// ------ Data that will remain unmodified after construction. ------
const UnstructuredGrid& grid_;
const double* porevol_;
const LinearSolverInterface& linsolver_;
std::vector<double> htrans_;
std::vector<double> trans_ ;
const Wells* wells_; // Outside may modify controls (only) between calls to solve().
// ------ Internal data for the cfs_tpfa_res solver. ------
struct cfs_tpfa_res_data* h_;
// ------ Data that will be modified for every solve. ------
std::vector<double> wellperf_gpot_;
// ------ Data that will be modified for every solver iteration. ------
// Gravity and capillary contributions (per face).
std::vector<double> face_gravcap_;
std::vector<double> wellperf_A_;
std::vector<double> wellperf_phasemob_;
std::vector<double> cell_A_;
std::vector<double> cell_dA_;
std::vector<double> face_A_;
std::vector<double> face_phasemob_;
std::vector<double> cell_voldisc_;
// The update to be applied to the pressures (cell and bhp).
std::vector<double> pressure_increment_;
};
} // namespace Opm
#endif // OPM_COMPRESSIBLETPFA_HEADER_INCLUDED

View File

@@ -606,6 +606,8 @@ compute_cell_contrib(struct UnstructuredGrid *G ,
pimpl->ratio->mat_row[ 0 ] += s * dt * dF1;
pimpl->ratio->mat_row[ off ] += s * dt * dF2;
dv += 2 * np; /* '2' == number of one-sided derivatives. */
}
}
}

View File

@@ -28,8 +28,9 @@ namespace Opm
namespace parameter { class ParameterGroup; }
class EclipseGridParser;
class IncompPropertiesInterface;
class BlackoilPropertiesInterface;
/// Initialize a state from parameters.
/// Initialize a two-phase state from parameters.
/// The following parameters are accepted (defaults):
/// convection_testcase (false) Water in the 'left' part of the grid.
/// ref_pressure (100) Initial pressure in bar for all cells
@@ -52,25 +53,49 @@ namespace Opm
/// In all three cases, pressure is initialised hydrostatically.
/// In case 2) and 3), the depth of the first cell is used as reference depth.
template <class State>
void initStateTwophaseBasic(const UnstructuredGrid& grid,
const IncompPropertiesInterface& props,
const parameter::ParameterGroup& param,
const double gravity,
State& state);
void initStateBasic(const UnstructuredGrid& grid,
const IncompPropertiesInterface& props,
const parameter::ParameterGroup& param,
const double gravity,
State& state);
/// Initialize a state from input deck.
/// Initialize a blackoil state from parameters.
/// The following parameters are accepted (defaults):
/// convection_testcase (false) Water in the 'left' part of the grid.
/// ref_pressure (100) Initial pressure in bar for all cells
/// (if convection_testcase is true),
/// or pressure at woc depth.
/// water_oil_contact (none) Depth of water-oil contact (woc).
/// If convection_testcase is true, the saturation is initialised
/// as indicated, and pressure is initialised to a constant value
/// ('ref_pressure').
/// Otherwise we have 2 cases:
/// 1) If 'water_oil_contact' is given, saturation is initialised
/// accordingly.
/// 2) Water saturation is set to minimum.
/// In both cases, pressure is initialised hydrostatically.
/// In case 2), the depth of the first cell is used as reference depth.
template <class State>
void initStateBasic(const UnstructuredGrid& grid,
const BlackoilPropertiesInterface& props,
const parameter::ParameterGroup& param,
const double gravity,
State& state);
/// Initialize a two-phase state from input deck.
/// If EQUIL is present:
/// - saturation is set according to the water-oil contact,
/// - pressure is set to hydrostatic equilibrium.
/// Otherwise:
/// - saturation is set according to SWAT,
/// - pressure is set according to PRESSURE.
template <class State>
void initStateTwophaseFromDeck(const UnstructuredGrid& grid,
const IncompPropertiesInterface& props,
const EclipseGridParser& deck,
const double gravity,
State& state);
template <class Props, class State>
void initStateFromDeck(const UnstructuredGrid& grid,
const Props& props,
const EclipseGridParser& deck,
const double gravity,
State& state);
} // namespace Opm

View File

@@ -25,9 +25,11 @@
#include <opm/core/grid.h>
#include <opm/core/eclipse/EclipseGridParser.hpp>
#include <opm/core/utility/ErrorMacros.hpp>
#include <opm/core/utility/MonotCubicInterpolator.hpp>
#include <opm/core/utility/Units.hpp>
#include <opm/core/fluid/IncompPropertiesInterface.hpp>
#include <opm/core/fluid/BlackoilPropertiesInterface.hpp>
#include <cmath>
namespace Opm
{
@@ -62,9 +64,9 @@ namespace Opm
// Initialize saturations so that there is water below woc,
// and oil above.
// If invert is true, water is instead above, oil below.
template <class State>
template <class Props, class State>
void initWaterOilContact(const UnstructuredGrid& grid,
const IncompPropertiesInterface& props,
const Props& props,
const double woc,
const WaterInit waterinit,
State& state)
@@ -73,10 +75,10 @@ namespace Opm
std::vector<int> water;
std::vector<int> oil;
// Assuming that water should go below the woc, but warning if oil is heavier.
if (props.density()[0] < props.density()[1]) {
std::cout << "*** warning: water density is less than oil density, "
"but initialising water below woc." << std::endl;
}
// if (props.density()[0] < props.density()[1]) {
// std::cout << "*** warning: water density is less than oil density, "
// "but initialising water below woc." << std::endl;
// }
switch (waterinit) {
case WaterBelow:
cellsBelowAbove(grid, woc, water, oil);
@@ -85,10 +87,11 @@ namespace Opm
cellsBelowAbove(grid, woc, oil, water);
}
// Set saturations.
state.setWaterSat(oil, props, State::MinSat);
state.setWaterSat(water, props, State::MaxSat);
state.setFirstSat(oil, props, State::MinSat);
state.setFirstSat(water, props, State::MaxSat);
}
// Initialize hydrostatic pressures depending only on gravity,
// (constant) phase densities and a water-oil contact depth.
// The pressure difference between points is equal to
@@ -123,11 +126,146 @@ namespace Opm
}
}
// Facade to initHydrostaticPressure() taking a property object,
// for similarity to initHydrostaticPressure() for compressible fluids.
template <class State>
void initHydrostaticPressure(const UnstructuredGrid& grid,
const IncompPropertiesInterface& props,
const double woc,
const double gravity,
const double datum_z,
const double datum_p,
State& state)
{
const double* densities = props.density();
initHydrostaticPressure(grid, densities, woc, gravity, datum_z, datum_p, state);
}
// Helper functor for initHydrostaticPressure() for compressible fluids.
struct Density
{
const BlackoilPropertiesInterface& props_;
Density(const BlackoilPropertiesInterface& props) : props_(props) {}
double operator()(const double pressure, const int phase)
{
ASSERT(props_.numPhases() == 2);
const double surfvol[2][2] = { { 1.0, 0.0 },
{ 0.0, 1.0 } };
// We do not handle multi-region PVT/EQUIL at this point.
const int* cells = 0;
double A[4] = { 0.0 };
props_.matrix(1, &pressure, surfvol[phase], cells, A, 0);
double rho[2] = { 0.0 };
props_.density(1, A, rho);
return rho[phase];
}
};
// Initialize hydrostatic pressures depending only on gravity,
// phase densities that may vary with pressure and a water-oil
// contact depth. The pressure ODE is given as
// \grad p = \rho g \grad z
// where rho is the oil density above the woc, water density
// below woc. Note that even if there is (immobile) water in
// the oil zone, it does not contribute to the pressure there.
template <class State>
void initHydrostaticPressure(const UnstructuredGrid& grid,
const BlackoilPropertiesInterface& props,
const double woc,
const double gravity,
const double datum_z,
const double datum_p,
State& state)
{
ASSERT(props.numPhases() == 2);
// Obtain max and min z for which we will need to compute p.
const int num_cells = grid.number_of_cells;
const int dim = grid.dimensions;
double zlim[2] = { 1e100, -1e100 };
for (int c = 0; c < num_cells; ++c) {
const double z = grid.cell_centroids[dim*c + dim - 1];
zlim[0] = std::min(zlim[0], z);
zlim[1] = std::max(zlim[1], z);
}
// We'll use a minimum stepsize of 1/100 of the z range.
const double hmin = (zlim[1] - zlim[0])/100.0;
// Store p(z) results in an associative array.
std::map<double, double> press_by_z;
press_by_z[datum_z] = datum_p;
// Set up density evaluator.
Density rho(props);
// Solve the ODE from datum_z to woc.
int phase = (datum_z > woc) ? 0 : 1;
int num_steps = int(std::ceil(std::fabs(woc - datum_z)/hmin));
double pval = datum_p;
double zval = datum_z;
double h = (woc - datum_z)/double(num_steps);
for (int i = 0; i < num_steps; ++i) {
zval += h;
const double dp = rho(pval, phase)*gravity;
pval += h*dp;
press_by_z[zval] = pval;
}
double woc_p = pval;
// Solve the ODE from datum_z to the end of the interval.
double z_end = (datum_z > woc) ? zlim[1] : zlim[0];
num_steps = int(std::ceil(std::fabs(z_end - datum_z)/hmin));
pval = datum_p;
zval = datum_z;
h = (z_end - datum_z)/double(num_steps);
for (int i = 0; i < num_steps; ++i) {
zval += h;
const double dp = rho(pval, phase)*gravity;
pval += h*dp;
press_by_z[zval] = pval;
}
// Solve the ODE from woc to the other end of the interval.
// Switching phase and z_end.
phase = (phase + 1) % 2;
z_end = (datum_z > woc) ? zlim[0] : zlim[1];
pval = woc_p;
zval = woc;
h = (z_end - datum_z)/double(num_steps);
for (int i = 0; i < num_steps; ++i) {
zval += h;
const double dp = rho(pval, phase)*gravity;
pval += h*dp;
press_by_z[zval] = pval;
}
// Create monotone spline for interpolating solution.
std::vector<double> zv, pv;
zv.reserve(press_by_z.size());
pv.reserve(press_by_z.size());
for (std::map<double, double>::const_iterator it = press_by_z.begin();
it != press_by_z.end(); ++it) {
zv.push_back(it->first);
pv.push_back(it->second);
}
MonotCubicInterpolator press(zv, pv);
// Evaluate pressure at each cell centroid.
std::vector<double>& p = state.pressure();
for (int c = 0; c < num_cells; ++c) {
const double z = grid.cell_centroids[dim*c + dim - 1];
p[c] = press(z);
}
}
} // anonymous namespace
/// Initialize a state from parameters.
/// Initialize a twophase state from parameters.
/// The following parameters are accepted (defaults):
/// convection_testcase (false) Water in the 'left' part of the grid.
/// ref_pressure (100) Initial pressure in bar for all cells
@@ -150,23 +288,24 @@ namespace Opm
/// In all three cases, pressure is initialised hydrostatically.
/// In case 2) and 3), the depth of the first cell is used as reference depth.
template <class State>
void initStateTwophaseBasic(const UnstructuredGrid& grid,
const IncompPropertiesInterface& props,
const parameter::ParameterGroup& param,
const double gravity,
State& state)
void initStateBasic(const UnstructuredGrid& grid,
const IncompPropertiesInterface& props,
const parameter::ParameterGroup& param,
const double gravity,
State& state)
{
if (state.numPhases() != 2) {
THROW("initStateTwophaseFromDeck(): state must have two phases.");
const int num_phases = props.numPhases();
if (num_phases != 2) {
THROW("initStateTwophaseBasic(): currently handling only two-phase scenarios.");
}
state.init(grid);
state.init(grid, num_phases);
const int num_cells = props.numCells();
// By default: initialise water saturation to minimum everywhere.
std::vector<int> all_cells(num_cells);
for (int i = 0; i < num_cells; ++i) {
all_cells[i] = i;
}
state.setWaterSat(all_cells, props, State::MinSat);
state.setFirstSat(all_cells, props, State::MinSat);
const bool convection_testcase = param.getDefault("convection_testcase", false);
const bool segregation_testcase = param.getDefault("segregation_testcase", false);
if (convection_testcase) {
@@ -182,7 +321,7 @@ namespace Opm
left_cells.push_back(cell);
}
}
state.setWaterSat(left_cells, props, State::MaxSat);
state.setFirstSat(left_cells, props, State::MaxSat);
const double init_p = param.getDefault("ref_pressure", 100)*unit::barsa;
std::fill(state.pressure().begin(), state.pressure().end(), init_p);
} else if (segregation_testcase) {
@@ -239,6 +378,84 @@ namespace Opm
}
/// Initialize a blackoil state from parameters.
/// The following parameters are accepted (defaults):
/// convection_testcase (false) Water in the 'left' part of the grid.
/// ref_pressure (100) Initial pressure in bar for all cells
/// (if convection_testcase is true),
/// or pressure at woc depth.
/// water_oil_contact (none) Depth of water-oil contact (woc).
/// If convection_testcase is true, the saturation is initialised
/// as indicated, and pressure is initialised to a constant value
/// ('ref_pressure').
/// Otherwise we have 2 cases:
/// 1) If 'water_oil_contact' is given, saturation is initialised
/// accordingly.
/// 2) Water saturation is set to minimum.
/// In both cases, pressure is initialised hydrostatically.
/// In case 2), the depth of the first cell is used as reference depth.
template <class State>
void initStateBasic(const UnstructuredGrid& grid,
const BlackoilPropertiesInterface& props,
const parameter::ParameterGroup& param,
const double gravity,
State& state)
{
// TODO: Refactor to exploit similarity with IncompProp* case.
const int num_phases = props.numPhases();
if (num_phases != 2) {
THROW("initStateTwophaseBasic(): currently handling only two-phase scenarios.");
}
state.init(grid, num_phases);
const int num_cells = props.numCells();
// By default: initialise water saturation to minimum everywhere.
std::vector<int> all_cells(num_cells);
for (int i = 0; i < num_cells; ++i) {
all_cells[i] = i;
}
state.setFirstSat(all_cells, props, State::MinSat);
const bool convection_testcase = param.getDefault("convection_testcase", false);
if (convection_testcase) {
// Initialise water saturation to max in the 'left' part.
std::vector<int> left_cells;
left_cells.reserve(num_cells/2);
const int *glob_cell = grid.global_cell;
const int* cd = grid.cartdims;
for (int cell = 0; cell < num_cells; ++cell) {
const int gc = glob_cell == 0 ? cell : glob_cell[cell];
bool left = (gc % cd[0]) < cd[0]/2;
if (left) {
left_cells.push_back(cell);
}
}
state.setFirstSat(left_cells, props, State::MaxSat);
const double init_p = param.getDefault("ref_pressure", 100)*unit::barsa;
std::fill(state.pressure().begin(), state.pressure().end(), init_p);
} else if (param.has("water_oil_contact")) {
// Warn against error-prone usage.
if (gravity == 0.0) {
std::cout << "**** Warning: running gravity convection scenario, but gravity is zero." << std::endl;
}
if (grid.cartdims[2] <= 1) {
std::cout << "**** Warning: running gravity convection scenario, which expects nz > 1." << std::endl;
}
// Initialise water saturation to max below water-oil contact.
const double woc = param.get<double>("water_oil_contact");
initWaterOilContact(grid, props, woc, WaterBelow, state);
// Initialise pressure to hydrostatic state.
const double ref_p = param.getDefault("ref_pressure", 100)*unit::barsa;
initHydrostaticPressure(grid, props, woc, gravity, woc, ref_p, state);
} else {
// Use default: water saturation is minimum everywhere.
// Initialise pressure to hydrostatic state.
const double ref_p = param.getDefault("ref_pressure", 100)*unit::barsa;
const double ref_z = grid.cell_centroids[0 + grid.dimensions - 1];
const double woc = -1e100;
initHydrostaticPressure(grid, props, woc, gravity, ref_z, ref_p, state);
}
}
/// Initialize a state from input deck.
/// If EQUIL is present:
/// - saturation is set according to the water-oil contact,
@@ -246,17 +463,18 @@ namespace Opm
/// Otherwise:
/// - saturation is set according to SWAT,
/// - pressure is set according to PRESSURE.
template <class State>
void initStateTwophaseFromDeck(const UnstructuredGrid& grid,
const IncompPropertiesInterface& props,
const EclipseGridParser& deck,
const double gravity,
State& state)
template <class Props, class State>
void initStateFromDeck(const UnstructuredGrid& grid,
const Props& props,
const EclipseGridParser& deck,
const double gravity,
State& state)
{
if (state.numPhases() != 2) {
THROW("initStateTwophaseFromDeck(): state must have two phases.");
const int num_phases = props.numPhases();
if (num_phases != 2) {
THROW("initStateFromDeck(): currently handling only two-phase scenarios.");
}
state.init(grid);
state.init(grid, num_phases);
if (deck.hasField("EQUIL")) {
// Set saturations depending on oil-water contact.
const EQUIL& equil= deck.getEQUIL();
@@ -268,7 +486,7 @@ namespace Opm
// Set pressure depending on densities and depths.
const double datum_z = equil.equil[0].datum_depth_;
const double datum_p = equil.equil[0].datum_depth_pressure_;
initHydrostaticPressure(grid, props.density(), woc, gravity, datum_z, datum_p, state);
initHydrostaticPressure(grid, props, woc, gravity, datum_z, datum_p, state);
} else if (deck.hasField("SWAT") && deck.hasField("PRESSURE")) {
// Set saturations from SWAT, pressure from PRESSURE.
std::vector<double>& s = state.saturation();
@@ -283,7 +501,7 @@ namespace Opm
p[c] = p_deck[c_deck];
}
} else {
THROW("initStateTwophaseFromDeck(): we must either have EQUIL, or both SWAT and PRESSURE.");
THROW("initStateFromDeck(): we must either have EQUIL, or both SWAT and PRESSURE.");
}
}

View File

@@ -22,6 +22,7 @@
#include <opm/core/grid.h>
#include <opm/core/newwells.h>
#include <opm/core/fluid/IncompPropertiesInterface.hpp>
#include <opm/core/fluid/BlackoilPropertiesInterface.hpp>
#include <opm/core/fluid/RockCompressibility.hpp>
#include <opm/core/utility/ErrorMacros.hpp>
#include <algorithm>
@@ -614,6 +615,60 @@ namespace Opm
void WellReport::push(const BlackoilPropertiesInterface& props,
const Wells& wells,
const std::vector<double>& p,
const std::vector<double>& z,
const std::vector<double>& s,
const double time,
const std::vector<double>& well_bhp,
const std::vector<double>& well_perfrates)
{
// 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.");
}
std::vector<double> data_now;
data_now.reserve(1 + 3*nw);
data_now.push_back(time/unit::day);
for (int w = 0; w < nw; ++w) {
data_now.push_back(well_bhp[w]/(unit::barsa));
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);
well_rate_total += perf_rate;
if (perf_rate > 0.0) {
// Injection.
well_rate_water += perf_rate*wells.comp_frac[0];
} else {
// Production.
const int cell = wells.well_cells[perf];
double mob[2];
props.relperm(1, &s[2*cell], &cell, mob, 0);
double visc[2];
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]);
well_rate_water += perf_rate*fracflow;
}
}
data_now.push_back(well_rate_total);
if (well_rate_total == 0.0) {
data_now.push_back(0.0);
} else {
data_now.push_back(well_rate_water/well_rate_total);
}
}
data_.push_back(data_now);
}
void WellReport::write(std::ostream& os) const
{
const int sz = data_.size();

View File

@@ -30,6 +30,7 @@ namespace Opm
{
class IncompPropertiesInterface;
class BlackoilPropertiesInterface;
class RockCompressibility;
/// @brief Computes pore volume of all cells in a grid.
@@ -247,6 +248,15 @@ namespace Opm
const double time,
const std::vector<double>& well_bhp,
const std::vector<double>& well_perfrates);
/// Add a report point (compressible fluids).
void push(const BlackoilPropertiesInterface& props,
const Wells& wells,
const std::vector<double>& p,
const std::vector<double>& z,
const std::vector<double>& s,
const double time,
const std::vector<double>& well_bhp,
const std::vector<double>& well_perfrates);
/// Write report to a stream.
void write(std::ostream& os) const;
private:

View File

@@ -0,0 +1,224 @@
/*
Copyright 2012 SINTEF ICT, Applied Mathematics.
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/>.
*/
#include <opm/core/utility/miscUtilitiesBlackoil.hpp>
#include <opm/core/utility/Units.hpp>
#include <opm/core/grid.h>
#include <opm/core/fluid/BlackoilPropertiesInterface.hpp>
#include <opm/core/utility/ErrorMacros.hpp>
#include <algorithm>
#include <functional>
#include <cmath>
#include <iterator>
namespace Opm
{
/// @brief Computes injected and produced volumes of all phases.
/// Note 1: assumes that only the first phase is injected.
/// Note 2: assumes that transport has been done with an
/// implicit method, i.e. that the current state
/// gives the mobilities used for the preceding timestep.
/// @param[in] props fluid and rock properties.
/// @param[in] p pressure (one value per cell)
/// @param[in] z surface-volume values (for all P phases)
/// @param[in] s saturation values (for all P phases)
/// @param[in] src if < 0: total outflow, if > 0: first phase inflow.
/// @param[in] dt timestep used
/// @param[out] injected must point to a valid array with P elements,
/// where P = s.size()/src.size().
/// @param[out] produced must also point to a valid array with P elements.
void computeInjectedProduced(const BlackoilPropertiesInterface& props,
const std::vector<double>& press,
const std::vector<double>& z,
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);
std::vector<double> visc(np);
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);
props.viscosity(1, &press[c], &z[np*c], &c, &visc[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;
}
}
}
}
/// @brief Computes total mobility for a set of saturation values.
/// @param[in] props rock and fluid properties
/// @param[in] cells cells with which the saturation values are associated
/// @param[in] p pressure (one value per cell)
/// @param[in] z surface-volume values (for all P phases)
/// @param[in] s saturation values (for all phases)
/// @param[out] totmob total mobilities.
void computeTotalMobility(const Opm::BlackoilPropertiesInterface& props,
const std::vector<int>& cells,
const std::vector<double>& press,
const std::vector<double>& z,
const std::vector<double>& s,
std::vector<double>& totmob)
{
std::vector<double> pmobc;
computePhaseMobilities(props, cells, press, z, s, pmobc);
const std::size_t np = props.numPhases();
const std::vector<int>::size_type nc = cells.size();
totmob.clear();
totmob.resize(nc, 0.0);
for (std::vector<int>::size_type c = 0; c < nc; ++c) {
for (std::size_t p = 0; p < np; ++p) {
totmob[ c ] += pmobc[c*np + p];
}
}
}
/*
/// @brief Computes total mobility and omega for a set of saturation values.
/// @param[in] props rock and fluid properties
/// @param[in] cells cells with which the saturation values are associated
/// @param[in] p pressure (one value per cell)
/// @param[in] z surface-volume values (for all P phases)
/// @param[in] s saturation values (for all phases)
/// @param[out] totmob total mobility
/// @param[out] omega fractional-flow weighted fluid densities.
void computeTotalMobilityOmega(const Opm::BlackoilPropertiesInterface& props,
const std::vector<int>& cells,
const std::vector<double>& p,
const std::vector<double>& z,
const std::vector<double>& s,
std::vector<double>& totmob,
std::vector<double>& omega)
{
std::vector<double> pmobc;
computePhaseMobilities(props, cells, p, z, s, pmobc);
const std::size_t np = props.numPhases();
const std::vector<int>::size_type nc = cells.size();
totmob.clear();
totmob.resize(nc, 0.0);
omega.clear();
omega.resize(nc, 0.0);
const double* rho = props.density();
for (std::vector<int>::size_type c = 0; c < nc; ++c) {
for (std::size_t p = 0; p < np; ++p) {
totmob[ c ] += pmobc[c*np + p];
omega [ c ] += pmobc[c*np + p] * rho[ p ];
}
omega[ c ] /= totmob[ c ];
}
}
*/
/// @brief Computes phase mobilities for a set of saturation values.
/// @param[in] props rock and fluid properties
/// @param[in] cells cells with which the saturation values are associated
/// @param[in] p pressure (one value per cell)
/// @param[in] z surface-volume values (for all P phases)
/// @param[in] s saturation values (for all phases)
/// @param[out] pmobc phase mobilities (for all phases).
void computePhaseMobilities(const Opm::BlackoilPropertiesInterface& props,
const std::vector<int>& cells,
const std::vector<double>& p,
const std::vector<double>& z,
const std::vector<double>& s,
std::vector<double>& pmobc)
{
const int nc = props.numCells();
const int np = props.numPhases();
ASSERT (int(s.size()) == nc * np);
std::vector<double> mu(nc*np);
props.viscosity(nc, &p[0], &z[0], &cells[0], &mu[0], 0);
pmobc.clear();
pmobc.resize(nc*np, 0.0);
double* dpmobc = 0;
props.relperm(nc, &s[0], &cells[0],
&pmobc[0], dpmobc);
std::transform(pmobc.begin(), pmobc.end(),
mu.begin(),
pmobc.begin(),
std::divides<double>());
}
/// Computes the fractional flow for each cell in the cells argument
/// @param[in] props rock and fluid properties
/// @param[in] cells cells with which the saturation values are associated
/// @param[in] p pressure (one value per cell)
/// @param[in] z surface-volume values (for all P phases)
/// @param[in] s saturation values (for all phases)
/// @param[out] fractional_flow the fractional flow for each phase for each cell.
void computeFractionalFlow(const Opm::BlackoilPropertiesInterface& props,
const std::vector<int>& cells,
const std::vector<double>& p,
const std::vector<double>& z,
const std::vector<double>& s,
std::vector<double>& fractional_flows)
{
const int num_phases = props.numPhases();
std::vector<double> pc_mobs(cells.size() * num_phases);
computePhaseMobilities(props, cells, p, z, s, pc_mobs);
fractional_flows.resize(cells.size() * num_phases);
for (size_t i = 0; i < cells.size(); ++i) {
double phase_sum = 0.0;
for (int phase = 0; phase < num_phases; ++phase) {
phase_sum += pc_mobs[i * num_phases + phase];
}
for (int phase = 0; phase < num_phases; ++phase) {
fractional_flows[i * num_phases + phase] = pc_mobs[i * num_phases + phase] / phase_sum;
}
}
}
} // namespace Opm

View File

@@ -0,0 +1,117 @@
/*
Copyright 2012 SINTEF ICT, Applied Mathematics.
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/>.
*/
#ifndef OPM_MISCUTILITIESBLACKOIL_HEADER_INCLUDED
#define OPM_MISCUTILITIESBLACKOIL_HEADER_INCLUDED
#include <vector>
struct UnstructuredGrid;
namespace Opm
{
class BlackoilPropertiesInterface;
/// @brief Computes injected and produced volumes of all phases.
/// Note 1: assumes that only the first phase is injected.
/// Note 2: assumes that transport has been done with an
/// implicit method, i.e. that the current state
/// gives the mobilities used for the preceding timestep.
/// @param[in] props fluid and rock properties.
/// @param[in] p pressure (one value per cell)
/// @param[in] z surface-volume values (for all P phases)
/// @param[in] s saturation values (for all P phases)
/// @param[in] src if < 0: total outflow, if > 0: first phase inflow.
/// @param[in] dt timestep used
/// @param[out] injected must point to a valid array with P elements,
/// where P = s.size()/src.size().
/// @param[out] produced must also point to a valid array with P elements.
void computeInjectedProduced(const BlackoilPropertiesInterface& props,
const std::vector<double>& p,
const std::vector<double>& z,
const std::vector<double>& s,
const std::vector<double>& src,
const double dt,
double* injected,
double* produced);
/// @brief Computes total mobility for a set of saturation values.
/// @param[in] props rock and fluid properties
/// @param[in] cells cells with which the saturation values are associated
/// @param[in] p pressure (one value per cell)
/// @param[in] z surface-volume values (for all P phases)
/// @param[in] s saturation values (for all phases)
/// @param[out] totmob total mobilities.
void computeTotalMobility(const Opm::BlackoilPropertiesInterface& props,
const std::vector<int>& cells,
const std::vector<double>& p,
const std::vector<double>& z,
const std::vector<double>& s,
std::vector<double>& totmob);
/// @brief Computes total mobility and omega for a set of saturation values.
/// @param[in] props rock and fluid properties
/// @param[in] cells cells with which the saturation values are associated
/// @param[in] p pressure (one value per cell)
/// @param[in] z surface-volume values (for all P phases)
/// @param[in] s saturation values (for all phases)
/// @param[out] totmob total mobility
/// @param[out] omega fractional-flow weighted fluid densities.
void computeTotalMobilityOmega(const Opm::BlackoilPropertiesInterface& props,
const std::vector<int>& cells,
const std::vector<double>& p,
const std::vector<double>& z,
const std::vector<double>& s,
std::vector<double>& totmob,
std::vector<double>& omega);
/// @brief Computes phase mobilities for a set of saturation values.
/// @param[in] props rock and fluid properties
/// @param[in] cells cells with which the saturation values are associated
/// @param[in] p pressure (one value per cell)
/// @param[in] z surface-volume values (for all P phases)
/// @param[in] s saturation values (for all phases)
/// @param[out] pmobc phase mobilities (for all phases).
void computePhaseMobilities(const Opm::BlackoilPropertiesInterface& props,
const std::vector<int>& cells,
const std::vector<double>& p,
const std::vector<double>& z,
const std::vector<double>& s,
std::vector<double>& pmobc);
/// Computes the fractional flow for each cell in the cells argument
/// @param[in] props rock and fluid properties
/// @param[in] cells cells with which the saturation values are associated
/// @param[in] p pressure (one value per cell)
/// @param[in] z surface-volume values (for all P phases)
/// @param[in] s saturation values (for all phases)
/// @param[out] fractional_flow the fractional flow for each phase for each cell.
void computeFractionalFlow(const Opm::BlackoilPropertiesInterface& props,
const std::vector<int>& cells,
const std::vector<double>& p,
const std::vector<double>& z,
const std::vector<double>& s,
std::vector<double>& fractional_flows);
} // namespace Opm
#endif // OPM_MISCUTILITIESBLACKOIL_HEADER_INCLUDED