mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
bf70195448
@ -36,8 +36,6 @@ 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
|
||||
|
||||
|
||||
@ -87,7 +85,7 @@ namespace Opm
|
||||
double perm_threshold)
|
||||
{
|
||||
const int dim = 3;
|
||||
const int num_global_cells = numGlobalCells(parser);
|
||||
const int num_global_cells = grid.cartdims[0]*grid.cartdims[1]*grid.cartdims[2];
|
||||
const int nc = grid.number_of_cells;
|
||||
|
||||
ASSERT (num_global_cells > 0);
|
||||
@ -334,26 +332,6 @@ 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
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <opm/core/newwells.h>
|
||||
#include <opm/core/simulator/BlackoilState.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/core/fluid/RockCompressibility.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@ -58,6 +59,7 @@ namespace Opm
|
||||
/// to change.
|
||||
CompressibleTpfa::CompressibleTpfa(const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
const LinearSolverInterface& linsolver,
|
||||
const double residual_tol,
|
||||
const double change_tol,
|
||||
@ -66,6 +68,7 @@ namespace Opm
|
||||
const struct Wells* wells)
|
||||
: grid_(grid),
|
||||
props_(props),
|
||||
rock_comp_props_(rock_comp_props),
|
||||
linsolver_(linsolver),
|
||||
residual_tol_(residual_tol),
|
||||
change_tol_(change_tol),
|
||||
@ -74,8 +77,8 @@ namespace Opm
|
||||
wells_(wells),
|
||||
htrans_(grid.cell_facepos[ grid.number_of_cells ]),
|
||||
trans_ (grid.number_of_faces),
|
||||
porevol_(grid.number_of_cells),
|
||||
allcells_(grid.number_of_cells)
|
||||
allcells_(grid.number_of_cells),
|
||||
singular_(false)
|
||||
{
|
||||
if (wells_ && (wells_->number_of_phases != props.numPhases())) {
|
||||
THROW("Inconsistent number of phases specified (wells vs. props): "
|
||||
@ -86,7 +89,12 @@ namespace Opm
|
||||
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
|
||||
tpfa_htrans_compute(gg, props.permeability(), &htrans_[0]);
|
||||
tpfa_trans_compute(gg, &htrans_[0], &trans_[0]);
|
||||
computePorevolume(grid_, props.porosity(), porevol_);
|
||||
// If we have rock compressibility, pore volumes are updated
|
||||
// in the compute*() methods, otherwise they are constant and
|
||||
// hence may be computed here.
|
||||
if (rock_comp_props_ == NULL || !rock_comp_props_->isActive()) {
|
||||
computePorevolume(grid_, props.porosity(), porevol_);
|
||||
}
|
||||
for (int c = 0; c < grid.number_of_cells; ++c) {
|
||||
allcells_[c] = c;
|
||||
}
|
||||
@ -182,6 +190,21 @@ namespace Opm
|
||||
|
||||
|
||||
|
||||
/// @brief After solve(), was the resulting pressure singular.
|
||||
/// Returns true if the pressure is singular in the following
|
||||
/// sense: if everything is incompressible and there are no
|
||||
/// pressure conditions, the absolute values of the pressure
|
||||
/// solution are arbitrary. (But the differences in pressure
|
||||
/// are significant.)
|
||||
bool CompressibleTpfa::singularPressure() const
|
||||
{
|
||||
return singular_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Compute well potentials.
|
||||
void CompressibleTpfa::computeWellPotentials(const BlackoilState& state)
|
||||
{
|
||||
@ -230,6 +253,9 @@ namespace Opm
|
||||
const WellState& /*well_state*/)
|
||||
{
|
||||
computeWellPotentials(state);
|
||||
if (rock_comp_props_ && rock_comp_props_->isActive()) {
|
||||
computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), initial_porevol_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -252,6 +278,8 @@ namespace Opm
|
||||
// std::vector<double> face_gravcap_;
|
||||
// std::vector<double> wellperf_A_;
|
||||
// std::vector<double> wellperf_phasemob_;
|
||||
// std::vector<double> porevol_; // Only modified if rock_comp_props_ is non-null.
|
||||
// std::vector<double> rock_comp_; // Empty unless rock_comp_props_ is non-null.
|
||||
computeCellDynamicData(dt, state, well_state);
|
||||
computeFaceDynamicData(dt, state, well_state);
|
||||
computeWellDynamicData(dt, state, well_state);
|
||||
@ -273,6 +301,8 @@ namespace Opm
|
||||
// std::vector<double> cell_viscosity_;
|
||||
// std::vector<double> cell_phasemob_;
|
||||
// std::vector<double> cell_voldisc_;
|
||||
// std::vector<double> porevol_; // Only modified if rock_comp_props_ is non-null.
|
||||
// std::vector<double> rock_comp_; // Empty unless rock_comp_props_ is non-null.
|
||||
const int nc = grid_.number_of_cells;
|
||||
const int np = props_.numPhases();
|
||||
const double* cell_p = &state.pressure()[0];
|
||||
@ -296,6 +326,14 @@ namespace Opm
|
||||
// TODO: Check this!
|
||||
cell_voldisc_.clear();
|
||||
cell_voldisc_.resize(nc, 0.0);
|
||||
|
||||
if (rock_comp_props_ && rock_comp_props_->isActive()) {
|
||||
computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol_);
|
||||
rock_comp_.resize(nc);
|
||||
for (int cell = 0; cell < nc; ++cell) {
|
||||
rock_comp_[cell] = rock_comp_props_->rockComp(state.pressure()[cell]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -465,9 +503,20 @@ namespace Opm
|
||||
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_[0], h_);
|
||||
int was_adjusted = 0;
|
||||
if (! (rock_comp_props_ && rock_comp_props_->isActive())) {
|
||||
was_adjusted =
|
||||
cfs_tpfa_res_assemble(gg, dt, &forces, z, &cq, &trans_[0],
|
||||
&face_gravcap_[0], cell_press, well_bhp,
|
||||
&porevol_[0], h_);
|
||||
} else {
|
||||
was_adjusted =
|
||||
cfs_tpfa_res_comprock_assemble(gg, dt, &forces, z, &cq, &trans_[0],
|
||||
&face_gravcap_[0], cell_press, well_bhp,
|
||||
&porevol_[0], &initial_porevol_[0],
|
||||
&rock_comp_[0], h_);
|
||||
}
|
||||
singular_ = (was_adjusted == 1);
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,6 +33,7 @@ namespace Opm
|
||||
|
||||
class BlackoilState;
|
||||
class BlackoilPropertiesInterface;
|
||||
class RockCompressibility;
|
||||
class LinearSolverInterface;
|
||||
class WellState;
|
||||
|
||||
@ -44,23 +45,25 @@ namespace Opm
|
||||
{
|
||||
public:
|
||||
/// Construct solver.
|
||||
/// \param[in] grid A 2d or 3d grid.
|
||||
/// \param[in] props Rock and fluid properties.
|
||||
/// \param[in] linsolver Linear solver to use.
|
||||
/// \param[in] residual_tol Solution accepted if inf-norm of residual is smaller.
|
||||
/// \param[in] change_tol Solution accepted if inf-norm of change in pressure is smaller.
|
||||
/// \param[in] maxiter Maximum acceptable number of iterations.
|
||||
/// \param[in] gravity Gravity vector. If non-null, the array should
|
||||
/// have D elements.
|
||||
/// \param[in] wells The wells argument. Will be used in solution,
|
||||
/// is ignored if NULL.
|
||||
/// Note: this class observes the well object, and
|
||||
/// makes the assumption that the well topology
|
||||
/// and completions does not change during the
|
||||
/// run. However, controls (only) are allowed
|
||||
/// to change.
|
||||
CompressibleTpfa(const UnstructuredGrid& grid,
|
||||
/// \param[in] grid A 2d or 3d grid.
|
||||
/// \param[in] props Rock and fluid properties.
|
||||
/// \param[in] rock_comp_props Rock compressibility properties. May be null.
|
||||
/// \param[in] linsolver Linear solver to use.
|
||||
/// \param[in] residual_tol Solution accepted if inf-norm of residual is smaller.
|
||||
/// \param[in] change_tol Solution accepted if inf-norm of change in pressure is smaller.
|
||||
/// \param[in] maxiter Maximum acceptable number of iterations.
|
||||
/// \param[in] gravity Gravity vector. If non-null, the array should
|
||||
/// have D elements.
|
||||
/// \param[in] wells The wells argument. Will be used in solution,
|
||||
/// is ignored if NULL.
|
||||
/// Note: this class observes the well object, and
|
||||
/// makes the assumption that the well topology
|
||||
/// and completions does not change during the
|
||||
/// run. However, controls (only) are allowed
|
||||
/// to change.
|
||||
CompressibleTpfa(const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
const LinearSolverInterface& linsolver,
|
||||
const double residual_tol,
|
||||
const double change_tol,
|
||||
@ -68,8 +71,8 @@ namespace Opm
|
||||
const double* gravity,
|
||||
const Wells* wells);
|
||||
|
||||
/// Destructor.
|
||||
~CompressibleTpfa();
|
||||
/// Destructor.
|
||||
~CompressibleTpfa();
|
||||
|
||||
/// Solve the pressure equation by Newton-Raphson scheme.
|
||||
/// May throw an exception if the number of iterations
|
||||
@ -78,6 +81,14 @@ namespace Opm
|
||||
BlackoilState& state,
|
||||
WellState& well_state);
|
||||
|
||||
/// @brief After solve(), was the resulting pressure singular.
|
||||
/// Returns true if the pressure is singular in the following
|
||||
/// sense: if everything is incompressible and there are no
|
||||
/// pressure conditions, the absolute values of the pressure
|
||||
/// solution are arbitrary. (But the differences in pressure
|
||||
/// are significant.)
|
||||
bool singularPressure() const;
|
||||
|
||||
private:
|
||||
void computePerSolveDynamicData(const double dt,
|
||||
const BlackoilState& state,
|
||||
@ -101,28 +112,29 @@ namespace Opm
|
||||
void solveIncrement();
|
||||
double residualNorm() const;
|
||||
double incrementNorm() const;
|
||||
void computeResults(BlackoilState& state,
|
||||
void computeResults(BlackoilState& state,
|
||||
WellState& well_state) const;
|
||||
|
||||
// ------ Data that will remain unmodified after construction. ------
|
||||
const UnstructuredGrid& grid_;
|
||||
const UnstructuredGrid& grid_;
|
||||
const BlackoilPropertiesInterface& props_;
|
||||
const RockCompressibility* rock_comp_props_;
|
||||
const LinearSolverInterface& linsolver_;
|
||||
const double residual_tol_;
|
||||
const double change_tol_;
|
||||
const int maxiter_;
|
||||
const double* gravity_; // May be NULL
|
||||
const Wells* wells_; // May be NULL, outside may modify controls (only) between calls to solve().
|
||||
std::vector<double> htrans_;
|
||||
std::vector<double> trans_ ;
|
||||
std::vector<double> porevol_;
|
||||
std::vector<double> htrans_;
|
||||
std::vector<double> trans_ ;
|
||||
std::vector<int> allcells_;
|
||||
|
||||
// ------ Internal data for the cfs_tpfa_res solver. ------
|
||||
struct cfs_tpfa_res_data* h_;
|
||||
struct cfs_tpfa_res_data* h_;
|
||||
|
||||
// ------ Data that will be modified for every solve. ------
|
||||
std::vector<double> wellperf_gpot_;
|
||||
std::vector<double> initial_porevol_;
|
||||
|
||||
// ------ Data that will be modified for every solver iteration. ------
|
||||
std::vector<double> cell_A_;
|
||||
@ -135,13 +147,15 @@ namespace Opm
|
||||
std::vector<double> face_gravcap_;
|
||||
std::vector<double> wellperf_A_;
|
||||
std::vector<double> wellperf_phasemob_;
|
||||
std::vector<double> porevol_; // Only modified if rock_comp_props_ is non-null.
|
||||
std::vector<double> rock_comp_; // Empty unless rock_comp_props_ is non-null.
|
||||
// The update to be applied to the pressures (cell and bhp).
|
||||
std::vector<double> pressure_increment_;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// True if the matrix assembled would be singular but for the
|
||||
// adjustment made in the cfs_*_assemble() calls. This happens
|
||||
// if everything is incompressible and there are no pressure
|
||||
// conditions.
|
||||
bool singular_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
@ -1156,7 +1156,7 @@ cfs_tpfa_res_construct(struct UnstructuredGrid *G ,
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
int
|
||||
cfs_tpfa_res_assemble(struct UnstructuredGrid *G ,
|
||||
double dt ,
|
||||
struct cfs_tpfa_res_forces *forces ,
|
||||
@ -1170,7 +1170,7 @@ cfs_tpfa_res_assemble(struct UnstructuredGrid *G ,
|
||||
struct cfs_tpfa_res_data *h )
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int res_is_neumann, well_is_neumann, c, np2;
|
||||
int res_is_neumann, well_is_neumann, c, np2, singular;
|
||||
|
||||
csrmatrix_zero( h->J);
|
||||
vector_zero (h->J->m, h->F);
|
||||
@ -1207,9 +1207,76 @@ cfs_tpfa_res_assemble(struct UnstructuredGrid *G ,
|
||||
assemble_sources(dt, forces->src, h);
|
||||
}
|
||||
|
||||
if (res_is_neumann && well_is_neumann && h->pimpl->is_incomp) {
|
||||
h->J->sa[0] *= 2;
|
||||
singular = res_is_neumann && well_is_neumann && h->pimpl->is_incomp;
|
||||
if (singular) {
|
||||
h->J->sa[0] *= 2.0;
|
||||
}
|
||||
|
||||
return singular;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
int
|
||||
cfs_tpfa_res_comprock_assemble(
|
||||
struct UnstructuredGrid *G ,
|
||||
double dt ,
|
||||
struct cfs_tpfa_res_forces *forces ,
|
||||
const double *zc ,
|
||||
struct compr_quantities_gen *cq ,
|
||||
const double *trans ,
|
||||
const double *gravcap_f,
|
||||
const double *cpress ,
|
||||
const double *wpress ,
|
||||
const double *porevol ,
|
||||
const double *porevol0 ,
|
||||
const double *rock_comp,
|
||||
struct cfs_tpfa_res_data *h )
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
/* We want to add this term to the usual residual:
|
||||
*
|
||||
* (porevol(pressure)-porevol(initial_pressure))/dt.
|
||||
*
|
||||
* Its derivative (for the diagonal term of the Jacobian) is:
|
||||
*
|
||||
* porevol(pressure)*rock_comp(pressure)/dt
|
||||
*/
|
||||
|
||||
int c, rock_is_incomp, singular;
|
||||
size_t j;
|
||||
double dpv;
|
||||
|
||||
/* Assemble usual system (without rock compressibility). */
|
||||
singular = cfs_tpfa_res_assemble(G, dt, forces, zc, cq, trans, gravcap_f,
|
||||
cpress, wpress, porevol0, h);
|
||||
|
||||
/* If we made a singularity-removing adjustment in the
|
||||
regular assembly, we undo it here. */
|
||||
if (singular) {
|
||||
h->J->sa[0] /= 2.0;
|
||||
}
|
||||
|
||||
/* Add new terms to residual and Jacobian. */
|
||||
rock_is_incomp = 1;
|
||||
for (c = 0; c < G->number_of_cells; c++) {
|
||||
j = csrmatrix_elm_index(c, c, h->J);
|
||||
|
||||
dpv = (porevol[c] - porevol0[c]);
|
||||
if (dpv != 0.0 || rock_comp[c] != 0.0) {
|
||||
rock_is_incomp = 0;
|
||||
}
|
||||
|
||||
h->J->sa[j] += porevol[c] * rock_comp[c];
|
||||
h->F[c] += dpv;
|
||||
}
|
||||
|
||||
/* Re-do the singularity-removing adjustment if necessary */
|
||||
if (rock_is_incomp && singular) {
|
||||
h->J->sa[0] *= 2.0;
|
||||
}
|
||||
|
||||
return rock_is_incomp && singular;
|
||||
}
|
||||
|
||||
|
||||
|
@ -59,7 +59,11 @@ cfs_tpfa_res_construct(struct UnstructuredGrid *G ,
|
||||
void
|
||||
cfs_tpfa_res_destroy(struct cfs_tpfa_res_data *h);
|
||||
|
||||
void
|
||||
/* Return value is 1 if the assembled matrix was adjusted to remove a
|
||||
singularity. This happens if all fluids are incompressible and
|
||||
there are no pressure conditions on wells or boundaries.
|
||||
Otherwise return 0. */
|
||||
int
|
||||
cfs_tpfa_res_assemble(struct UnstructuredGrid *G,
|
||||
double dt,
|
||||
struct cfs_tpfa_res_forces *forces,
|
||||
@ -72,6 +76,27 @@ cfs_tpfa_res_assemble(struct UnstructuredGrid *G,
|
||||
const double *porevol,
|
||||
struct cfs_tpfa_res_data *h);
|
||||
|
||||
/* Return value is 1 if the assembled matrix was adjusted to remove a
|
||||
singularity. This happens if all fluids are incompressible, the
|
||||
rock is incompressible, and there are no pressure conditions on
|
||||
wells or boundaries.
|
||||
Otherwise return 0. */
|
||||
int
|
||||
cfs_tpfa_res_comprock_assemble(
|
||||
struct UnstructuredGrid *G,
|
||||
double dt,
|
||||
struct cfs_tpfa_res_forces *forces,
|
||||
const double *zc,
|
||||
struct compr_quantities_gen *cq,
|
||||
const double *trans,
|
||||
const double *gravcap_f,
|
||||
const double *cpress,
|
||||
const double *wpress,
|
||||
const double *porevol,
|
||||
const double *porevol0,
|
||||
const double *rock_comp,
|
||||
struct cfs_tpfa_res_data *h);
|
||||
|
||||
void
|
||||
cfs_tpfa_res_flux(struct UnstructuredGrid *G ,
|
||||
struct cfs_tpfa_res_forces *forces ,
|
||||
|
@ -771,7 +771,7 @@ ifs_tpfa_assemble_comprock_increment(struct UnstructuredGrid *G ,
|
||||
assemble_incompressible(G, F, trans, gpress, h, &system_singular, &ok);
|
||||
|
||||
/* We want to solve a Newton step for the residual
|
||||
* (porevol(pressure)-porevol(initial_pressure))/dt + residual_for_imcompressible
|
||||
* (porevol(pressure)-porevol(initial_pressure))/dt + residual_for_incompressible
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <opm/core/transport/reorder/reordersequence.h>
|
||||
#include <opm/core/utility/RootFinders.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/miscUtilitiesBlackoil.hpp>
|
||||
#include <opm/core/pressure/tpfa/trans_tpfa.h>
|
||||
|
||||
#include <fstream>
|
||||
@ -38,7 +39,8 @@ namespace Opm
|
||||
typedef RegulaFalsi<WarnAndContinueOnError> RootFinder;
|
||||
|
||||
|
||||
TransportModelCompressibleTwophase::TransportModelCompressibleTwophase(const UnstructuredGrid& grid,
|
||||
TransportModelCompressibleTwophase::TransportModelCompressibleTwophase(
|
||||
const UnstructuredGrid& grid,
|
||||
const Opm::BlackoilPropertiesInterface& props,
|
||||
const double tol,
|
||||
const int maxit)
|
||||
@ -51,6 +53,7 @@ namespace Opm
|
||||
dt_(0.0),
|
||||
saturation_(grid.number_of_cells, -1.0),
|
||||
fractionalflow_(grid.number_of_cells, -1.0),
|
||||
gravity_(0),
|
||||
mob_(2*grid.number_of_cells, -1.0),
|
||||
ia_upw_(grid.number_of_cells + 1, -1),
|
||||
ja_upw_(grid.number_of_faces, -1),
|
||||
@ -75,15 +78,15 @@ namespace Opm
|
||||
|
||||
void TransportModelCompressibleTwophase::solve(const double* darcyflux,
|
||||
const double* pressure,
|
||||
const double* surfacevol0,
|
||||
const double* porevolume0,
|
||||
const double* porevolume,
|
||||
const double* source,
|
||||
const double dt,
|
||||
std::vector<double>& saturation)
|
||||
std::vector<double>& saturation,
|
||||
std::vector<double>& surfacevol)
|
||||
{
|
||||
darcyflux_ = darcyflux;
|
||||
surfacevol0_ = surfacevol0;
|
||||
surfacevol0_ = &surfacevol[0];
|
||||
porevolume0_ = porevolume0;
|
||||
porevolume_ = porevolume;
|
||||
source_ = source;
|
||||
@ -93,6 +96,11 @@ namespace Opm
|
||||
props_.viscosity(props_.numCells(), pressure, NULL, &allcells_[0], &visc_[0], NULL);
|
||||
props_.matrix(props_.numCells(), pressure, NULL, &allcells_[0], &A_[0], NULL);
|
||||
|
||||
// Check immiscibility requirement (only done for first cell).
|
||||
if (A_[1] != 0.0 || A_[2] != 0.0) {
|
||||
THROW("TransportModelCompressibleTwophase requires a property object without miscibility.");
|
||||
}
|
||||
|
||||
std::vector<int> seq(grid_.number_of_cells);
|
||||
std::vector<int> comp(grid_.number_of_cells + 1);
|
||||
int ncomp;
|
||||
@ -107,6 +115,9 @@ namespace Opm
|
||||
&ia_downw_[0], &ja_downw_[0]);
|
||||
reorderAndTransport(grid_, darcyflux);
|
||||
toBothSat(saturation_, saturation);
|
||||
|
||||
// Compute surface volume as a postprocessing step from saturation and A_
|
||||
computeSurfacevol(grid_.number_of_cells, props_.numPhases(), &A_[0], &saturation[0], &surfacevol[0]);
|
||||
}
|
||||
|
||||
// Residual function r(s) for a single-cell implicit Euler transport
|
||||
@ -375,20 +386,24 @@ namespace Opm
|
||||
|
||||
|
||||
|
||||
void TransportModelCompressibleTwophase::initGravity()
|
||||
void TransportModelCompressibleTwophase::initGravity(const double* grav)
|
||||
{
|
||||
// Set up transmissibilities.
|
||||
std::vector<double> htrans(grid_.cell_facepos[grid_.number_of_cells]);
|
||||
const int nf = grid_.number_of_faces;
|
||||
trans_.resize(nf);
|
||||
gravflux_.resize(nf);
|
||||
tpfa_htrans_compute(const_cast<UnstructuredGrid*>(&grid_), props_.permeability(), &htrans[0]);
|
||||
tpfa_trans_compute(const_cast<UnstructuredGrid*>(&grid_), &htrans[0], &trans_[0]);
|
||||
|
||||
// Remember gravity vector.
|
||||
gravity_ = grav;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TransportModelCompressibleTwophase::initGravityDynamic(const double* grav)
|
||||
void TransportModelCompressibleTwophase::initGravityDynamic()
|
||||
{
|
||||
// Set up gravflux_ = T_ij g [ (b_w,i rho_w,S - b_o,i rho_o,S) (z_i - z_f)
|
||||
// + (b_w,j rho_w,S - b_o,j rho_o,S) (z_f - z_j) ]
|
||||
@ -410,7 +425,7 @@ namespace Opm
|
||||
for (int ci = 0; ci < 2; ++ci) {
|
||||
double gdz = 0.0;
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
gdz += grav[d]*(grid_.cell_centroids[dim*c[ci] + d] - grid_.face_centroids[dim*f + d]);
|
||||
gdz += gravity_[d]*(grid_.cell_centroids[dim*c[ci] + d] - grid_.face_centroids[dim*f + d]);
|
||||
}
|
||||
gravflux_[f] += signs[ci]*trans_[f]*gdz*(density_[2*c[ci]] - density_[2*c[ci] + 1]);
|
||||
}
|
||||
@ -494,11 +509,11 @@ namespace Opm
|
||||
const double* pressure,
|
||||
const double* porevolume0,
|
||||
const double dt,
|
||||
const double* grav,
|
||||
std::vector<double>& saturation)
|
||||
std::vector<double>& saturation,
|
||||
std::vector<double>& surfacevol)
|
||||
{
|
||||
// Assume that solve() has already been called, so that A_ is current.
|
||||
initGravityDynamic(grav);
|
||||
initGravityDynamic();
|
||||
|
||||
// Initialize mobilities.
|
||||
const int nc = grid_.number_of_cells;
|
||||
@ -531,6 +546,9 @@ namespace Opm
|
||||
std::cout << "Gauss-Seidel column solver average iterations: "
|
||||
<< double(num_iters)/double(columns.size()) << std::endl;
|
||||
toBothSat(saturation_, saturation);
|
||||
|
||||
// Compute surface volume as a postprocessing step from saturation and A_
|
||||
computeSurfacevol(grid_.number_of_cells, props_.numPhases(), &A_[0], &saturation[0], &surfacevol[0]);
|
||||
}
|
||||
|
||||
} // namespace Opm
|
||||
|
@ -30,6 +30,8 @@ namespace Opm
|
||||
|
||||
class BlackoilPropertiesInterface;
|
||||
|
||||
/// Implements a reordering transport solver for compressible,
|
||||
/// non-miscible two-phase flow.
|
||||
class TransportModelCompressibleTwophase : public TransportModelInterface
|
||||
{
|
||||
public:
|
||||
@ -52,17 +54,19 @@ namespace Opm
|
||||
/// \param[in] source Transport source term.
|
||||
/// \param[in] dt Time step.
|
||||
/// \param[in, out] saturation Phase saturations.
|
||||
/// \param[in, out] surfacevol Surface volume densities for each phase.
|
||||
void solve(const double* darcyflux,
|
||||
const double* pressure,
|
||||
const double* surfacevol0,
|
||||
const double* porevolume0,
|
||||
const double* porevolume,
|
||||
const double* source,
|
||||
const double dt,
|
||||
std::vector<double>& saturation);
|
||||
std::vector<double>& saturation,
|
||||
std::vector<double>& surfacevol);
|
||||
|
||||
/// Initialise quantities needed by gravity solver.
|
||||
void initGravity();
|
||||
/// \param[in] grav Gravity vector
|
||||
void initGravity(const double* grav);
|
||||
|
||||
/// Solve for gravity segregation.
|
||||
/// This uses a column-wise nonlinear Gauss-Seidel approach.
|
||||
@ -72,14 +76,14 @@ namespace Opm
|
||||
/// \param[in] columns Vector of cell-columns.
|
||||
/// \param[in] porevolume0 Array of pore volumes at start of timestep.
|
||||
/// \param[in] dt Time step.
|
||||
/// \param[in] grav Gravity vector.
|
||||
/// \param[in, out] saturation Phase saturations.
|
||||
/// \param[in, out] surfacevol Surface volume densities for each phase.
|
||||
void solveGravity(const std::vector<std::vector<int> >& columns,
|
||||
const double* pressure,
|
||||
const double* porevolume0,
|
||||
const double dt,
|
||||
const double* grav,
|
||||
std::vector<double>& saturation);
|
||||
std::vector<double>& saturation,
|
||||
std::vector<double>& surfacevol);
|
||||
|
||||
private:
|
||||
virtual void solveSingleCell(const int cell);
|
||||
@ -88,7 +92,7 @@ namespace Opm
|
||||
const int pos,
|
||||
const double* gravflux);
|
||||
int solveGravityColumn(const std::vector<int>& cells);
|
||||
void initGravityDynamic(const double* grav);
|
||||
void initGravityDynamic();
|
||||
|
||||
private:
|
||||
const UnstructuredGrid& grid_;
|
||||
@ -110,6 +114,7 @@ namespace Opm
|
||||
std::vector<double> saturation_; // P (= num. phases) per cell
|
||||
std::vector<double> fractionalflow_; // = m[0]/(m[0] + m[1]) per cell
|
||||
// For gravity segregation.
|
||||
const double* gravity_;
|
||||
std::vector<double> trans_;
|
||||
std::vector<double> density_;
|
||||
std::vector<double> gravflux_;
|
||||
|
@ -52,8 +52,8 @@ namespace Opm
|
||||
source_(0),
|
||||
dt_(0.0),
|
||||
saturation_(grid.number_of_cells, -1.0),
|
||||
reorder_iterations_(grid.number_of_cells, 0),
|
||||
fractionalflow_(grid.number_of_cells, -1.0),
|
||||
reorder_iterations_(grid.number_of_cells, 0),
|
||||
mob_(2*grid.number_of_cells, -1.0)
|
||||
#ifdef EXPERIMENT_GAUSS_SEIDEL
|
||||
, ia_upw_(grid.number_of_cells + 1, -1),
|
||||
@ -107,6 +107,13 @@ namespace Opm
|
||||
toBothSat(saturation_, saturation);
|
||||
}
|
||||
|
||||
|
||||
const std::vector<int>& TransportModelTwophase::getReorderIterations() const
|
||||
{
|
||||
return reorder_iterations_;
|
||||
}
|
||||
|
||||
|
||||
// Residual function r(s) for a single-cell implicit Euler transport
|
||||
//
|
||||
// r(s) = s - s0 + dt/pv*( influx + outflux*f(s) )
|
||||
@ -645,10 +652,7 @@ namespace Opm
|
||||
|
||||
toBothSat(saturation_, saturation);
|
||||
}
|
||||
void TransportModelTwophase::getReorderIterations()
|
||||
{
|
||||
return reorder_iterations_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
|
||||
|
@ -74,10 +74,11 @@ namespace Opm
|
||||
const double* porevolume,
|
||||
const double dt,
|
||||
std::vector<double>& saturation);
|
||||
//// Return reorder iterations
|
||||
////
|
||||
//// \param[out] vector of iteration per cell
|
||||
const std::vector<int>& getReorderIterations(){return reorder_iterations_;};
|
||||
|
||||
//// Return the number of iterations used by the reordering solver.
|
||||
//// \return vector of iteration per cell
|
||||
const std::vector<int>& getReorderIterations() const;
|
||||
|
||||
private:
|
||||
virtual void solveSingleCell(const int cell);
|
||||
virtual void solveMultiCell(const int num_cells, const int* cells);
|
||||
@ -86,7 +87,7 @@ namespace Opm
|
||||
const int pos,
|
||||
const double* gravflux);
|
||||
int solveGravityColumn(const std::vector<int>& cells);
|
||||
private:
|
||||
private:
|
||||
const UnstructuredGrid& grid_;
|
||||
const IncompPropertiesInterface& props_;
|
||||
const double* visc_;
|
||||
|
@ -48,39 +48,39 @@ namespace Opm
|
||||
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 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 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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -93,11 +93,11 @@ namespace Opm
|
||||
/// @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<int>& cells,
|
||||
const std::vector<double>& press,
|
||||
const std::vector<double>& z,
|
||||
const std::vector<double>& s,
|
||||
std::vector<double>& totmob)
|
||||
const std::vector<double>& s,
|
||||
std::vector<double>& totmob)
|
||||
{
|
||||
std::vector<double> pmobc;
|
||||
|
||||
@ -126,12 +126,12 @@ namespace Opm
|
||||
/// @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<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)
|
||||
const std::vector<double>& s,
|
||||
std::vector<double>& totmob,
|
||||
std::vector<double>& omega)
|
||||
{
|
||||
std::vector<double> pmobc;
|
||||
|
||||
@ -185,10 +185,10 @@ namespace Opm
|
||||
props.relperm(nc, &s[0], &cells[0],
|
||||
&pmobc[0], dpmobc);
|
||||
|
||||
std::transform(pmobc.begin(), pmobc.end(),
|
||||
mu.begin(),
|
||||
pmobc.begin(),
|
||||
std::divides<double>());
|
||||
std::transform(pmobc.begin(), pmobc.end(),
|
||||
mu.begin(),
|
||||
pmobc.begin(),
|
||||
std::divides<double>());
|
||||
}
|
||||
|
||||
/// Computes the fractional flow for each cell in the cells argument
|
||||
@ -220,5 +220,35 @@ namespace Opm
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the surface volume densities from saturations by the formula
|
||||
/// z = A s
|
||||
/// for a number of data points, where z is the surface volume density,
|
||||
/// s is the saturation (both as column vectors) and A is the
|
||||
/// phase-to-component relation matrix.
|
||||
/// @param[in] n number of data points
|
||||
/// @param[in] np number of phases, must be 2 or 3
|
||||
/// @param[in] A array containing n square matrices of size num_phases^2,
|
||||
/// in Fortran ordering, typically the output of a call
|
||||
/// to the matrix() method of a BlackoilProperties* class.
|
||||
/// @param[in] saturation concatenated saturation values (for all P phases)
|
||||
/// @param[out] surfacevol concatenated surface-volume values (for all P phases)
|
||||
void computeSurfacevol(const int n,
|
||||
const int np,
|
||||
const double* A,
|
||||
const double* saturation,
|
||||
double* surfacevol)
|
||||
{
|
||||
// Note: since this is a simple matrix-vector product, it can
|
||||
// be done by a BLAS call, but then we have to reorder the A
|
||||
// matrix data.
|
||||
std::fill(surfacevol, surfacevol + n*np, 0.0);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
for (int col = 0; col < np; ++col) {
|
||||
for (int row = 0; row < np; ++row) {
|
||||
surfacevol[i*np + row] += A[i*np*np + row + col*np] * saturation[i*np + col];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Opm
|
||||
|
@ -46,11 +46,11 @@ namespace Opm
|
||||
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);
|
||||
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
|
||||
@ -60,11 +60,11 @@ namespace Opm
|
||||
/// @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<int>& cells,
|
||||
const std::vector<double>& p,
|
||||
const std::vector<double>& z,
|
||||
const std::vector<double>& s,
|
||||
std::vector<double>& totmob);
|
||||
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
|
||||
@ -75,12 +75,12 @@ namespace Opm
|
||||
/// @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<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);
|
||||
const std::vector<double>& s,
|
||||
std::vector<double>& totmob,
|
||||
std::vector<double>& omega);
|
||||
|
||||
|
||||
/// @brief Computes phase mobilities for a set of saturation values.
|
||||
@ -96,7 +96,7 @@ namespace Opm
|
||||
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
|
||||
@ -112,6 +112,25 @@ namespace Opm
|
||||
const std::vector<double>& s,
|
||||
std::vector<double>& fractional_flows);
|
||||
|
||||
|
||||
/// Computes the surface volume densities from saturations by the formula
|
||||
/// z = A s
|
||||
/// for a number of data points, where z is the surface volume density,
|
||||
/// s is the saturation (both as column vectors) and A is the
|
||||
/// phase-to-component relation matrix.
|
||||
/// @param[in] n number of data points
|
||||
/// @param[in] np number of phases, must be 2 or 3
|
||||
/// @param[in] A array containing n square matrices of size num_phases,
|
||||
/// in Fortran ordering, typically the output of a call
|
||||
/// to the matrix() method of a BlackoilProperties* class.
|
||||
/// @param[in] saturation concatenated saturation values (for all P phases)
|
||||
/// @param[out] surfacevol concatenated surface-volume values (for all P phases)
|
||||
void computeSurfacevol(const int n,
|
||||
const int np,
|
||||
const double* A,
|
||||
const double* saturation,
|
||||
double* surfacevol);
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_MISCUTILITIESBLACKOIL_HEADER_INCLUDED
|
||||
|
Loading…
Reference in New Issue
Block a user