Major simplification of IncompTpfa interface.

Most significant changes:
     - Single solve() call used for all cases (with or without gravity,
       with or without rock compressibility). This is intentionally
       similar to CompressibleTpfa::solve().
     - Constructor take a property object and computation of necessary total
       mobilities etc. moved inside class.
     - Optional constructor args for rock compressibility, gravity, wells,
       boundary conditions (null pointer accepted) and source terms (empty
       vector accepted).
     - Nonlinear iterations for the compressible rock case now handled inside
       IncompTpfa. This part intentionally made similar to CompressibleTpfa.
This commit is contained in:
Atgeirr Flø Rasmussen 2012-06-12 10:27:48 +02:00
parent 7a5a32819b
commit a558878917
3 changed files with 541 additions and 417 deletions

View File

@ -18,15 +18,23 @@
*/
#include <opm/core/pressure/IncompTpfa.hpp>
#include <opm/core/fluid/IncompPropertiesInterface.hpp>
#include <opm/core/fluid/RockCompressibility.hpp>
#include <opm/core/pressure/tpfa/ifs_tpfa.h>
#include <opm/core/pressure/tpfa/trans_tpfa.h>
#include <opm/core/pressure/mimetic/mimetic.h>
#include <opm/core/pressure/flow_bc.h>
#include <opm/core/linalg/LinearSolverInterface.hpp>
#include <opm/core/linalg/sparse_sys.h>
#include <opm/core/simulator/TwophaseState.hpp>
#include <opm/core/simulator/WellState.hpp>
#include <opm/core/utility/ErrorMacros.hpp>
#include <opm/core/utility/miscUtilities.hpp>
#include <opm/core/newwells.h>
#include <string.h>
#include <iomanip>
#include <cmath>
#include <algorithm>
namespace Opm
{
@ -34,39 +42,74 @@ 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
IncompTpfa::IncompTpfa(const UnstructuredGrid& g,
const double* permeability,
const double* gravity,
/// \param[in] grid A 2d or 3d grid.
/// \param[in] props Rock and fluid properties.
/// \param[in] rock_comp_props Rock compressibility 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.
/// \param[in] src Source terms.
/// \param[in] bcs Boundary conditions, treat as all noflow if null.
IncompTpfa::IncompTpfa(const UnstructuredGrid& grid,
const IncompPropertiesInterface& props,
const RockCompressibility* rock_comp_props,
LinearSolverInterface& linsolver,
const struct Wells* wells)
: grid_(g),
const double residual_tol,
const double change_tol,
const int maxiter,
const double* gravity,
const Wells* wells,
const std::vector<double>& src,
const FlowBoundaryConditions* bcs)
: grid_(grid),
props_(props),
rock_comp_props_(rock_comp_props),
linsolver_(linsolver),
htrans_(g.cell_facepos[ g.number_of_cells ]),
trans_ (g.number_of_faces),
wells_(wells)
residual_tol_(residual_tol),
change_tol_(change_tol),
maxiter_(maxiter),
gravity_(gravity),
wells_(wells),
src_(src),
bcs_(bcs),
htrans_(grid.cell_facepos[ grid.number_of_cells ]),
allcells_(grid.number_of_cells),
trans_ (grid.number_of_faces)
{
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
tpfa_htrans_compute(gg, permeability, &htrans_[0]);
if (gravity) {
gpress_.resize(g.cell_facepos[ g.number_of_cells ], 0.0);
if (wells_ && (wells_->number_of_phases != props.numPhases())) {
THROW("Inconsistent number of phases specified (wells vs. props): "
<< wells_->number_of_phases << " != " << props.numPhases());
}
const int num_dofs = grid.number_of_cells + (wells ? wells->number_of_wells : 0);
pressures_.resize(num_dofs);
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
tpfa_htrans_compute(gg, props.permeability(), &htrans_[0]);
if (gravity) {
gpress_.resize(gg->cell_facepos[ gg->number_of_cells ], 0.0);
mim_ip_compute_gpress(gg->number_of_cells, gg->dimensions, gravity,
gg->cell_facepos, gg->cell_faces,
gg->face_centroids, gg->cell_centroids,
&gpress_[0]);
}
// gpress_omegaweighted_ is sent to assembler always, and it dislikes
// getting a zero pointer.
gpress_omegaweighted_.resize(gg->cell_facepos[ gg->number_of_cells ], 0.0);
for (int c = 0; c < grid.number_of_cells; ++c) {
allcells_[c] = c;
}
h_ = ifs_tpfa_construct(gg, const_cast<struct Wells*>(wells_));
mim_ip_compute_gpress(gg->number_of_cells, gg->dimensions, gravity,
gg->cell_facepos, gg->cell_faces,
gg->face_centroids, gg->cell_centroids,
&gpress_[0]);
}
// gpress_omegaweighted_ is sent to assembler always, and it dislikes
// getting a zero pointer.
gpress_omegaweighted_.resize(g.cell_facepos[ g.number_of_cells ], 0.0);
h_ = ifs_tpfa_construct(gg, const_cast<struct Wells*>(wells_));
}
@ -75,222 +118,369 @@ namespace Opm
/// Destructor.
IncompTpfa::~IncompTpfa()
{
ifs_tpfa_destroy(h_);
ifs_tpfa_destroy(h_);
}
/// Assemble and solve pressure system.
/// \param[in] totmob Must contain N total mobility values (one per cell).
/// totmob = \sum_{p} kr_p/mu_p.
/// \param[in] omega Must be empty if constructor gravity argument was null.
/// Otherwise must contain N mobility-weighted density values (one per cell).
/// omega = \frac{\sum_{p} mob_p rho_p}{\sum_p rho_p}.
/// \param[in] src Must contain N source rates (one per cell).
/// Positive values represent total inflow rates,
/// negative values represent total outflow rates.
/// \param[in] bcs If non-null, specifies boundary conditions.
/// If null, noflow conditions are assumed.
/// \param[out] pressure Will contain N cell-pressure values.
/// \param[out] faceflux Will contain F signed face flux values.
/// \param[out] well_bhp Will contain bhp values for each well passed
/// in the constructor.
/// \param[out] well_rate Will contain rate values for each
/// connection in all wells passed in the
/// constructor
void IncompTpfa::solve(const std::vector<double>& totmob,
const std::vector<double>& omega,
const std::vector<double>& src,
const std::vector<double>& wdp,
const FlowBoundaryConditions* bcs,
std::vector<double>& pressure,
std::vector<double>& faceflux,
std::vector<double>& well_bhp,
std::vector<double>& well_rate)
/// Solve the pressure equation. If there is no pressure
/// dependency introduced by rock compressibility effects,
/// the equation is linear, and it is solved directly.
/// Otherwise, the nonlinear equations ares solved by a
/// Newton-Raphson scheme.
/// May throw an exception if the number of iterations
/// exceed maxiter (set in constructor).
void IncompTpfa::solve(const double dt,
TwophaseState& state,
WellState& well_state)
{
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
tpfa_eff_trans_compute(gg, &totmob[0], &htrans_[0], &trans_[0]);
if (!omega.empty()) {
if (gpress_.empty()) {
THROW("Nozero omega argument given, but gravity was null in constructor.");
}
mim_ip_density_update(gg->number_of_cells, gg->cell_facepos,
&omega[0],
&gpress_[0], &gpress_omegaweighted_[0]);
} else {
if (!gpress_.empty()) {
THROW("Empty omega argument given, but gravity was non-null in constructor.");
}
}
ifs_tpfa_forces F = { NULL, NULL, wells_, NULL, NULL };
if (! src.empty()) { F.src = &src[0]; }
F.bc = bcs;
F.totmob = &totmob[0];
if (! wdp.empty()) { F.wdp = &wdp[0]; }
int ok = ifs_tpfa_assemble(gg, &F, &trans_[0], &gpress_omegaweighted_[0], h_);
if (!ok) {
THROW("Failed assembling pressure system.");
}
linsolver_.solve(h_->A, h_->b, h_->x);
pressure.resize(grid_.number_of_cells);
faceflux.resize(grid_.number_of_faces);
ifs_tpfa_solution soln = { NULL, NULL, NULL, NULL };
soln.cell_press = &pressure[0];
soln.face_flux = &faceflux[0];
if(wells_ != NULL) {
well_bhp.resize(wells_->number_of_wells);
well_rate.resize(wells_->well_connpos[ wells_->number_of_wells ]);
soln.well_flux = &well_rate[0];
soln.well_press = &well_bhp[0];
}
ifs_tpfa_press_flux(gg, &F, &trans_[0], h_, &soln);
}
/// Assemble and solve pressure system with rock compressibility (assumed constant per cell).
/// \param[in] totmob Must contain N total mobility values (one per cell).
/// totmob = \sum_{p} kr_p/mu_p.
/// \param[in] omega Must be empty if constructor gravity argument was null.
/// Otherwise must contain N fractional-flow-weighted density
/// values (one per cell).
/// \param[in] src Must contain N source rates (one per cell).
/// Positive values represent total inflow rates,
/// negative values represent total outflow rates.
/// \param[in] bcs If non-null, specifies boundary conditions.
/// If null, noflow conditions are assumed.
/// \param[in] porevol Must contain N pore volumes.
/// \param[in] rock_comp Must contain N rock compressibilities.
/// rock_comp = (d poro / d p)*(1/poro).
/// \param[in] dt Timestep.
/// \param[out] pressure Will contain N cell-pressure values.
/// \param[out] faceflux Will contain F signed face flux values.
/// \param[out] well_bhp Will contain bhp values for each well passed
/// in the constructor
/// \param[out] well_rate Will contain rate values for each
/// connection in all wells passed in the
/// constructor
void IncompTpfa::solve(const std::vector<double>& totmob,
const std::vector<double>& omega,
const std::vector<double>& src,
const std::vector<double>& wdp,
const FlowBoundaryConditions* bcs,
const std::vector<double>& porevol,
const std::vector<double>& rock_comp,
const double dt,
std::vector<double>& pressure,
std::vector<double>& faceflux,
std::vector<double>& well_bhp,
std::vector<double>& well_rate)
{
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
tpfa_eff_trans_compute(gg, &totmob[0], &htrans_[0], &trans_[0]);
if (!omega.empty()) {
if (gpress_.empty()) {
THROW("Nozero omega argument given, but gravity was null in constructor.");
}
mim_ip_density_update(gg->number_of_cells, gg->cell_facepos,
&omega[0],
&gpress_[0], &gpress_omegaweighted_[0]);
} else {
if (!gpress_.empty()) {
THROW("Empty omega argument given, but gravity was non-null in constructor.");
}
}
ifs_tpfa_forces F = { NULL, NULL, wells_, NULL, NULL };
if (! src.empty()) { F.src = &src[0]; }
F.bc = bcs;
F.totmob = &totmob[0];
if (! wdp.empty()) { F.wdp = &wdp[0]; }
int ok = true;
if (rock_comp.empty()) {
ok = ifs_tpfa_assemble(gg, &F, &trans_[0], &gpress_omegaweighted_[0], h_);
if (rock_comp_props_ != 0 && rock_comp_props_->isActive()) {
solveRockComp(dt, state, well_state);
} else {
ok = ifs_tpfa_assemble_comprock(gg, &F, &trans_[0], &gpress_omegaweighted_[0],
&porevol[0], &rock_comp[0], dt, &pressure[0], h_);
solveIncomp(dt, state, well_state);
}
}
// Solve with no rock compressibility (linear eqn).
void IncompTpfa::solveIncomp(const double dt,
TwophaseState& state,
WellState& well_state)
{
// Set up properties.
computePerSolveDynamicData(dt, state, well_state);
// Assemble.
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
int ok = ifs_tpfa_assemble(gg, &forces_, &trans_[0], &gpress_omegaweighted_[0], h_);
if (!ok) {
THROW("Failed assembling pressure system.");
}
linsolver_.solve(h_->A, h_->b, h_->x);
pressure.resize(grid_.number_of_cells);
faceflux.resize(grid_.number_of_faces);
// Solve.
linsolver_.solve(h_->A, h_->b, h_->x);
// Obtain solution.
ASSERT(int(state.pressure().size()) == grid_.number_of_cells);
ASSERT(int(state.faceflux().size()) == grid_.number_of_faces);
ifs_tpfa_solution soln = { NULL, NULL, NULL, NULL };
soln.cell_press = &pressure[0];
soln.face_flux = &faceflux[0];
if(wells_ != NULL) {
well_bhp.resize(wells_->number_of_wells);
well_rate.resize(wells_->well_connpos[ wells_->number_of_wells ]);
soln.well_flux = &well_rate[0];
soln.well_press = &well_bhp[0];
soln.cell_press = &state.pressure()[0];
soln.face_flux = &state.faceflux()[0];
if (wells_ != NULL) {
ASSERT(int(well_state.bhp().size()) == wells_->number_of_wells);
ASSERT(int(well_state.perfRates().size()) == wells_->well_connpos[ wells_->number_of_wells ]);
soln.well_flux = &well_state.perfRates()[0];
soln.well_press = &well_state.bhp()[0];
}
ifs_tpfa_press_flux(gg, &F, &trans_[0], h_, &soln);
ifs_tpfa_press_flux(gg, &forces_, &trans_[0], h_, &soln);
}
// Solve with rock compressibility (nonlinear eqn).
void IncompTpfa::solveRockComp(const double dt,
TwophaseState& state,
WellState& well_state)
{
// This function is identical to CompressibleTpfa::solve().
// \TODO refactor?
const int nc = grid_.number_of_cells;
const int nw = wells_->number_of_wells;
// Set up dynamic data.
computePerSolveDynamicData(dt, state, well_state);
computePerIterationDynamicData(dt, state, well_state);
// Assemble J and F.
assemble(dt, state, well_state);
double inc_norm = 0.0;
int iter = 0;
double res_norm = residualNorm();
std::cout << "\nIteration Residual Change in p\n"
<< std::setw(9) << iter
<< std::setw(18) << res_norm
<< std::setw(18) << '*' << std::endl;
while ((iter < maxiter_) && (res_norm > residual_tol_)) {
// Solve for increment in Newton method:
// incr = x_{n+1} - x_{n} = -J^{-1}F
// (J is Jacobian matrix, F is residual)
solveIncrement();
++iter;
// Update pressure vars with increment.
for (int c = 0; c < nc; ++c) {
state.pressure()[c] += h_->x[c];
}
for (int w = 0; w < nw; ++w) {
well_state.bhp()[w] += h_->x[nc + w];
}
// Stop iterating if increment is small.
inc_norm = incrementNorm();
if (inc_norm <= change_tol_) {
std::cout << std::setw(9) << iter
<< std::setw(18) << '*'
<< std::setw(18) << inc_norm << std::endl;
break;
}
// Set up dynamic data.
computePerIterationDynamicData(dt, state, well_state);
// Assemble J and F.
assemble(dt, state, well_state);
// Update residual norm.
res_norm = residualNorm();
std::cout << std::setw(9) << iter
<< std::setw(18) << res_norm
<< std::setw(18) << inc_norm << std::endl;
}
if ((iter == maxiter_) && (res_norm > residual_tol_) && (inc_norm > change_tol_)) {
THROW("CompressibleTpfa::solve() failed to converge in " << maxiter_ << " iterations.");
}
std::cout << "Solved pressure in " << iter << " iterations." << std::endl;
// Compute fluxes and face pressures.
computeResults(state, well_state);
}
/// Compute per-solve dynamic properties.
void IncompTpfa::computePerSolveDynamicData(const double /*dt*/,
const TwophaseState& state,
const WellState& /*well_state*/)
{
// Computed here:
//
// std::vector<double> wdp_;
// std::vector<double> totmob_;
// std::vector<double> omega_;
// std::vector<double> trans_;
// std::vector<double> gpress_omegaweighted_;
// std::vector<double> initial_porevol_;
// ifs_tpfa_forces forces_;
// wdp_
if (wells_) {
Opm::computeWDP(*wells_, grid_, state.saturation(), props_.density(),
gravity_ ? gravity_[2] : 0.0, true, wdp_);
}
// totmob_, omega_, gpress_omegaweighted_
if (gravity_) {
computeTotalMobilityOmega(props_, allcells_, state.saturation(), totmob_, omega_);
mim_ip_density_update(grid_.number_of_cells, grid_.cell_facepos,
&omega_[0],
&gpress_[0], &gpress_omegaweighted_[0]);
} else {
computeTotalMobility(props_, allcells_, state.saturation(), totmob_);
}
// trans_
tpfa_eff_trans_compute(const_cast<UnstructuredGrid*>(&grid_), &totmob_[0], &htrans_[0], &trans_[0]);
// initial_porevol_
if (rock_comp_props_ && rock_comp_props_->isActive()) {
computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), initial_porevol_);
}
// forces_
forces_.src = src_.empty() ? NULL : &src_[0];
forces_.bc = bcs_;
forces_.W = wells_;
forces_.totmob = &totmob_[0];
forces_.wdp = wdp_.empty() ? NULL : &wdp_[0];
}
/// Compute per-iteration dynamic properties.
void IncompTpfa::computePerIterationDynamicData(const double /*dt*/,
const TwophaseState& state,
const WellState& well_state)
{
// These are the variables that get computed by this function:
//
// std::vector<double> porevol_
// std::vector<double> rock_comp_
// std::vector<double> pressures_
computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol_);
if (rock_comp_props_ && rock_comp_props_->isActive()) {
for (int cell = 0; cell < grid_.number_of_cells; ++cell) {
rock_comp_[cell] = rock_comp_props_->rockComp(state.pressure()[cell]);
}
}
if (wells_) {
std::copy(state.pressure().begin(), state.pressure().end(), pressures_.begin());
std::copy(well_state.bhp().begin(), well_state.bhp().end(), pressures_.begin() + grid_.number_of_cells);
}
}
/// Compute the residual in h_->b and Jacobian in h_->A.
void IncompTpfa::assemble(const double dt,
const TwophaseState& state,
const WellState& /*well_state*/)
{
const double* pressures = wells_ ? &pressures_[0] : &state.pressure()[0];
bool ok = ifs_tpfa_assemble_comprock_increment(const_cast<UnstructuredGrid*>(&grid_),
&forces_, &trans_[0], &gpress_omegaweighted_[0],
&porevol_[0], &rock_comp_[0], dt, pressures,
&initial_porevol_[0], h_);
if (!ok) {
THROW("Failed assembling pressure system.");
}
}
/// Computes pressure increment, puts it in h_->x
void IncompTpfa::solveIncrement()
{
// Increment is equal to -J^{-1}R.
// The Jacobian is in h_->A, residual in h_->b.
linsolver_.solve(h_->A, h_->b, h_->x);
std::transform(h_->x, h_->x + h_->A->m, h_->x, std::negate<double>());
}
namespace {
template <class FI>
double infnorm(FI beg, FI end)
{
double norm = 0.0;
for (; beg != end; ++beg) {
norm = std::max(norm, std::fabs(*beg));
}
return norm;
}
} // anonymous namespace
/// Computes the inf-norm of the residual.
double IncompTpfa::residualNorm() const
{
return infnorm(h_->b, h_->b + h_->A->m);
}
/// Computes the inf-norm of pressure_increment_.
double IncompTpfa::incrementNorm() const
{
return infnorm(h_->x, h_->x + h_->A->m);
}
/// Compute the output.
void IncompTpfa::computeResults(TwophaseState& state,
WellState& well_state) const
{
// Make sure h_ contains the direct-solution matrix
// and right hand side (not jacobian and residual).
// TODO: optimize by only adjusting b and diagonal of A.
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
ifs_tpfa_assemble(gg, &forces_, &trans_[0], &gpress_omegaweighted_[0], h_);
// Make sure h_->x contains the direct solution vector.
ASSERT(int(state.pressure().size()) == grid_.number_of_cells);
ASSERT(int(state.faceflux().size()) == grid_.number_of_faces);
std::copy(state.pressure().begin(), state.pressure().end(), h_->x);
std::copy(well_state.bhp().begin(), well_state.bhp().end(), h_->x + grid_.number_of_cells);
// Obtain solution.
ifs_tpfa_solution soln = { NULL, NULL, NULL, NULL };
soln.cell_press = &state.pressure()[0];
soln.face_flux = &state.faceflux()[0];
if (wells_ != NULL) {
ASSERT(int(well_state.bhp().size()) == wells_->number_of_wells);
ASSERT(int(well_state.perfRates().size()) == wells_->well_connpos[ wells_->number_of_wells ]);
soln.well_flux = &well_state.perfRates()[0];
soln.well_press = &well_state.bhp()[0];
}
ifs_tpfa_press_flux(gg, &forces_, &trans_[0], h_, &soln);
}
#if 0
void IncompTpfa::solveIncrement(const std::vector<double>& totmob,
const std::vector<double>& omega,
const std::vector<double>& src,
const std::vector<double>& wdp,
const FlowBoundaryConditions* bcs,
const std::vector<double>& porevol,
const std::vector<double>& rock_comp,
const std::vector<double>& prev_pressure,
const std::vector<double>& initial_porevol,
const double dt,
std::vector<double>& pressure_increment)
const std::vector<double>& omega,
const std::vector<double>& src,
const std::vector<double>& wdp,
const FlowBoundaryConditions* bcs,
const std::vector<double>& porevol,
const std::vector<double>& rock_comp,
const std::vector<double>& prev_pressure,
const std::vector<double>& initial_porevol,
const double dt,
std::vector<double>& pressure_increment)
{
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
tpfa_eff_trans_compute(gg, &totmob[0], &htrans_[0], &trans_[0]);
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
tpfa_eff_trans_compute(gg, &totmob[0], &htrans_[0], &trans_[0]);
if (!omega.empty()) {
if (gpress_.empty()) {
THROW("Nozero omega argument given, but gravity was null in constructor.");
}
mim_ip_density_update(gg->number_of_cells, gg->cell_facepos,
&omega[0],
&gpress_[0], &gpress_omegaweighted_[0]);
} else {
if (!gpress_.empty()) {
THROW("Empty omega argument given, but gravity was non-null in constructor.");
}
}
if (!omega.empty()) {
if (gpress_.empty()) {
THROW("Nozero omega argument given, but gravity was null in constructor.");
}
mim_ip_density_update(gg->number_of_cells, gg->cell_facepos,
&omega[0],
&gpress_[0], &gpress_omegaweighted_[0]);
} else {
if (!gpress_.empty()) {
THROW("Empty omega argument given, but gravity was non-null in constructor.");
}
}
ifs_tpfa_forces F = { NULL, NULL, wells_, NULL, NULL };
if (! src.empty()) { F.src = &src[0]; }
F.bc = bcs;
F.totmob = &totmob[0];
if (! wdp.empty()) { F.wdp = &wdp[0]; }
bool ok = true;
if (rock_comp.empty()) {
ok = ifs_tpfa_assemble(gg, &F, &trans_[0], &gpress_omegaweighted_[0], h_);
} else {
ok = ifs_tpfa_assemble_comprock_increment(gg, &F, &trans_[0], &gpress_omegaweighted_[0],
&porevol[0], &rock_comp[0], dt, &prev_pressure[0],
&initial_porevol[0], h_);
&porevol[0], &rock_comp[0], dt, &prev_pressure[0],
&initial_porevol[0], h_);
}
if (!ok) {
THROW("Failed assembling pressure system.");
}
linsolver_.solve(h_->A, h_->b, &pressure_increment[0]);
linsolver_.solve(h_->A, h_->b, &pressure_increment[0]);
}
@ -305,30 +495,30 @@ namespace Opm
void IncompTpfa::computeFaceFlux(const std::vector<double>& totmob,
const std::vector<double>& omega,
const std::vector<double>& src,
const std::vector<double>& wdp,
const FlowBoundaryConditions* bcs,
std::vector<double>& pressure,
std::vector<double>& faceflux,
std::vector<double>& well_bhp,
std::vector<double>& well_rate) {
const std::vector<double>& omega,
const std::vector<double>& src,
const std::vector<double>& wdp,
const FlowBoundaryConditions* bcs,
std::vector<double>& pressure,
std::vector<double>& faceflux,
std::vector<double>& well_bhp,
std::vector<double>& well_rate) {
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
tpfa_eff_trans_compute(gg, &totmob[0], &htrans_[0], &trans_[0]);
UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_);
tpfa_eff_trans_compute(gg, &totmob[0], &htrans_[0], &trans_[0]);
if (!omega.empty()) {
if (gpress_.empty()) {
THROW("Nozero omega argument given, but gravity was null in constructor.");
}
mim_ip_density_update(gg->number_of_cells, gg->cell_facepos,
&omega[0],
&gpress_[0], &gpress_omegaweighted_[0]);
} else {
if (!gpress_.empty()) {
THROW("Empty omega argument given, but gravity was non-null in constructor.");
}
}
if (!omega.empty()) {
if (gpress_.empty()) {
THROW("Nozero omega argument given, but gravity was null in constructor.");
}
mim_ip_density_update(gg->number_of_cells, gg->cell_facepos,
&omega[0],
&gpress_[0], &gpress_omegaweighted_[0]);
} else {
if (!gpress_.empty()) {
THROW("Empty omega argument given, but gravity was non-null in constructor.");
}
}
ifs_tpfa_forces F = { NULL, NULL, wells_, NULL, NULL };
if (! src.empty()) { F.src = &src[0]; }
@ -336,27 +526,27 @@ namespace Opm
F.totmob = &totmob[0];
if (! wdp.empty()) { F.wdp = &wdp[0]; }
ifs_tpfa_assemble(gg, &F, &trans_[0], &gpress_omegaweighted_[0], h_);
faceflux.resize(grid_.number_of_faces);
ifs_tpfa_assemble(gg, &F, &trans_[0], &gpress_omegaweighted_[0], h_);
faceflux.resize(grid_.number_of_faces);
ifs_tpfa_solution soln = { NULL, NULL, NULL, NULL };
soln.cell_press = &pressure[0];
soln.face_flux = &faceflux[0];
if(wells_ != NULL) {
if (wells_ != NULL) {
well_bhp.resize(wells_->number_of_wells);
well_rate.resize(wells_->well_connpos[ wells_->number_of_wells ]);
soln.well_flux = &well_rate[0];
soln.well_press = &well_bhp[0];
}
// memcpy(h_->x, &pressure[0], grid_.number_of_cells * sizeof *(h_->x));
ASSERT(int(pressure.size()) == grid_.number_of_cells);
std::copy(pressure.begin(), pressure.end(), h_->x);
std::copy(well_bhp.begin(), well_bhp.end(), h_->x + grid_.number_of_cells);
// memcpy(h_->x, &pressure[0], grid_.number_of_cells * sizeof *(h_->x));
ASSERT(int(pressure.size()) == grid_.number_of_cells);
std::copy(pressure.begin(), pressure.end(), h_->x);
std::copy(well_bhp.begin(), well_bhp.end(), h_->x + grid_.number_of_cells);
ifs_tpfa_press_flux(gg, &F, &trans_[0], h_, &soln);
}
#endif
} // namespace Opm

View File

@ -21,158 +21,138 @@
#define OPM_INCOMPTPFA_HEADER_INCLUDED
#include <opm/core/pressure/tpfa/ifs_tpfa.h>
#include <vector>
struct UnstructuredGrid;
struct ifs_tpfa_data;
struct Wells;
struct FlowBoundaryConditions;
namespace Opm
{
class IncompPropertiesInterface;
class RockCompressibility;
class LinearSolverInterface;
class TwophaseState;
class WellState;
/// Encapsulating a tpfa pressure solver for the incompressible-fluid case.
/// Supports gravity, wells controlled by bhp or reservoir rates,
/// boundary conditions and simple sources as driving forces.
/// Rock compressibility can be included, but any nonlinear iterations
/// are not handled in this class.
/// Rock compressibility can be included, and necessary nonlinear
/// iterations are handled.
/// 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 IncompTpfa
{
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] linsolver A linear solver.
/// \param[in] wells The wells argument. Will be used in solution,
/// is ignored if NULL
IncompTpfa(const UnstructuredGrid& g,
const double* permeability,
const double* gravity,
/// \param[in] grid A 2d or 3d grid.
/// \param[in] props Rock and fluid properties.
/// \param[in] rock_comp_props Rock compressibility 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.
/// \param[in] src Source terms. May be empty().
/// \param[in] bcs Boundary conditions, treat as all noflow if null.
IncompTpfa(const UnstructuredGrid& grid,
const IncompPropertiesInterface& props,
const RockCompressibility* rock_comp_props,
LinearSolverInterface& linsolver,
const Wells* wells);
const double residual_tol,
const double change_tol,
const int maxiter,
const double* gravity,
const Wells* wells,
const std::vector<double>& src,
const FlowBoundaryConditions* bcs);
/// Destructor.
~IncompTpfa();
/// Assemble and solve incompressible pressure system.
/// \param[in] totmob Must contain N total mobility values (one per cell).
/// \f$totmob = \sum_{p} kr_p/mu_p\f$.
/// \param[in] omega Must be empty if constructor gravity argument was null.
/// Otherwise must contain N mobility-weighted density values (one per cell).
/// \f$omega = \frac{\sum_{p} mob_p rho_p}{\sum_p rho_p}\f$.
/// \param[in] src Must contain N source rates (one per cell).
/// Positive values represent total inflow rates,
/// negative values represent total outflow rates.
/// \param[in] wdp Should contain the differences between
/// well BHP and perforation pressures.
/// May be empty if there are no wells.
/// \param[in] bcs If non-null, specifies boundary conditions.
/// If null, noflow conditions are assumed.
/// \param[out] pressure Will contain N cell-pressure values.
/// \param[out] faceflux Will contain F signed face flux values.
/// \param[out] well_bhp Will contain bhp values for each well passed
/// in the constructor
/// \param[out] well_rate Will contain rate values for each well passed
/// in the constructor
void solve(const std::vector<double>& totmob,
const std::vector<double>& omega,
const std::vector<double>& src,
const std::vector<double>& wdp,
const FlowBoundaryConditions* bcs,
std::vector<double>& pressure,
std::vector<double>& faceflux,
std::vector<double>& well_bhp,
std::vector<double>& well_rate);
/// Solve the pressure equation. If there is no pressure
/// dependency introduced by rock compressibility effects,
/// the equation is linear, and it is solved directly.
/// Otherwise, the nonlinear equations ares solved by a
/// Newton-Raphson scheme.
/// May throw an exception if the number of iterations
/// exceed maxiter (set in constructor).
void solve(const double dt,
TwophaseState& state,
WellState& well_state);
/// Assemble and solve pressure system with rock compressibility (assumed constant per cell).
/// \param[in] totmob Must contain N total mobility values (one per cell).
/// totmob = \sum_{p} kr_p/mu_p.
/// \param[in] omega Must be empty if constructor gravity argument was null.
/// Otherwise must contain N fractional-flow-weighted density
/// values (one per cell).
/// omega = \frac{\sum_{p} mob_p rho_p}{\sum_p mob_p}.
/// \param[in] src Must contain N source rates (one per cell).
/// Positive values represent total inflow rates,
/// negative values represent total outflow rates.
/// \param[in] wdp Should contain the differences between
/// well BHP and perforation pressures.
/// May be empty if there are no wells.
/// \param[in] bcs If non-null, specifies boundary conditions.
/// If null, noflow conditions are assumed.
/// \param[in] porevol Must contain N pore volumes.
/// \param[in] rock_comp Must contain N rock compressibilities.
/// rock_comp = (d poro / d p)*(1/poro).
/// \param[in] dt Timestep.
/// \param[out] pressure Will contain N cell-pressure values.
/// \param[out] faceflux Will contain F signed face flux values.
/// \param[out] well_bhp Will contain bhp values for each well passed
/// in the constructor
/// \param[out] well_rate Will contain rate values for each well passed
/// in the constructor
void solve(const std::vector<double>& totmob,
const std::vector<double>& omega,
const std::vector<double>& src,
const std::vector<double>& wdp,
const FlowBoundaryConditions* bcs,
const std::vector<double>& porevol,
const std::vector<double>& rock_comp,
const double dt,
std::vector<double>& pressure,
std::vector<double>& faceflux,
std::vector<double>& well_bhp,
std::vector<double>& well_rate);
void solveIncrement(const std::vector<double>& totmob,
const std::vector<double>& omega,
const std::vector<double>& src,
const std::vector<double>& wdp,
const FlowBoundaryConditions* bcs,
const std::vector<double>& porevol,
const std::vector<double>& rock_comp,
const std::vector<double>& prev_pressure,
const std::vector<double>& initial_porevol,
const double dt,
std::vector<double>& pressure_increment);
void computeFaceFlux(const std::vector<double>& totmob,
const std::vector<double>& omega,
const std::vector<double>& src,
const std::vector<double>& wdp,
const FlowBoundaryConditions* bcs,
std::vector<double>& pressure,
std::vector<double>& faceflux,
std::vector<double>& well_bhp,
std::vector<double>& well_rate);
/// Expose read-only reference to internal half-transmissibility.
const ::std::vector<double>& getHalfTrans() const { return htrans_; }
/// Set tolerance for the linear solver.
/// \param[in] tol tolerance value
void setTolerance(const double tol);
/// Get tolerance of the linear solver.
/// \param[out] tolerance value
double getTolerance() const;
const std::vector<double>& getHalfTrans() const { return htrans_; }
private:
// Solve with no rock compressibility (linear eqn).
void solveIncomp(const double dt,
TwophaseState& state,
WellState& well_state);
// Solve with rock compressibility (nonlinear eqn).
void solveRockComp(const double dt,
TwophaseState& state,
WellState& well_state);
// Helper functions.
void computePerSolveDynamicData(const double dt,
const TwophaseState& state,
const WellState& well_state);
void computePerIterationDynamicData(const double dt,
const TwophaseState& state,
const WellState& well_state);
void assemble(const double dt,
const TwophaseState& state,
const WellState& well_state);
void solveIncrement();
double residualNorm() const;
double incrementNorm() const;
void computeResults(TwophaseState& state,
WellState& well_state) const;
private:
// ------ Data that will remain unmodified after construction. ------
const UnstructuredGrid& grid_;
LinearSolverInterface& linsolver_;
::std::vector<double> htrans_;
::std::vector<double> trans_ ;
::std::vector<double> gpress_;
::std::vector<double> gpress_omegaweighted_;
const struct Wells* wells_;
const IncompPropertiesInterface& 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().
const std::vector<double>& src_;
const FlowBoundaryConditions* bcs_;
std::vector<double> htrans_;
std::vector<double> gpress_;
std::vector<int> allcells_;
// ------ Data that will be modified for every solve. ------
std::vector<double> trans_ ;
std::vector<double> wdp_;
std::vector<double> totmob_;
std::vector<double> omega_;
std::vector<double> gpress_omegaweighted_;
std::vector<double> initial_porevol_;
struct ifs_tpfa_forces forces_;
// ------ Data that will be modified for every solver iteration. ------
std::vector<double> porevol_;
std::vector<double> rock_comp_;
std::vector<double> pressures_;
// ------ Internal data for the ifs_tpfa solver. ------
struct ifs_tpfa_data* h_;
};

View File

@ -79,12 +79,7 @@ namespace Opm
bool output_;
std::string output_dir_;
int output_interval_;
// Parameters for pressure solver.
int nl_pressure_maxiter_;
double nl_pressure_tolerance_;
// Parameters for transport solver.
int nl_maxiter_;
double nl_tolerance_;
int num_transport_substeps_;
bool use_segregation_split_;
// Observed objects.
@ -214,8 +209,14 @@ namespace Opm
bcs_(bcs),
linsolver_(linsolver),
gravity_(gravity),
psolver_(grid, props.permeability(), gravity, linsolver, wells),
tsolver_(grid, props, 1e-9, 30)
psolver_(grid, props, rock_comp, linsolver,
param.getDefault("nl_pressure_residual_tolerance", 1e-8),
param.getDefault("nl_pressure_change_tolerance", 1.0),
param.getDefault("nl_pressure_maxiter", 10),
gravity, wells, src, bcs),
tsolver_(grid, props,
param.getDefault("nl_tolerance", 1e-9),
param.getDefault("nl_maxiter", 30))
{
// For output.
output_ = param.getDefault("output", true);
@ -232,13 +233,7 @@ namespace Opm
output_interval_ = param.getDefault("output_interval", 1);
}
// For pressure solver
nl_pressure_maxiter_ = param.getDefault("nl_pressure_maxiter", 10);
nl_pressure_tolerance_ = param.getDefault("nl_pressure_tolerance", 1.0); // Pascal
// For transport solver.
nl_maxiter_ = param.getDefault("nl_maxiter", 30);
nl_tolerance_ = param.getDefault("nl_tolerance", 1e-9);
// Transport related init.
num_transport_substeps_ = param.getDefault("num_transport_substeps", 1);
use_segregation_split_ = param.getDefault("use_segregation_split", false);
if (gravity != 0 && use_segregation_split_){
@ -325,48 +320,7 @@ namespace Opm
}
do {
pressure_timer.start();
if (rock_comp_ && rock_comp_->isActive()) {
rc.resize(num_cells);
std::vector<double> initial_pressure = state.pressure();
std::vector<double> initial_porevolume(num_cells);
computePorevolume(grid_, props_.porosity(), *rock_comp_, initial_pressure, initial_porevolume);
std::vector<double> pressure_increment(num_cells + num_wells);
std::vector<double> prev_pressure(num_cells + num_wells);
for (int iter = 0; iter < nl_pressure_maxiter_; ++iter) {
for (int cell = 0; cell < num_cells; ++cell) {
rc[cell] = rock_comp_->rockComp(state.pressure()[cell]);
}
computePorevolume(grid_, props_.porosity(), *rock_comp_, state.pressure(), porevol);
std::copy(state.pressure().begin(), state.pressure().end(), prev_pressure.begin());
std::copy(well_state.bhp().begin(), well_state.bhp().end(), prev_pressure.begin() + num_cells);
// prev_pressure = state.pressure();
// compute pressure increment
psolver_.solveIncrement(totmob, omega, src_, wdp, bcs_, porevol, rc,
prev_pressure, initial_porevolume, timer.currentStepLength(),
pressure_increment);
double max_change = 0.0;
for (int cell = 0; cell < num_cells; ++cell) {
state.pressure()[cell] += pressure_increment[cell];
max_change = std::max(max_change, std::fabs(pressure_increment[cell]));
}
for (int well = 0; well < num_wells; ++well) {
well_state.bhp()[well] += pressure_increment[num_cells + well];
max_change = std::max(max_change, std::fabs(pressure_increment[num_cells + well]));
}
std::cout << "Pressure iter " << iter << " max change = " << max_change << std::endl;
if (max_change < nl_pressure_tolerance_) {
break;
}
}
psolver_.computeFaceFlux(totmob, omega, src_, wdp, bcs_, state.pressure(), state.faceflux(),
well_state.bhp(), well_state.perfRates());
} else {
psolver_.solve(totmob, omega, src_, wdp, bcs_, state.pressure(), state.faceflux(),
well_state.bhp(), well_state.perfRates());
}
psolver_.solve(timer.currentStepLength(), state, well_state);
pressure_timer.stop();
double pt = pressure_timer.secsSinceStart();
std::cout << "Pressure solver took: " << pt << " seconds." << std::endl;