mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Merge pull request #1323 from totto82/refactWell_minimum
Minimized PR: Make the wellModel self-contained
This commit is contained in:
@@ -29,15 +29,8 @@
|
||||
|
||||
#include <opm/autodiff/BlackoilModelParameters.hpp>
|
||||
#include <opm/autodiff/BlackoilWellModel.hpp>
|
||||
#include <opm/autodiff/AutoDiffBlock.hpp>
|
||||
#include <opm/autodiff/AutoDiffHelpers.hpp>
|
||||
#include <opm/autodiff/GridHelpers.hpp>
|
||||
#include <opm/autodiff/WellHelpers.hpp>
|
||||
#include <opm/autodiff/GeoProps.hpp>
|
||||
#include <opm/autodiff/WellDensitySegmented.hpp>
|
||||
#include <opm/autodiff/VFPProperties.hpp>
|
||||
#include <opm/autodiff/VFPProdProperties.hpp>
|
||||
#include <opm/autodiff/VFPInjProperties.hpp>
|
||||
#include <opm/autodiff/BlackoilDetails.hpp>
|
||||
#include <opm/autodiff/BlackoilModelEnums.hpp>
|
||||
#include <opm/autodiff/NewtonIterationBlackoilInterface.hpp>
|
||||
@@ -136,7 +129,7 @@ namespace Opm {
|
||||
|
||||
// For the conversion between the surface volume rate and resrevoir voidage rate
|
||||
using RateConverterType = RateConverter::
|
||||
SurfaceToReservoirVoidage<BlackoilPropsAdFromDeck::FluidSystem, std::vector<int> >;
|
||||
SurfaceToReservoirVoidage<FluidSystem, std::vector<int> >;
|
||||
|
||||
typedef Opm::FIPData FIPDataType;
|
||||
|
||||
@@ -155,7 +148,6 @@ namespace Opm {
|
||||
BlackoilModelEbos(Simulator& ebosSimulator,
|
||||
const ModelParameters& param,
|
||||
BlackoilWellModel<TypeTag>& well_model,
|
||||
RateConverterType& rate_converter,
|
||||
const NewtonIterationBlackoilInterface& linsolver,
|
||||
const bool terminal_output
|
||||
)
|
||||
@@ -163,9 +155,6 @@ namespace Opm {
|
||||
, grid_(ebosSimulator_.gridManager().grid())
|
||||
, istlSolver_( dynamic_cast< const ISTLSolverType* > (&linsolver) )
|
||||
, phaseUsage_(phaseUsageFromDeck(eclState()))
|
||||
, vfp_properties_(
|
||||
eclState().getTableManager().getVFPInjTables(),
|
||||
eclState().getTableManager().getVFPProdTables())
|
||||
, active_(detail::activePhases(phaseUsage_))
|
||||
, has_disgas_(FluidSystem::enableDissolvedGas())
|
||||
, has_vapoil_(FluidSystem::enableVaporizedOil())
|
||||
@@ -174,19 +163,12 @@ namespace Opm {
|
||||
, param_( param )
|
||||
, well_model_ (well_model)
|
||||
, terminal_output_ (terminal_output)
|
||||
, rate_converter_(rate_converter)
|
||||
, current_relaxation_(1.0)
|
||||
, dx_old_(AutoDiffGrid::numCells(grid_))
|
||||
, isBeginReportStep_(false)
|
||||
{
|
||||
// Wells are active if they are active wells on at least
|
||||
// one process.
|
||||
int wellsActive = localWellsActive() ? 1 : 0;
|
||||
wellsActive = grid_.comm().max(wellsActive);
|
||||
wellModel().setWellsActive( wellsActive );
|
||||
// compute global sum of number of cells
|
||||
global_nc_ = detail::countGlobalCells(grid_);
|
||||
wellModel().setVFPProperties(&vfp_properties_);
|
||||
if (!istlSolver_)
|
||||
{
|
||||
OPM_THROW(std::logic_error,"solver down cast to ISTLSolver failed");
|
||||
@@ -207,13 +189,12 @@ namespace Opm {
|
||||
const ReservoirState& /*reservoir_state*/,
|
||||
const WellState& /* well_state */)
|
||||
{
|
||||
if ( wellModel().wellCollection()->havingVREPGroups() ) {
|
||||
updateRateConverter();
|
||||
}
|
||||
|
||||
unsigned numDof = ebosSimulator_.model().numGridDof();
|
||||
wasSwitched_.resize(numDof);
|
||||
std::fill(wasSwitched_.begin(), wasSwitched_.end(), false);
|
||||
|
||||
wellModel().beginTimeStep();
|
||||
}
|
||||
|
||||
|
||||
@@ -231,7 +212,7 @@ namespace Opm {
|
||||
const SimulatorTimerInterface& timer,
|
||||
NonlinearSolverType& nonlinear_solver,
|
||||
ReservoirState& /*reservoir_state*/,
|
||||
WellState& well_state)
|
||||
WellState& /*well_state*/)
|
||||
{
|
||||
SimulatorReport report;
|
||||
failureReport_ = SimulatorReport();
|
||||
@@ -249,7 +230,7 @@ namespace Opm {
|
||||
report.total_linearizations = 1;
|
||||
|
||||
try {
|
||||
report += assemble(timer, iteration, well_state);
|
||||
report += assemble(timer, iteration);
|
||||
report.assemble_time += perfTimer.stop();
|
||||
}
|
||||
catch (...) {
|
||||
@@ -266,8 +247,8 @@ namespace Opm {
|
||||
report.converged = getConvergence(timer, iteration,residual_norms) && iteration > nonlinear_solver.minIter();
|
||||
|
||||
// checking whether the group targets are converged
|
||||
if (wellModel().wellCollection()->groupControlActive()) {
|
||||
report.converged = report.converged && wellModel().wellCollection()->groupTargetConverged(well_state.wellRates());
|
||||
if (wellModel().wellCollection().groupControlActive()) {
|
||||
report.converged = report.converged && wellModel().wellCollection().groupTargetConverged(wellModel().wellState().wellRates());
|
||||
}
|
||||
|
||||
report.update_time += perfTimer.stop();
|
||||
@@ -282,7 +263,6 @@ namespace Opm {
|
||||
|
||||
// Compute the nonlinear update.
|
||||
const int nc = AutoDiffGrid::numCells(grid_);
|
||||
const int nw = numWells();
|
||||
BVector x(nc);
|
||||
|
||||
try {
|
||||
@@ -304,11 +284,7 @@ namespace Opm {
|
||||
// handling well state update before oscillation treatment is a decision based
|
||||
// on observation to avoid some big performance degeneration under some circumstances.
|
||||
// there is no theorectical explanation which way is better for sure.
|
||||
|
||||
if( nw > 0 )
|
||||
{
|
||||
wellModel().recoverWellSolutionAndUpdateWellState(x, well_state);
|
||||
}
|
||||
wellModel().recoverWellSolutionAndUpdateWellState(x);
|
||||
|
||||
if (param_.use_update_stabilization_) {
|
||||
// Stabilize the nonlinear update.
|
||||
@@ -356,6 +332,9 @@ namespace Opm {
|
||||
DUNE_UNUSED_PARAMETER(timer);
|
||||
DUNE_UNUSED_PARAMETER(reservoir_state);
|
||||
DUNE_UNUSED_PARAMETER(well_state);
|
||||
|
||||
wellModel().timeStepSucceeded();
|
||||
|
||||
}
|
||||
|
||||
/// Assemble the residual and Jacobian of the nonlinear system.
|
||||
@@ -363,18 +342,8 @@ namespace Opm {
|
||||
/// \param[in, out] well_state well state variables
|
||||
/// \param[in] initial_assembly pass true if this is the first call to assemble() in this timestep
|
||||
SimulatorReport assemble(const SimulatorTimerInterface& timer,
|
||||
const int iterationIdx,
|
||||
WellState& well_state)
|
||||
const int iterationIdx)
|
||||
{
|
||||
using namespace Opm::AutoDiffGrid;
|
||||
|
||||
SimulatorReport report;
|
||||
|
||||
// when having VREP group control, update the rate converter based on reservoir state
|
||||
if ( wellModel().wellCollection()->havingVREPGroups() ) {
|
||||
updateRateConverter();
|
||||
}
|
||||
|
||||
// -------- Mass balance equations --------
|
||||
assembleMassBalanceEq(timer, iterationIdx);
|
||||
|
||||
@@ -383,7 +352,9 @@ namespace Opm {
|
||||
|
||||
try
|
||||
{
|
||||
report = wellModel().assemble(ebosSimulator_, iterationIdx, dt, well_state);
|
||||
// assembles the well equations and applies the wells to
|
||||
// the reservoir equations as a source term.
|
||||
wellModel().assemble(iterationIdx, dt);
|
||||
|
||||
// apply well residual to the residual.
|
||||
auto& ebosResid = ebosSimulator_.model().linearizer().residual();
|
||||
@@ -394,7 +365,7 @@ namespace Opm {
|
||||
OPM_THROW(Opm::NumericalProblem,"Error encounted when solving well equations");
|
||||
}
|
||||
|
||||
return report;
|
||||
return wellModel().lastReport();
|
||||
}
|
||||
|
||||
// compute the "relative" change of the solution between time steps
|
||||
@@ -476,14 +447,6 @@ namespace Opm {
|
||||
}
|
||||
|
||||
|
||||
/// The size (number of unknowns) of the nonlinear system of equations.
|
||||
int sizeNonLinear() const
|
||||
{
|
||||
const int nc = Opm::AutoDiffGrid::numCells(grid_);
|
||||
const int nw = numWells();
|
||||
return numComponents() * (nc + nw);
|
||||
}
|
||||
|
||||
/// Number of linear iterations used in last call to solveJacobianSystem().
|
||||
int linearIterationsLastSolve() const
|
||||
{
|
||||
@@ -497,6 +460,18 @@ namespace Opm {
|
||||
const auto& ebosJac = ebosSimulator_.model().linearizer().matrix();
|
||||
auto& ebosResid = ebosSimulator_.model().linearizer().residual();
|
||||
|
||||
// J = [A, B; C, D], where A is the reservoir equations, B and C the interaction of well
|
||||
// with the reservoir and D is the wells itself.
|
||||
// The full system is reduced to a number of cells X number of cells system via Schur complement
|
||||
// A -= B^T D^-1 C
|
||||
// Instead of modifying A, the Ax operator is modified. i.e Ax -= B^T D^-1 C x in the WellModelMatrixAdapter.
|
||||
// The residual is modified similarly.
|
||||
// r = [r, r_well], where r is the residual and r_well the well residual.
|
||||
// r -= B^T * D^-1 r_well
|
||||
// Note: Currently the residual is modified in the assembleMassBalanceEq call.
|
||||
// It conceptually belongs here, but moving it changes the solution path for some
|
||||
// unknown reasons.
|
||||
|
||||
// set initial guess
|
||||
x = 0.0;
|
||||
|
||||
@@ -504,14 +479,14 @@ namespace Opm {
|
||||
if( isParallel() )
|
||||
{
|
||||
typedef WellModelMatrixAdapter< Mat, BVector, BVector, BlackoilWellModel<TypeTag>, true > Operator;
|
||||
Operator opA(ebosJac, well_model_, istlSolver().parallelInformation() );
|
||||
Operator opA(ebosJac, wellModel(), istlSolver().parallelInformation() );
|
||||
assert( opA.comm() );
|
||||
istlSolver().solve( opA, x, ebosResid, *(opA.comm()) );
|
||||
}
|
||||
else
|
||||
{
|
||||
typedef WellModelMatrixAdapter< Mat, BVector, BVector, BlackoilWellModel<TypeTag>, false > Operator;
|
||||
Operator opA(ebosJac, well_model_);
|
||||
Operator opA(ebosJac, wellModel());
|
||||
istlSolver().solve( opA, x, ebosResid );
|
||||
}
|
||||
}
|
||||
@@ -916,7 +891,7 @@ namespace Opm {
|
||||
residual_norms.push_back(CNV[compIdx]);
|
||||
}
|
||||
|
||||
const bool converged_Well = wellModel().getWellConvergence(ebosSimulator_, B_avg);
|
||||
const bool converged_Well = wellModel().getWellConvergence(B_avg);
|
||||
|
||||
bool converged = converged_MB && converged_Well;
|
||||
|
||||
@@ -1486,7 +1461,6 @@ namespace Opm {
|
||||
const Grid& grid_;
|
||||
const ISTLSolverType* istlSolver_;
|
||||
const PhaseUsage phaseUsage_;
|
||||
VFPProperties vfp_properties_;
|
||||
// For each canonical phase -> true if active
|
||||
const std::vector<bool> active_;
|
||||
// Size = # active phases. Maps active -> canonical phase indices.
|
||||
@@ -1507,9 +1481,6 @@ namespace Opm {
|
||||
/// \brief The number of cells of the global grid.
|
||||
long int global_nc_;
|
||||
|
||||
// rate converter between the surface volume rates and reservoir voidage rates
|
||||
RateConverterType& rate_converter_;
|
||||
|
||||
std::vector<std::vector<double>> residual_norms_history_;
|
||||
double current_relaxation_;
|
||||
BVector dx_old_;
|
||||
@@ -1523,15 +1494,6 @@ namespace Opm {
|
||||
const BlackoilWellModel<TypeTag>&
|
||||
wellModel() const { return well_model_; }
|
||||
|
||||
int numWells() const { return well_model_.numWells(); }
|
||||
|
||||
/// return true if wells are available on this process
|
||||
bool localWellsActive() const { return well_model_.localWellsActive(); }
|
||||
|
||||
|
||||
|
||||
|
||||
public:
|
||||
int flowPhaseToEbosCompIdx( const int phaseIdx ) const
|
||||
{
|
||||
const auto& pu = phaseUsage_;
|
||||
@@ -1561,15 +1523,6 @@ namespace Opm {
|
||||
return phaseIdx;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void updateRateConverter()
|
||||
{
|
||||
rate_converter_.defineState<ElementContext>(ebosSimulator_);
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
void beginReportStep()
|
||||
{
|
||||
isBeginReportStep_ = true;
|
||||
|
||||
@@ -116,6 +116,8 @@ namespace Opm {
|
||||
using Base::numPhases;
|
||||
using Base::numMaterials;
|
||||
using Base::materialName;
|
||||
using Base::wellModel;
|
||||
|
||||
|
||||
protected:
|
||||
// --------- Data members ---------
|
||||
@@ -143,7 +145,6 @@ namespace Opm {
|
||||
using Base::vfp_properties_;
|
||||
using Base::well_model_;
|
||||
|
||||
using Base::wellModel;
|
||||
// using Base::wells;
|
||||
using Base::wellsActive;
|
||||
using Base::updatePrimalVariableFromState;
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include <opm/autodiff/WellInterface.hpp>
|
||||
#include <opm/autodiff/StandardWell.hpp>
|
||||
#include <opm/autodiff/MultisegmentWell.hpp>
|
||||
#include<opm/autodiff/SimFIBODetails.hpp>
|
||||
#include<dune/common/fmatrix.hh>
|
||||
#include<dune/istl/bcrsmatrix.hh>
|
||||
#include<dune/istl/matrixmatrix.hh>
|
||||
@@ -69,6 +70,10 @@ namespace Opm {
|
||||
typedef WellStateFullyImplicitBlackoil WellState;
|
||||
typedef BlackoilModelParameters ModelParameters;
|
||||
|
||||
static const int Water = WellInterface<TypeTag>::Water;
|
||||
static const int Oil = WellInterface<TypeTag>::Oil;
|
||||
static const int Gas = WellInterface<TypeTag>::Gas;
|
||||
|
||||
typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, ElementContext) ElementContext;
|
||||
@@ -88,32 +93,16 @@ namespace Opm {
|
||||
|
||||
// For the conversion between the surface volume rate and resrevoir voidage rate
|
||||
using RateConverterType = RateConverter::
|
||||
SurfaceToReservoirVoidage<BlackoilPropsAdFromDeck::FluidSystem, std::vector<int> >;
|
||||
SurfaceToReservoirVoidage<FluidSystem, std::vector<int> >;
|
||||
|
||||
// --------- Public methods ---------
|
||||
BlackoilWellModel(const Wells* wells_arg,
|
||||
WellCollection* well_collection,
|
||||
const std::vector< const Well* >& wells_ecl,
|
||||
BlackoilWellModel(Simulator& ebosSimulator,
|
||||
const ModelParameters& param,
|
||||
const RateConverterType& rate_converter,
|
||||
const bool terminal_output,
|
||||
const int current_index,
|
||||
const std::vector<int>& pvt_region_idx);
|
||||
const bool terminal_output);
|
||||
|
||||
void init(const PhaseUsage phase_usage_arg,
|
||||
const std::vector<bool>& active_arg,
|
||||
const double gravity_arg,
|
||||
const std::vector<double>& depth_arg,
|
||||
long int global_nc,
|
||||
const Grid& grid);
|
||||
|
||||
void setVFPProperties(const VFPProperties* vfp_properties_arg);
|
||||
|
||||
|
||||
SimulatorReport assemble(Simulator& ebosSimulator,
|
||||
const int iterationIdx,
|
||||
const double dt,
|
||||
WellState& well_state);
|
||||
// compute the well fluxes and assemble them in to the reservoir equations as source terms
|
||||
// and in the well equations.
|
||||
void assemble(const int iterationIdx,
|
||||
const double dt);
|
||||
|
||||
// substract Binv(D)rw from r;
|
||||
void apply( BVector& r) const;
|
||||
@@ -126,44 +115,46 @@ namespace Opm {
|
||||
|
||||
// using the solution x to recover the solution xw for wells and applying
|
||||
// xw to update Well State
|
||||
void recoverWellSolutionAndUpdateWellState(const BVector& x, WellState& well_state) const;
|
||||
void recoverWellSolutionAndUpdateWellState(const BVector& x);
|
||||
|
||||
int numWells() const;
|
||||
// Check if well equations is converged.
|
||||
bool getWellConvergence(const std::vector<Scalar>& B_avg) const;
|
||||
|
||||
/// return true if wells are available in the reservoir
|
||||
bool wellsActive() const;
|
||||
// return all the wells.
|
||||
const WellCollection& wellCollection() const;
|
||||
// return non const reference to all the wells.
|
||||
WellCollection& wellCollection();
|
||||
|
||||
void setWellsActive(const bool wells_active);
|
||||
// return the internal well state, ignore the passed one.
|
||||
// Used by the legacy code to make it compatible with the legacy well models.
|
||||
const WellState& wellState(const WellState& well_state OPM_UNUSED) const;
|
||||
|
||||
/// return true if wells are available on this process
|
||||
bool localWellsActive() const;
|
||||
// return the internal well state
|
||||
const WellState& wellState() const;
|
||||
|
||||
bool getWellConvergence(const Simulator& ebosSimulator,
|
||||
const std::vector<Scalar>& B_avg) const;
|
||||
// only use this for restart.
|
||||
void setRestartWellState(const WellState& well_state);
|
||||
|
||||
/// upate the dynamic lists related to economic limits
|
||||
void updateListEconLimited(const Schedule& schedule,
|
||||
const int current_step,
|
||||
const Wells* wells_struct,
|
||||
const WellState& well_state,
|
||||
DynamicListEconLimited& list_econ_limited) const;
|
||||
// called at the beginning of a time step
|
||||
void beginTimeStep();
|
||||
// called at the end of a time step
|
||||
void timeStepSucceeded();
|
||||
|
||||
WellCollection* wellCollection() const;
|
||||
// called at the beginning of a report step
|
||||
void beginReportStep(const int time_step);
|
||||
|
||||
// called at the end of a report step
|
||||
void endReportStep();
|
||||
|
||||
const SimulatorReport& lastReport() const;
|
||||
|
||||
protected:
|
||||
|
||||
Simulator& ebosSimulator_;
|
||||
std::unique_ptr<WellsManager> wells_manager_;
|
||||
std::vector< const Well* > wells_ecl_;
|
||||
|
||||
bool wells_active_;
|
||||
const Wells* wells_;
|
||||
const std::vector< const Well* > wells_ecl_;
|
||||
|
||||
// the number of wells in this process
|
||||
// trying not to use things from Wells struct
|
||||
// TODO: maybe a better name to emphasize it is local?
|
||||
const int number_of_wells_;
|
||||
|
||||
const int number_of_phases_;
|
||||
|
||||
const ModelParameters& param_;
|
||||
|
||||
using WellInterfacePtr = std::unique_ptr<WellInterface<TypeTag> >;
|
||||
// a vector of all the wells.
|
||||
@@ -175,59 +166,60 @@ namespace Opm {
|
||||
using ConvergenceReport = typename WellInterface<TypeTag>::ConvergenceReport;
|
||||
|
||||
// create the well container
|
||||
static std::vector<WellInterfacePtr > createWellContainer(const Wells* wells,
|
||||
const std::vector<const Well*>& wells_ecl,
|
||||
const bool use_multisegment_well,
|
||||
const int time_step,
|
||||
const ModelParameters& param);
|
||||
std::vector<WellInterfacePtr > createWellContainer(const int time_step) const;
|
||||
|
||||
// Well collection is used to enforce the group control
|
||||
WellCollection* well_collection_;
|
||||
WellState well_state_;
|
||||
WellState previous_well_state_;
|
||||
|
||||
const ModelParameters param_;
|
||||
bool terminal_output_;
|
||||
bool has_solvent_;
|
||||
bool has_polymer_;
|
||||
int current_timeIdx_;
|
||||
|
||||
std::vector<int> pvt_region_idx_;
|
||||
PhaseUsage phase_usage_;
|
||||
std::vector<bool> active_;
|
||||
const RateConverterType& rate_converter_;
|
||||
const std::vector<int>& pvt_region_idx_;
|
||||
|
||||
size_t global_nc_;
|
||||
// the number of the cells in the local grid
|
||||
int number_of_cells_;
|
||||
size_t number_of_cells_;
|
||||
double gravity_;
|
||||
std::vector<double> depth_;
|
||||
|
||||
long int global_nc_;
|
||||
DynamicListEconLimited dynamic_list_econ_limited_;
|
||||
std::unique_ptr<RateConverterType> rateConverter_;
|
||||
std::unique_ptr<VFPProperties> vfp_properties_;
|
||||
|
||||
SimulatorReport last_report_;
|
||||
|
||||
// used to better efficiency of calcuation
|
||||
mutable BVector scaleAddRes_;
|
||||
|
||||
void updateWellControls(WellState& xw) const;
|
||||
const Wells* wells() const { return wells_manager_->c_wells(); }
|
||||
|
||||
void updateGroupControls(WellState& well_state) const;
|
||||
const Schedule& schedule() const
|
||||
{ return ebosSimulator_.gridManager().schedule(); }
|
||||
|
||||
void updateWellControls();
|
||||
|
||||
void updateGroupControls();
|
||||
|
||||
// setting the well_solutions_ based on well_state.
|
||||
void updatePrimaryVariables(const WellState& well_state) const;
|
||||
void updatePrimaryVariables();
|
||||
|
||||
void setupCompressedToCartesian(const int* global_cell, int number_of_cells, std::map<int,int>& cartesian_to_compressed ) const;
|
||||
|
||||
void computeRepRadiusPerfLength(const Grid& grid);
|
||||
|
||||
|
||||
void computeAverageFormationFactor(const Simulator& ebosSimulator,
|
||||
std::vector<double>& B_avg) const;
|
||||
void computeAverageFormationFactor(std::vector<double>& B_avg) const;
|
||||
|
||||
void applyVREPGroupControl(WellState& well_state) const;
|
||||
void applyVREPGroupControl();
|
||||
|
||||
void computeWellVoidageRates(const WellState& well_state,
|
||||
std::vector<double>& well_voidage_rates,
|
||||
void computeWellVoidageRates(std::vector<double>& well_voidage_rates,
|
||||
std::vector<double>& voidage_conversion_coeffs) const;
|
||||
|
||||
// Calculating well potentials for each well
|
||||
// TODO: getBhp() will be refactored to reduce the duplication of the code calculating the bhp from THP.
|
||||
void computeWellPotentials(const Simulator& ebosSimulator,
|
||||
const WellState& well_state,
|
||||
std::vector<double>& well_potentials) const;
|
||||
void computeWellPotentials(std::vector<double>& well_potentials);
|
||||
|
||||
const std::vector<double>& wellPerfEfficiencyFactors() const;
|
||||
|
||||
@@ -238,47 +230,51 @@ namespace Opm {
|
||||
// twice at the beginning of the time step
|
||||
/// Calculating the explict quantities used in the well calculation. By explicit, we mean they are cacluated
|
||||
/// at the beginning of the time step and no derivatives are included in these quantities
|
||||
void calculateExplicitQuantities(const Simulator& ebosSimulator,
|
||||
const WellState& xw) const;
|
||||
void calculateExplicitQuantities() const;
|
||||
|
||||
SimulatorReport solveWellEq(Simulator& ebosSimulator,
|
||||
const double dt,
|
||||
WellState& well_state) const;
|
||||
SimulatorReport solveWellEq(const double dt);
|
||||
|
||||
void initPrimaryVariablesEvaluation() const;
|
||||
|
||||
// The number of components in the model.
|
||||
int numComponents() const
|
||||
{
|
||||
if (numPhases() == 2) {
|
||||
return 2;
|
||||
}
|
||||
int numComp = FluidSystem::numComponents;
|
||||
if (has_solvent_) {
|
||||
numComp ++;
|
||||
}
|
||||
int numComponents() const;
|
||||
|
||||
return numComp;
|
||||
}
|
||||
int numWells() const;
|
||||
|
||||
int numPhases() const;
|
||||
|
||||
|
||||
int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const;
|
||||
|
||||
void resetWellControlFromState(const WellState& xw) const;
|
||||
void resetWellControlFromState() const;
|
||||
|
||||
void assembleWellEq(Simulator& ebosSimulator,
|
||||
const double dt,
|
||||
WellState& well_state,
|
||||
bool only_wells) const;
|
||||
void assembleWellEq(const double dt,
|
||||
bool only_wells);
|
||||
|
||||
// some preparation work, mostly related to group control and RESV,
|
||||
// at the beginning of each time step (Not report step)
|
||||
void prepareTimeStep(const Simulator& ebos_simulator,
|
||||
WellState& well_state) const;
|
||||
void prepareTimeStep();
|
||||
|
||||
void prepareGroupControl(const Simulator& ebos_simulator,
|
||||
WellState& well_state) const;
|
||||
void prepareGroupControl();
|
||||
|
||||
void computeRESV(const std::size_t step);
|
||||
|
||||
void extractLegacyCellPvtRegionIndex_();
|
||||
|
||||
void extractLegacyDepth_();
|
||||
|
||||
/// return true if wells are available in the reservoir
|
||||
bool wellsActive() const;
|
||||
|
||||
void setWellsActive(const bool wells_active);
|
||||
|
||||
/// return true if wells are available on this process
|
||||
bool localWellsActive() const;
|
||||
|
||||
/// upate the dynamic lists related to economic limits
|
||||
void updateListEconLimited(DynamicListEconLimited& list_econ_limited) const;
|
||||
|
||||
void updatePerforationIntensiveQuantities();
|
||||
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -728,8 +728,7 @@ namespace Opm
|
||||
FluidSystem::enableDissolvedGas(),
|
||||
FluidSystem::enableVaporizedOil(),
|
||||
eclState(),
|
||||
*output_writer_,
|
||||
defunctWellNames()));
|
||||
*output_writer_));
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -806,9 +805,6 @@ namespace Opm
|
||||
Scalar gravity() const
|
||||
{ return ebosProblem().gravity()[2]; }
|
||||
|
||||
std::unordered_set<std::string> defunctWellNames() const
|
||||
{ return ebosSimulator_->gridManager().defunctWellNames(); }
|
||||
|
||||
data::Solution computeLegacySimProps_()
|
||||
{
|
||||
const int* dims = UgGridHelpers::cartDims(grid());
|
||||
|
||||
@@ -47,6 +47,9 @@ namespace Opm
|
||||
|
||||
using Base::has_solvent;
|
||||
using Base::has_polymer;
|
||||
using Base::Water;
|
||||
using Base::Oil;
|
||||
using Base::Gas;
|
||||
|
||||
// TODO: for now, not considering the polymer, solvent and so on to simplify the development process.
|
||||
|
||||
|
||||
@@ -237,6 +237,10 @@ namespace Opm {
|
||||
|
||||
const Vector& wellPerfEfficiencyFactors() const;
|
||||
|
||||
// just return the passed well state
|
||||
template<class WellState>
|
||||
const WellState& wellState(const WellState& well_state) const { return well_state; }
|
||||
|
||||
|
||||
protected:
|
||||
// TODO: probably a wells_active_ will be required here.
|
||||
|
||||
@@ -21,13 +21,12 @@
|
||||
#ifndef OPM_RATECONVERTER_HPP_HEADER_INCLUDED
|
||||
#define OPM_RATECONVERTER_HPP_HEADER_INCLUDED
|
||||
|
||||
#include <opm/autodiff/BlackoilPropsAdFromDeck.hpp>
|
||||
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/core/simulator/BlackoilState.hpp>
|
||||
#include <opm/core/utility/RegionMapping.hpp>
|
||||
#include <opm/core/linalg/ParallelIstlInformation.hpp>
|
||||
|
||||
#include <dune/grid/common/gridenums.hh>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
@@ -99,8 +99,7 @@ public:
|
||||
const bool has_disgas,
|
||||
const bool has_vapoil,
|
||||
const EclipseState& /* eclState */,
|
||||
OutputWriter& output_writer,
|
||||
const std::unordered_set<std::string>& defunct_well_names)
|
||||
OutputWriter& output_writer)
|
||||
: ebosSimulator_(ebosSimulator),
|
||||
param_(param),
|
||||
model_param_(param),
|
||||
@@ -111,8 +110,6 @@ public:
|
||||
has_vapoil_(has_vapoil),
|
||||
terminal_output_(param.getDefault("output_terminal", true)),
|
||||
output_writer_(output_writer),
|
||||
rateConverter_(createRateConverter_()),
|
||||
defunct_well_names_( defunct_well_names ),
|
||||
is_parallel_run_( false )
|
||||
{
|
||||
#if HAVE_MPI
|
||||
@@ -141,7 +138,6 @@ public:
|
||||
ExtraData extra;
|
||||
|
||||
failureReport_ = SimulatorReport();
|
||||
extractLegacyDepth_();
|
||||
|
||||
// communicate the initial solution to ebos
|
||||
if (timer.initialStep()) {
|
||||
@@ -214,7 +210,6 @@ public:
|
||||
ebosSimulator_.model().invalidateIntensiveQuantitiesCache(/*timeIdx=*/0);
|
||||
}
|
||||
|
||||
DynamicListEconLimited dynamic_list_econ_limited;
|
||||
SimulatorReport report;
|
||||
SimulatorReport stepReport;
|
||||
|
||||
@@ -231,6 +226,13 @@ public:
|
||||
std::vector<std::vector<double>> originalFluidInPlace;
|
||||
std::vector<double> originalFluidInPlaceTotals;
|
||||
|
||||
WellModel well_model(ebosSimulator_, model_param_, terminal_output_);
|
||||
if (output_writer_.isRestart()) {
|
||||
well_model.setRestartWellState(prev_well_state); // Neccessary for perfect restarts
|
||||
}
|
||||
|
||||
WellState wellStateDummy; //not used. Only passed to make the old interfaces happy
|
||||
|
||||
// Main simulation loop.
|
||||
while (!timer.done()) {
|
||||
// Report timestep.
|
||||
@@ -242,77 +244,10 @@ public:
|
||||
OpmLog::note(ss.str());
|
||||
}
|
||||
|
||||
// Create wells and well state.
|
||||
WellsManager wells_manager(eclState(),
|
||||
schedule(),
|
||||
timer.currentStepNum(),
|
||||
Opm::UgGridHelpers::numCells(grid()),
|
||||
Opm::UgGridHelpers::globalCell(grid()),
|
||||
Opm::UgGridHelpers::cartDims(grid()),
|
||||
Opm::UgGridHelpers::dimensions(grid()),
|
||||
Opm::UgGridHelpers::cell2Faces(grid()),
|
||||
Opm::UgGridHelpers::beginFaceCentroids(grid()),
|
||||
dynamic_list_econ_limited,
|
||||
is_parallel_run_,
|
||||
defunct_well_names_ );
|
||||
const Wells* wells = wells_manager.c_wells();
|
||||
WellState well_state;
|
||||
|
||||
// The well state initialize bhp with the cell pressure in the top cell.
|
||||
// We must therefore provide it with updated cell pressures
|
||||
size_t nc = Opm::UgGridHelpers::numCells(grid());
|
||||
std::vector<double> cellPressures(nc, 0.0);
|
||||
const auto& gridView = ebosSimulator_.gridManager().gridView();
|
||||
ElementContext elemCtx(ebosSimulator_);
|
||||
const auto& elemEndIt = gridView.template end</*codim=*/0>();
|
||||
for (auto elemIt = gridView.template begin</*codim=*/0>();
|
||||
elemIt != elemEndIt;
|
||||
++elemIt)
|
||||
{
|
||||
const auto& elem = *elemIt;
|
||||
if (elem.partitionType() != Dune::InteriorEntity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
elemCtx.updatePrimaryStencil(elem);
|
||||
elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
|
||||
|
||||
const unsigned cellIdx = elemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
|
||||
const auto& intQuants = elemCtx.intensiveQuantities(/*spaceIdx=*/0, /*timeIdx=*/0);
|
||||
const auto& fs = intQuants.fluidState();
|
||||
|
||||
const double p = fs.pressure(FluidSystem::oilPhaseIdx).value();
|
||||
cellPressures[cellIdx] = p;
|
||||
}
|
||||
well_state.init(wells, cellPressures, prev_well_state, phaseUsage_);
|
||||
|
||||
// give the polymer and surfactant simulators the chance to do their stuff
|
||||
handleAdditionalWellInflow(timer, wells_manager, well_state, wells);
|
||||
|
||||
// Compute reservoir volumes for RESV controls.
|
||||
computeRESV(timer.currentStepNum(), wells, well_state);
|
||||
|
||||
// Run a multiple steps of the solver depending on the time step control.
|
||||
solver_timer.start();
|
||||
|
||||
const auto& wells_ecl = schedule().getWells(timer.currentStepNum());
|
||||
extractLegacyCellPvtRegionIndex_();
|
||||
WellModel well_model(wells, &(wells_manager.wellCollection()), wells_ecl, model_param_,
|
||||
rateConverter_, terminal_output_, timer.currentStepNum(), legacyCellPvtRegionIdx_);
|
||||
|
||||
// handling MS well related
|
||||
if (model_param_.use_multisegment_well_) { // if we use MultisegmentWell model
|
||||
for (const auto& well : wells_ecl) {
|
||||
// TODO: this is acutally not very accurate, because sometimes a deck just claims a MS well
|
||||
// while keep the well shut. More accurately, we should check if the well exisits in the Wells
|
||||
// structure here
|
||||
if (well->isMultiSegment(timer.currentStepNum()) ) { // there is one well is MS well
|
||||
well_state.initWellStateMSWell(wells, wells_ecl, timer.currentStepNum(), phaseUsage_, prev_well_state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
well_model.beginReportStep(timer.currentStepNum());
|
||||
|
||||
auto solver = createSolver(well_model);
|
||||
|
||||
@@ -344,7 +279,7 @@ public:
|
||||
|
||||
// No per cell data is written for initial step, but will be
|
||||
// for subsequent steps, when we have started simulating
|
||||
output_writer_.writeTimeStep( timer, state, well_state, solver->model() );
|
||||
output_writer_.writeTimeStep( timer, state, well_model.wellState(), solver->model() );
|
||||
|
||||
report.output_write_time += perfTimer.stop();
|
||||
}
|
||||
@@ -373,14 +308,14 @@ public:
|
||||
events.hasEvent(ScheduleEvents::PRODUCTION_UPDATE, timer.currentStepNum()) ||
|
||||
events.hasEvent(ScheduleEvents::INJECTION_UPDATE, timer.currentStepNum()) ||
|
||||
events.hasEvent(ScheduleEvents::WELL_STATUS_CHANGE, timer.currentStepNum());
|
||||
stepReport = adaptiveTimeStepping->step( timer, *solver, state, well_state, event, output_writer_,
|
||||
stepReport = adaptiveTimeStepping->step( timer, *solver, state, wellStateDummy, event, output_writer_,
|
||||
output_writer_.requireFIPNUM() ? &fipnum : nullptr );
|
||||
report += stepReport;
|
||||
failureReport_ += adaptiveTimeStepping->failureReport();
|
||||
}
|
||||
else {
|
||||
// solve for complete report step
|
||||
stepReport = solver->step(timer, state, well_state);
|
||||
stepReport = solver->step(timer, state, wellStateDummy);
|
||||
report += stepReport;
|
||||
failureReport_ += solver->failureReport();
|
||||
|
||||
@@ -400,6 +335,7 @@ public:
|
||||
}
|
||||
|
||||
solver->model().endReportStep();
|
||||
well_model.endReportStep();
|
||||
|
||||
// take time that was used to solve system for this reportStep
|
||||
solver_timer.stop();
|
||||
@@ -450,13 +386,9 @@ public:
|
||||
Dune::Timer perfTimer;
|
||||
perfTimer.start();
|
||||
const double nextstep = adaptiveTimeStepping ? adaptiveTimeStepping->suggestedNextStep() : -1.0;
|
||||
output_writer_.writeTimeStep( timer, state, well_state, solver->model(), false, nextstep, report);
|
||||
output_writer_.writeTimeStep( timer, state, well_model.wellState(), solver->model(), false, nextstep, report);
|
||||
report.output_write_time += perfTimer.stop();
|
||||
|
||||
prev_well_state = well_state;
|
||||
|
||||
updateListEconLimited(solver, schedule(), timer.currentStepNum(), wells,
|
||||
well_state, dynamic_list_econ_limited);
|
||||
}
|
||||
|
||||
// Stop timer and create timing report
|
||||
@@ -474,222 +406,18 @@ public:
|
||||
{ return ebosSimulator_.gridManager().grid(); }
|
||||
|
||||
protected:
|
||||
void handleAdditionalWellInflow(SimulatorTimer& /*timer*/,
|
||||
WellsManager& /* wells_manager */,
|
||||
WellState& /* well_state */,
|
||||
const Wells* /* wells */)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Solver> createSolver(WellModel& well_model)
|
||||
{
|
||||
const auto& gridView = ebosSimulator_.gridView();
|
||||
const PhaseUsage& phaseUsage = phaseUsage_;
|
||||
const std::vector<bool> activePhases = detail::activePhases(phaseUsage);
|
||||
const double gravity = ebosSimulator_.problem().gravity()[2];
|
||||
|
||||
// calculate the number of elements of the compressed sequential grid. this needs
|
||||
// to be done in two steps because the dune communicator expects a reference as
|
||||
// argument for sum()
|
||||
int globalNumCells = gridView.size(/*codim=*/0);
|
||||
globalNumCells = gridView.comm().sum(globalNumCells);
|
||||
|
||||
well_model.init(phaseUsage,
|
||||
activePhases,
|
||||
gravity,
|
||||
legacyDepth_,
|
||||
globalNumCells,
|
||||
grid());
|
||||
auto model = std::unique_ptr<Model>(new Model(ebosSimulator_,
|
||||
model_param_,
|
||||
well_model,
|
||||
rateConverter_,
|
||||
solver_,
|
||||
terminal_output_));
|
||||
|
||||
return std::unique_ptr<Solver>(new Solver(solver_param_, std::move(model)));
|
||||
}
|
||||
|
||||
void computeRESV(const std::size_t step,
|
||||
const Wells* wells,
|
||||
WellState& xw)
|
||||
{
|
||||
typedef SimFIBODetails::WellMap WellMap;
|
||||
|
||||
const auto w_ecl = schedule().getWells(step);
|
||||
const WellMap& wmap = SimFIBODetails::mapWells(w_ecl);
|
||||
|
||||
const std::vector<int>& resv_wells = SimFIBODetails::resvWells(wells, step, wmap);
|
||||
|
||||
const std::size_t number_resv_wells = resv_wells.size();
|
||||
std::size_t global_number_resv_wells = number_resv_wells;
|
||||
#if HAVE_MPI
|
||||
if ( solver_.parallelInformation().type() == typeid(ParallelISTLInformation) )
|
||||
{
|
||||
const auto& info =
|
||||
boost::any_cast<const ParallelISTLInformation&>(solver_.parallelInformation());
|
||||
global_number_resv_wells = info.communicator().sum(global_number_resv_wells);
|
||||
if ( global_number_resv_wells )
|
||||
{
|
||||
// At least one process has resv wells. Therefore rate converter needs
|
||||
// to calculate averages over regions that might cross process
|
||||
// borders. This needs to be done by all processes and therefore
|
||||
// outside of the next if statement.
|
||||
rateConverter_.template defineState<ElementContext>(ebosSimulator_);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if ( global_number_resv_wells )
|
||||
{
|
||||
rateConverter_.template defineState<ElementContext>(ebosSimulator_);
|
||||
}
|
||||
}
|
||||
|
||||
if (! resv_wells.empty()) {
|
||||
const PhaseUsage& pu = phaseUsage_;
|
||||
const std::vector<double>::size_type np = phaseUsage_.num_phases;
|
||||
|
||||
std::vector<double> distr (np);
|
||||
std::vector<double> hrates(np);
|
||||
std::vector<double> prates(np);
|
||||
|
||||
for (std::vector<int>::const_iterator
|
||||
rp = resv_wells.begin(), e = resv_wells.end();
|
||||
rp != e; ++rp)
|
||||
{
|
||||
WellControls* ctrl = wells->ctrls[*rp];
|
||||
const bool is_producer = wells->type[*rp] == PRODUCER;
|
||||
const int well_cell_top = wells->well_cells[wells->well_connpos[*rp]];
|
||||
const auto& eclProblem = ebosSimulator_.problem();
|
||||
const int pvtreg = eclProblem.pvtRegionIndex(well_cell_top);
|
||||
|
||||
// RESV control mode, all wells
|
||||
{
|
||||
const int rctrl = SimFIBODetails::resv_control(ctrl);
|
||||
|
||||
if (0 <= rctrl) {
|
||||
const std::vector<double>::size_type off = (*rp) * np;
|
||||
|
||||
if (is_producer) {
|
||||
// Convert to positive rates to avoid issues
|
||||
// in coefficient calculations.
|
||||
std::transform(xw.wellRates().begin() + (off + 0*np),
|
||||
xw.wellRates().begin() + (off + 1*np),
|
||||
prates.begin(), std::negate<double>());
|
||||
} else {
|
||||
std::copy(xw.wellRates().begin() + (off + 0*np),
|
||||
xw.wellRates().begin() + (off + 1*np),
|
||||
prates.begin());
|
||||
}
|
||||
|
||||
const int fipreg = 0; // Hack. Ignore FIP regions.
|
||||
rateConverter_.calcCoeff(fipreg, pvtreg, distr);
|
||||
|
||||
well_controls_iset_distr(ctrl, rctrl, & distr[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// RESV control, WCONHIST wells. A bit of duplicate
|
||||
// work, regrettably.
|
||||
if (is_producer && wells->name[*rp] != 0) {
|
||||
WellMap::const_iterator i = wmap.find(wells->name[*rp]);
|
||||
|
||||
if (i != wmap.end()) {
|
||||
const auto* wp = i->second;
|
||||
|
||||
const WellProductionProperties& p =
|
||||
wp->getProductionProperties(step);
|
||||
|
||||
if (! p.predictionMode) {
|
||||
// History matching (WCONHIST/RESV)
|
||||
SimFIBODetails::historyRates(pu, p, hrates);
|
||||
|
||||
const int fipreg = 0; // Hack. Ignore FIP regions.
|
||||
rateConverter_.calcCoeff(fipreg, pvtreg, distr);
|
||||
|
||||
// WCONHIST/RESV target is sum of all
|
||||
// observed phase rates translated to
|
||||
// reservoir conditions. Recall sign
|
||||
// convention: Negative for producers.
|
||||
const double target =
|
||||
- std::inner_product(distr.begin(), distr.end(),
|
||||
hrates.begin(), 0.0);
|
||||
|
||||
well_controls_clear(ctrl);
|
||||
well_controls_assert_number_of_phases(ctrl, int(np));
|
||||
|
||||
static const double invalid_alq = -std::numeric_limits<double>::max();
|
||||
static const int invalid_vfp = -std::numeric_limits<int>::max();
|
||||
|
||||
const int ok_resv =
|
||||
well_controls_add_new(RESERVOIR_RATE, target,
|
||||
invalid_alq, invalid_vfp,
|
||||
& distr[0], ctrl);
|
||||
|
||||
// For WCONHIST the BHP limit is set to 1 atm.
|
||||
// or a value specified using WELTARG
|
||||
double bhp_limit = (p.BHPLimit > 0) ? p.BHPLimit : unit::convert::from(1.0, unit::atm);
|
||||
const int ok_bhp =
|
||||
well_controls_add_new(BHP, bhp_limit,
|
||||
invalid_alq, invalid_vfp,
|
||||
NULL, ctrl);
|
||||
|
||||
if (ok_resv != 0 && ok_bhp != 0) {
|
||||
xw.currentControls()[*rp] = 0;
|
||||
well_controls_set_current(ctrl, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( wells )
|
||||
{
|
||||
for (int w = 0, nw = wells->number_of_wells; w < nw; ++w) {
|
||||
WellControls* ctrl = wells->ctrls[w];
|
||||
const bool is_producer = wells->type[w] == PRODUCER;
|
||||
if (!is_producer && wells->name[w] != 0) {
|
||||
WellMap::const_iterator i = wmap.find(wells->name[w]);
|
||||
if (i != wmap.end()) {
|
||||
const auto* wp = i->second;
|
||||
const WellInjectionProperties& injector = wp->getInjectionProperties(step);
|
||||
if (!injector.predictionMode) {
|
||||
//History matching WCONINJEH
|
||||
static const double invalid_alq = -std::numeric_limits<double>::max();
|
||||
static const int invalid_vfp = -std::numeric_limits<int>::max();
|
||||
// For WCONINJEH the BHP limit is set to a large number
|
||||
// or a value specified using WELTARG
|
||||
double bhp_limit = (injector.BHPLimit > 0) ? injector.BHPLimit : std::numeric_limits<double>::max();
|
||||
const int ok_bhp =
|
||||
well_controls_add_new(BHP, bhp_limit,
|
||||
invalid_alq, invalid_vfp,
|
||||
NULL, ctrl);
|
||||
if (!ok_bhp) {
|
||||
OPM_THROW(std::runtime_error, "Failed to add well control.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void updateListEconLimited(const std::unique_ptr<Solver>& solver,
|
||||
const Schedule& schedule,
|
||||
const int current_step,
|
||||
const Wells* wells,
|
||||
const WellState& well_state,
|
||||
DynamicListEconLimited& list_econ_limited) const
|
||||
{
|
||||
solver->model().wellModel().updateListEconLimited(schedule, current_step, wells,
|
||||
well_state, list_econ_limited);
|
||||
}
|
||||
|
||||
void FIPUnitConvert(const UnitSystem& units,
|
||||
std::vector<std::vector<double>>& fip)
|
||||
{
|
||||
@@ -845,23 +573,6 @@ protected:
|
||||
const Schedule& schedule() const
|
||||
{ return ebosSimulator_.gridManager().schedule(); }
|
||||
|
||||
const SummaryConfig& summaryConfig() const
|
||||
{ return ebosSimulator_.gridManager().summaryConfig( ); }
|
||||
|
||||
|
||||
void extractLegacyCellPvtRegionIndex_()
|
||||
{
|
||||
const auto& grid = ebosSimulator_.gridManager().grid();
|
||||
const auto& eclProblem = ebosSimulator_.problem();
|
||||
const unsigned numCells = grid.size(/*codim=*/0);
|
||||
|
||||
legacyCellPvtRegionIdx_.resize(numCells);
|
||||
for (unsigned cellIdx = 0; cellIdx < numCells; ++cellIdx) {
|
||||
legacyCellPvtRegionIdx_[cellIdx] =
|
||||
eclProblem.pvtRegionIndex(cellIdx);
|
||||
}
|
||||
}
|
||||
|
||||
void initHysteresisParams(ReservoirState& state) {
|
||||
const int num_cells = Opm::UgGridHelpers::numCells(grid());
|
||||
|
||||
@@ -895,17 +606,6 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void extractLegacyDepth_()
|
||||
{
|
||||
const auto& grid = ebosSimulator_.gridManager().grid();
|
||||
const unsigned numCells = grid.size(/*codim=*/0);
|
||||
|
||||
legacyDepth_.resize(numCells);
|
||||
for (unsigned cellIdx = 0; cellIdx < numCells; ++cellIdx) {
|
||||
legacyDepth_[cellIdx] =
|
||||
grid.cellCenterDepth(cellIdx);
|
||||
}
|
||||
}
|
||||
|
||||
// Used to convert initial Reservoirstate to primary variables in the SolutionVector
|
||||
void convertInput( const int iterationIdx,
|
||||
@@ -1014,18 +714,10 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
RateConverterType createRateConverter_() {
|
||||
RateConverterType rate_converter(phaseUsage_,
|
||||
std::vector<int>(AutoDiffGrid::numCells(grid()), 0)); // FIP = 0
|
||||
return rate_converter;
|
||||
}
|
||||
|
||||
|
||||
// Data.
|
||||
Simulator& ebosSimulator_;
|
||||
|
||||
std::vector<int> legacyCellPvtRegionIdx_;
|
||||
std::vector<double> legacyDepth_;
|
||||
typedef typename Solver::SolverParameters SolverParameters;
|
||||
|
||||
SimulatorReport failureReport_;
|
||||
@@ -1043,11 +735,6 @@ protected:
|
||||
bool terminal_output_;
|
||||
// output_writer
|
||||
OutputWriter& output_writer_;
|
||||
RateConverterType rateConverter_;
|
||||
// The names of wells that should be defunct
|
||||
// (e.g. in a parallel run when they are handeled by
|
||||
// a different process)
|
||||
std::unordered_set<std::string> defunct_well_names_;
|
||||
|
||||
// Whether this a parallel simulation or not
|
||||
bool is_parallel_run_;
|
||||
|
||||
@@ -1045,8 +1045,7 @@ namespace Opm
|
||||
miscSummaryData["TCPU"] = totalSolverTime;
|
||||
}
|
||||
}
|
||||
|
||||
writeTimeStepWithCellProperties(timer, localState, localCellData, localWellState, miscSummaryData, extraRestartData, substep);
|
||||
writeTimeStepWithCellProperties(timer, localState, localCellData, physicalModel.wellModel().wellState(localWellState), miscSummaryData, extraRestartData, substep);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -67,6 +67,9 @@ namespace Opm
|
||||
using Base::has_solvent;
|
||||
using Base::has_polymer;
|
||||
using Base::name;
|
||||
using Base::Water;
|
||||
using Base::Oil;
|
||||
using Base::Gas;
|
||||
|
||||
// TODO: with flow_ebos,for a 2P deck, // TODO: for the 2p deck, numEq will be 3, a dummy phase is already added from the reservoir side.
|
||||
// it will cause problem here without processing the dummy phase.
|
||||
|
||||
@@ -199,6 +199,11 @@ namespace Opm {
|
||||
|
||||
const Vector& wellPerfEfficiencyFactors() const;
|
||||
|
||||
// just return the passed well state
|
||||
template<class WellState>
|
||||
const WellState& wellState(const WellState& well_state) const { return well_state; }
|
||||
|
||||
|
||||
protected:
|
||||
bool wells_active_;
|
||||
const Wells* wells_;
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <opm/autodiff/WellDensitySegmented.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/autodiff/WellStateFullyImplicitBlackoil.hpp>
|
||||
#include <opm/autodiff/WellStateFullyImplicitBlackoilSolvent.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <numeric>
|
||||
@@ -148,125 +147,6 @@ Opm::WellDensitySegmented::computeConnectionDensities(const Wells& wells,
|
||||
|
||||
|
||||
|
||||
|
||||
std::vector<double>
|
||||
Opm::WellDensitySegmented::computeConnectionDensities(const Wells& wells,
|
||||
const WellStateFullyImplicitBlackoilSolvent& wstate,
|
||||
const PhaseUsage& phase_usage,
|
||||
const std::vector<double>& b_perf,
|
||||
const std::vector<double>& rsmax_perf,
|
||||
const std::vector<double>& rvmax_perf,
|
||||
const std::vector<double>& surf_dens_perf)
|
||||
{
|
||||
// Verify that we have consistent input.
|
||||
const int np = wells.number_of_phases;
|
||||
const int nw = wells.number_of_wells;
|
||||
const int nperf = wells.well_connpos[nw];
|
||||
if (wells.number_of_phases != phase_usage.num_phases) {
|
||||
OPM_THROW(std::logic_error, "Inconsistent input: wells vs. phase_usage.");
|
||||
}
|
||||
if (nperf*np != int(surf_dens_perf.size())) {
|
||||
OPM_THROW(std::logic_error, "Inconsistent input: wells vs. surf_dens.");
|
||||
}
|
||||
if (nperf*np != int(wstate.perfPhaseRates().size())) {
|
||||
OPM_THROW(std::logic_error, "Inconsistent input: wells vs. wstate.");
|
||||
}
|
||||
if (nperf*np != int(b_perf.size())) {
|
||||
OPM_THROW(std::logic_error, "Inconsistent input: wells vs. b_perf.");
|
||||
}
|
||||
if ((!rsmax_perf.empty()) || (!rvmax_perf.empty())) {
|
||||
// Need both oil and gas phases.
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Liquid]) {
|
||||
OPM_THROW(std::logic_error, "Oil phase inactive, but non-empty rsmax_perf or rvmax_perf.");
|
||||
}
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Vapour]) {
|
||||
OPM_THROW(std::logic_error, "Gas phase inactive, but non-empty rsmax_perf or rvmax_perf.");
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Compute the flow (in surface volume units for each
|
||||
// component) exiting up the wellbore from each perforation,
|
||||
// taking into account flow from lower in the well, and
|
||||
// in/out-flow at each perforation.
|
||||
std::vector<double> q_out_perf(nperf*np);
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
// Iterate over well perforations from bottom to top.
|
||||
for (int perf = wells.well_connpos[w+1] - 1; perf >= wells.well_connpos[w]; --perf) {
|
||||
for (int phase = 0; phase < np; ++phase) {
|
||||
if (perf == wells.well_connpos[w+1] - 1) {
|
||||
// This is the bottom perforation. No flow from below.
|
||||
q_out_perf[perf*np + phase] = 0.0;
|
||||
} else {
|
||||
// Set equal to flow from below.
|
||||
q_out_perf[perf*np + phase] = q_out_perf[(perf+1)*np + phase];
|
||||
}
|
||||
// Subtract outflow through perforation.
|
||||
q_out_perf[perf*np + phase] -= wstate.perfPhaseRates()[perf*np + phase];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Compute the component mix at each perforation as the
|
||||
// absolute values of the surface rates divided by their sum.
|
||||
// Then compute volume ratios (formation factors) for each perforation.
|
||||
// Finally compute densities for the segments associated with each perforation.
|
||||
const int gaspos = phase_usage.phase_pos[BlackoilPhases::Vapour];
|
||||
const int oilpos = phase_usage.phase_pos[BlackoilPhases::Liquid];
|
||||
std::vector<double> mix(np);
|
||||
std::vector<double> x(np);
|
||||
std::vector<double> surf_dens(np);
|
||||
std::vector<double> dens(nperf);
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
for (int perf = wells.well_connpos[w]; perf < wells.well_connpos[w+1]; ++perf) {
|
||||
// Find component mix.
|
||||
const double tot_surf_rate = std::accumulate(q_out_perf.begin() + np*perf,
|
||||
q_out_perf.begin() + np*(perf+1), 0.0);
|
||||
if (tot_surf_rate != 0.0) {
|
||||
for (int phase = 0; phase < np; ++phase) {
|
||||
mix[phase] = std::fabs(q_out_perf[perf*np + phase]/tot_surf_rate);
|
||||
}
|
||||
} else {
|
||||
// No flow => use well specified fractions for mix.
|
||||
std::copy(wells.comp_frac + w*np, wells.comp_frac + (w+1)*np, mix.begin());
|
||||
}
|
||||
// Compute volume ratio.
|
||||
x = mix;
|
||||
double rs = 0.0;
|
||||
double rv = 0.0;
|
||||
if (!rsmax_perf.empty() && mix[oilpos] > 0.0) {
|
||||
rs = std::min(mix[gaspos]/mix[oilpos], rsmax_perf[perf]);
|
||||
}
|
||||
const double gas_without_solvent = mix[gaspos] * (1.0 - wstate.solventFraction()[w]);
|
||||
if (!rvmax_perf.empty() && gas_without_solvent > 0.0) {
|
||||
rv = std::min(mix[oilpos]/gas_without_solvent, rvmax_perf[perf]);
|
||||
}
|
||||
if (rs != 0.0) {
|
||||
// Subtract gas in oil from gas mixture
|
||||
x[gaspos] = (mix[gaspos] - mix[oilpos]*rs)/(1.0 - rs*rv);
|
||||
}
|
||||
if (rv != 0.0) {
|
||||
// Subtract oil in gas from oil mixture
|
||||
x[oilpos] = (mix[oilpos] - gas_without_solvent*rv)/(1.0 - rs*rv);;
|
||||
}
|
||||
double volrat = 0.0;
|
||||
for (int phase = 0; phase < np; ++phase) {
|
||||
volrat += x[phase] / b_perf[perf*np + phase];
|
||||
}
|
||||
for (int phase = 0; phase < np; ++phase) {
|
||||
surf_dens[phase] = surf_dens_perf[perf*np + phase];
|
||||
}
|
||||
|
||||
// Compute segment density.
|
||||
dens[perf] = std::inner_product(surf_dens.begin(), surf_dens.end(), mix.begin(), 0.0) / volrat;
|
||||
}
|
||||
}
|
||||
|
||||
return dens;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::vector<double>
|
||||
Opm::WellDensitySegmented::computeConnectionPressureDelta(const Wells& wells,
|
||||
const std::vector<double>& z_perf,
|
||||
|
||||
@@ -59,25 +59,6 @@ namespace Opm
|
||||
|
||||
|
||||
|
||||
/// Compute well segment densities for solvent model
|
||||
/// Notation: N = number of perforations, P = number of phases.
|
||||
/// \param[in] wells struct with static well info
|
||||
/// \param[in] wstate dynamic well solution information, perfRates() and solventFraction() is used
|
||||
/// \param[in] phase_usage specifies which phases are active and not
|
||||
/// \param[in] b_perf inverse ('little b') formation volume factor, size NP, P values per perforation
|
||||
/// \param[in] rsmax_perf saturation point for rs (gas in oil) at each perforation, size N
|
||||
/// \param[in] rvmax_perf saturation point for rv (oil in gas) at each perforation, size N
|
||||
/// \param[in] surf_dens surface densities for active components, size NP, P values per perforation
|
||||
static std::vector<double> computeConnectionDensities(const Wells& wells,
|
||||
const WellStateFullyImplicitBlackoilSolvent& wstate,
|
||||
const PhaseUsage& phase_usage,
|
||||
const std::vector<double>& b_perf,
|
||||
const std::vector<double>& rsmax_perf,
|
||||
const std::vector<double>& rvmax_perf,
|
||||
const std::vector<double>& surf_dens_perf);
|
||||
|
||||
|
||||
|
||||
|
||||
/// Compute pressure deltas.
|
||||
/// Notation: N = number of perforations
|
||||
|
||||
@@ -64,6 +64,11 @@ namespace Opm
|
||||
using WellState = WellStateFullyImplicitBlackoil;
|
||||
|
||||
typedef BlackoilModelParameters ModelParameters;
|
||||
|
||||
static const int Water = BlackoilPhases::Aqua;
|
||||
static const int Oil = BlackoilPhases::Liquid;
|
||||
static const int Gas = BlackoilPhases::Vapour;
|
||||
|
||||
typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, Simulator) Simulator;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem;
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#ifndef OPM_WELLSTATEFULLYIMPLICITBLACKOIL_HEADER_INCLUDED
|
||||
#define OPM_WELLSTATEFULLYIMPLICITBLACKOIL_HEADER_INCLUDED
|
||||
|
||||
#include <opm/autodiff/BlackoilModelEnums.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/well_controls.h>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
@@ -375,8 +374,8 @@ namespace Opm
|
||||
const int start_perf = wells->well_connpos[w];
|
||||
const int start_perf_next_well = wells->well_connpos[w + 1];
|
||||
assert(nperf == (start_perf_next_well - start_perf)); // make sure the information from wells_ecl consistent with wells
|
||||
if (pu.phase_used[Gas]) {
|
||||
const int gaspos = pu.phase_pos[Gas];
|
||||
if (pu.phase_used[BlackoilPhases::Vapour]) {
|
||||
const int gaspos = pu.phase_pos[BlackoilPhases::Vapour];
|
||||
// scale the phase rates for Gas to avoid too bad initial guess for gas fraction
|
||||
// it will probably benefit the standard well too, while it needs to be justified
|
||||
// TODO: to see if this strategy can benefit StandardWell too
|
||||
|
||||
Reference in New Issue
Block a user