2017-02-13 09:45:06 -06:00
namespace Opm {
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
StandardWellsDense < TypeTag > : :
2017-02-13 09:45:06 -06:00
StandardWellsDense ( const Wells * wells_arg ,
WellCollection * well_collection ,
2017-05-09 01:21:51 -05:00
const std : : vector < const Well * > & wells_ecl ,
2017-02-13 09:45:06 -06:00
const ModelParameters & param ,
2017-05-09 01:21:51 -05:00
const bool terminal_output ,
const int current_timeIdx )
2017-02-13 09:45:06 -06:00
: wells_active_ ( wells_arg ! = nullptr )
, wells_ ( wells_arg )
2017-05-09 01:21:51 -05:00
, wells_ecl_ ( wells_ecl )
2017-02-13 09:45:06 -06:00
, well_collection_ ( well_collection )
, param_ ( param )
, terminal_output_ ( terminal_output )
2017-05-09 01:21:51 -05:00
, has_solvent_ ( GET_PROP_VALUE ( TypeTag , EnableSolvent ) )
, current_timeIdx_ ( current_timeIdx )
2017-02-13 09:45:06 -06:00
, well_perforation_efficiency_factors_ ( ( wells_ ! = nullptr ? wells_ - > well_connpos [ wells_ - > number_of_wells ] : 0 ) , 1.0 )
, well_perforation_densities_ ( wells_ ? wells_arg - > well_connpos [ wells_arg - > number_of_wells ] : 0 )
, well_perforation_pressure_diffs_ ( wells_ ? wells_arg - > well_connpos [ wells_arg - > number_of_wells ] : 0 )
2017-05-03 06:34:15 -05:00
, wellVariables_ ( wells_ ? ( wells_arg - > number_of_wells * numWellEq ) : 0 )
, F0_ ( wells_ ? ( wells_arg - > number_of_wells * numWellEq ) : 0 )
2017-02-13 09:45:06 -06:00
{
if ( wells_ )
{
invDuneD_ . setBuildMode ( Mat : : row_wise ) ;
duneC_ . setBuildMode ( Mat : : row_wise ) ;
duneB_ . setBuildMode ( Mat : : row_wise ) ;
}
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 09:45:06 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 09:45:06 -06:00
init ( const PhaseUsage phase_usage_arg ,
const std : : vector < bool > & active_arg ,
const VFPProperties * vfp_properties_arg ,
const double gravity_arg ,
const std : : vector < double > & depth_arg ,
const std : : vector < double > & pv_arg ,
2017-03-24 09:12:42 -05:00
const RateConverterType * rate_converter ,
long int global_nc )
2017-02-13 09:45:06 -06:00
{
2017-03-24 09:12:42 -05:00
// has to be set always for the convergence check!
global_nc_ = global_nc ;
2017-02-13 09:45:06 -06:00
if ( ! localWellsActive ( ) ) {
return ;
}
phase_usage_ = phase_usage_arg ;
active_ = active_arg ;
vfp_properties_ = vfp_properties_arg ;
gravity_ = gravity_arg ;
cell_depths_ = extractPerfData ( depth_arg ) ;
pv_ = pv_arg ;
rate_converter_ = rate_converter ;
calculateEfficiencyFactors ( ) ;
// setup sparsity pattern for the matrices
//[A B^T [x = [ res
// C D] x_well] res_well]
const int nw = wells ( ) . number_of_wells ;
const int nperf = wells ( ) . well_connpos [ nw ] ;
const int nc = numCells ( ) ;
# ifndef NDEBUG
const auto pu = phase_usage_ ;
const int np = pu . num_phases ;
// assumes the gas fractions are stored after water fractions
// WellVariablePositions needs to be changed for 2p runs
assert ( np = = 3 | | ( np = = 2 & & ! pu . phase_used [ Gas ] ) ) ;
# endif
// set invDuneD
invDuneD_ . setSize ( nw , nw , nw ) ;
// set duneC
duneC_ . setSize ( nw , nc , nperf ) ;
// set duneB
duneB_ . setSize ( nw , nc , nperf ) ;
for ( auto row = invDuneD_ . createbegin ( ) , end = invDuneD_ . createend ( ) ; row ! = end ; + + row ) {
// Add nonzeros for diagonal
row . insert ( row . index ( ) ) ;
}
for ( auto row = duneC_ . createbegin ( ) , end = duneC_ . createend ( ) ; row ! = end ; + + row ) {
// Add nonzeros for diagonal
for ( int perf = wells ( ) . well_connpos [ row . index ( ) ] ; perf < wells ( ) . well_connpos [ row . index ( ) + 1 ] ; + + perf ) {
const int cell_idx = wells ( ) . well_cells [ perf ] ;
row . insert ( cell_idx ) ;
}
}
// make the B^T matrix
for ( auto row = duneB_ . createbegin ( ) , end = duneB_ . createend ( ) ; row ! = end ; + + row ) {
for ( int perf = wells ( ) . well_connpos [ row . index ( ) ] ; perf < wells ( ) . well_connpos [ row . index ( ) + 1 ] ; + + perf ) {
const int cell_idx = wells ( ) . well_cells [ perf ] ;
row . insert ( cell_idx ) ;
}
}
resWell_ . resize ( nw ) ;
// resize temporary class variables
Cx_ . resize ( duneC_ . N ( ) ) ;
invDrw_ . resize ( invDuneD_ . N ( ) ) ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 09:45:06 -06:00
SimulatorReport
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 09:45:06 -06:00
assemble ( Simulator & ebosSimulator ,
const int iterationIdx ,
const double dt ,
WellState & well_state )
{
2017-03-09 08:43:13 -06:00
2017-03-24 09:11:49 -05:00
if ( iterationIdx = = 0 ) {
prepareTimeStep ( ebosSimulator , well_state ) ;
}
2017-02-13 09:45:06 -06:00
SimulatorReport report ;
2017-04-12 10:37:34 -05:00
if ( ! wellsActive ( ) ) {
2017-02-13 09:45:06 -06:00
return report ;
}
updateWellControls ( well_state ) ;
// Set the primary variables for the wells
setWellVariables ( well_state ) ;
if ( iterationIdx = = 0 ) {
computeWellConnectionPressures ( ebosSimulator , well_state ) ;
computeAccumWells ( ) ;
}
if ( param_ . solve_welleq_initially_ & & iterationIdx = = 0 ) {
// solve the well equations as a pre-processing step
report = solveWellEq ( ebosSimulator , dt , well_state ) ;
}
assembleWellEq ( ebosSimulator , dt , well_state , false ) ;
report . converged = true ;
return report ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 09:45:06 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 09:45:06 -06:00
assembleWellEq ( Simulator & ebosSimulator ,
const double dt ,
WellState & well_state ,
bool only_wells )
{
const int nw = wells ( ) . number_of_wells ;
2017-05-03 06:34:15 -05:00
const int numComp = numComponents ( ) ;
2017-05-09 01:21:51 -05:00
const int np = numPhases ( ) ;
2017-02-13 09:45:06 -06:00
// clear all entries
duneB_ = 0.0 ;
duneC_ = 0.0 ;
invDuneD_ = 0.0 ;
resWell_ = 0.0 ;
auto & ebosJac = ebosSimulator . model ( ) . linearizer ( ) . matrix ( ) ;
auto & ebosResid = ebosSimulator . model ( ) . linearizer ( ) . residual ( ) ;
const double volume = 0.002831684659200 ; // 0.1 cu ft;
for ( int w = 0 ; w < nw ; + + w ) {
bool allow_cf = allow_cross_flow ( w , ebosSimulator ) ;
2017-02-14 04:34:03 -06:00
const EvalWell bhp = getBhp ( w ) ;
2017-02-13 09:45:06 -06:00
for ( int perf = wells ( ) . well_connpos [ w ] ; perf < wells ( ) . well_connpos [ w + 1 ] ; + + perf ) {
const int cell_idx = wells ( ) . well_cells [ perf ] ;
const auto & intQuants = * ( ebosSimulator . model ( ) . cachedIntensiveQuantities ( cell_idx , /*timeIdx=*/ 0 ) ) ;
2017-05-03 06:34:15 -05:00
std : : vector < EvalWell > cq_s ( numComp , 0.0 ) ;
std : : vector < EvalWell > mob ( numComp , 0.0 ) ;
2017-04-06 07:21:59 -05:00
getMobility ( ebosSimulator , perf , cell_idx , mob ) ;
2017-05-03 06:34:15 -05:00
computeWellFlux ( w , wells ( ) . WI [ perf ] , intQuants , mob , bhp , wellPerforationPressureDiffs ( ) [ perf ] , allow_cf , cq_s ) ;
2017-02-13 09:45:06 -06:00
2017-05-03 06:34:15 -05:00
for ( int componentIdx = 0 ; componentIdx < numComp ; + + componentIdx ) {
2017-02-13 09:45:06 -06:00
// the cq_s entering mass balance equations need to consider the efficiency factors.
2017-05-03 06:34:15 -05:00
const EvalWell cq_s_effective = cq_s [ componentIdx ] * well_perforation_efficiency_factors_ [ perf ] ;
2017-02-13 09:45:06 -06:00
if ( ! only_wells ) {
2017-05-03 06:34:15 -05:00
// subtract sum of component fluxes in the reservoir equation.
2017-02-13 09:45:06 -06:00
// need to consider the efficiency factor
2017-05-03 06:34:15 -05:00
ebosResid [ cell_idx ] [ flowPhaseToEbosCompIdx ( componentIdx ) ] - = cq_s_effective . value ( ) ;
2017-02-13 09:45:06 -06:00
}
// subtract sum of phase fluxes in the well equations.
2017-05-03 06:34:15 -05:00
resWell_ [ w ] [ flowPhaseToEbosCompIdx ( componentIdx ) ] - = cq_s [ componentIdx ] . value ( ) ;
2017-02-13 09:45:06 -06:00
// assemble the jacobians
2017-05-03 06:34:15 -05:00
for ( int pvIdx = 0 ; pvIdx < numWellEq ; + + pvIdx ) {
2017-02-13 09:45:06 -06:00
if ( ! only_wells ) {
// also need to consider the efficiency factor when manipulating the jacobians.
2017-05-03 06:34:15 -05:00
ebosJac [ cell_idx ] [ cell_idx ] [ flowPhaseToEbosCompIdx ( componentIdx ) ] [ flowToEbosPvIdx ( pvIdx ) ] - = cq_s_effective . derivative ( pvIdx ) ;
duneB_ [ w ] [ cell_idx ] [ flowToEbosPvIdx ( pvIdx ) ] [ flowPhaseToEbosCompIdx ( componentIdx ) ] - = cq_s_effective . derivative ( pvIdx + numEq ) ; // intput in transformed matrix
duneC_ [ w ] [ cell_idx ] [ flowPhaseToEbosCompIdx ( componentIdx ) ] [ flowToEbosPvIdx ( pvIdx ) ] - = cq_s_effective . derivative ( pvIdx ) ;
2017-02-13 09:45:06 -06:00
}
2017-05-03 06:34:15 -05:00
invDuneD_ [ w ] [ w ] [ flowPhaseToEbosCompIdx ( componentIdx ) ] [ flowToEbosPvIdx ( pvIdx ) ] - = cq_s [ componentIdx ] . derivative ( pvIdx + numEq ) ;
2017-02-13 09:45:06 -06:00
}
// add trivial equation for 2p cases (Only support water + oil)
2017-05-03 06:34:15 -05:00
if ( numComp = = 2 ) {
2017-02-13 09:45:06 -06:00
assert ( ! active_ [ Gas ] ) ;
invDuneD_ [ w ] [ w ] [ flowPhaseToEbosCompIdx ( Gas ) ] [ flowToEbosPvIdx ( Gas ) ] = 1.0 ;
}
// Store the perforation phase flux for later usage.
2017-05-09 01:21:51 -05:00
if ( componentIdx = = solventCompIdx ) { // if (flowPhaseToEbosCompIdx(componentIdx) == Solvent)
well_state . perfRateSolvent ( ) [ perf ] = cq_s [ componentIdx ] . value ( ) ;
} else {
well_state . perfPhaseRates ( ) [ perf * np + componentIdx ] = cq_s [ componentIdx ] . value ( ) ;
}
2017-02-13 09:45:06 -06:00
}
// Store the perforation pressure for later usage.
well_state . perfPress ( ) [ perf ] = well_state . bhp ( ) [ w ] + wellPerforationPressureDiffs ( ) [ perf ] ;
}
// add vol * dF/dt + Q to the well equations;
2017-05-03 06:34:15 -05:00
for ( int componentIdx = 0 ; componentIdx < numComp ; + + componentIdx ) {
EvalWell resWell_loc = ( wellSurfaceVolumeFraction ( w , componentIdx ) - F0_ [ w + nw * componentIdx ] ) * volume / dt ;
resWell_loc + = getQs ( w , componentIdx ) ;
for ( int pvIdx = 0 ; pvIdx < numWellEq ; + + pvIdx ) {
invDuneD_ [ w ] [ w ] [ flowPhaseToEbosCompIdx ( componentIdx ) ] [ flowToEbosPvIdx ( pvIdx ) ] + = resWell_loc . derivative ( pvIdx + numEq ) ;
2017-02-13 09:45:06 -06:00
}
2017-05-03 06:34:15 -05:00
resWell_ [ w ] [ flowPhaseToEbosCompIdx ( componentIdx ) ] + = resWell_loc . value ( ) ;
2017-02-13 09:45:06 -06:00
}
}
// do the local inversion of D.
localInvert ( invDuneD_ ) ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-04-06 07:21:59 -05:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-04-06 07:21:59 -05:00
getMobility ( const Simulator & ebosSimulator , const int perf , const int cell_idx , std : : vector < EvalWell > & mob ) const
{
const int np = wells ( ) . number_of_phases ;
2017-05-03 06:34:15 -05:00
assert ( int ( mob . size ( ) ) = = numComponents ( ) ) ;
2017-04-06 07:21:59 -05:00
const auto & intQuants = * ( ebosSimulator . model ( ) . cachedIntensiveQuantities ( cell_idx , /*timeIdx=*/ 0 ) ) ;
const auto & materialLawManager = ebosSimulator . problem ( ) . materialLawManager ( ) ;
// either use mobility of the perforation cell or calcualte its own
// based on passing the saturation table index
const int satid = wells ( ) . sat_table_id [ perf ] - 1 ;
const int satid_elem = materialLawManager - > satnumRegionIdx ( cell_idx ) ;
if ( satid = = satid_elem ) { // the same saturation number is used. i.e. just use the mobilty from the cell
for ( int phase = 0 ; phase < np ; + + phase ) {
int ebosPhaseIdx = flowPhaseToEbosPhaseIdx ( phase ) ;
mob [ phase ] = extendEval ( intQuants . mobility ( ebosPhaseIdx ) ) ;
}
2017-05-09 01:21:51 -05:00
if ( has_solvent_ ) {
mob [ solventCompIdx ] = extendEval ( intQuants . solventMobility ( ) ) ;
}
2017-04-06 07:21:59 -05:00
} else {
const auto & paramsCell = materialLawManager - > connectionMaterialLawParams ( satid , cell_idx ) ;
Eval relativePerms [ 3 ] ;
MaterialLaw : : relativePermeabilities ( relativePerms , paramsCell , intQuants . fluidState ( ) ) ;
// reset the satnumvalue back to original
materialLawManager - > connectionMaterialLawParams ( satid_elem , cell_idx ) ;
// compute the mobility
for ( int phase = 0 ; phase < np ; + + phase ) {
int ebosPhaseIdx = flowPhaseToEbosPhaseIdx ( phase ) ;
mob [ phase ] = extendEval ( relativePerms [ ebosPhaseIdx ] / intQuants . fluidState ( ) . viscosity ( ebosPhaseIdx ) ) ;
}
2017-05-09 01:21:51 -05:00
// this may not work if viscosity and relperms has been modified?
if ( has_solvent_ ) {
OPM_THROW ( std : : runtime_error , " individual mobility for wells does not work in combination with solvent " ) ;
}
2017-04-06 07:21:59 -05:00
}
}
2017-02-13 10:07:34 -06:00
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
bool
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
allow_cross_flow ( const int w , const Simulator & ebosSimulator ) const
2017-02-13 10:07:34 -06:00
{
if ( wells ( ) . allow_cf [ w ] ) {
return true ;
}
// check for special case where all perforations have cross flow
// then the wells must allow for cross flow
for ( int perf = wells ( ) . well_connpos [ w ] ; perf < wells ( ) . well_connpos [ w + 1 ] ; + + perf ) {
const int cell_idx = wells ( ) . well_cells [ perf ] ;
const auto & intQuants = * ( ebosSimulator . model ( ) . cachedIntensiveQuantities ( cell_idx , /*timeIdx=*/ 0 ) ) ;
const auto & fs = intQuants . fluidState ( ) ;
EvalWell pressure = extendEval ( fs . pressure ( FluidSystem : : oilPhaseIdx ) ) ;
EvalWell bhp = getBhp ( w ) ;
// Pressure drawdown (also used to determine direction of flow)
EvalWell well_pressure = bhp + wellPerforationPressureDiffs ( ) [ perf ] ;
EvalWell drawdown = pressure - well_pressure ;
if ( drawdown . value ( ) < 0 & & wells ( ) . type [ w ] = = INJECTOR ) {
return false ;
}
if ( drawdown . value ( ) > 0 & & wells ( ) . type [ w ] = = PRODUCER ) {
return false ;
}
}
return true ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
localInvert ( Mat & istlA ) const
{
for ( auto row = istlA . begin ( ) , rowend = istlA . end ( ) ; row ! = rowend ; + + row ) {
for ( auto col = row - > begin ( ) , colend = row - > end ( ) ; col ! = colend ; + + col ) {
//std::cout << (*col) << std::endl;
( * col ) . invert ( ) ;
}
}
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
print ( Mat & istlA ) const
{
for ( auto row = istlA . begin ( ) , rowend = istlA . end ( ) ; row ! = rowend ; + + row ) {
for ( auto col = row - > begin ( ) , colend = row - > end ( ) ; col ! = colend ; + + col ) {
std : : cout < < row . index ( ) < < " " < < col . index ( ) < < " /n \n " < < ( * col ) < < std : : endl ;
}
}
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
apply ( BVector & r ) const
{
if ( ! localWellsActive ( ) ) {
return ;
}
assert ( invDrw_ . size ( ) = = invDuneD_ . N ( ) ) ;
invDuneD_ . mv ( resWell_ , invDrw_ ) ;
duneB_ . mmtv ( invDrw_ , r ) ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
apply ( const BVector & x , BVector & Ax )
{
if ( ! localWellsActive ( ) ) {
return ;
}
assert ( Cx_ . size ( ) = = duneC_ . N ( ) ) ;
BVector & invDCx = invDrw_ ;
assert ( invDCx . size ( ) = = invDuneD_ . N ( ) ) ;
duneC_ . mv ( x , Cx_ ) ;
invDuneD_ . mv ( Cx_ , invDCx ) ;
duneB_ . mmtv ( invDCx , Ax ) ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
applyScaleAdd ( const Scalar alpha , const BVector & x , BVector & Ax )
{
if ( ! localWellsActive ( ) ) {
return ;
}
if ( scaleAddRes_ . size ( ) ! = Ax . size ( ) ) {
scaleAddRes_ . resize ( Ax . size ( ) ) ;
}
scaleAddRes_ = 0.0 ;
apply ( x , scaleAddRes_ ) ;
Ax . axpy ( alpha , scaleAddRes_ ) ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
recoverVariable ( const BVector & x , BVector & xw ) const
{
if ( ! localWellsActive ( ) ) {
return ;
}
BVector resWell = resWell_ ;
duneC_ . mmv ( x , resWell ) ;
invDuneD_ . mv ( resWell , xw ) ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
int
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
flowPhaseToEbosCompIdx ( const int phaseIdx ) const
{
2017-05-09 01:21:51 -05:00
const int phaseToComp [ 4 ] = { FluidSystem : : waterCompIdx , FluidSystem : : oilCompIdx , FluidSystem : : gasCompIdx , solventCompIdx } ;
2017-02-13 10:07:34 -06:00
return phaseToComp [ phaseIdx ] ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
int
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
flowToEbosPvIdx ( const int flowPv ) const
{
2017-05-09 01:21:51 -05:00
const int flowToEbos [ 4 ] = {
2017-02-13 10:07:34 -06:00
BlackoilIndices : : pressureSwitchIdx ,
BlackoilIndices : : waterSaturationIdx ,
2017-05-09 01:21:51 -05:00
BlackoilIndices : : compositionSwitchIdx ,
BlackoilIndices : : solventSaturationIdx
2017-02-13 10:07:34 -06:00
} ;
return flowToEbos [ flowPv ] ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 06:39:53 -06:00
int
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 06:39:53 -06:00
flowPhaseToEbosPhaseIdx ( const int phaseIdx ) const
{
2017-05-03 06:34:15 -05:00
assert ( phaseIdx < 3 ) ;
2017-02-14 06:39:53 -06:00
const int flowToEbos [ 3 ] = { FluidSystem : : waterPhaseIdx , FluidSystem : : oilPhaseIdx , FluidSystem : : gasPhaseIdx } ;
return flowToEbos [ phaseIdx ] ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
int
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
ebosCompToFlowPhaseIdx ( const int compIdx ) const
{
2017-05-03 06:34:15 -05:00
assert ( compIdx < 3 ) ;
2017-02-13 10:07:34 -06:00
const int compToPhase [ 3 ] = { Oil , Water , Gas } ;
return compToPhase [ compIdx ] ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
std : : vector < double >
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
extractPerfData ( const std : : vector < double > & in ) const
{
const int nw = wells ( ) . number_of_wells ;
const int nperf = wells ( ) . well_connpos [ nw ] ;
std : : vector < double > out ( nperf ) ;
for ( int w = 0 ; w < nw ; + + w ) {
for ( int perf = wells ( ) . well_connpos [ w ] ; perf < wells ( ) . well_connpos [ w + 1 ] ; + + perf ) {
const int well_idx = wells ( ) . well_cells [ perf ] ;
out [ perf ] = in [ well_idx ] ;
}
}
return out ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
int
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
numPhases ( ) const
{
return wells ( ) . number_of_phases ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
int
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
numCells ( ) const
{
return pv_ . size ( ) ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-03-08 04:29:06 -06:00
resetWellControlFromState ( const WellState & xw ) const
2017-02-13 10:07:34 -06:00
{
const int nw = wells_ - > number_of_wells ;
for ( int w = 0 ; w < nw ; + + w ) {
WellControls * wc = wells_ - > ctrls [ w ] ;
well_controls_set_current ( wc , xw . currentControls ( ) [ w ] ) ;
}
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
const Wells &
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
wells ( ) const
{
assert ( wells_ ! = 0 ) ;
return * ( wells_ ) ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
const Wells *
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
wellsPointer ( ) const
{
return wells_ ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
bool
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
wellsActive ( ) const
{
return wells_active_ ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
setWellsActive ( const bool wells_active )
{
wells_active_ = wells_active ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
bool
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
localWellsActive ( ) const
{
return wells_ ? ( wells_ - > number_of_wells > 0 ) : false ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
int
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
numWellVars ( ) const
{
if ( ! localWellsActive ( ) ) {
return 0 ;
}
const int nw = wells ( ) . number_of_wells ;
2017-05-03 06:34:15 -05:00
return numWellEq * nw ;
2017-02-13 10:07:34 -06:00
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
const std : : vector < double > &
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
wellPerforationDensities ( ) const
{
return well_perforation_densities_ ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
const std : : vector < double > &
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
wellPerforationPressureDiffs ( ) const
{
return well_perforation_pressure_diffs_ ;
}
2017-02-14 08:06:57 -06:00
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
typename StandardWellsDense < TypeTag > : : EvalWell
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
extendEval ( Eval in ) const {
EvalWell out = 0.0 ;
out . setValue ( in . value ( ) ) ;
2017-05-03 06:34:15 -05:00
for ( int eqIdx = 0 ; eqIdx < numEq ; + + eqIdx ) {
out . setDerivative ( eqIdx , in . derivative ( flowToEbosPvIdx ( eqIdx ) ) ) ;
2017-02-13 10:07:34 -06:00
}
return out ;
}
2017-02-14 08:06:57 -06:00
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
setWellVariables ( const WellState & xw )
{
const int nw = wells ( ) . number_of_wells ;
2017-05-03 06:34:15 -05:00
for ( int eqIdx = 0 ; eqIdx < numWellEq ; + + eqIdx ) {
2017-02-13 10:07:34 -06:00
for ( int w = 0 ; w < nw ; + + w ) {
2017-05-03 06:34:15 -05:00
wellVariables_ [ w + nw * eqIdx ] = 0.0 ;
wellVariables_ [ w + nw * eqIdx ] . setValue ( xw . wellSolutions ( ) [ w + nw * eqIdx ] ) ;
wellVariables_ [ w + nw * eqIdx ] . setDerivative ( numWellEq + eqIdx , 1.0 ) ;
2017-02-13 10:07:34 -06:00
}
}
}
2017-02-14 08:06:57 -06:00
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
print ( EvalWell in ) const
{
std : : cout < < in . value ( ) < < std : : endl ;
for ( int i = 0 ; i < in . size ; + + i ) {
std : : cout < < in . derivative ( i ) < < std : : endl ;
}
}
2017-02-14 08:06:57 -06:00
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-13 10:07:34 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-13 10:07:34 -06:00
computeAccumWells ( )
{
const int nw = wells ( ) . number_of_wells ;
2017-05-03 06:34:15 -05:00
for ( int eqIdx = 0 ; eqIdx < numWellEq ; + + eqIdx ) {
2017-02-13 10:07:34 -06:00
for ( int w = 0 ; w < nw ; + + w ) {
2017-05-03 06:34:15 -05:00
F0_ [ w + nw * eqIdx ] = wellSurfaceVolumeFraction ( w , eqIdx ) . value ( ) ;
2017-02-13 10:07:34 -06:00
}
}
}
2017-02-14 04:34:03 -06:00
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 04:34:03 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 04:34:03 -06:00
computeWellFlux ( const int & w , const double & Tw ,
2017-05-03 06:34:15 -05:00
const IntensiveQuantities & intQuants ,
2017-04-06 07:21:59 -05:00
const std : : vector < EvalWell > & mob_perfcells_dense ,
2017-02-14 04:34:03 -06:00
const EvalWell & bhp , const double & cdp ,
const bool & allow_cf , std : : vector < EvalWell > & cq_s ) const
{
const Opm : : PhaseUsage & pu = phase_usage_ ;
const int np = wells ( ) . number_of_phases ;
2017-05-03 06:34:15 -05:00
const int numComp = numComponents ( ) ;
std : : vector < EvalWell > cmix_s ( numComp , 0.0 ) ;
for ( int componentIdx = 0 ; componentIdx < numComp ; + + componentIdx ) {
cmix_s [ componentIdx ] = wellSurfaceVolumeFraction ( w , componentIdx ) ;
2017-02-14 04:34:03 -06:00
}
2017-05-03 06:34:15 -05:00
auto & fs = intQuants . fluidState ( ) ;
2017-02-14 04:34:03 -06:00
EvalWell pressure = extendEval ( fs . pressure ( FluidSystem : : oilPhaseIdx ) ) ;
EvalWell rs = extendEval ( fs . Rs ( ) ) ;
EvalWell rv = extendEval ( fs . Rv ( ) ) ;
2017-05-03 06:34:15 -05:00
std : : vector < EvalWell > b_perfcells_dense ( numComp , 0.0 ) ;
2017-02-14 04:34:03 -06:00
for ( int phase = 0 ; phase < np ; + + phase ) {
int ebosPhaseIdx = flowPhaseToEbosPhaseIdx ( phase ) ;
b_perfcells_dense [ phase ] = extendEval ( fs . invB ( ebosPhaseIdx ) ) ;
}
2017-05-09 01:21:51 -05:00
if ( has_solvent_ ) {
b_perfcells_dense [ solventCompIdx ] = extendEval ( intQuants . solventInverseFormationVolumeFactor ( ) ) ;
}
2017-02-14 04:34:03 -06:00
// Pressure drawdown (also used to determine direction of flow)
EvalWell well_pressure = bhp + cdp ;
EvalWell drawdown = pressure - well_pressure ;
2017-03-09 08:49:41 -06:00
// producing perforations
2017-02-14 04:34:03 -06:00
if ( drawdown . value ( ) > 0 ) {
//Do nothing if crossflow is not allowed
if ( ! allow_cf & & wells ( ) . type [ w ] = = INJECTOR ) {
return ;
}
2017-05-03 06:34:15 -05:00
// compute component volumetric rates at standard conditions
for ( int componentIdx = 0 ; componentIdx < numComp ; + + componentIdx ) {
const EvalWell cq_p = - Tw * ( mob_perfcells_dense [ componentIdx ] * drawdown ) ;
cq_s [ componentIdx ] = b_perfcells_dense [ componentIdx ] * cq_p ;
2017-02-14 04:34:03 -06:00
}
if ( active_ [ Oil ] & & active_ [ Gas ] ) {
const int oilpos = pu . phase_pos [ Oil ] ;
const int gaspos = pu . phase_pos [ Gas ] ;
2017-05-03 06:34:15 -05:00
const EvalWell cq_sOil = cq_s [ oilpos ] ;
const EvalWell cq_sGas = cq_s [ gaspos ] ;
cq_s [ gaspos ] + = rs * cq_sOil ;
cq_s [ oilpos ] + = rv * cq_sGas ;
2017-02-14 04:34:03 -06:00
}
} else {
//Do nothing if crossflow is not allowed
if ( ! allow_cf & & wells ( ) . type [ w ] = = PRODUCER ) {
return ;
}
// Using total mobilities
EvalWell total_mob_dense = mob_perfcells_dense [ 0 ] ;
2017-05-03 06:34:15 -05:00
for ( int componentIdx = 1 ; componentIdx < numComp ; + + componentIdx ) {
total_mob_dense + = mob_perfcells_dense [ componentIdx ] ;
2017-02-14 04:34:03 -06:00
}
// injection perforations total volume rates
const EvalWell cqt_i = - Tw * ( total_mob_dense * drawdown ) ;
// compute volume ratio between connection at standard conditions
EvalWell volumeRatio = 0.0 ;
if ( active_ [ Water ] ) {
const int watpos = pu . phase_pos [ Water ] ;
volumeRatio + = cmix_s [ watpos ] / b_perfcells_dense [ watpos ] ;
}
2017-05-09 01:21:51 -05:00
if ( has_solvent_ ) {
volumeRatio + = cmix_s [ solventCompIdx ] / b_perfcells_dense [ solventCompIdx ] ;
}
2017-02-14 04:34:03 -06:00
if ( active_ [ Oil ] & & active_ [ Gas ] ) {
const int oilpos = pu . phase_pos [ Oil ] ;
const int gaspos = pu . phase_pos [ Gas ] ;
2017-03-02 05:33:27 -06:00
// Incorporate RS/RV factors if both oil and gas active
const EvalWell d = 1.0 - rv * rs ;
2017-02-14 04:34:03 -06:00
2017-03-02 05:33:27 -06:00
if ( d . value ( ) = = 0.0 ) {
OPM_THROW ( Opm : : NumericalProblem , " Zero d value obtained for well " < < wells ( ) . name [ w ] < < " during flux calcuation "
< < " with rs " < < rs < < " and rv " < < rv ) ;
2017-02-14 04:34:03 -06:00
}
2017-03-02 05:33:27 -06:00
const EvalWell tmp_oil = ( cmix_s [ oilpos ] - rv * cmix_s [ gaspos ] ) / d ;
2017-02-14 04:34:03 -06:00
//std::cout << "tmp_oil " <<tmp_oil << std::endl;
volumeRatio + = tmp_oil / b_perfcells_dense [ oilpos ] ;
2017-03-02 05:33:27 -06:00
const EvalWell tmp_gas = ( cmix_s [ gaspos ] - rs * cmix_s [ oilpos ] ) / d ;
2017-02-14 04:34:03 -06:00
//std::cout << "tmp_gas " <<tmp_gas << std::endl;
volumeRatio + = tmp_gas / b_perfcells_dense [ gaspos ] ;
}
else {
if ( active_ [ Oil ] ) {
const int oilpos = pu . phase_pos [ Oil ] ;
volumeRatio + = cmix_s [ oilpos ] / b_perfcells_dense [ oilpos ] ;
}
if ( active_ [ Gas ] ) {
const int gaspos = pu . phase_pos [ Gas ] ;
volumeRatio + = cmix_s [ gaspos ] / b_perfcells_dense [ gaspos ] ;
}
}
// injecting connections total volumerates at standard conditions
EvalWell cqt_is = cqt_i / volumeRatio ;
//std::cout << "volrat " << volumeRatio << " " << volrat_perf_[perf] << std::endl;
2017-05-03 06:34:15 -05:00
for ( int componentIdx = 0 ; componentIdx < numComp ; + + componentIdx ) {
cq_s [ componentIdx ] = cmix_s [ componentIdx ] * cqt_is ; // * b_perfcells_dense[phase];
2017-02-14 04:34:03 -06:00
}
}
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 04:34:03 -06:00
SimulatorReport
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 04:34:03 -06:00
solveWellEq ( Simulator & ebosSimulator ,
const double dt ,
WellState & well_state )
{
const int nw = wells ( ) . number_of_wells ;
WellState well_state0 = well_state ;
int it = 0 ;
bool converged ;
do {
assembleWellEq ( ebosSimulator , dt , well_state , true ) ;
converged = getWellConvergence ( ebosSimulator , it ) ;
// checking whether the group targets are converged
if ( wellCollection ( ) - > groupControlActive ( ) ) {
converged = converged & & wellCollection ( ) - > groupTargetConverged ( well_state . wellRates ( ) ) ;
}
if ( converged ) {
break ;
}
+ + it ;
if ( localWellsActive ( ) )
{
BVector dx_well ( nw ) ;
invDuneD_ . mv ( resWell_ , dx_well ) ;
updateWellState ( dx_well , well_state ) ;
2017-04-12 10:37:34 -05:00
}
// updateWellControls uses communication
// Therefore the following is executed if there
// are active wells anywhere in the global domain.
if ( wellsActive ( ) )
{
2017-02-14 04:34:03 -06:00
updateWellControls ( well_state ) ;
setWellVariables ( well_state ) ;
}
} while ( it < 15 ) ;
if ( ! converged ) {
well_state = well_state0 ;
2017-03-08 07:02:00 -06:00
// also recover the old well controls
for ( int w = 0 ; w < nw ; + + w ) {
WellControls * wc = wells ( ) . ctrls [ w ] ;
well_controls_set_current ( wc , well_state . currentControls ( ) [ w ] ) ;
}
2017-02-14 04:34:03 -06:00
}
SimulatorReport report ;
report . converged = converged ;
report . total_well_iterations = it ;
return report ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 04:34:03 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 04:34:03 -06:00
printIf ( const int c , const double x , const double y , const double eps , const std : : string type ) const
{
if ( std : : abs ( x - y ) > eps ) {
std : : cout < < type < < " " < < c < < " : " < < x < < " " < < y < < std : : endl ;
}
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 04:34:03 -06:00
std : : vector < double >
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 04:34:03 -06:00
residual ( ) const
{
if ( ! wellsActive ( ) )
{
return std : : vector < double > ( ) ;
}
const int nw = wells ( ) . number_of_wells ;
2017-05-03 06:34:15 -05:00
const int numComp = numComponents ( ) ;
std : : vector < double > res ( numComp * nw ) ;
for ( int compIdx = 0 ; compIdx < numComp ; + + compIdx ) {
const int ebosCompIdx = flowPhaseToEbosCompIdx ( compIdx ) ;
for ( int wellIdx = 0 ; wellIdx < nw ; + + wellIdx ) {
int idx = wellIdx + nw * compIdx ;
res [ idx ] = resWell_ [ wellIdx ] [ ebosCompIdx ] ;
2017-02-14 04:34:03 -06:00
}
}
return res ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 04:34:03 -06:00
bool
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 04:34:03 -06:00
getWellConvergence ( Simulator & ebosSimulator ,
const int iteration ) const
{
2017-03-24 06:12:06 -05:00
typedef double Scalar ;
typedef std : : vector < Scalar > Vector ;
2017-02-14 04:34:03 -06:00
const int np = numPhases ( ) ;
2017-05-03 06:34:15 -05:00
const int numComp = numComponents ( ) ;
2017-02-14 04:34:03 -06:00
const double tol_wells = param_ . tolerance_wells_ ;
const double maxResidualAllowed = param_ . max_residual_allowed_ ;
2017-05-03 06:34:15 -05:00
std : : vector < Scalar > B_avg ( numComp , Scalar ( ) ) ;
std : : vector < Scalar > maxNormWell ( numComp , Scalar ( ) ) ;
2017-02-14 04:34:03 -06:00
2017-03-24 05:58:54 -05:00
auto & grid = ebosSimulator . gridManager ( ) . grid ( ) ;
const auto & gridView = grid . leafGridView ( ) ;
ElementContext elemCtx ( ebosSimulator ) ;
const auto & elemEndIt = gridView . template end < /*codim=*/ 0 , Dune : : Interior_Partition > ( ) ;
2017-02-14 04:34:03 -06:00
2017-03-24 05:58:54 -05:00
for ( auto elemIt = gridView . template begin < /*codim=*/ 0 , Dune : : Interior_Partition > ( ) ;
elemIt ! = elemEndIt ; + + elemIt )
2017-02-14 04:34:03 -06:00
{
2017-03-24 05:58:54 -05:00
elemCtx . updatePrimaryStencil ( * elemIt ) ;
elemCtx . updatePrimaryIntensiveQuantities ( /*timeIdx=*/ 0 ) ;
2017-02-14 04:34:03 -06:00
2017-03-24 05:58:54 -05:00
const auto & intQuants = elemCtx . intensiveQuantities ( /*spaceIdx=*/ 0 , /*timeIdx=*/ 0 ) ;
const auto & fs = intQuants . fluidState ( ) ;
2017-05-03 06:34:15 -05:00
for ( int phaseIdx = 0 ; phaseIdx < np ; + + phaseIdx )
2017-03-24 05:58:54 -05:00
{
2017-05-03 06:34:15 -05:00
auto & B = B_avg [ phaseIdx ] ;
const int ebosPhaseIdx = flowPhaseToEbosPhaseIdx ( phaseIdx ) ;
2017-02-14 04:34:03 -06:00
2017-03-24 06:12:06 -05:00
B + = 1 / fs . invB ( ebosPhaseIdx ) . value ( ) ;
2017-02-14 04:34:03 -06:00
}
2017-05-09 01:21:51 -05:00
if ( has_solvent_ ) {
auto & B = B_avg [ solventCompIdx ] ;
B + = 1 / intQuants . solventInverseFormationVolumeFactor ( ) . value ( ) ;
}
2017-02-14 04:34:03 -06:00
}
2017-03-24 06:12:06 -05:00
// compute global average
grid . comm ( ) . sum ( B_avg . data ( ) , B_avg . size ( ) ) ;
for ( auto & bval : B_avg )
2017-04-04 02:58:49 -05:00
{
2017-03-24 09:12:42 -05:00
bval / = global_nc_ ;
2017-04-04 02:58:49 -05:00
}
2017-03-24 06:12:06 -05:00
auto res = residual ( ) ;
2017-05-03 06:34:15 -05:00
const int nw = res . size ( ) / numComp ;
2017-02-14 04:34:03 -06:00
2017-05-03 06:34:15 -05:00
for ( int compIdx = 0 ; compIdx < numComp ; + + compIdx )
2017-03-24 06:12:06 -05:00
{
for ( int w = 0 ; w < nw ; + + w ) {
2017-05-03 06:34:15 -05:00
maxNormWell [ compIdx ] = std : : max ( maxNormWell [ compIdx ] , std : : abs ( res [ nw * compIdx + w ] ) ) ;
2017-03-24 06:12:06 -05:00
}
}
grid . comm ( ) . max ( maxNormWell . data ( ) , maxNormWell . size ( ) ) ;
2017-05-03 06:34:15 -05:00
Vector well_flux_residual ( numComp ) ;
2017-02-14 04:34:03 -06:00
bool converged_Well = true ;
2017-03-24 06:12:06 -05:00
2017-02-14 04:34:03 -06:00
// Finish computation
2017-05-03 06:34:15 -05:00
for ( int compIdx = 0 ; compIdx < numComp ; + + compIdx )
2017-02-14 04:34:03 -06:00
{
2017-05-03 06:34:15 -05:00
well_flux_residual [ compIdx ] = B_avg [ compIdx ] * maxNormWell [ compIdx ] ;
converged_Well = converged_Well & & ( well_flux_residual [ compIdx ] < tol_wells ) ;
2017-02-14 04:34:03 -06:00
}
// if one of the residuals is NaN, throw exception, so that the solver can be restarted
for ( int phaseIdx = 0 ; phaseIdx < np ; + + phaseIdx ) {
const auto & phaseName = FluidSystem : : phaseName ( flowPhaseToEbosPhaseIdx ( phaseIdx ) ) ;
if ( std : : isnan ( well_flux_residual [ phaseIdx ] ) ) {
OPM_THROW ( Opm : : NumericalProblem , " NaN residual for phase " < < phaseName ) ;
}
if ( well_flux_residual [ phaseIdx ] > maxResidualAllowed ) {
OPM_THROW ( Opm : : NumericalProblem , " Too large residual for phase " < < phaseName ) ;
}
}
if ( terminal_output_ )
{
// Only rank 0 does print to std::cout
if ( iteration = = 0 ) {
std : : string msg ;
msg = " Iter " ;
for ( int phaseIdx = 0 ; phaseIdx < np ; + + phaseIdx ) {
const std : : string & phaseName = FluidSystem : : phaseName ( flowPhaseToEbosPhaseIdx ( phaseIdx ) ) ;
msg + = " W-FLUX( " + phaseName + " ) " ;
}
OpmLog : : note ( msg ) ;
}
std : : ostringstream ss ;
const std : : streamsize oprec = ss . precision ( 3 ) ;
const std : : ios : : fmtflags oflags = ss . setf ( std : : ios : : scientific ) ;
ss < < std : : setw ( 4 ) < < iteration ;
2017-05-03 06:34:15 -05:00
for ( int compIdx = 0 ; compIdx < numComp ; + + compIdx ) {
ss < < std : : setw ( 11 ) < < well_flux_residual [ compIdx ] ;
2017-02-14 04:34:03 -06:00
}
ss . precision ( oprec ) ;
ss . flags ( oflags ) ;
OpmLog : : note ( ss . str ( ) ) ;
}
return converged_Well ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 04:34:03 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 04:34:03 -06:00
computeWellConnectionPressures ( const Simulator & ebosSimulator ,
const WellState & xw )
{
if ( ! localWellsActive ( ) ) return ;
// 1. Compute properties required by computeConnectionPressureDelta().
// Note that some of the complexity of this part is due to the function
// taking std::vector<double> arguments, and not Eigen objects.
std : : vector < double > b_perf ;
std : : vector < double > rsmax_perf ;
std : : vector < double > rvmax_perf ;
std : : vector < double > surf_dens_perf ;
computePropertiesForWellConnectionPressures ( ebosSimulator , xw , b_perf , rsmax_perf , rvmax_perf , surf_dens_perf ) ;
computeWellConnectionDensitesPressures ( xw , b_perf , rsmax_perf , rvmax_perf , surf_dens_perf , cell_depths_ , gravity_ ) ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 04:34:03 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 04:34:03 -06:00
computePropertiesForWellConnectionPressures ( const Simulator & ebosSimulator ,
const WellState & xw ,
std : : vector < double > & b_perf ,
std : : vector < double > & rsmax_perf ,
std : : vector < double > & rvmax_perf ,
std : : vector < double > & surf_dens_perf ) const
{
const int nperf = wells ( ) . well_connpos [ wells ( ) . number_of_wells ] ;
const int nw = wells ( ) . number_of_wells ;
2017-05-03 06:34:15 -05:00
const int numComp = numComponents ( ) ;
2017-02-14 04:34:03 -06:00
const PhaseUsage & pu = phase_usage_ ;
2017-05-03 06:34:15 -05:00
b_perf . resize ( nperf * numComp ) ;
surf_dens_perf . resize ( nperf * numComp ) ;
2017-02-14 04:34:03 -06:00
//rs and rv are only used if both oil and gas is present
if ( pu . phase_used [ BlackoilPhases : : Vapour ] & & pu . phase_pos [ BlackoilPhases : : Liquid ] ) {
rsmax_perf . resize ( nperf ) ;
rvmax_perf . resize ( nperf ) ;
}
// Compute the average pressure in each well block
for ( int w = 0 ; w < nw ; + + w ) {
for ( int perf = wells ( ) . well_connpos [ w ] ; perf < wells ( ) . well_connpos [ w + 1 ] ; + + perf ) {
const int cell_idx = wells ( ) . well_cells [ perf ] ;
const auto & intQuants = * ( ebosSimulator . model ( ) . cachedIntensiveQuantities ( cell_idx , /*timeIdx=*/ 0 ) ) ;
const auto & fs = intQuants . fluidState ( ) ;
const double p_above = perf = = wells ( ) . well_connpos [ w ] ? xw . bhp ( ) [ w ] : xw . perfPress ( ) [ perf - 1 ] ;
const double p_avg = ( xw . perfPress ( ) [ perf ] + p_above ) / 2 ;
const double temperature = fs . temperature ( FluidSystem : : oilPhaseIdx ) . value ( ) ;
if ( pu . phase_used [ BlackoilPhases : : Aqua ] ) {
2017-05-03 06:34:15 -05:00
b_perf [ pu . phase_pos [ BlackoilPhases : : Aqua ] + perf * numComp ] =
2017-02-14 04:34:03 -06:00
FluidSystem : : waterPvt ( ) . inverseFormationVolumeFactor ( fs . pvtRegionIndex ( ) , temperature , p_avg ) ;
}
if ( pu . phase_used [ BlackoilPhases : : Vapour ] ) {
2017-05-03 06:34:15 -05:00
const int gaspos = pu . phase_pos [ BlackoilPhases : : Vapour ] + perf * numComp ;
2017-05-23 08:46:11 -05:00
const int gaspos_well = pu . phase_pos [ BlackoilPhases : : Vapour ] + w * pu . num_phases ;
2017-02-14 04:34:03 -06:00
if ( pu . phase_used [ BlackoilPhases : : Liquid ] ) {
2017-05-23 08:46:11 -05:00
const int oilpos_well = pu . phase_pos [ BlackoilPhases : : Liquid ] + w * pu . num_phases ;
2017-02-14 04:34:03 -06:00
const double oilrate = std : : abs ( xw . wellRates ( ) [ oilpos_well ] ) ; //in order to handle negative rates in producers
rvmax_perf [ perf ] = FluidSystem : : gasPvt ( ) . saturatedOilVaporizationFactor ( fs . pvtRegionIndex ( ) , temperature , p_avg ) ;
if ( oilrate > 0 ) {
2017-05-23 08:46:11 -05:00
const double gasrate = std : : abs ( xw . wellRates ( ) [ gaspos_well ] ) - xw . solventWellRate ( w ) ;
2017-02-14 04:34:03 -06:00
double rv = 0.0 ;
if ( gasrate > 0 ) {
rv = oilrate / gasrate ;
}
rv = std : : min ( rv , rvmax_perf [ perf ] ) ;
b_perf [ gaspos ] = FluidSystem : : gasPvt ( ) . inverseFormationVolumeFactor ( fs . pvtRegionIndex ( ) , temperature , p_avg , rv ) ;
}
else {
b_perf [ gaspos ] = FluidSystem : : gasPvt ( ) . saturatedInverseFormationVolumeFactor ( fs . pvtRegionIndex ( ) , temperature , p_avg ) ;
}
} else {
b_perf [ gaspos ] = FluidSystem : : gasPvt ( ) . saturatedInverseFormationVolumeFactor ( fs . pvtRegionIndex ( ) , temperature , p_avg ) ;
}
}
if ( pu . phase_used [ BlackoilPhases : : Liquid ] ) {
2017-05-03 06:34:15 -05:00
const int oilpos = pu . phase_pos [ BlackoilPhases : : Liquid ] + perf * numComp ;
2017-05-23 08:46:11 -05:00
const int oilpos_well = pu . phase_pos [ BlackoilPhases : : Liquid ] + w * pu . num_phases ;
2017-02-14 04:34:03 -06:00
if ( pu . phase_used [ BlackoilPhases : : Vapour ] ) {
rsmax_perf [ perf ] = FluidSystem : : oilPvt ( ) . saturatedGasDissolutionFactor ( fs . pvtRegionIndex ( ) , temperature , p_avg ) ;
2017-05-23 08:46:11 -05:00
const int gaspos_well = pu . phase_pos [ BlackoilPhases : : Vapour ] + w * pu . num_phases ;
const double gasrate = std : : abs ( xw . wellRates ( ) [ gaspos_well ] ) - xw . solventWellRate ( w ) ;
2017-02-14 04:34:03 -06:00
if ( gasrate > 0 ) {
const double oilrate = std : : abs ( xw . wellRates ( ) [ oilpos_well ] ) ;
double rs = 0.0 ;
if ( oilrate > 0 ) {
rs = gasrate / oilrate ;
}
rs = std : : min ( rs , rsmax_perf [ perf ] ) ;
b_perf [ oilpos ] = FluidSystem : : oilPvt ( ) . inverseFormationVolumeFactor ( fs . pvtRegionIndex ( ) , temperature , p_avg , rs ) ;
} else {
b_perf [ oilpos ] = FluidSystem : : oilPvt ( ) . saturatedInverseFormationVolumeFactor ( fs . pvtRegionIndex ( ) , temperature , p_avg ) ;
}
} else {
b_perf [ oilpos ] = FluidSystem : : oilPvt ( ) . saturatedInverseFormationVolumeFactor ( fs . pvtRegionIndex ( ) , temperature , p_avg ) ;
}
}
// Surface density.
for ( int p = 0 ; p < pu . num_phases ; + + p ) {
2017-05-03 06:34:15 -05:00
surf_dens_perf [ numComp * perf + p ] = FluidSystem : : referenceDensity ( flowPhaseToEbosPhaseIdx ( p ) , fs . pvtRegionIndex ( ) ) ;
2017-02-14 04:34:03 -06:00
}
2017-05-09 01:21:51 -05:00
2017-05-30 07:33:17 -05:00
// We use cell values for solvent injector
2017-05-09 01:21:51 -05:00
if ( has_solvent_ ) {
b_perf [ numComp * perf + solventCompIdx ] = intQuants . solventInverseFormationVolumeFactor ( ) . value ( ) ;
surf_dens_perf [ numComp * perf + solventCompIdx ] = intQuants . solventRefDensity ( ) ;
}
2017-02-14 04:34:03 -06:00
}
}
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 04:34:03 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 04:34:03 -06:00
updateWellState ( const BVector & dwells ,
WellState & well_state ) const
{
if ( ! localWellsActive ( ) ) return ;
const int np = wells ( ) . number_of_phases ;
const int nw = wells ( ) . number_of_wells ;
double dFLimit = dWellFractionMax ( ) ;
double dBHPLimit = dbhpMaxRel ( ) ;
std : : vector < double > xvar_well_old = well_state . wellSolutions ( ) ;
for ( int w = 0 ; w < nw ; + + w ) {
// update the second and third well variable (The flux fractions)
std : : vector < double > F ( np , 0.0 ) ;
if ( active_ [ Water ] ) {
const int sign2 = dwells [ w ] [ flowPhaseToEbosCompIdx ( WFrac ) ] > 0 ? 1 : - 1 ;
const double dx2_limited = sign2 * std : : min ( std : : abs ( dwells [ w ] [ flowPhaseToEbosCompIdx ( WFrac ) ] ) , dFLimit ) ;
well_state . wellSolutions ( ) [ WFrac * nw + w ] = xvar_well_old [ WFrac * nw + w ] - dx2_limited ;
}
if ( active_ [ Gas ] ) {
const int sign3 = dwells [ w ] [ flowPhaseToEbosCompIdx ( GFrac ) ] > 0 ? 1 : - 1 ;
const double dx3_limited = sign3 * std : : min ( std : : abs ( dwells [ w ] [ flowPhaseToEbosCompIdx ( GFrac ) ] ) , dFLimit ) ;
well_state . wellSolutions ( ) [ GFrac * nw + w ] = xvar_well_old [ GFrac * nw + w ] - dx3_limited ;
}
2017-05-09 01:21:51 -05:00
if ( has_solvent_ ) {
const int sign4 = dwells [ w ] [ flowPhaseToEbosCompIdx ( SFrac ) ] > 0 ? 1 : - 1 ;
const double dx4_limited = sign4 * std : : min ( std : : abs ( dwells [ w ] [ flowPhaseToEbosCompIdx ( SFrac ) ] ) , dFLimit ) ;
well_state . wellSolutions ( ) [ SFrac * nw + w ] = xvar_well_old [ SFrac * nw + w ] - dx4_limited ;
}
2017-02-14 04:34:03 -06:00
assert ( active_ [ Oil ] ) ;
F [ Oil ] = 1.0 ;
if ( active_ [ Water ] ) {
F [ Water ] = well_state . wellSolutions ( ) [ WFrac * nw + w ] ;
F [ Oil ] - = F [ Water ] ;
}
if ( active_ [ Gas ] ) {
F [ Gas ] = well_state . wellSolutions ( ) [ GFrac * nw + w ] ;
F [ Oil ] - = F [ Gas ] ;
}
2017-05-09 01:21:51 -05:00
double F_solvent = 0.0 ;
if ( has_solvent_ ) {
F_solvent = well_state . wellSolutions ( ) [ SFrac * nw + w ] ;
F [ Oil ] - = F_solvent ;
}
2017-02-14 04:34:03 -06:00
if ( active_ [ Water ] ) {
if ( F [ Water ] < 0.0 ) {
if ( active_ [ Gas ] ) {
F [ Gas ] / = ( 1.0 - F [ Water ] ) ;
}
2017-05-09 01:21:51 -05:00
if ( has_solvent_ ) {
F_solvent / = ( 1.0 - F [ Water ] ) ;
}
2017-02-14 04:34:03 -06:00
F [ Oil ] / = ( 1.0 - F [ Water ] ) ;
F [ Water ] = 0.0 ;
}
}
if ( active_ [ Gas ] ) {
if ( F [ Gas ] < 0.0 ) {
if ( active_ [ Water ] ) {
F [ Water ] / = ( 1.0 - F [ Gas ] ) ;
}
2017-05-09 01:21:51 -05:00
if ( has_solvent_ ) {
F_solvent / = ( 1.0 - F [ Gas ] ) ;
}
2017-02-14 04:34:03 -06:00
F [ Oil ] / = ( 1.0 - F [ Gas ] ) ;
F [ Gas ] = 0.0 ;
}
}
if ( F [ Oil ] < 0.0 ) {
if ( active_ [ Water ] ) {
F [ Water ] / = ( 1.0 - F [ Oil ] ) ;
}
if ( active_ [ Gas ] ) {
F [ Gas ] / = ( 1.0 - F [ Oil ] ) ;
}
2017-05-09 01:21:51 -05:00
if ( has_solvent_ ) {
F_solvent / = ( 1.0 - F [ Oil ] ) ;
}
2017-02-14 04:34:03 -06:00
F [ Oil ] = 0.0 ;
}
if ( active_ [ Water ] ) {
well_state . wellSolutions ( ) [ WFrac * nw + w ] = F [ Water ] ;
}
if ( active_ [ Gas ] ) {
well_state . wellSolutions ( ) [ GFrac * nw + w ] = F [ Gas ] ;
}
2017-05-09 01:21:51 -05:00
if ( has_solvent_ ) {
well_state . wellSolutions ( ) [ SFrac * nw + w ] = F_solvent ;
}
2017-05-30 07:33:17 -05:00
// F_solvent is added to F_gas. This means that well_rate[Gas] also contains solvent.
// More testing is needed to make sure this is correct for well groups and THP.
2017-05-09 01:21:51 -05:00
if ( has_solvent_ ) {
F [ Gas ] + = F_solvent ;
}
2017-02-14 04:34:03 -06:00
// The interpretation of the first well variable depends on the well control
const WellControls * wc = wells ( ) . ctrls [ w ] ;
// The current control in the well state overrides
// the current control set in the Wells struct, which
// is instead treated as a default.
const int current = well_state . currentControls ( ) [ w ] ;
const double target_rate = well_controls_iget_target ( wc , current ) ;
std : : vector < double > g = { 1 , 1 , 0.01 } ;
if ( well_controls_iget_type ( wc , current ) = = RESERVOIR_RATE ) {
const double * distr = well_controls_iget_distr ( wc , current ) ;
for ( int p = 0 ; p < np ; + + p ) {
2017-03-23 10:36:48 -05:00
if ( distr [ p ] > 0. ) { // For injection wells, there only one non-zero distr value
F [ p ] / = distr [ p ] ;
} else {
F [ p ] = 0. ;
}
2017-02-14 04:34:03 -06:00
}
} else {
for ( int p = 0 ; p < np ; + + p ) {
F [ p ] / = g [ p ] ;
}
}
switch ( well_controls_iget_type ( wc , current ) ) {
case THP : // The BHP and THP both uses the total rate as first well variable.
case BHP :
{
well_state . wellSolutions ( ) [ nw * XvarWell + w ] = xvar_well_old [ nw * XvarWell + w ] - dwells [ w ] [ flowPhaseToEbosCompIdx ( XvarWell ) ] ;
switch ( wells ( ) . type [ w ] ) {
case INJECTOR :
for ( int p = 0 ; p < np ; + + p ) {
const double comp_frac = wells ( ) . comp_frac [ np * w + p ] ;
well_state . wellRates ( ) [ w * np + p ] = comp_frac * well_state . wellSolutions ( ) [ nw * XvarWell + w ] ;
}
break ;
case PRODUCER :
for ( int p = 0 ; p < np ; + + p ) {
well_state . wellRates ( ) [ w * np + p ] = well_state . wellSolutions ( ) [ nw * XvarWell + w ] * F [ p ] ;
}
break ;
}
if ( well_controls_iget_type ( wc , current ) = = THP ) {
// Calculate bhp from thp control and well rates
double aqua = 0.0 ;
double liquid = 0.0 ;
double vapour = 0.0 ;
const Opm : : PhaseUsage & pu = phase_usage_ ;
if ( active_ [ Water ] ) {
aqua = well_state . wellRates ( ) [ w * np + pu . phase_pos [ Water ] ] ;
}
if ( active_ [ Oil ] ) {
liquid = well_state . wellRates ( ) [ w * np + pu . phase_pos [ Oil ] ] ;
}
if ( active_ [ Gas ] ) {
vapour = well_state . wellRates ( ) [ w * np + pu . phase_pos [ Gas ] ] ;
}
const int vfp = well_controls_iget_vfp ( wc , current ) ;
const double & thp = well_controls_iget_target ( wc , current ) ;
const double & alq = well_controls_iget_alq ( wc , current ) ;
//Set *BHP* target by calculating bhp from THP
const WellType & well_type = wells ( ) . type [ w ] ;
// pick the density in the top layer
const int perf = wells ( ) . well_connpos [ w ] ;
const double rho = well_perforation_densities_ [ perf ] ;
if ( well_type = = INJECTOR ) {
const double dp = wellhelpers : : computeHydrostaticCorrection (
wells ( ) , w , vfp_properties_ - > getInj ( ) - > getTable ( vfp ) - > getDatumDepth ( ) ,
rho , gravity_ ) ;
well_state . bhp ( ) [ w ] = vfp_properties_ - > getInj ( ) - > bhp ( vfp , aqua , liquid , vapour , thp ) - dp ;
}
else if ( well_type = = PRODUCER ) {
const double dp = wellhelpers : : computeHydrostaticCorrection (
wells ( ) , w , vfp_properties_ - > getProd ( ) - > getTable ( vfp ) - > getDatumDepth ( ) ,
rho , gravity_ ) ;
well_state . bhp ( ) [ w ] = vfp_properties_ - > getProd ( ) - > bhp ( vfp , aqua , liquid , vapour , thp , alq ) - dp ;
}
else {
OPM_THROW ( std : : logic_error , " Expected INJECTOR or PRODUCER well " ) ;
}
}
}
break ;
case SURFACE_RATE : // Both rate controls use bhp as first well variable
case RESERVOIR_RATE :
{
const int sign1 = dwells [ w ] [ flowPhaseToEbosCompIdx ( XvarWell ) ] > 0 ? 1 : - 1 ;
const double dx1_limited = sign1 * std : : min ( std : : abs ( dwells [ w ] [ flowPhaseToEbosCompIdx ( XvarWell ) ] ) , std : : abs ( xvar_well_old [ nw * XvarWell + w ] ) * dBHPLimit ) ;
well_state . wellSolutions ( ) [ nw * XvarWell + w ] = std : : max ( xvar_well_old [ nw * XvarWell + w ] - dx1_limited , 1e5 ) ;
well_state . bhp ( ) [ w ] = well_state . wellSolutions ( ) [ nw * XvarWell + w ] ;
if ( well_controls_iget_type ( wc , current ) = = SURFACE_RATE ) {
if ( wells ( ) . type [ w ] = = PRODUCER ) {
const double * distr = well_controls_iget_distr ( wc , current ) ;
double F_target = 0.0 ;
for ( int p = 0 ; p < np ; + + p ) {
F_target + = distr [ p ] * F [ p ] ;
}
for ( int p = 0 ; p < np ; + + p ) {
well_state . wellRates ( ) [ np * w + p ] = F [ p ] * target_rate / F_target ;
}
} else {
for ( int p = 0 ; p < np ; + + p ) {
well_state . wellRates ( ) [ w * np + p ] = wells ( ) . comp_frac [ np * w + p ] * target_rate ;
}
}
} else { // RESERVOIR_RATE
for ( int p = 0 ; p < np ; + + p ) {
well_state . wellRates ( ) [ np * w + p ] = F [ p ] * target_rate ;
}
}
}
break ;
} // end of switch (well_controls_iget_type(wc, current))
} // end of for (int w = 0; w < nw; ++w)
2017-02-15 13:50:43 -06:00
// for the wells having a THP constaint, we should update their thp value
// If it is under THP control, it will be set to be the target value. Otherwise,
// the thp value will be calculated based on the bhp value, assuming the bhp value is correctly calculated.
for ( int w = 0 ; w < nw ; + + w ) {
const WellControls * wc = wells ( ) . ctrls [ w ] ;
const int nwc = well_controls_get_num ( wc ) ;
// Looping over all controls until we find a THP constraint
2017-03-09 08:51:03 -06:00
int ctrl_index = 0 ;
for ( ; ctrl_index < nwc ; + + ctrl_index ) {
2017-02-15 13:50:43 -06:00
if ( well_controls_iget_type ( wc , ctrl_index ) = = THP ) {
// the current control
const int current = well_state . currentControls ( ) [ w ] ;
// If under THP control at the moment
if ( current = = ctrl_index ) {
const double thp_target = well_controls_iget_target ( wc , current ) ;
well_state . thp ( ) [ w ] = thp_target ;
} else { // otherwise we calculate the thp from the bhp value
double aqua = 0.0 ;
double liquid = 0.0 ;
double vapour = 0.0 ;
const Opm : : PhaseUsage & pu = phase_usage_ ;
if ( active_ [ Water ] ) {
aqua = well_state . wellRates ( ) [ w * np + pu . phase_pos [ Water ] ] ;
}
if ( active_ [ Oil ] ) {
liquid = well_state . wellRates ( ) [ w * np + pu . phase_pos [ Oil ] ] ;
}
if ( active_ [ Gas ] ) {
vapour = well_state . wellRates ( ) [ w * np + pu . phase_pos [ Gas ] ] ;
}
const double alq = well_controls_iget_alq ( wc , ctrl_index ) ;
const int table_id = well_controls_iget_vfp ( wc , ctrl_index ) ;
const WellType & well_type = wells ( ) . type [ w ] ;
const int perf = wells ( ) . well_connpos [ w ] ; //first perforation.
if ( well_type = = INJECTOR ) {
const double dp = wellhelpers : : computeHydrostaticCorrection (
wells ( ) , w , vfp_properties_ - > getInj ( ) - > getTable ( table_id ) - > getDatumDepth ( ) ,
wellPerforationDensities ( ) [ perf ] , gravity_ ) ;
const double bhp = well_state . bhp ( ) [ w ] ;
well_state . thp ( ) [ w ] = vfp_properties_ - > getInj ( ) - > thp ( table_id , aqua , liquid , vapour , bhp + dp ) ;
} else if ( well_type = = PRODUCER ) {
const double dp = wellhelpers : : computeHydrostaticCorrection (
wells ( ) , w , vfp_properties_ - > getProd ( ) - > getTable ( table_id ) - > getDatumDepth ( ) ,
wellPerforationDensities ( ) [ perf ] , gravity_ ) ;
const double bhp = well_state . bhp ( ) [ w ] ;
well_state . thp ( ) [ w ] = vfp_properties_ - > getProd ( ) - > thp ( table_id , aqua , liquid , vapour , bhp + dp , alq ) ;
} else {
OPM_THROW ( std : : logic_error , " Expected INJECTOR or PRODUCER well " ) ;
}
}
// the THP control is found, we leave the loop now
break ;
}
} // end of for loop for seaching THP constraints
2017-03-09 08:51:03 -06:00
// no THP constraint found
if ( ctrl_index = = nwc ) { // not finding a THP contstraints
well_state . thp ( ) [ w ] = 0.0 ;
}
2017-02-15 13:50:43 -06:00
} // end of for (int w = 0; w < nw; ++w)
2017-02-14 04:34:03 -06:00
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 04:34:03 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 04:34:03 -06:00
updateWellControls ( WellState & xw ) const
{
2017-04-12 10:37:34 -05:00
// Even if there no wells active locally, we cannot
// return as the Destructor of the WellSwitchingLogger
// uses global communication. For no well active globally
// we simply return.
if ( ! wellsActive ( ) ) return ;
2017-02-14 04:34:03 -06:00
const int np = wells ( ) . number_of_phases ;
const int nw = wells ( ) . number_of_wells ;
// keeping a copy of the current controls, to see whether control changes later.
std : : vector < int > old_control_index ( nw , 0 ) ;
for ( int w = 0 ; w < nw ; + + w ) {
old_control_index [ w ] = xw . currentControls ( ) [ w ] ;
}
// Find, for each well, if any constraints are broken. If so,
// switch control to first broken constraint.
# pragma omp parallel for schedule(dynamic)
for ( int w = 0 ; w < nw ; + + w ) {
WellControls * wc = wells ( ) . ctrls [ w ] ;
// The current control in the well state overrides
// the current control set in the Wells struct, which
// is instead treated as a default.
int current = xw . currentControls ( ) [ w ] ;
// Loop over all controls except the current one, and also
// skip any RESERVOIR_RATE controls, since we cannot
// handle those.
const int nwc = well_controls_get_num ( wc ) ;
int ctrl_index = 0 ;
for ( ; ctrl_index < nwc ; + + ctrl_index ) {
if ( ctrl_index = = current ) {
// This is the currently used control, so it is
// used as an equation. So this is not used as an
// inequality constraint, and therefore skipped.
continue ;
}
if ( wellhelpers : : constraintBroken (
xw . bhp ( ) , xw . thp ( ) , xw . wellRates ( ) ,
w , np , wells ( ) . type [ w ] , wc , ctrl_index ) ) {
// ctrl_index will be the index of the broken constraint after the loop.
break ;
}
}
if ( ctrl_index ! = nwc ) {
// Constraint number ctrl_index was broken, switch to it.
xw . currentControls ( ) [ w ] = ctrl_index ;
current = xw . currentControls ( ) [ w ] ;
well_controls_set_current ( wc , current ) ;
}
// update whether well is under group control
if ( wellCollection ( ) - > groupControlActive ( ) ) {
// get well node in the well collection
WellNode & well_node = well_collection_ - > findWellNode ( std : : string ( wells ( ) . name [ w ] ) ) ;
// update whehter the well is under group control or individual control
if ( well_node . groupControlIndex ( ) > = 0 & & current = = well_node . groupControlIndex ( ) ) {
// under group control
well_node . setIndividualControl ( false ) ;
} else {
// individual control
well_node . setIndividualControl ( true ) ;
}
}
}
// the new well control indices after all the related updates,
std : : vector < int > updated_control_index ( nw , 0 ) ;
for ( int w = 0 ; w < nw ; + + w ) {
updated_control_index [ w ] = xw . currentControls ( ) [ w ] ;
}
// checking whether control changed
wellhelpers : : WellSwitchingLogger logger ;
for ( int w = 0 ; w < nw ; + + w ) {
2017-03-23 10:41:54 -05:00
const WellControls * wc = wells ( ) . ctrls [ w ] ;
2017-02-14 04:34:03 -06:00
if ( updated_control_index [ w ] ! = old_control_index [ w ] ) {
logger . wellSwitched ( wells ( ) . name [ w ] ,
well_controls_iget_type ( wc , old_control_index [ w ] ) ,
well_controls_iget_type ( wc , updated_control_index [ w ] ) ) ;
2017-03-23 10:41:54 -05:00
}
if ( updated_control_index [ w ] ! = old_control_index [ w ] | | well_collection_ - > groupControlActive ( ) ) {
2017-02-14 04:34:03 -06:00
updateWellStateWithTarget ( wc , updated_control_index [ w ] , w , xw ) ;
}
}
2017-03-31 05:50:50 -05:00
// upate the well targets following group controls
// it will not change the control mode, only update the targets
if ( wellCollection ( ) - > groupControlActive ( ) ) {
applyVREPGroupControl ( xw ) ;
wellCollection ( ) - > updateWellTargets ( xw . wellRates ( ) ) ;
for ( int w = 0 ; w < nw ; + + w ) {
const WellControls * wc = wells ( ) . ctrls [ w ] ;
updateWellStateWithTarget ( wc , updated_control_index [ w ] , w , xw ) ;
}
}
2017-02-14 04:34:03 -06:00
}
2017-02-14 06:39:53 -06:00
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 06:39:53 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 06:39:53 -06:00
updateListEconLimited ( const Schedule & schedule ,
const int current_step ,
const Wells * wells_struct ,
const WellState & well_state ,
DynamicListEconLimited & list_econ_limited ) const
{
// With no wells (on process) wells_struct is a null pointer
const int nw = ( wells_struct ) ? wells_struct - > number_of_wells : 0 ;
for ( int w = 0 ; w < nw ; + + w ) {
// flag to check if the mim oil/gas rate limit is violated
bool rate_limit_violated = false ;
const std : : string & well_name = wells_struct - > name [ w ] ;
const Well * well_ecl = schedule . getWell ( well_name ) ;
const WellEconProductionLimits & econ_production_limits = well_ecl - > getEconProductionLimits ( current_step ) ;
// economic limits only apply for production wells.
if ( wells_struct - > type [ w ] ! = PRODUCER ) {
continue ;
}
// if no limit is effective here, then continue to the next well
if ( ! econ_production_limits . onAnyEffectiveLimit ( ) ) {
continue ;
}
// for the moment, we only handle rate limits, not handling potential limits
// the potential limits should not be difficult to add
const WellEcon : : QuantityLimitEnum & quantity_limit = econ_production_limits . quantityLimit ( ) ;
if ( quantity_limit = = WellEcon : : POTN ) {
const std : : string msg = std : : string ( " POTN limit for well " ) + well_name + std : : string ( " is not supported for the moment. \n " )
+ std : : string ( " All the limits will be evaluated based on RATE. " ) ;
OpmLog : : warning ( " NOT_SUPPORTING_POTN " , msg ) ;
}
const WellMapType & well_map = well_state . wellMap ( ) ;
const typename WellMapType : : const_iterator i_well = well_map . find ( well_name ) ;
assert ( i_well ! = well_map . end ( ) ) ; // should always be found?
const WellMapEntryType & map_entry = i_well - > second ;
const int well_number = map_entry [ 0 ] ;
if ( econ_production_limits . onAnyRateLimit ( ) ) {
rate_limit_violated = checkRateEconLimits ( econ_production_limits , well_state , well_number ) ;
}
if ( rate_limit_violated ) {
if ( econ_production_limits . endRun ( ) ) {
const std : : string warning_message = std : : string ( " ending run after well closed due to economic limits is not supported yet \n " )
+ std : : string ( " the program will keep running after " ) + well_name + std : : string ( " is closed " ) ;
OpmLog : : warning ( " NOT_SUPPORTING_ENDRUN " , warning_message ) ;
}
if ( econ_production_limits . validFollowonWell ( ) ) {
OpmLog : : warning ( " NOT_SUPPORTING_FOLLOWONWELL " , " opening following on well after well closed is not supported yet " ) ;
}
if ( well_ecl - > getAutomaticShutIn ( ) ) {
list_econ_limited . addShutWell ( well_name ) ;
const std : : string msg = std : : string ( " well " ) + well_name + std : : string ( " will be shut in due to economic limit " ) ;
OpmLog : : info ( msg ) ;
} else {
list_econ_limited . addStoppedWell ( well_name ) ;
const std : : string msg = std : : string ( " well " ) + well_name + std : : string ( " will be stopped due to economic limit " ) ;
OpmLog : : info ( msg ) ;
}
// the well is closed, not need to check other limits
continue ;
}
// checking for ratio related limits, mostly all kinds of ratio.
bool ratio_limits_violated = false ;
RatioCheckTuple ratio_check_return ;
if ( econ_production_limits . onAnyRatioLimit ( ) ) {
ratio_check_return = checkRatioEconLimits ( econ_production_limits , well_state , map_entry ) ;
ratio_limits_violated = std : : get < 0 > ( ratio_check_return ) ;
}
if ( ratio_limits_violated ) {
const bool last_connection = std : : get < 1 > ( ratio_check_return ) ;
const int worst_offending_connection = std : : get < 2 > ( ratio_check_return ) ;
const int perf_start = map_entry [ 1 ] ;
assert ( ( worst_offending_connection > = 0 ) & & ( worst_offending_connection < map_entry [ 2 ] ) ) ;
const int cell_worst_offending_connection = wells_struct - > well_cells [ perf_start + worst_offending_connection ] ;
list_econ_limited . addClosedConnectionsForWell ( well_name , cell_worst_offending_connection ) ;
const std : : string msg = std : : string ( " Connection " ) + std : : to_string ( worst_offending_connection ) + std : : string ( " for well " )
+ well_name + std : : string ( " will be closed due to economic limit " ) ;
OpmLog : : info ( msg ) ;
if ( last_connection ) {
list_econ_limited . addShutWell ( well_name ) ;
const std : : string msg2 = well_name + std : : string ( " will be shut due to the last connection closed " ) ;
OpmLog : : info ( msg2 ) ;
}
}
} // for (int w = 0; w < nw; ++w)
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 06:39:53 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 06:39:53 -06:00
computeWellConnectionDensitesPressures ( const WellState & xw ,
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 ,
const std : : vector < double > & depth_perf ,
const double grav )
{
// Compute densities
2017-05-09 01:21:51 -05:00
const int nperf = depth_perf . size ( ) ;
const int numComponent = b_perf . size ( ) / nperf ;
const int np = wells ( ) . number_of_phases ;
std : : vector < double > perfRates ( b_perf . size ( ) , 0.0 ) ;
for ( int perf = 0 ; perf < nperf ; + + perf ) {
for ( int phase = 0 ; phase < np ; + + phase ) {
perfRates [ perf * numComponent + phase ] = xw . perfPhaseRates ( ) [ perf * np + phase ] ;
}
if ( has_solvent_ ) {
perfRates [ perf * numComponent + solventCompIdx ] = xw . perfRateSolvent ( ) [ perf ] ;
}
}
2017-02-14 06:39:53 -06:00
well_perforation_densities_ =
WellDensitySegmented : : computeConnectionDensities (
2017-05-09 01:21:51 -05:00
wells ( ) , phase_usage_ , perfRates ,
2017-02-14 06:39:53 -06:00
b_perf , rsmax_perf , rvmax_perf , surf_dens_perf ) ;
// Compute pressure deltas
well_perforation_pressure_diffs_ =
WellDensitySegmented : : computeConnectionPressureDelta (
wells ( ) , depth_perf , well_perforation_densities_ , grav ) ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 06:39:53 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 06:39:53 -06:00
computeWellPotentials ( const Simulator & ebosSimulator ,
2017-03-23 05:36:49 -05:00
const WellState & well_state ,
2017-04-03 08:07:56 -05:00
std : : vector < double > & well_potentials ) const
2017-02-14 06:39:53 -06:00
{
// number of wells and phases
const int nw = wells ( ) . number_of_wells ;
const int np = wells ( ) . number_of_phases ;
2017-03-23 05:36:49 -05:00
well_potentials . resize ( nw * np , 0.0 ) ;
2017-02-14 06:39:53 -06:00
for ( int w = 0 ; w < nw ; + + w ) {
2017-03-31 05:52:44 -05:00
2017-03-31 09:33:20 -05:00
// get the bhp value based on the bhp constraints
2017-04-05 03:19:34 -05:00
const double bhp = mostStrictBhpFromBhpLimits ( w ) ;
2017-03-31 09:33:20 -05:00
// does the well have a THP related constraint?
2017-04-11 08:02:36 -05:00
const bool has_thp_control = wellHasTHPConstraints ( w ) ;
2017-03-31 05:52:44 -05:00
2017-03-31 09:33:20 -05:00
std : : vector < double > potentials ( np ) ;
2017-03-31 05:52:44 -05:00
2017-04-11 08:02:36 -05:00
if ( ! has_thp_control ) {
2017-03-07 04:25:14 -06:00
2017-03-31 09:33:20 -05:00
assert ( std : : abs ( bhp ) ! = std : : numeric_limits < double > : : max ( ) ) ;
2017-03-14 09:06:40 -05:00
2017-03-31 09:33:20 -05:00
computeWellRatesWithBhp ( ebosSimulator , bhp , w , potentials ) ;
2017-02-14 06:39:53 -06:00
2017-03-31 09:33:20 -05:00
} else { // the well has a THP related constraint
2017-04-03 08:07:56 -05:00
// checking whether a well is newly added, it only happens at the beginning of the report step
if ( ! well_state . isNewWell ( w ) ) {
for ( int p = 0 ; p < np ; + + p ) {
2017-04-04 07:27:41 -05:00
// This is dangerous for new added well
2017-04-03 08:07:56 -05:00
// since we are not handling the initialization correctly for now
potentials [ p ] = well_state . wellRates ( ) [ w * np + p ] ;
}
} else {
// We need to generate a reasonable rates to start the iteration process
computeWellRatesWithBhp ( ebosSimulator , bhp , w , potentials ) ;
for ( double & value : potentials ) {
// make the value a little safer in case the BHP limits are default ones
// TODO: a better way should be a better rescaling based on the investigation of the VFP table.
2017-04-11 08:02:36 -05:00
const double rate_safety_scaling_factor = 0.00001 ;
value * = rate_safety_scaling_factor ;
2017-04-03 08:07:56 -05:00
}
2017-02-14 06:39:53 -06:00
}
2017-03-31 09:33:20 -05:00
potentials = computeWellPotentialWithTHP ( ebosSimulator , w , bhp , potentials ) ;
2017-02-14 06:39:53 -06:00
}
2017-03-31 09:33:20 -05:00
// putting the sucessfully calculated potentials to the well_potentials
for ( int p = 0 ; p < np ; + + p ) {
well_potentials [ w * np + p ] = std : : abs ( potentials [ p ] ) ;
2017-02-14 06:39:53 -06:00
}
2017-02-14 08:06:57 -06:00
} // end of for (int w = 0; w < nw; ++w)
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-03-16 10:39:05 -05:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-03-16 10:39:05 -05:00
prepareTimeStep ( const Simulator & ebos_simulator ,
WellState & well_state )
{
2017-03-31 05:50:50 -05:00
const int nw = wells ( ) . number_of_wells ;
for ( int w = 0 ; w < nw ; + + w ) {
2017-04-07 07:17:54 -05:00
// after restarting, the well_controls can be modified while
// the well_state still uses the old control index
// we need to synchronize these two.
2017-04-11 08:02:36 -05:00
resetWellControlFromState ( well_state ) ;
2017-03-31 05:50:50 -05:00
if ( wellCollection ( ) - > groupControlActive ( ) ) {
2017-04-07 07:17:54 -05:00
WellControls * wc = wells ( ) . ctrls [ w ] ;
2017-03-31 05:50:50 -05:00
WellNode & well_node = well_collection_ - > findWellNode ( std : : string ( wells ( ) . name [ w ] ) ) ;
2017-04-07 07:17:54 -05:00
// handling the situation that wells do not have a valid control
// it happens the well specified with GRUP and restarting due to non-convergencing
// putting the well under group control for this situation
int ctrl_index = well_controls_get_current ( wc ) ;
const int group_control_index = well_node . groupControlIndex ( ) ;
if ( group_control_index > = 0 & & ctrl_index < 0 ) {
// put well under group control
well_controls_set_current ( wc , group_control_index ) ;
well_state . currentControls ( ) [ w ] = group_control_index ;
}
// Final step, update whehter the well is under group control or individual control
// updated ctrl_index from the well control
ctrl_index = well_controls_get_current ( wc ) ;
2017-03-31 05:50:50 -05:00
if ( well_node . groupControlIndex ( ) > = 0 & & ctrl_index = = well_node . groupControlIndex ( ) ) {
// under group control
well_node . setIndividualControl ( false ) ;
} else {
// individual control
well_node . setIndividualControl ( true ) ;
}
}
}
2017-03-16 10:39:05 -05:00
if ( well_collection_ - > groupControlActive ( ) ) {
2017-04-07 04:00:51 -05:00
if ( well_collection_ - > requireWellPotentials ( ) ) {
2017-03-23 10:41:13 -05:00
2017-04-07 07:17:54 -05:00
// calculate the well potentials
2017-03-16 10:39:05 -05:00
setWellVariables ( well_state ) ;
computeWellConnectionPressures ( ebos_simulator , well_state ) ;
// To store well potentials for each well
std : : vector < double > well_potentials ;
2017-03-23 05:36:49 -05:00
computeWellPotentials ( ebos_simulator , well_state , well_potentials ) ;
2017-03-16 10:39:05 -05:00
// update/setup guide rates for each well based on the well_potentials
2017-03-23 10:41:13 -05:00
well_collection_ - > setGuideRatesWithPotentials ( wellsPointer ( ) , phase_usage_ , well_potentials ) ;
2017-04-06 07:53:44 -05:00
}
2017-03-31 05:50:50 -05:00
2017-03-16 10:39:05 -05:00
applyVREPGroupControl ( well_state ) ;
2017-03-23 10:41:13 -05:00
if ( ! wellCollection ( ) - > groupControlApplied ( ) ) {
wellCollection ( ) - > applyGroupControls ( ) ;
} else {
wellCollection ( ) - > updateWellTargets ( well_state . wellRates ( ) ) ;
}
}
// since the controls are all updated, we should update well_state accordingly
for ( int w = 0 ; w < nw ; + + w ) {
WellControls * wc = wells ( ) . ctrls [ w ] ;
const int control = well_controls_get_current ( wc ) ;
well_state . currentControls ( ) [ w ] = control ;
updateWellStateWithTarget ( wc , control , w , well_state ) ;
2017-04-03 08:07:56 -05:00
// The wells are not considered to be newly added
// for next time step
if ( well_state . isNewWell ( w ) ) {
well_state . setNewWell ( w , false ) ;
}
2017-04-04 07:27:41 -05:00
} // end of for (int w = 0; w < nw; ++w)
2017-03-16 10:39:05 -05:00
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 08:06:57 -06:00
WellCollection *
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 08:06:57 -06:00
wellCollection ( ) const
{
return well_collection_ ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 08:06:57 -06:00
const std : : vector < double > &
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 08:06:57 -06:00
wellPerfEfficiencyFactors ( ) const
{
return well_perforation_efficiency_factors_ ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 08:06:57 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 08:06:57 -06:00
calculateEfficiencyFactors ( )
{
if ( ! localWellsActive ( ) ) {
return ;
}
const int nw = wells ( ) . number_of_wells ;
for ( int w = 0 ; w < nw ; + + w ) {
const std : : string well_name = wells ( ) . name [ w ] ;
const WellNode & well_node = wellCollection ( ) - > findWellNode ( well_name ) ;
const double well_efficiency_factor = well_node . getAccumulativeEfficiencyFactor ( ) ;
// assign the efficiency factor to each perforation related.
for ( int perf = wells ( ) . well_connpos [ w ] ; perf < wells ( ) . well_connpos [ w + 1 ] ; + + perf ) {
well_perforation_efficiency_factors_ [ perf ] = well_efficiency_factor ;
}
}
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 08:06:57 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 08:06:57 -06:00
computeWellVoidageRates ( const WellState & well_state ,
std : : vector < double > & well_voidage_rates ,
std : : vector < double > & voidage_conversion_coeffs ) const
{
if ( ! localWellsActive ( ) ) {
return ;
}
// TODO: for now, we store the voidage rates for all the production wells.
// For injection wells, the rates are stored as zero.
// We only store the conversion coefficients for all the injection wells.
// Later, more delicate model will be implemented here.
// And for the moment, group control can only work for serial running.
const int nw = well_state . numWells ( ) ;
const int np = well_state . numPhases ( ) ;
// we calculate the voidage rate for each well, that means the sum of all the phases.
well_voidage_rates . resize ( nw , 0 ) ;
// store the conversion coefficients, while only for the use of injection wells.
voidage_conversion_coeffs . resize ( nw * np , 1.0 ) ;
std : : vector < double > well_rates ( np , 0.0 ) ;
std : : vector < double > convert_coeff ( np , 1.0 ) ;
for ( int w = 0 ; w < nw ; + + w ) {
const bool is_producer = wells ( ) . type [ w ] = = PRODUCER ;
// not sure necessary to change all the value to be positive
if ( is_producer ) {
std : : transform ( well_state . wellRates ( ) . begin ( ) + np * w ,
well_state . wellRates ( ) . begin ( ) + np * ( w + 1 ) ,
well_rates . begin ( ) , std : : negate < double > ( ) ) ;
// the average hydrocarbon conditions of the whole field will be used
const int fipreg = 0 ; // Not considering FIP for the moment.
rate_converter_ - > calcCoeff ( well_rates , fipreg , convert_coeff ) ;
well_voidage_rates [ w ] = std : : inner_product ( well_rates . begin ( ) , well_rates . end ( ) ,
convert_coeff . begin ( ) , 0.0 ) ;
} else {
// TODO: Not sure whether will encounter situation with all zero rates
// and whether it will cause problem here.
std : : copy ( well_state . wellRates ( ) . begin ( ) + np * w ,
well_state . wellRates ( ) . begin ( ) + np * ( w + 1 ) ,
well_rates . begin ( ) ) ;
// the average hydrocarbon conditions of the whole field will be used
const int fipreg = 0 ; // Not considering FIP for the moment.
rate_converter_ - > calcCoeff ( well_rates , fipreg , convert_coeff ) ;
std : : copy ( convert_coeff . begin ( ) , convert_coeff . end ( ) ,
voidage_conversion_coeffs . begin ( ) + np * w ) ;
}
}
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 08:06:57 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 08:06:57 -06:00
applyVREPGroupControl ( WellState & well_state ) const
{
if ( wellCollection ( ) - > havingVREPGroups ( ) ) {
std : : vector < double > well_voidage_rates ;
std : : vector < double > voidage_conversion_coeffs ;
computeWellVoidageRates ( well_state , well_voidage_rates , voidage_conversion_coeffs ) ;
wellCollection ( ) - > applyVREPGroupControls ( well_voidage_rates , voidage_conversion_coeffs ) ;
2017-03-09 08:54:23 -06:00
// for the wells under group control, update the control index for the well_state and well_controls
2017-02-14 08:06:57 -06:00
for ( const WellNode * well_node : wellCollection ( ) - > getLeafNodes ( ) ) {
if ( well_node - > isInjector ( ) & & ! well_node - > individualControl ( ) ) {
const int well_index = well_node - > selfIndex ( ) ;
well_state . currentControls ( ) [ well_index ] = well_node - > groupControlIndex ( ) ;
2017-03-09 08:54:23 -06:00
WellControls * wc = wells ( ) . ctrls [ well_index ] ;
well_controls_set_current ( wc , well_node - > groupControlIndex ( ) ) ;
2017-02-14 08:06:57 -06:00
}
}
}
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
typename StandardWellsDense < TypeTag > : : EvalWell
StandardWellsDense < TypeTag > : :
2017-02-14 08:06:57 -06:00
getBhp ( const int wellIdx ) const {
const WellControls * wc = wells ( ) . ctrls [ wellIdx ] ;
if ( well_controls_get_current_type ( wc ) = = BHP ) {
EvalWell bhp = 0.0 ;
const double target_rate = well_controls_get_current_target ( wc ) ;
bhp . setValue ( target_rate ) ;
return bhp ;
} else if ( well_controls_get_current_type ( wc ) = = THP ) {
const int control = well_controls_get_current ( wc ) ;
const double thp = well_controls_get_current_target ( wc ) ;
const double alq = well_controls_iget_alq ( wc , control ) ;
const int table_id = well_controls_iget_vfp ( wc , control ) ;
EvalWell aqua = 0.0 ;
EvalWell liquid = 0.0 ;
EvalWell vapour = 0.0 ;
EvalWell bhp = 0.0 ;
double vfp_ref_depth = 0.0 ;
const Opm : : PhaseUsage & pu = phase_usage_ ;
if ( active_ [ Water ] ) {
aqua = getQs ( wellIdx , pu . phase_pos [ Water ] ) ;
}
if ( active_ [ Oil ] ) {
liquid = getQs ( wellIdx , pu . phase_pos [ Oil ] ) ;
}
if ( active_ [ Gas ] ) {
vapour = getQs ( wellIdx , pu . phase_pos [ Gas ] ) ;
}
if ( wells ( ) . type [ wellIdx ] = = INJECTOR ) {
bhp = vfp_properties_ - > getInj ( ) - > bhp ( table_id , aqua , liquid , vapour , thp ) ;
vfp_ref_depth = vfp_properties_ - > getInj ( ) - > getTable ( table_id ) - > getDatumDepth ( ) ;
} else {
bhp = vfp_properties_ - > getProd ( ) - > bhp ( table_id , aqua , liquid , vapour , thp , alq ) ;
vfp_ref_depth = vfp_properties_ - > getProd ( ) - > getTable ( table_id ) - > getDatumDepth ( ) ;
}
// pick the density in the top layer
const int perf = wells ( ) . well_connpos [ wellIdx ] ;
const double rho = well_perforation_densities_ [ perf ] ;
const double dp = wellhelpers : : computeHydrostaticCorrection ( wells ( ) , wellIdx , vfp_ref_depth , rho , gravity_ ) ;
bhp - = dp ;
return bhp ;
}
const int nw = wells ( ) . number_of_wells ;
return wellVariables_ [ nw * XvarWell + wellIdx ] ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
typename StandardWellsDense < TypeTag > : : EvalWell
StandardWellsDense < TypeTag > : :
getQs ( const int wellIdx , const int compIdx ) const
2017-02-14 08:06:57 -06:00
{
EvalWell qs = 0.0 ;
const WellControls * wc = wells ( ) . ctrls [ wellIdx ] ;
const int np = wells ( ) . number_of_phases ;
2017-05-09 01:21:51 -05:00
assert ( compIdx < numComponents ( ) ) ;
2017-02-14 08:06:57 -06:00
const int nw = wells ( ) . number_of_wells ;
2017-05-09 01:21:51 -05:00
const auto pu = phase_usage_ ;
2017-02-14 08:06:57 -06:00
const double target_rate = well_controls_get_current_target ( wc ) ;
2017-03-07 08:25:25 -06:00
// TODO: the formulation for the injectors decides it only work with single phase
// surface rate injection control. Improvement will be required.
2017-02-14 08:06:57 -06:00
if ( wells ( ) . type [ wellIdx ] = = INJECTOR ) {
2017-05-09 01:21:51 -05:00
if ( has_solvent_ ) {
2017-05-23 08:46:11 -05:00
double comp_frac = 0.0 ;
if ( compIdx = = solventCompIdx ) { // solvent
comp_frac = wells ( ) . comp_frac [ np * wellIdx + pu . phase_pos [ Gas ] ] * wsolvent ( wellIdx ) ;
} else if ( compIdx = = pu . phase_pos [ Gas ] ) {
comp_frac = wells ( ) . comp_frac [ np * wellIdx + compIdx ] * ( 1.0 - wsolvent ( wellIdx ) ) ;
} else {
comp_frac = wells ( ) . comp_frac [ np * wellIdx + compIdx ] ;
}
if ( comp_frac = = 0.0 ) {
return qs ; //zero
2017-05-09 01:21:51 -05:00
}
2017-05-23 08:46:11 -05:00
if ( well_controls_get_current_type ( wc ) = = BHP | | well_controls_get_current_type ( wc ) = = THP ) {
return comp_frac * wellVariables_ [ nw * XvarWell + wellIdx ] ;
2017-05-09 01:21:51 -05:00
}
2017-05-23 08:46:11 -05:00
qs . setValue ( comp_frac * target_rate ) ;
return qs ;
2017-05-09 01:21:51 -05:00
}
2017-05-03 06:34:15 -05:00
const double comp_frac = wells ( ) . comp_frac [ np * wellIdx + compIdx ] ;
2017-02-14 08:06:57 -06:00
if ( comp_frac = = 0.0 ) {
return qs ;
}
if ( well_controls_get_current_type ( wc ) = = BHP | | well_controls_get_current_type ( wc ) = = THP ) {
return wellVariables_ [ nw * XvarWell + wellIdx ] ;
}
qs . setValue ( target_rate ) ;
return qs ;
}
// Producers
if ( well_controls_get_current_type ( wc ) = = BHP | | well_controls_get_current_type ( wc ) = = THP ) {
2017-05-03 06:34:15 -05:00
return wellVariables_ [ nw * XvarWell + wellIdx ] * wellVolumeFractionScaled ( wellIdx , compIdx ) ;
2017-02-14 08:06:57 -06:00
}
if ( well_controls_get_current_type ( wc ) = = SURFACE_RATE ) {
// checking how many phases are included in the rate control
// to decide wheter it is a single phase rate control or not
const double * distr = well_controls_get_current_distr ( wc ) ;
int num_phases_under_rate_control = 0 ;
for ( int phase = 0 ; phase < np ; + + phase ) {
if ( distr [ phase ] > 0.0 ) {
num_phases_under_rate_control + = 1 ;
}
}
// there should be at least one phase involved
assert ( num_phases_under_rate_control > 0 ) ;
// when it is a single phase rate limit
if ( num_phases_under_rate_control = = 1 ) {
2017-03-08 04:02:47 -06:00
// looking for the phase under control
int phase_under_control = - 1 ;
for ( int phase = 0 ; phase < np ; + + phase ) {
if ( distr [ phase ] > 0.0 ) {
phase_under_control = phase ;
break ;
}
2017-02-14 08:06:57 -06:00
}
2017-03-08 04:02:47 -06:00
assert ( phase_under_control > = 0 ) ;
2017-05-24 03:53:48 -05:00
EvalWell wellVolumeFractionScaledPhaseUnderControl = wellVolumeFractionScaled ( wellIdx , phase_under_control ) ;
if ( has_solvent_ & & phase_under_control = = Gas ) {
// for GRAT controlled wells solvent is included in the target
wellVolumeFractionScaledPhaseUnderControl + = wellVolumeFractionScaled ( wellIdx , solventCompIdx ) ;
}
2017-05-03 06:34:15 -05:00
if ( compIdx = = phase_under_control ) {
2017-05-24 03:53:48 -05:00
if ( has_solvent_ & & phase_under_control = = Gas ) {
qs . setValue ( target_rate * wellVolumeFractionScaled ( wellIdx , Gas ) . value ( ) / wellVolumeFractionScaledPhaseUnderControl . value ( ) ) ;
return qs ;
}
2017-03-08 04:02:47 -06:00
qs . setValue ( target_rate ) ;
return qs ;
2017-02-14 08:06:57 -06:00
}
2017-03-08 04:02:47 -06:00
// TODO: not sure why the single phase under control will have near zero fraction
2017-02-14 08:06:57 -06:00
const double eps = 1e-6 ;
2017-05-24 03:53:48 -05:00
if ( wellVolumeFractionScaledPhaseUnderControl < eps ) {
2017-02-14 08:06:57 -06:00
return qs ;
}
2017-05-24 03:53:48 -05:00
return ( target_rate * wellVolumeFractionScaled ( wellIdx , compIdx ) / wellVolumeFractionScaledPhaseUnderControl ) ;
2017-02-14 08:06:57 -06:00
}
// when it is a combined two phase rate limit, such like LRAT
// we neec to calculate the rate for the certain phase
if ( num_phases_under_rate_control = = 2 ) {
EvalWell combined_volume_fraction = 0. ;
for ( int p = 0 ; p < np ; + + p ) {
if ( distr [ p ] = = 1.0 ) {
combined_volume_fraction + = wellVolumeFractionScaled ( wellIdx , p ) ;
}
}
2017-05-03 06:34:15 -05:00
return ( target_rate * wellVolumeFractionScaled ( wellIdx , compIdx ) / combined_volume_fraction ) ;
2017-02-14 08:06:57 -06:00
}
2017-03-07 08:25:25 -06:00
// TODO: three phase surface rate control is not tested yet
if ( num_phases_under_rate_control = = 3 ) {
2017-05-03 06:34:15 -05:00
return target_rate * wellSurfaceVolumeFraction ( wellIdx , compIdx ) ;
2017-03-07 08:25:25 -06:00
}
} else if ( well_controls_get_current_type ( wc ) = = RESERVOIR_RATE ) {
// ReservoirRate
2017-05-03 06:34:15 -05:00
return target_rate * wellVolumeFractionScaled ( wellIdx , compIdx ) ;
2017-03-07 08:25:25 -06:00
} else {
OPM_THROW ( std : : logic_error , " Unknown control type for well " < < wells ( ) . name [ wellIdx ] ) ;
2017-02-14 08:06:57 -06:00
}
2017-03-07 08:25:25 -06:00
// avoid warning of condition reaches end of non-void function
return qs ;
2017-02-14 08:06:57 -06:00
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
typename StandardWellsDense < TypeTag > : : EvalWell
StandardWellsDense < TypeTag > : :
wellVolumeFraction ( const int wellIdx , const int compIdx ) const
2017-02-14 08:06:57 -06:00
{
const int nw = wells ( ) . number_of_wells ;
2017-05-03 06:34:15 -05:00
if ( compIdx = = Water ) {
2017-02-14 08:06:57 -06:00
return wellVariables_ [ WFrac * nw + wellIdx ] ;
}
2017-05-03 06:34:15 -05:00
if ( compIdx = = Gas ) {
2017-02-14 08:06:57 -06:00
return wellVariables_ [ GFrac * nw + wellIdx ] ;
}
2017-05-09 01:21:51 -05:00
if ( compIdx = = solventCompIdx ) {
return wellVariables_ [ SFrac * nw + wellIdx ] ;
}
2017-02-14 08:06:57 -06:00
// Oil fraction
EvalWell well_fraction = 1.0 ;
if ( active_ [ Water ] ) {
well_fraction - = wellVariables_ [ WFrac * nw + wellIdx ] ;
}
if ( active_ [ Gas ] ) {
well_fraction - = wellVariables_ [ GFrac * nw + wellIdx ] ;
}
2017-05-09 01:21:51 -05:00
if ( has_solvent_ ) {
well_fraction - = wellVariables_ [ SFrac * nw + wellIdx ] ;
}
2017-02-14 08:06:57 -06:00
return well_fraction ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
typename StandardWellsDense < TypeTag > : : EvalWell
StandardWellsDense < TypeTag > : :
wellVolumeFractionScaled ( const int wellIdx , const int compIdx ) const
2017-02-14 08:06:57 -06:00
{
const WellControls * wc = wells ( ) . ctrls [ wellIdx ] ;
if ( well_controls_get_current_type ( wc ) = = RESERVOIR_RATE ) {
2017-05-24 03:53:48 -05:00
if ( has_solvent_ & & compIdx = = solventCompIdx ) {
return wellVolumeFraction ( wellIdx , compIdx ) ;
}
2017-02-14 08:06:57 -06:00
const double * distr = well_controls_get_current_distr ( wc ) ;
2017-05-03 06:34:15 -05:00
assert ( compIdx < 3 ) ;
if ( distr [ compIdx ] > 0. ) {
return wellVolumeFraction ( wellIdx , compIdx ) / distr [ compIdx ] ;
2017-03-23 10:36:48 -05:00
} else {
// TODO: not sure why return EvalWell(0.) causing problem here
// Probably due to the wrong Jacobians.
2017-05-03 06:34:15 -05:00
return wellVolumeFraction ( wellIdx , compIdx ) ;
2017-03-23 10:36:48 -05:00
}
2017-02-14 08:06:57 -06:00
}
2017-05-09 01:21:51 -05:00
std : : vector < double > g = { 1 , 1 , 0.01 , 0.01 } ;
2017-05-03 06:34:15 -05:00
return ( wellVolumeFraction ( wellIdx , compIdx ) / g [ compIdx ] ) ;
2017-02-14 08:06:57 -06:00
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
typename StandardWellsDense < TypeTag > : : EvalWell
StandardWellsDense < TypeTag > : :
wellSurfaceVolumeFraction ( const int well_index , const int compIdx ) const
2017-03-07 06:31:40 -06:00
{
EvalWell sum_volume_fraction_scaled = 0. ;
2017-05-03 06:34:15 -05:00
const int numComp = numComponents ( ) ;
for ( int idx = 0 ; idx < numComp ; + + idx ) {
sum_volume_fraction_scaled + = wellVolumeFractionScaled ( well_index , idx ) ;
2017-03-07 06:31:40 -06:00
}
assert ( sum_volume_fraction_scaled . value ( ) ! = 0. ) ;
2017-05-03 06:34:15 -05:00
return wellVolumeFractionScaled ( well_index , compIdx ) / sum_volume_fraction_scaled ;
2017-03-07 06:31:40 -06:00
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 08:06:57 -06:00
bool
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 08:06:57 -06:00
checkRateEconLimits ( const WellEconProductionLimits & econ_production_limits ,
const WellState & well_state ,
const int well_number ) const
{
2017-05-03 06:34:15 -05:00
2017-02-14 08:06:57 -06:00
const Opm : : PhaseUsage & pu = phase_usage_ ;
const int np = well_state . numPhases ( ) ;
if ( econ_production_limits . onMinOilRate ( ) ) {
assert ( active_ [ Oil ] ) ;
const double oil_rate = well_state . wellRates ( ) [ well_number * np + pu . phase_pos [ Oil ] ] ;
const double min_oil_rate = econ_production_limits . minOilRate ( ) ;
if ( std : : abs ( oil_rate ) < min_oil_rate ) {
return true ;
}
}
if ( econ_production_limits . onMinGasRate ( ) ) {
assert ( active_ [ Gas ] ) ;
const double gas_rate = well_state . wellRates ( ) [ well_number * np + pu . phase_pos [ Gas ] ] ;
const double min_gas_rate = econ_production_limits . minGasRate ( ) ;
if ( std : : abs ( gas_rate ) < min_gas_rate ) {
return true ;
}
}
if ( econ_production_limits . onMinLiquidRate ( ) ) {
assert ( active_ [ Oil ] ) ;
assert ( active_ [ Water ] ) ;
const double oil_rate = well_state . wellRates ( ) [ well_number * np + pu . phase_pos [ Oil ] ] ;
const double water_rate = well_state . wellRates ( ) [ well_number * np + pu . phase_pos [ Water ] ] ;
const double liquid_rate = oil_rate + water_rate ;
const double min_liquid_rate = econ_production_limits . minLiquidRate ( ) ;
if ( std : : abs ( liquid_rate ) < min_liquid_rate ) {
return true ;
}
}
if ( econ_production_limits . onMinReservoirFluidRate ( ) ) {
OpmLog : : warning ( " NOT_SUPPORTING_MIN_RESERVOIR_FLUID_RATE " , " Minimum reservoir fluid production rate limit is not supported yet " ) ;
}
return false ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
typename StandardWellsDense < TypeTag > : : RatioCheckTuple
StandardWellsDense < TypeTag > : :
2017-02-14 08:06:57 -06:00
checkRatioEconLimits ( const WellEconProductionLimits & econ_production_limits ,
const WellState & well_state ,
const WellMapEntryType & map_entry ) const
{
// TODO: not sure how to define the worst-offending connection when more than one
// ratio related limit is violated.
// The defintion used here is that we define the violation extent based on the
// ratio between the value and the corresponding limit.
// For each violated limit, we decide the worst-offending connection separately.
// Among the worst-offending connections, we use the one has the biggest violation
// extent.
bool any_limit_violated = false ;
bool last_connection = false ;
int worst_offending_connection = INVALIDCONNECTION ;
double violation_extent = - 1.0 ;
if ( econ_production_limits . onMaxWaterCut ( ) ) {
const RatioCheckTuple water_cut_return = checkMaxWaterCutLimit ( econ_production_limits , well_state , map_entry ) ;
bool water_cut_violated = std : : get < 0 > ( water_cut_return ) ;
if ( water_cut_violated ) {
any_limit_violated = true ;
const double violation_extent_water_cut = std : : get < 3 > ( water_cut_return ) ;
if ( violation_extent_water_cut > violation_extent ) {
violation_extent = violation_extent_water_cut ;
worst_offending_connection = std : : get < 2 > ( water_cut_return ) ;
last_connection = std : : get < 1 > ( water_cut_return ) ;
}
}
}
if ( econ_production_limits . onMaxGasOilRatio ( ) ) {
OpmLog : : warning ( " NOT_SUPPORTING_MAX_GOR " , " the support for max Gas-Oil ratio is not implemented yet! " ) ;
}
if ( econ_production_limits . onMaxWaterGasRatio ( ) ) {
OpmLog : : warning ( " NOT_SUPPORTING_MAX_WGR " , " the support for max Water-Gas ratio is not implemented yet! " ) ;
}
if ( econ_production_limits . onMaxGasLiquidRatio ( ) ) {
OpmLog : : warning ( " NOT_SUPPORTING_MAX_GLR " , " the support for max Gas-Liquid ratio is not implemented yet! " ) ;
}
if ( any_limit_violated ) {
assert ( worst_offending_connection > = 0 ) ;
assert ( violation_extent > 1. ) ;
}
return std : : make_tuple ( any_limit_violated , last_connection , worst_offending_connection , violation_extent ) ;
2017-02-14 06:39:53 -06:00
}
2017-02-14 08:06:57 -06:00
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
typename StandardWellsDense < TypeTag > : : RatioCheckTuple
StandardWellsDense < TypeTag > : :
2017-02-14 08:06:57 -06:00
checkMaxWaterCutLimit ( const WellEconProductionLimits & econ_production_limits ,
const WellState & well_state ,
const WellMapEntryType & map_entry ) const
{
bool water_cut_limit_violated = false ;
int worst_offending_connection = INVALIDCONNECTION ;
bool last_connection = false ;
double violation_extent = - 1.0 ;
const int np = well_state . numPhases ( ) ;
const Opm : : PhaseUsage & pu = phase_usage_ ;
const int well_number = map_entry [ 0 ] ;
assert ( active_ [ Oil ] ) ;
assert ( active_ [ Water ] ) ;
const double oil_rate = well_state . wellRates ( ) [ well_number * np + pu . phase_pos [ Oil ] ] ;
const double water_rate = well_state . wellRates ( ) [ well_number * np + pu . phase_pos [ Water ] ] ;
const double liquid_rate = oil_rate + water_rate ;
double water_cut ;
if ( std : : abs ( liquid_rate ) ! = 0. ) {
water_cut = water_rate / liquid_rate ;
} else {
water_cut = 0.0 ;
}
const double max_water_cut_limit = econ_production_limits . maxWaterCut ( ) ;
if ( water_cut > max_water_cut_limit ) {
water_cut_limit_violated = true ;
}
if ( water_cut_limit_violated ) {
// need to handle the worst_offending_connection
const int perf_start = map_entry [ 1 ] ;
const int perf_number = map_entry [ 2 ] ;
std : : vector < double > water_cut_perf ( perf_number ) ;
for ( int perf = 0 ; perf < perf_number ; + + perf ) {
const int i_perf = perf_start + perf ;
const double oil_perf_rate = well_state . perfPhaseRates ( ) [ i_perf * np + pu . phase_pos [ Oil ] ] ;
const double water_perf_rate = well_state . perfPhaseRates ( ) [ i_perf * np + pu . phase_pos [ Water ] ] ;
const double liquid_perf_rate = oil_perf_rate + water_perf_rate ;
if ( std : : abs ( liquid_perf_rate ) ! = 0. ) {
water_cut_perf [ perf ] = water_perf_rate / liquid_perf_rate ;
} else {
water_cut_perf [ perf ] = 0. ;
}
}
last_connection = ( perf_number = = 1 ) ;
if ( last_connection ) {
worst_offending_connection = 0 ;
violation_extent = water_cut_perf [ 0 ] / max_water_cut_limit ;
return std : : make_tuple ( water_cut_limit_violated , last_connection , worst_offending_connection , violation_extent ) ;
}
double max_water_cut_perf = 0. ;
for ( int perf = 0 ; perf < perf_number ; + + perf ) {
if ( water_cut_perf [ perf ] > max_water_cut_perf ) {
worst_offending_connection = perf ;
max_water_cut_perf = water_cut_perf [ perf ] ;
}
}
assert ( max_water_cut_perf ! = 0. ) ;
assert ( ( worst_offending_connection > = 0 ) & & ( worst_offending_connection < perf_number ) ) ;
violation_extent = max_water_cut_perf / max_water_cut_limit ;
}
return std : : make_tuple ( water_cut_limit_violated , last_connection , worst_offending_connection , violation_extent ) ;
}
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-02-14 08:06:57 -06:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-02-14 08:06:57 -06:00
updateWellStateWithTarget ( const WellControls * wc ,
const int current ,
const int well_index ,
WellState & xw ) const
{
// number of phases
const int np = wells ( ) . number_of_phases ;
// Updating well state and primary variables.
// Target values are used as initial conditions for BHP, THP, and SURFACE_RATE
const double target = well_controls_iget_target ( wc , current ) ;
const double * distr = well_controls_iget_distr ( wc , current ) ;
switch ( well_controls_iget_type ( wc , current ) ) {
case BHP :
xw . bhp ( ) [ well_index ] = target ;
2017-03-08 04:33:16 -06:00
// TODO: similar to the way below to handle THP
// we should not something related to thp here when there is thp constraint
2017-02-14 08:06:57 -06:00
break ;
case THP : {
2017-03-08 04:33:16 -06:00
xw . thp ( ) [ well_index ] = target ;
2017-02-14 08:06:57 -06:00
double aqua = 0.0 ;
double liquid = 0.0 ;
double vapour = 0.0 ;
const Opm : : PhaseUsage & pu = phase_usage_ ;
if ( active_ [ Water ] ) {
aqua = xw . wellRates ( ) [ well_index * np + pu . phase_pos [ Water ] ] ;
}
if ( active_ [ Oil ] ) {
liquid = xw . wellRates ( ) [ well_index * np + pu . phase_pos [ Oil ] ] ;
}
if ( active_ [ Gas ] ) {
vapour = xw . wellRates ( ) [ well_index * np + pu . phase_pos [ Gas ] ] ;
}
const int vfp = well_controls_iget_vfp ( wc , current ) ;
const double & thp = well_controls_iget_target ( wc , current ) ;
const double & alq = well_controls_iget_alq ( wc , current ) ;
//Set *BHP* target by calculating bhp from THP
const WellType & well_type = wells ( ) . type [ well_index ] ;
// pick the density in the top layer
const int perf = wells ( ) . well_connpos [ well_index ] ;
const double rho = well_perforation_densities_ [ perf ] ;
if ( well_type = = INJECTOR ) {
const double dp = wellhelpers : : computeHydrostaticCorrection (
wells ( ) , well_index , vfp_properties_ - > getInj ( ) - > getTable ( vfp ) - > getDatumDepth ( ) ,
rho , gravity_ ) ;
xw . bhp ( ) [ well_index ] = vfp_properties_ - > getInj ( ) - > bhp ( vfp , aqua , liquid , vapour , thp ) - dp ;
}
else if ( well_type = = PRODUCER ) {
const double dp = wellhelpers : : computeHydrostaticCorrection (
wells ( ) , well_index , vfp_properties_ - > getProd ( ) - > getTable ( vfp ) - > getDatumDepth ( ) ,
rho , gravity_ ) ;
xw . bhp ( ) [ well_index ] = vfp_properties_ - > getProd ( ) - > bhp ( vfp , aqua , liquid , vapour , thp , alq ) - dp ;
}
else {
OPM_THROW ( std : : logic_error , " Expected PRODUCER or INJECTOR type of well " ) ;
}
break ;
}
2017-04-11 08:02:36 -05:00
case RESERVOIR_RATE : // intentional fall-through
2017-02-14 08:06:57 -06:00
case SURFACE_RATE :
2017-03-21 11:04:42 -05:00
// checking the number of the phases under control
int numPhasesWithTargetsUnderThisControl = 0 ;
for ( int phase = 0 ; phase < np ; + + phase ) {
if ( distr [ phase ] > 0.0 ) {
numPhasesWithTargetsUnderThisControl + = 1 ;
}
}
assert ( numPhasesWithTargetsUnderThisControl > 0 ) ;
2017-02-14 08:06:57 -06:00
const WellType & well_type = wells ( ) . type [ well_index ] ;
if ( well_type = = INJECTOR ) {
2017-03-21 11:04:42 -05:00
// assign target value as initial guess for injectors
// only handles single phase control at the moment
assert ( numPhasesWithTargetsUnderThisControl = = 1 ) ;
2017-02-14 08:06:57 -06:00
for ( int phase = 0 ; phase < np ; + + phase ) {
2017-03-21 11:04:42 -05:00
if ( distr [ phase ] > 0. ) {
xw . wellRates ( ) [ np * well_index + phase ] = target / distr [ phase ] ;
2017-03-20 11:11:46 -05:00
} else {
2017-03-21 11:04:42 -05:00
xw . wellRates ( ) [ np * well_index + phase ] = 0. ;
2017-03-20 11:11:46 -05:00
}
2017-02-14 08:06:57 -06:00
}
} else if ( well_type = = PRODUCER ) {
2017-03-20 11:11:46 -05:00
// update the rates of phases under control based on the target,
// and also update rates of phases not under control to keep the rate ratio,
// assuming the mobility ratio does not change for the production wells
2017-04-11 08:02:36 -05:00
double original_rates_under_phase_control = 0.0 ;
2017-02-14 08:06:57 -06:00
for ( int phase = 0 ; phase < np ; + + phase ) {
2017-03-20 11:11:46 -05:00
if ( distr [ phase ] > 0.0 ) {
2017-04-11 08:02:36 -05:00
original_rates_under_phase_control + = xw . wellRates ( ) [ np * well_index + phase ] * distr [ phase ] ;
2017-02-14 08:06:57 -06:00
}
}
2017-03-20 11:11:46 -05:00
2017-04-11 08:02:36 -05:00
if ( original_rates_under_phase_control ! = 0.0 ) {
double scaling_factor = target / original_rates_under_phase_control ;
2017-03-20 11:11:46 -05:00
for ( int phase = 0 ; phase < np ; + + phase ) {
xw . wellRates ( ) [ np * well_index + phase ] * = scaling_factor ;
}
2017-04-11 08:02:36 -05:00
} else { // scaling factor is not well defied when original_rates_under_phase_control is zero
2017-03-21 11:04:42 -05:00
// separating targets equally between phases under control
2017-04-11 08:02:36 -05:00
const double target_rate_divided = target / numPhasesWithTargetsUnderThisControl ;
2017-03-21 11:04:42 -05:00
for ( int phase = 0 ; phase < np ; + + phase ) {
if ( distr [ phase ] > 0.0 ) {
2017-04-11 08:02:36 -05:00
xw . wellRates ( ) [ np * well_index + phase ] = target_rate_divided / distr [ phase ] ;
2017-03-21 11:04:42 -05:00
} else {
// this only happens for SURFACE_RATE control
2017-04-11 08:02:36 -05:00
xw . wellRates ( ) [ np * well_index + phase ] = target_rate_divided ;
2017-03-20 11:11:46 -05:00
}
}
2017-03-21 11:04:42 -05:00
}
2017-02-14 08:06:57 -06:00
} else {
OPM_THROW ( std : : logic_error , " Expected PRODUCER or INJECTOR type of well " ) ;
}
break ;
} // end of switch
std : : vector < double > g = { 1.0 , 1.0 , 0.01 } ;
if ( well_controls_iget_type ( wc , current ) = = RESERVOIR_RATE ) {
for ( int phase = 0 ; phase < np ; + + phase ) {
g [ phase ] = distr [ phase ] ;
}
}
// the number of wells
const int nw = wells ( ) . number_of_wells ;
switch ( well_controls_iget_type ( wc , current ) ) {
case THP :
case BHP : {
const WellType & well_type = wells ( ) . type [ well_index ] ;
xw . wellSolutions ( ) [ nw * XvarWell + well_index ] = 0.0 ;
if ( well_type = = INJECTOR ) {
for ( int p = 0 ; p < np ; + + p ) {
xw . wellSolutions ( ) [ nw * XvarWell + well_index ] + = xw . wellRates ( ) [ np * well_index + p ] * wells ( ) . comp_frac [ np * well_index + p ] ;
}
} else {
for ( int p = 0 ; p < np ; + + p ) {
xw . wellSolutions ( ) [ nw * XvarWell + well_index ] + = g [ p ] * xw . wellRates ( ) [ np * well_index + p ] ;
}
}
break ;
}
case RESERVOIR_RATE : // Intentional fall-through
case SURFACE_RATE :
xw . wellSolutions ( ) [ nw * XvarWell + well_index ] = xw . bhp ( ) [ well_index ] ;
break ;
} // end of switch
double tot_well_rate = 0.0 ;
for ( int p = 0 ; p < np ; + + p ) {
tot_well_rate + = g [ p ] * xw . wellRates ( ) [ np * well_index + p ] ;
}
if ( std : : abs ( tot_well_rate ) > 0 ) {
if ( active_ [ Water ] ) {
xw . wellSolutions ( ) [ WFrac * nw + well_index ] = g [ Water ] * xw . wellRates ( ) [ np * well_index + Water ] / tot_well_rate ;
}
if ( active_ [ Gas ] ) {
2017-05-09 01:21:51 -05:00
xw . wellSolutions ( ) [ GFrac * nw + well_index ] = g [ Gas ] * ( 1.0 - wsolvent ( well_index ) ) * xw . wellRates ( ) [ np * well_index + Gas ] / tot_well_rate ;
}
if ( has_solvent_ ) {
xw . wellSolutions ( ) [ SFrac * nw + well_index ] = g [ Gas ] * wsolvent ( well_index ) * xw . wellRates ( ) [ np * well_index + Gas ] / tot_well_rate ;
2017-02-14 08:06:57 -06:00
}
} else {
2017-03-21 11:04:42 -05:00
const WellType & well_type = wells ( ) . type [ well_index ] ;
if ( well_type = = INJECTOR ) {
// only single phase injection handled
if ( active_ [ Water ] ) {
if ( distr [ Water ] > 0.0 ) {
xw . wellSolutions ( ) [ WFrac * nw + well_index ] = 1.0 ;
} else {
xw . wellSolutions ( ) [ WFrac * nw + well_index ] = 0.0 ;
}
}
2017-02-14 08:06:57 -06:00
2017-03-21 11:04:42 -05:00
if ( active_ [ Gas ] ) {
if ( distr [ Gas ] > 0.0 ) {
2017-05-09 01:21:51 -05:00
xw . wellSolutions ( ) [ GFrac * nw + well_index ] = 1.0 - wsolvent ( well_index ) ;
if ( has_solvent_ ) {
xw . wellSolutions ( ) [ SFrac * nw + well_index ] = wsolvent ( well_index ) ;
}
2017-03-21 11:04:42 -05:00
} else {
xw . wellSolutions ( ) [ GFrac * nw + well_index ] = 0.0 ;
}
}
// TODO: it is possible to leave injector as a oil well,
// when F_w and F_g both equals to zero, not sure under what kind of circumstance
// this will happen.
} else if ( well_type = = PRODUCER ) { // producers
if ( active_ [ Water ] ) {
xw . wellSolutions ( ) [ WFrac * nw + well_index ] = 1.0 / np ;
}
if ( active_ [ Gas ] ) {
xw . wellSolutions ( ) [ GFrac * nw + well_index ] = 1.0 / np ;
}
} else {
OPM_THROW ( std : : logic_error , " Expected PRODUCER or INJECTOR type of well " ) ;
2017-02-14 08:06:57 -06:00
}
}
}
2017-03-31 05:52:44 -05:00
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-03-31 05:52:44 -05:00
bool
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-03-31 05:52:44 -05:00
wellHasTHPConstraints ( const int well_index ) const
{
const WellControls * well_control = wells ( ) . ctrls [ well_index ] ;
const int nwc = well_controls_get_num ( well_control ) ;
for ( int ctrl_index = 0 ; ctrl_index < nwc ; + + ctrl_index ) {
if ( well_controls_iget_type ( well_control , ctrl_index ) = = THP ) {
return true ;
}
}
return false ;
}
2017-03-31 05:56:30 -05:00
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-03-31 05:56:30 -05:00
void
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-03-31 05:56:30 -05:00
computeWellRatesWithBhp ( const Simulator & ebosSimulator ,
const EvalWell & bhp ,
const int well_index ,
std : : vector < double > & well_flux ) const
{
const int np = wells ( ) . number_of_phases ;
2017-05-24 03:53:48 -05:00
const int numComp = numComponents ( ) ;
2017-03-31 05:56:30 -05:00
well_flux . resize ( np , 0.0 ) ;
const bool allow_cf = allow_cross_flow ( well_index , ebosSimulator ) ;
for ( int perf = wells ( ) . well_connpos [ well_index ] ; perf < wells ( ) . well_connpos [ well_index + 1 ] ; + + perf ) {
const int cell_index = wells ( ) . well_cells [ perf ] ;
const auto & intQuants = * ( ebosSimulator . model ( ) . cachedIntensiveQuantities ( cell_index , /*timeIdx=*/ 0 ) ) ;
// flux for each perforation
2017-05-24 03:53:48 -05:00
std : : vector < EvalWell > cq_s ( numComp , 0.0 ) ;
std : : vector < EvalWell > mob ( numComp , 0.0 ) ;
2017-04-07 08:25:42 -05:00
getMobility ( ebosSimulator , perf , cell_index , mob ) ;
2017-05-03 06:34:15 -05:00
computeWellFlux ( well_index , wells ( ) . WI [ perf ] , intQuants , mob , bhp ,
2017-03-31 05:56:30 -05:00
wellPerforationPressureDiffs ( ) [ perf ] , allow_cf , cq_s ) ;
for ( int p = 0 ; p < np ; + + p ) {
well_flux [ p ] + = cq_s [ p ] . value ( ) ;
}
}
}
2017-03-31 09:30:25 -05:00
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-03-31 09:30:25 -05:00
double
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-04-04 07:27:41 -05:00
mostStrictBhpFromBhpLimits ( const int well_index ) const
2017-03-31 09:30:25 -05:00
{
double bhp ;
// type of the well, INJECTOR or PRODUCER
const WellType & well_type = wells ( ) . type [ well_index ] ;
// initial bhp value, making the value not usable
switch ( well_type ) {
2017-04-04 07:27:41 -05:00
case INJECTOR :
bhp = std : : numeric_limits < double > : : max ( ) ;
break ;
case PRODUCER :
bhp = - std : : numeric_limits < double > : : max ( ) ;
break ;
default :
OPM_THROW ( std : : logic_error , " Expected PRODUCER or INJECTOR type for well " < < wells ( ) . name [ well_index ] ) ;
2017-03-31 09:30:25 -05:00
}
// the well controls
const WellControls * well_control = wells ( ) . ctrls [ well_index ] ;
// The number of the well controls/constraints
const int nwc = well_controls_get_num ( well_control ) ;
for ( int ctrl_index = 0 ; ctrl_index < nwc ; + + ctrl_index ) {
// finding a BHP constraint
if ( well_controls_iget_type ( well_control , ctrl_index ) = = BHP ) {
// get the bhp constraint value, it should always be postive assummingly
const double bhp_target = well_controls_iget_target ( well_control , ctrl_index ) ;
switch ( well_type ) {
2017-04-04 07:27:41 -05:00
case INJECTOR : // using the lower bhp contraint from Injectors
if ( bhp_target < bhp ) {
bhp = bhp_target ;
}
break ;
case PRODUCER :
if ( bhp_target > bhp ) {
bhp = bhp_target ;
}
break ;
default :
OPM_THROW ( std : : logic_error , " Expected PRODUCER or INJECTOR type for well " < < wells ( ) . name [ well_index ] ) ;
2017-03-31 09:30:25 -05:00
} // end of switch
}
}
return bhp ;
}
2017-03-31 09:33:20 -05:00
2017-05-03 06:34:15 -05:00
template < typename TypeTag >
2017-03-31 09:33:20 -05:00
std : : vector < double >
2017-05-03 06:34:15 -05:00
StandardWellsDense < TypeTag > : :
2017-03-31 09:33:20 -05:00
computeWellPotentialWithTHP ( const Simulator & ebosSimulator ,
const int well_index ,
const double initial_bhp , // bhp from BHP constraints
const std : : vector < double > & initial_potential ) const
{
// TODO: pay attention to the situation that finally the potential is calculated based on the bhp control
// TODO: should we consider the bhp constraints during the iterative process?
const int np = wells ( ) . number_of_phases ;
assert ( np = = int ( initial_potential . size ( ) ) ) ;
std : : vector < double > potentials = initial_potential ;
std : : vector < double > old_potentials = potentials ; // keeping track of the old potentials
double bhp = initial_bhp ;
double old_bhp = bhp ;
bool converged = false ;
const int max_iteration = 1000 ;
const double bhp_tolerance = 1000. ; // 1000 pascal
int iteration = 0 ;
while ( ! converged & & iteration < max_iteration ) {
2017-04-11 08:02:36 -05:00
// for each iteration, we calculate the bhp based on the rates/potentials with thp constraints
// with considering the bhp value from the bhp limits. At the beginning of each iteration,
// we initialize the bhp to be the bhp value from the bhp limits. Then based on the bhp values calculated
// from the thp constraints, we decide the effective bhp value for well potential calculation.
2017-03-31 09:33:20 -05:00
bhp = initial_bhp ;
// the well controls
const WellControls * well_control = wells ( ) . ctrls [ well_index ] ;
// The number of the well controls/constraints
const int nwc = well_controls_get_num ( well_control ) ;
for ( int ctrl_index = 0 ; ctrl_index < nwc ; + + ctrl_index ) {
if ( well_controls_iget_type ( well_control , ctrl_index ) = = THP ) {
double aqua = 0.0 ;
double liquid = 0.0 ;
double vapour = 0.0 ;
const Opm : : PhaseUsage & pu = phase_usage_ ;
if ( active_ [ Water ] ) {
aqua = potentials [ pu . phase_pos [ Water ] ] ;
}
if ( active_ [ Oil ] ) {
liquid = potentials [ pu . phase_pos [ Oil ] ] ;
}
if ( active_ [ Gas ] ) {
vapour = potentials [ pu . phase_pos [ Gas ] ] ;
}
const int vfp = well_controls_iget_vfp ( well_control , ctrl_index ) ;
2017-04-11 08:02:36 -05:00
const double thp = well_controls_iget_target ( well_control , ctrl_index ) ;
const double alq = well_controls_iget_alq ( well_control , ctrl_index ) ;
2017-03-31 09:33:20 -05:00
// Calculating the BHP value based on THP
2017-04-11 08:02:36 -05:00
// TODO: check whether it is always correct to do calculation based on the depth of the first perforation.
2017-03-31 09:33:20 -05:00
const int first_perf = wells ( ) . well_connpos [ well_index ] ; //first perforation
const WellType & well_type = wells ( ) . type [ well_index ] ;
if ( well_type = = INJECTOR ) {
const double dp = wellhelpers : : computeHydrostaticCorrection (
wells ( ) , well_index , vfp_properties_ - > getInj ( ) - > getTable ( vfp ) - > getDatumDepth ( ) ,
wellPerforationDensities ( ) [ first_perf ] , gravity_ ) ;
const double bhp_calculated = vfp_properties_ - > getInj ( ) - > bhp ( vfp , aqua , liquid , vapour , thp ) - dp ;
// apply the strictest of the bhp controlls i.e. smallest bhp for injectors
if ( bhp_calculated < bhp ) {
bhp = bhp_calculated ;
}
}
else if ( well_type = = PRODUCER ) {
const double dp = wellhelpers : : computeHydrostaticCorrection (
wells ( ) , well_index , vfp_properties_ - > getProd ( ) - > getTable ( vfp ) - > getDatumDepth ( ) ,
wellPerforationDensities ( ) [ first_perf ] , gravity_ ) ;
const double bhp_calculated = vfp_properties_ - > getProd ( ) - > bhp ( vfp , aqua , liquid , vapour , thp , alq ) - dp ;
// apply the strictest of the bhp controlls i.e. largest bhp for producers
if ( bhp_calculated > bhp ) {
bhp = bhp_calculated ;
}
} else {
OPM_THROW ( std : : logic_error , " Expected PRODUCER or INJECTOR type of well " ) ;
}
}
}
2017-04-11 08:29:33 -05:00
// there should be always some available bhp/thp constraints there
2017-03-31 09:33:20 -05:00
if ( std : : isinf ( bhp ) | | std : : isnan ( bhp ) ) {
OPM_THROW ( std : : runtime_error , " Unvalid bhp value obtained during the potential calculation for well " < < wells ( ) . name [ well_index ] ) ;
}
converged = std : : abs ( old_bhp - bhp ) < bhp_tolerance ;
computeWellRatesWithBhp ( ebosSimulator , bhp , well_index , potentials ) ;
2017-04-11 08:29:33 -05:00
// checking whether the potentials have valid values
for ( const double value : potentials ) {
if ( std : : isinf ( value ) | | std : : isnan ( value ) ) {
OPM_THROW ( std : : runtime_error , " Unvalid potential value obtained during the potential calculation for well " < < wells ( ) . name [ well_index ] ) ;
}
}
2017-03-31 09:33:20 -05:00
if ( ! converged ) {
old_bhp = bhp ;
for ( int p = 0 ; p < np ; + + p ) {
2017-04-11 05:45:06 -05:00
// TODO: improve the interpolation, will it always be valid with the way below?
// TODO: finding better paramters, better iteration strategy for better convergence rate.
2017-04-11 08:02:36 -05:00
const double potential_update_damping_factor = 0.001 ;
potentials [ p ] = potential_update_damping_factor * potentials [ p ] + ( 1.0 - potential_update_damping_factor ) * old_potentials [ p ] ;
2017-03-31 09:33:20 -05:00
old_potentials [ p ] = potentials [ p ] ;
}
}
+ + iteration ;
}
if ( ! converged ) {
OPM_THROW ( std : : runtime_error , " Failed in getting converged for the potential calculation for well " < < wells ( ) . name [ well_index ] ) ;
}
return potentials ;
}
2017-05-09 01:21:51 -05:00
template < typename TypeTag >
double
StandardWellsDense < TypeTag > : :
wsolvent ( const int well_index ) const {
if ( ! has_solvent_ ) {
return 0.0 ;
}
// loop over all wells until we find the well with the matching name
for ( const auto & well : wells_ecl_ ) {
if ( well - > getStatus ( current_timeIdx_ ) = = WellCommon : : SHUT ) {
continue ;
}
WellInjectionProperties injection = well - > getInjectionProperties ( current_timeIdx_ ) ;
if ( injection . injectorType = = WellInjector : : GAS ) {
double solventFraction = well - > getSolventFraction ( current_timeIdx_ ) ;
// Look until we find the correct well
if ( well - > name ( ) = = wells ( ) . name [ well_index ] ) {
return solventFraction ;
}
}
}
// we didn't find it return 0;
2017-05-30 07:33:17 -05:00
assert ( false ) ;
2017-05-09 01:21:51 -05:00
return 0.0 ;
}
2017-02-13 09:45:06 -06:00
} // namespace Opm