2019-02-07 14:43:17 +01:00
/*
Copyright 2016 - 2019 SINTEF Digital , Mathematics & Cybernetics .
Copyright 2016 - 2018 Equinor ASA .
Copyright 2017 Dr . Blatt - HPC - Simulation - Software & Services
Copyright 2016 - 2018 Norce AS
2017-02-13 16:45:06 +01:00
2019-02-07 14:43:17 +01:00
This file is part of the Open Porous Media project ( OPM ) .
2017-02-13 16:45:06 +01:00
2019-02-07 14:43:17 +01:00
OPM is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
OPM is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with OPM . If not , see < http : //www.gnu.org/licenses/>.
*/
2019-05-07 13:06:02 +02:00
# include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
2019-11-25 10:34:50 +01:00
# include <opm/core/props/phaseUsageFromDeck.hpp>
2017-02-13 16:45:06 +01:00
2020-10-29 23:30:09 +01:00
# include <opm/parser/eclipse/Units/UnitSystem.hpp>
2020-09-22 14:12:15 +02:00
# include <algorithm>
2020-10-29 23:16:31 +01:00
# include <utility>
2020-10-01 18:27:57 +02:00
# include <fmt/format.h>
2020-07-20 21:38:30 +02:00
2017-02-13 16:45:06 +01:00
namespace Opm {
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2018-08-16 11:51:36 +02:00
BlackoilWellModel ( Simulator & ebosSimulator )
2017-11-08 13:57:36 +01:00
: ebosSimulator_ ( ebosSimulator )
2020-08-27 09:13:30 +02:00
, has_solvent_ ( getPropValue < TypeTag , Properties : : EnableSolvent > ( ) )
2020-10-10 16:20:25 +02:00
, has_zFraction_ ( getPropValue < TypeTag , Properties : : EnableExtbo > ( ) )
2020-08-27 09:13:30 +02:00
, has_polymer_ ( getPropValue < TypeTag , Properties : : EnablePolymer > ( ) )
2017-02-13 16:45:06 +01:00
{
2018-08-16 11:51:36 +02:00
terminal_output_ = false ;
if ( ebosSimulator . gridView ( ) . comm ( ) . rank ( ) = = 0 )
terminal_output_ = EWOMS_GET_PARAM ( TypeTag , bool , EnableTerminalOutput ) ;
2019-09-30 12:49:36 +02:00
// Create the guide rate container.
guideRate_ . reset ( new GuideRate ( ebosSimulator_ . vanguard ( ) . schedule ( ) ) ) ;
2019-10-23 09:09:45 +02:00
2020-10-05 20:02:13 +02:00
local_num_cells_ = ebosSimulator_ . gridView ( ) . size ( 0 ) ;
2020-10-05 12:43:55 +02:00
// Number of cells the global grid view
2020-10-05 20:02:13 +02:00
global_num_cells_ = ebosSimulator_ . vanguard ( ) . globalNumCells ( ) ;
2019-10-23 09:09:45 +02:00
// Set up cartesian mapping.
const auto & grid = ebosSimulator_ . vanguard ( ) . grid ( ) ;
const auto & cartDims = Opm : : UgGridHelpers : : cartDims ( grid ) ;
setupCartesianToCompressed_ ( Opm : : UgGridHelpers : : globalCell ( grid ) ,
cartDims [ 0 ] * cartDims [ 1 ] * cartDims [ 2 ] ) ;
2020-10-06 14:52:44 +02:00
auto & parallel_wells = ebosSimulator . vanguard ( ) . parallelWells ( ) ;
parallel_well_info_ . assign ( parallel_wells . begin ( ) , parallel_wells . end ( ) ) ;
2020-12-02 14:35:10 +01:00
const auto & pwell_info = parallel_well_info_ ;
std : : size_t numProcs = ebosSimulator . gridView ( ) . comm ( ) . size ( ) ;
is_shut_or_defunct_ = [ & pwell_info , numProcs ] ( const Well & well ) {
2020-09-22 14:12:15 +02:00
if ( well . getStatus ( ) = = Well : : Status : : SHUT )
return true ;
2020-12-02 14:35:10 +01:00
if ( numProcs = = 1u )
2020-09-22 14:12:15 +02:00
return false ;
std : : pair < std : : string , bool > value { well . name ( ) , true } ; // false indicate not active!
2020-12-02 14:35:10 +01:00
auto candidate = std : : lower_bound ( pwell_info . begin ( ) ,
pwell_info . end ( ) ,
2020-10-06 14:52:44 +02:00
value ) ;
2020-12-02 14:35:10 +01:00
return candidate = = pwell_info . end ( ) | | * candidate ! = value ;
2020-09-22 14:12:15 +02:00
} ;
2020-10-15 17:56:11 +02:00
alternative_well_rate_init_ = EWOMS_GET_PARAM ( TypeTag , bool , AlternativeWellRateInit ) ;
2018-08-16 11:51:36 +02:00
}
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
2019-04-03 17:26:57 +02:00
init ( )
2018-08-16 11:51:36 +02:00
{
2019-04-03 17:26:57 +02:00
const Opm : : EclipseState & eclState = ebosSimulator_ . vanguard ( ) . eclState ( ) ;
2018-08-16 11:51:36 +02:00
extractLegacyCellPvtRegionIndex_ ( ) ;
extractLegacyDepth_ ( ) ;
2017-11-08 13:57:36 +01:00
phase_usage_ = phaseUsageFromDeck ( eclState ) ;
gravity_ = ebosSimulator_ . problem ( ) . gravity ( ) [ 2 ] ;
2017-02-13 16:45:06 +01:00
2018-03-23 12:56:19 +01:00
initial_step_ = true ;
2018-08-16 11:51:36 +02:00
// add the eWoms auxiliary module for the wells to the list
ebosSimulator_ . model ( ) . addAuxiliaryModule ( this ) ;
2018-11-15 14:37:01 +01:00
2020-10-05 20:02:13 +02:00
is_cell_perforated_ . resize ( local_num_cells_ , false ) ;
2018-08-16 11:51:36 +02:00
}
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
addNeighbors ( std : : vector < NeighborSet > & neighbors ) const
{
if ( ! param_ . matrix_add_well_contributions_ ) {
return ;
}
// Create cartesian to compressed mapping
2019-11-13 23:16:11 +01:00
const auto & schedule_wells = schedule ( ) . getWellsatEnd ( ) ;
2018-08-16 11:51:36 +02:00
// initialize the additional cell connections introduced by wells.
2019-05-02 12:51:25 +02:00
for ( const auto & well : schedule_wells )
2018-08-16 11:51:36 +02:00
{
std : : vector < int > wellCells ;
// All possible connections of the well
2019-05-02 12:51:25 +02:00
const auto & connectionSet = well . getConnections ( ) ;
2018-08-16 11:51:36 +02:00
wellCells . reserve ( connectionSet . size ( ) ) ;
for ( size_t c = 0 ; c < connectionSet . size ( ) ; c + + )
{
const auto & connection = connectionSet . get ( c ) ;
2020-12-07 20:22:54 +01:00
int compressed_idx = cartesian_to_compressed_
. at ( connection . global_index ( ) ) ;
2018-08-16 11:51:36 +02:00
if ( compressed_idx > = 0 ) { // Ignore connections in inactive/remote cells.
wellCells . push_back ( compressed_idx ) ;
}
}
for ( int cellIdx : wellCells ) {
neighbors [ cellIdx ] . insert ( wellCells . begin ( ) ,
wellCells . end ( ) ) ;
}
}
}
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
2019-10-02 12:48:29 +02:00
linearize ( SparseMatrixAdapter & jacobian , GlobalEqVector & res )
2018-08-16 11:51:36 +02:00
{
if ( ! localWellsActive ( ) )
return ;
2019-02-25 10:51:30 +01:00
if ( ! param_ . matrix_add_well_contributions_ ) {
// if the well contributions are not supposed to be included explicitly in
// the matrix, we only apply the vector part of the Schur complement here.
for ( const auto & well : well_container_ ) {
// r = r - duneC_^T * invDuneD_ * resWell_
well - > apply ( res ) ;
}
return ;
}
2018-08-16 11:51:36 +02:00
for ( const auto & well : well_container_ ) {
2019-10-02 12:48:29 +02:00
well - > addWellContributions ( jacobian ) ;
2018-08-16 11:51:36 +02:00
// applying the well residual to reservoir residuals
// r = r - duneC_^T * invDuneD_ * resWell_
well - > apply ( res ) ;
}
2017-11-08 13:57:36 +01:00
}
2017-02-13 16:45:06 +01:00
2018-11-22 11:01:58 +01:00
/// Return true if any well has a THP constraint.
template < typename TypeTag >
bool
BlackoilWellModel < TypeTag > : :
hasTHPConstraints ( ) const
{
2019-01-14 22:14:01 +01:00
int local_result = false ;
2019-08-07 14:13:11 +02:00
const auto & summaryState = ebosSimulator_ . vanguard ( ) . summaryState ( ) ;
2018-11-22 11:01:58 +01:00
for ( const auto & well : well_container_ ) {
2019-08-07 14:13:11 +02:00
if ( well - > wellHasTHPConstraints ( summaryState ) ) {
2019-01-14 22:14:01 +01:00
local_result = true ;
2018-11-22 11:01:58 +01:00
}
}
2019-01-14 22:14:01 +01:00
return grid ( ) . comm ( ) . max ( local_result ) ;
2018-11-22 11:01:58 +01:00
}
2018-12-03 13:24:34 +01:00
/// Return true if the well was found and shut.
2018-11-22 16:24:17 +01:00
template < typename TypeTag >
2018-12-03 13:24:34 +01:00
bool
2018-11-22 16:24:17 +01:00
BlackoilWellModel < TypeTag > : :
2018-11-23 12:51:13 +01:00
forceShutWellByNameIfPredictionMode ( const std : : string & wellname ,
const double simulation_time )
2018-11-22 16:24:17 +01:00
{
// Only add the well to the closed list on the
// process that owns it.
2018-12-03 13:24:34 +01:00
int well_was_shut = 0 ;
2018-11-22 16:24:17 +01:00
for ( const auto & well : well_container_ ) {
2019-08-07 14:13:11 +02:00
if ( well - > name ( ) = = wellname & & ! well - > wellIsStopped ( ) ) {
2019-06-26 09:50:56 +02:00
if ( well - > underPredictionMode ( ) ) {
2019-06-25 08:13:37 +02:00
wellTestState_ . closeWell ( wellname , WellTestConfig : : Reason : : PHYSICAL , simulation_time ) ;
2018-12-03 13:24:34 +01:00
well_was_shut = 1 ;
2018-11-23 12:51:13 +01:00
}
2018-11-22 16:24:17 +01:00
break ;
}
}
2018-12-03 13:24:34 +01:00
// Communicate across processes if a well was shut.
well_was_shut = ebosSimulator_ . vanguard ( ) . grid ( ) . comm ( ) . max ( well_was_shut ) ;
2018-11-22 16:24:17 +01:00
// Only log a message on the output rank.
2018-12-03 13:24:34 +01:00
if ( terminal_output_ & & well_was_shut ) {
2018-11-22 16:24:17 +01:00
const std : : string msg = " Well " + wellname
+ " will be shut because it cannot get converged. " ;
OpmLog : : info ( msg ) ;
}
2018-12-03 13:24:34 +01:00
return ( well_was_shut = = 1 ) ;
2018-11-22 16:24:17 +01:00
}
2020-10-09 15:09:28 +02:00
template < typename TypeTag >
std : : vector < Well >
BlackoilWellModel < TypeTag > : :
2020-12-04 10:06:14 +01:00
getLocalNonshutWells ( const int timeStepIdx , int & globalNumWells ) const
2020-10-09 15:09:28 +02:00
{
auto w = schedule ( ) . getWells ( timeStepIdx ) ;
globalNumWells = w . size ( ) ;
w . erase ( std : : remove_if ( w . begin ( ) , w . end ( ) , is_shut_or_defunct_ ) , w . end ( ) ) ;
2020-12-04 10:06:14 +01:00
return w ;
}
template < typename TypeTag >
std : : vector < ParallelWellInfo * >
BlackoilWellModel < TypeTag > : : createLocalParallelWellInfo ( const std : : vector < Well > & wells )
{
std : : vector < ParallelWellInfo * > local_parallel_well_info ;
local_parallel_well_info . reserve ( wells . size ( ) ) ;
for ( const auto & well : wells )
2020-10-09 15:09:28 +02:00
{
auto wellPair = std : : make_pair ( well . name ( ) , true ) ;
auto pwell = std : : lower_bound ( parallel_well_info_ . begin ( ) ,
parallel_well_info_ . end ( ) ,
wellPair ) ;
assert ( pwell ! = parallel_well_info_ . end ( ) & &
* pwell = = wellPair ) ;
2020-12-04 10:06:14 +01:00
local_parallel_well_info . push_back ( & ( * pwell ) ) ;
2020-10-09 15:09:28 +02:00
}
2020-12-04 10:06:14 +01:00
return local_parallel_well_info ;
2020-10-09 15:09:28 +02:00
}
2018-11-22 16:24:17 +01:00
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-13 16:45:06 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2017-11-08 13:57:36 +01:00
beginReportStep ( const int timeStepIdx )
2017-02-13 16:45:06 +01:00
{
2019-02-07 14:43:17 +01:00
Opm : : DeferredLogger local_deferredLogger ;
2020-01-29 08:41:41 +01:00
report_step_starts_ = true ;
2019-02-07 14:43:17 +01:00
2018-02-01 16:27:42 +01:00
const Grid & grid = ebosSimulator_ . vanguard ( ) . grid ( ) ;
2019-05-29 07:44:23 +02:00
const auto & summaryState = ebosSimulator_ . vanguard ( ) . summaryState ( ) ;
2019-12-12 09:22:37 +01:00
int globalNumWells = 0 ;
2019-10-23 09:09:45 +02:00
// Make wells_ecl_ contain only this partition's non-shut wells.
2020-10-09 15:09:28 +02:00
wells_ecl_ = getLocalNonshutWells ( timeStepIdx , globalNumWells ) ;
2020-12-04 10:06:14 +01:00
local_parallel_well_info_ = createLocalParallelWellInfo ( wells_ecl_ ) ;
2020-10-09 13:38:33 +02:00
2020-10-20 00:16:52 +02:00
// The well state initialize bhp with the cell pressure in the top cell.
// We must therefore provide it with updated cell pressures
this - > initializeWellPerfData ( ) ;
this - > initializeWellState ( timeStepIdx , globalNumWells , summaryState ) ;
2017-11-16 11:42:34 +01:00
2017-11-08 13:57:36 +01:00
// Wells are active if they are active wells on at least
// one process.
wells_active_ = localWellsActive ( ) ? 1 : 0 ;
wells_active_ = grid . comm ( ) . max ( wells_active_ ) ;
// handling MS well related
2019-10-23 09:09:45 +02:00
if ( param_ . use_multisegment_well_ & & anyMSWellOpenLocal ( ) ) { // if we use MultisegmentWell model
well_state_ . initWellStateMSWell ( wells_ecl_ , phase_usage_ , & previous_well_state_ ) ;
2017-06-07 09:29:31 +02:00
}
2019-10-23 09:09:45 +02:00
const int nw = wells_ecl_ . size ( ) ;
2019-08-07 14:13:11 +02:00
for ( int w = 0 ; w < nw ; + + w ) {
2019-10-23 09:09:45 +02:00
const auto & well = wells_ecl_ [ w ] ;
2019-08-07 14:13:11 +02:00
const uint64_t effective_events_mask = ScheduleEvents : : WELL_STATUS_CHANGE
+ ScheduleEvents : : PRODUCTION_UPDATE
+ ScheduleEvents : : INJECTION_UPDATE
+ ScheduleEvents : : NEW_WELL ;
2019-02-07 14:43:17 +01:00
2021-01-10 21:46:45 +01:00
if ( ! schedule ( ) [ timeStepIdx ] . wellgroup_events ( ) . hasEvent ( well . name ( ) , effective_events_mask ) )
2019-08-07 14:13:11 +02:00
continue ;
if ( well . isProducer ( ) ) {
const auto controls = well . productionControls ( summaryState ) ;
well_state_ . currentProductionControls ( ) [ w ] = controls . cmode ;
}
else {
const auto controls = well . injectionControls ( summaryState ) ;
well_state_ . currentInjectionControls ( ) [ w ] = controls . cmode ;
}
2019-02-07 14:43:17 +01:00
}
2019-11-13 23:16:11 +01:00
const Group & fieldGroup = schedule ( ) . getGroup ( " FIELD " , timeStepIdx ) ;
2020-03-27 13:27:45 +01:00
WellGroupHelpers : : setCmodeGroup ( fieldGroup , schedule ( ) , summaryState , timeStepIdx , well_state_ ) ;
2019-08-07 14:13:11 +02:00
// Compute reservoir volumes for RESV controls.
rateConverter_ . reset ( new RateConverterType ( phase_usage_ ,
2020-10-05 20:02:13 +02:00
std : : vector < int > ( local_num_cells_ , 0 ) ) ) ;
2019-08-07 14:13:11 +02:00
rateConverter_ - > template defineState < ElementContext > ( ebosSimulator_ ) ;
2017-11-08 13:57:36 +01:00
2018-06-06 15:17:59 +02:00
// update VFP properties
2018-11-13 10:45:02 +01:00
vfp_properties_ . reset ( new VFPProperties < VFPInjProperties , VFPProdProperties > (
2018-06-06 15:17:59 +02:00
schedule ( ) . getVFPInjTables ( timeStepIdx ) ,
schedule ( ) . getVFPProdTables ( timeStepIdx ) ) ) ;
2020-10-29 23:30:09 +01:00
this - > initializeWellProdIndCalculators ( ) ;
2021-01-10 21:46:45 +01:00
if ( this - > schedule ( ) [ timeStepIdx ] . events ( ) . hasEvent ( ScheduleEvents : : Events : : WELL_PRODUCTIVITY_INDEX ) ) {
2020-10-29 23:30:09 +01:00
this - > runWellPIScaling ( timeStepIdx , local_deferredLogger ) ;
}
2019-08-07 14:13:11 +02:00
// update the previous well state. This is used to restart failed steps.
previous_well_state_ = well_state_ ;
2018-06-06 15:17:59 +02:00
}
// called at the beginning of a time step
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
2018-08-16 11:51:36 +02:00
beginTimeStep ( ) {
2019-02-07 14:43:17 +01:00
2020-05-15 11:21:32 +02:00
updatePerforationIntensiveQuantities ( ) ;
2019-02-07 14:43:17 +01:00
Opm : : DeferredLogger local_deferredLogger ;
2018-06-06 15:17:59 +02:00
well_state_ = previous_well_state_ ;
2020-09-30 10:04:39 +02:00
well_state_ . disableGliftOptimization ( ) ;
2018-08-16 11:51:36 +02:00
const int reportStepIdx = ebosSimulator_ . episodeIndex ( ) ;
const double simulationTime = ebosSimulator_ . time ( ) ;
2018-06-06 15:17:59 +02:00
2019-02-07 14:43:17 +01:00
int exception_thrown = 0 ;
try {
// test wells
wellTesting ( reportStepIdx , simulationTime , local_deferredLogger ) ;
2018-06-06 15:17:59 +02:00
2019-02-07 14:43:17 +01:00
// create the well container
2019-10-23 09:09:45 +02:00
well_container_ = createWellContainer ( reportStepIdx ) ;
2017-11-08 13:57:36 +01:00
2019-02-07 14:43:17 +01:00
// do the initialization for all the wells
// TODO: to see whether we can postpone of the intialization of the well containers to
// optimize the usage of the following several member variables
2020-11-27 07:57:55 +01:00
std : : vector < Scalar > B_avg ( numComponents ( ) , Scalar ( ) ) ;
computeAverageFormationFactor ( B_avg ) ;
2019-02-07 14:43:17 +01:00
for ( auto & well : well_container_ ) {
2020-11-27 07:57:55 +01:00
well - > init ( & phase_usage_ , depth_ , gravity_ , local_num_cells_ , B_avg ) ;
2019-02-07 14:43:17 +01:00
}
2017-08-07 11:35:59 +02:00
2019-02-07 14:43:17 +01:00
// update the updated cell flag
std : : fill ( is_cell_perforated_ . begin ( ) , is_cell_perforated_ . end ( ) , false ) ;
for ( auto & well : well_container_ ) {
well - > updatePerforatedCell ( is_cell_perforated_ ) ;
}
2018-11-15 14:37:01 +01:00
2019-02-07 14:43:17 +01:00
// calculate the efficiency factors for each well
2019-08-07 14:13:11 +02:00
calculateEfficiencyFactors ( reportStepIdx ) ;
2017-08-07 11:35:59 +02:00
2019-02-07 14:43:17 +01:00
if ( has_polymer_ )
{
const Grid & grid = ebosSimulator_ . vanguard ( ) . grid ( ) ;
2020-08-27 09:13:30 +02:00
if ( PolymerModule : : hasPlyshlog ( ) | | getPropValue < TypeTag , Properties : : EnablePolymerMW > ( ) ) {
2019-02-07 14:43:17 +01:00
computeRepRadiusPerfLength ( grid , local_deferredLogger ) ;
}
2017-11-08 13:57:36 +01:00
}
2019-02-07 14:43:17 +01:00
} catch ( std : : exception & e ) {
exception_thrown = 1 ;
2017-08-07 14:50:03 +02:00
}
2017-08-07 11:35:59 +02:00
2019-02-07 14:43:17 +01:00
logAndCheckForExceptionsAndThrow ( local_deferredLogger , exception_thrown , " beginTimeStep() failed. " , terminal_output_ ) ;
2018-04-07 21:41:34 +02:00
for ( auto & well : well_container_ ) {
well - > setVFPProperties ( vfp_properties_ . get ( ) ) ;
2019-09-30 12:49:36 +02:00
well - > setGuideRate ( guideRate_ . get ( ) ) ;
2018-04-07 21:41:34 +02:00
}
2017-08-07 11:35:59 +02:00
2018-10-31 15:32:50 +01:00
// Close completions due to economical reasons
2018-06-06 15:17:59 +02:00
for ( auto & well : well_container_ ) {
2018-07-03 15:13:30 +02:00
well - > closeCompletions ( wellTestState_ ) ;
2018-06-06 15:17:59 +02:00
}
2019-09-30 12:49:36 +02:00
// calculate the well potentials
try {
std : : vector < double > well_potentials ;
computeWellPotentials ( well_potentials , reportStepIdx , local_deferredLogger ) ;
} catch ( std : : runtime_error & e ) {
const std : : string msg = " A zero well potential is returned for output purposes. " ;
local_deferredLogger . warning ( " WELL_POTENTIAL_CALCULATION_FAILED " , msg ) ;
}
2020-10-15 17:56:11 +02:00
if ( alternative_well_rate_init_ ) {
2020-12-07 10:05:46 +01:00
// Update the well rates of well_state_, if only single-phase rates, to
// have proper multi-phase rates proportional to rates at bhp zero.
// This is done only for producers, as injectors will only have a single
// nonzero phase anyway.
2020-10-15 17:56:11 +02:00
for ( auto & well : well_container_ ) {
2020-12-07 10:05:46 +01:00
if ( well - > isProducer ( ) ) {
well - > updateWellStateRates ( ebosSimulator_ , well_state_ , local_deferredLogger ) ;
}
2020-10-15 17:56:11 +02:00
}
2020-05-15 11:21:32 +02:00
}
2019-09-30 12:49:36 +02:00
//compute well guideRates
2019-12-12 09:22:37 +01:00
const auto & comm = ebosSimulator_ . vanguard ( ) . grid ( ) . comm ( ) ;
2020-03-27 13:27:45 +01:00
WellGroupHelpers : : updateGuideRatesForWells ( schedule ( ) , phase_usage_ , reportStepIdx , simulationTime , well_state_ , comm , guideRate_ . get ( ) ) ;
2020-09-30 10:04:39 +02:00
logAndCheckForExceptionsAndThrow ( local_deferredLogger ,
exception_thrown , " beginTimeStep() failed. " , terminal_output_ ) ;
2017-08-10 15:27:05 +02:00
}
2020-09-30 10:04:39 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : : gliftDebug (
const std : : string & msg , Opm : : DeferredLogger & deferred_logger ) const
{
if ( this - > glift_debug ) {
2020-10-01 18:27:57 +02:00
const std : : string message = fmt : : format (
" GLIFT (DEBUG) : BlackoilWellModel : {} " , msg ) ;
deferred_logger . info ( message ) ;
2020-09-30 10:04:39 +02:00
}
}
2017-08-10 15:27:05 +02:00
2017-11-08 15:48:30 +01:00
template < typename TypeTag >
void
2019-02-07 14:43:17 +01:00
BlackoilWellModel < TypeTag > : : wellTesting ( const int timeStepIdx , const double simulationTime , Opm : : DeferredLogger & deferred_logger ) {
2018-06-06 15:17:59 +02:00
const auto & wtest_config = schedule ( ) . wtestConfig ( timeStepIdx ) ;
2019-02-03 08:13:11 +01:00
if ( wtest_config . size ( ) ! = 0 ) { // there is a WTEST request
2018-10-31 14:56:56 +01:00
2019-02-03 08:13:11 +01:00
// average B factors are required for the convergence checking of well equations
// Note: this must be done on all processes, even those with
// no wells needing testing, otherwise we will have locking.
std : : vector < Scalar > B_avg ( numComponents ( ) , Scalar ( ) ) ;
computeAverageFormationFactor ( B_avg ) ;
2018-06-06 15:17:59 +02:00
2019-07-02 16:33:12 +02:00
const auto & wellsForTesting = wellTestState_ . updateWells ( wtest_config , wells_ecl_ , simulationTime ) ;
2019-02-03 08:13:11 +01:00
for ( const auto & testWell : wellsForTesting ) {
const std : : string & well_name = testWell . first ;
2017-08-10 15:27:05 +02:00
2019-02-03 08:13:11 +01:00
// this is the well we will test
2019-02-07 14:43:17 +01:00
WellInterfacePtr well = createWellForWellTest ( well_name , timeStepIdx , deferred_logger ) ;
2018-06-06 15:17:59 +02:00
2019-02-03 08:13:11 +01:00
// some preparation before the well can be used
2020-11-27 07:57:55 +01:00
well - > init ( & phase_usage_ , depth_ , gravity_ , local_num_cells_ , B_avg ) ;
2019-11-13 23:16:11 +01:00
const Well & wellEcl = schedule ( ) . getWell ( well_name , timeStepIdx ) ;
2019-08-07 14:13:11 +02:00
double well_efficiency_factor = wellEcl . getEfficiencyFactor ( ) ;
2020-03-27 13:27:45 +01:00
WellGroupHelpers : : accumulateGroupEfficiencyFactor ( schedule ( ) . getGroup ( wellEcl . groupName ( ) , timeStepIdx ) , schedule ( ) , timeStepIdx , well_efficiency_factor ) ;
2019-02-03 08:13:11 +01:00
well - > setWellEfficiencyFactor ( well_efficiency_factor ) ;
well - > setVFPProperties ( vfp_properties_ . get ( ) ) ;
2019-09-30 12:49:36 +02:00
well - > setGuideRate ( guideRate_ . get ( ) ) ;
2017-11-08 15:48:30 +01:00
2019-02-03 08:13:11 +01:00
const WellTestConfig : : Reason testing_reason = testWell . second ;
2018-06-28 13:28:30 +02:00
2019-02-03 08:13:11 +01:00
well - > wellTesting ( ebosSimulator_ , B_avg , simulationTime , timeStepIdx ,
2019-02-07 14:43:17 +01:00
testing_reason , well_state_ , wellTestState_ , deferred_logger ) ;
2019-02-03 08:13:11 +01:00
}
2019-01-18 14:04:30 +01:00
}
2017-11-08 15:48:30 +01:00
}
// called at the end of a report step
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
endReportStep ( ) {
2020-11-27 18:26:59 +01:00
// Clear the communication data structures for above values.
for ( auto & & pinfo : local_parallel_well_info_ )
{
2020-12-08 19:02:33 +01:00
pinfo - > clearCommunicateAboveBelow ( ) ;
2020-11-27 18:26:59 +01:00
}
2017-11-08 15:48:30 +01:00
}
// called at the end of a report step
template < typename TypeTag >
2020-05-07 16:13:39 +02:00
const SimulatorReportSingle &
2017-11-08 15:48:30 +01:00
BlackoilWellModel < TypeTag > : :
lastReport ( ) const { return last_report_ ; }
// called at the end of a time step
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
2018-11-30 12:15:51 +01:00
timeStepSucceeded ( const double & simulationTime , const double dt ) {
2019-02-03 08:13:11 +01:00
2020-01-29 08:41:41 +01:00
// time step is finished and we are not any more at the beginning of an report step
report_step_starts_ = false ;
2020-04-24 15:25:38 +02:00
const int reportStepIdx = ebosSimulator_ . episodeIndex ( ) ;
2020-01-29 08:41:41 +01:00
2019-02-03 08:13:11 +01:00
Opm : : DeferredLogger local_deferredLogger ;
2018-02-14 13:34:35 +01:00
for ( const auto & well : well_container_ ) {
2020-08-27 09:13:30 +02:00
if ( getPropValue < TypeTag , Properties : : EnablePolymerMW > ( ) & & well - > isInjector ( ) ) {
2018-11-30 12:15:51 +01:00
well - > updateWaterThroughput ( dt , well_state_ ) ;
}
2018-02-14 13:34:35 +01:00
}
2018-06-28 13:47:10 +02:00
updateWellTestState ( simulationTime , wellTestState_ ) ;
2018-11-07 14:53:43 +01:00
2019-08-07 14:13:11 +02:00
// update the rate converter with current averages pressures etc in
rateConverter_ - > template defineState < ElementContext > ( ebosSimulator_ ) ;
// calculate the well potentials
2019-02-07 14:43:17 +01:00
try {
2018-11-07 14:53:43 +01:00
std : : vector < double > well_potentials ;
2020-04-24 15:25:38 +02:00
2019-08-07 14:13:11 +02:00
computeWellPotentials ( well_potentials , reportStepIdx , local_deferredLogger ) ;
2019-02-07 14:43:17 +01:00
} catch ( std : : runtime_error & e ) {
2018-11-07 14:53:43 +01:00
const std : : string msg = " A zero well potential is returned for output purposes. " ;
2019-02-03 08:13:11 +01:00
local_deferredLogger . warning ( " WELL_POTENTIAL_CALCULATION_FAILED " , msg ) ;
2018-11-07 14:53:43 +01:00
}
2020-05-04 15:56:34 +02:00
// check group sales limits at the end of the timestep
const Group & fieldGroup = schedule ( ) . getGroup ( " FIELD " , reportStepIdx ) ;
checkGconsaleLimits ( fieldGroup , well_state_ , local_deferredLogger ) ;
2020-10-09 13:38:33 +02:00
this - > calculateProductivityIndexValues ( local_deferredLogger ) ;
2017-11-08 15:48:30 +01:00
previous_well_state_ = well_state_ ;
2019-02-03 08:13:11 +01:00
Opm : : DeferredLogger global_deferredLogger = gatherDeferredLogger ( local_deferredLogger ) ;
if ( terminal_output_ ) {
global_deferredLogger . logMessages ( ) ;
}
2017-11-08 15:48:30 +01:00
}
2017-08-10 15:27:05 +02:00
2018-08-16 11:51:36 +02:00
template < typename TypeTag >
template < class Context >
void
BlackoilWellModel < TypeTag > : :
computeTotalRatesForDof ( RateVector & rate ,
const Context & context ,
unsigned spaceIdx ,
unsigned timeIdx ) const
{
rate = 0 ;
int elemIdx = context . globalSpaceIndex ( spaceIdx , timeIdx ) ;
2018-11-15 14:37:01 +01:00
if ( ! is_cell_perforated_ [ elemIdx ] )
return ;
2018-08-16 11:51:36 +02:00
for ( const auto & well : well_container_ )
well - > addCellRates ( rate , elemIdx ) ;
}
2018-11-14 13:18:48 +01:00
template < typename TypeTag >
typename BlackoilWellModel < TypeTag > : : WellInterfacePtr
BlackoilWellModel < TypeTag > : :
well ( const std : : string & wellName ) const
{
for ( const auto & well : well_container_ ) {
if ( well - > name ( ) = = wellName ) {
return well ;
}
}
OPM_THROW ( std : : invalid_argument , " The well with name " + wellName + " is not in the well Container " ) ;
return nullptr ;
}
2018-08-16 11:51:36 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
initFromRestartFile ( const RestartValue & restartValues )
{
2019-03-22 09:41:29 +01:00
// The restart step value is used to identify wells present at the given
// time step. Wells that are added at the same time step as RESTART is initiated
// will not be present in a restart file. Use the previous time step to retrieve
// wells that have information written to the restart file.
const int report_step = std : : max ( eclState ( ) . getInitConfig ( ) . getRestartStep ( ) - 1 , 0 ) ;
2019-05-29 07:44:23 +02:00
const auto & summaryState = ebosSimulator_ . vanguard ( ) . summaryState ( ) ;
2019-12-12 09:22:37 +01:00
int globalNumWells = 0 ;
2019-10-23 09:09:45 +02:00
// Make wells_ecl_ contain only this partition's non-shut wells.
2020-10-09 15:09:28 +02:00
wells_ecl_ = getLocalNonshutWells ( report_step , globalNumWells ) ;
2020-11-27 18:19:23 +01:00
local_parallel_well_info_ = createLocalParallelWellInfo ( wells_ecl_ ) ;
2018-08-16 11:51:36 +02:00
2020-10-09 13:38:33 +02:00
this - > initializeWellProdIndCalculators ( ) ;
2019-10-23 09:09:45 +02:00
initializeWellPerfData ( ) ;
2019-10-01 14:30:11 +02:00
2019-10-23 09:09:45 +02:00
const int nw = wells_ecl_ . size ( ) ;
2018-08-16 11:51:36 +02:00
if ( nw > 0 ) {
2018-11-12 14:57:13 +01:00
const auto phaseUsage = phaseUsageFromDeck ( eclState ( ) ) ;
const size_t numCells = Opm : : UgGridHelpers : : numCells ( grid ( ) ) ;
2019-10-23 09:09:45 +02:00
const bool handle_ms_well = ( param_ . use_multisegment_well_ & & anyMSWellOpenLocal ( ) ) ;
2020-11-27 18:22:23 +01:00
well_state_ . resize ( wells_ecl_ , local_parallel_well_info_ , schedule ( ) , handle_ms_well , numCells , phaseUsage , well_perf_data_ , summaryState , globalNumWells ) ; // Resize for restart step
2020-09-20 22:16:07 +02:00
wellsToState ( restartValues . wells , restartValues . grp_nwrk , phaseUsage , handle_ms_well , well_state_ ) ;
2018-08-16 11:51:36 +02:00
}
2019-08-07 14:13:11 +02:00
previous_well_state_ = well_state_ ;
2018-08-16 11:51:36 +02:00
initial_step_ = false ;
}
2018-11-17 23:36:31 +01:00
2020-10-09 13:38:33 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
initializeWellProdIndCalculators ( )
{
this - > prod_index_calc_ . clear ( ) ;
this - > prod_index_calc_ . reserve ( this - > wells_ecl_ . size ( ) ) ;
for ( const auto & well : this - > wells_ecl_ ) {
this - > prod_index_calc_ . emplace_back ( well ) ;
}
}
2019-10-23 09:09:45 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
initializeWellPerfData ( )
{
well_perf_data_ . resize ( wells_ecl_ . size ( ) ) ;
int well_index = 0 ;
for ( const auto & well : wells_ecl_ ) {
2020-11-27 18:26:59 +01:00
int completion_index = 0 ;
int completion_index_above = - 1 ; // -1 marks no above perf available
2019-10-23 09:09:45 +02:00
well_perf_data_ [ well_index ] . clear ( ) ;
well_perf_data_ [ well_index ] . reserve ( well . getConnections ( ) . size ( ) ) ;
2020-11-27 18:19:23 +01:00
CheckDistributedWellConnections checker ( well , * local_parallel_well_info_ [ well_index ] ) ;
2020-11-27 18:22:23 +01:00
bool hasFirstPerforation = false ;
bool firstOpenCompletion = true ;
2020-11-27 18:26:59 +01:00
auto & parallelWellInfo = * local_parallel_well_info_ [ well_index ] ;
parallelWellInfo . beginReset ( ) ;
2020-11-27 18:19:23 +01:00
2019-10-23 09:09:45 +02:00
for ( const auto & completion : well . getConnections ( ) ) {
2020-12-07 20:22:54 +01:00
const int active_index =
cartesian_to_compressed_ [ completion . global_index ( ) ] ;
2019-10-23 09:09:45 +02:00
if ( completion . state ( ) = = Connection : : State : : OPEN ) {
2020-11-27 18:19:23 +01:00
if ( active_index > = 0 ) {
2020-11-27 18:22:23 +01:00
if ( firstOpenCompletion )
{
hasFirstPerforation = true ;
}
2020-11-27 18:19:23 +01:00
checker . connectionFound ( completion_index ) ;
2019-10-23 09:09:45 +02:00
PerforationData pd ;
pd . cell_index = active_index ;
2020-03-31 16:43:21 +02:00
pd . connection_transmissibility_factor = completion . CF ( ) ;
2019-10-23 09:09:45 +02:00
pd . satnum_id = completion . satTableId ( ) ;
2020-11-12 14:04:27 +01:00
pd . ecl_index = completion_index ;
2019-10-23 09:09:45 +02:00
well_perf_data_ [ well_index ] . push_back ( pd ) ;
2020-11-27 18:26:59 +01:00
parallelWellInfo . pushBackEclIndex ( completion_index_above ,
completion_index ) ;
2019-10-23 09:09:45 +02:00
}
2020-11-27 18:22:23 +01:00
firstOpenCompletion = false ;
2019-10-23 09:09:45 +02:00
} else {
2020-11-27 18:19:23 +01:00
checker . connectionFound ( completion_index ) ;
2019-10-23 09:09:45 +02:00
if ( completion . state ( ) ! = Connection : : State : : SHUT ) {
OPM_THROW ( std : : runtime_error ,
" Completion state: " < < Connection : : State2String ( completion . state ( ) ) < < " not handled " ) ;
}
}
2020-11-27 18:26:59 +01:00
// Note: we rely on the connections being filtered! I.e. there are only connections
// to active cells in the global grid.
2020-11-12 14:04:27 +01:00
+ + completion_index ;
2020-11-27 18:26:59 +01:00
+ + completion_index_above ;
2019-10-23 09:09:45 +02:00
}
2020-11-27 18:26:59 +01:00
parallelWellInfo . endReset ( ) ;
2020-11-27 18:19:23 +01:00
checker . checkAllConnectionsFound ( ) ;
2020-11-27 18:26:59 +01:00
parallelWellInfo . communicateFirstPerforation ( hasFirstPerforation ) ;
2019-10-23 09:09:45 +02:00
+ + well_index ;
}
}
2020-10-20 00:16:52 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
initializeWellState ( const int timeStepIdx ,
const int globalNumWells ,
const SummaryState & summaryState )
{
std : : vector < double > cellPressures ( this - > local_num_cells_ , 0.0 ) ;
ElementContext elemCtx ( ebosSimulator_ ) ;
const auto & gridView = ebosSimulator_ . vanguard ( ) . gridView ( ) ;
const auto & elemEndIt = gridView . template end < /*codim=*/ 0 > ( ) ;
for ( auto elemIt = gridView . template begin < /*codim=*/ 0 > ( ) ;
elemIt ! = elemEndIt ;
+ + elemIt )
{
if ( elemIt - > partitionType ( ) ! = Dune : : InteriorEntity ) {
continue ;
}
elemCtx . updatePrimaryStencil ( * elemIt ) ;
elemCtx . updatePrimaryIntensiveQuantities ( /*timeIdx=*/ 0 ) ;
const auto & fs = elemCtx . intensiveQuantities ( /*spaceIdx=*/ 0 , /*timeIdx=*/ 0 ) . fluidState ( ) ;
// copy of get perfpressure in Standard well except for value
double & perf_pressure = cellPressures [ elemCtx . globalSpaceIndex ( /*spaceIdx=*/ 0 , /*timeIdx=*/ 0 ) ] ;
if ( Indices : : oilEnabled ) {
perf_pressure = fs . pressure ( FluidSystem : : oilPhaseIdx ) . value ( ) ;
} else if ( Indices : : waterEnabled ) {
perf_pressure = fs . pressure ( FluidSystem : : waterPhaseIdx ) . value ( ) ;
} else {
perf_pressure = fs . pressure ( FluidSystem : : gasPhaseIdx ) . value ( ) ;
}
}
2020-11-27 18:22:23 +01:00
well_state_ . init ( cellPressures , schedule ( ) , wells_ecl_ , local_parallel_well_info_ , timeStepIdx ,
2020-10-20 00:16:52 +02:00
& previous_well_state_ , phase_usage_ , well_perf_data_ ,
summaryState , globalNumWells ) ;
}
2017-06-15 17:19:49 +02:00
template < typename TypeTag >
2017-09-26 10:52:05 +02:00
std : : vector < typename BlackoilWellModel < TypeTag > : : WellInterfacePtr >
BlackoilWellModel < TypeTag > : :
2019-10-23 09:09:45 +02:00
createWellContainer ( const int time_step )
2017-06-15 17:19:49 +02:00
{
2017-08-21 10:23:42 +02:00
std : : vector < WellInterfacePtr > well_container ;
2019-12-13 11:08:36 +01:00
Opm : : DeferredLogger local_deferredLogger ;
2019-11-01 15:11:21 +01:00
const int nw = numLocalWells ( ) ;
2017-06-15 17:19:49 +02:00
2017-08-21 10:23:42 +02:00
if ( nw > 0 ) {
well_container . reserve ( nw ) ;
2017-06-15 17:19:49 +02:00
for ( int w = 0 ; w < nw ; + + w ) {
2019-10-23 09:09:45 +02:00
const Well & well_ecl = wells_ecl_ [ w ] ;
const std : : string & well_name = well_ecl . name ( ) ;
2017-06-15 17:19:49 +02:00
2019-07-31 16:15:41 +02:00
// A new WCON keywords can re-open a well that was closed/shut due to Physical limit
if ( wellTestState_ . hasWellClosed ( well_name ) ) {
// TODO: more checking here, to make sure this standard more specific and complete
// maybe there is some WCON keywords will not open the well
if ( well_state_ . effectiveEventsOccurred ( w ) ) {
if ( wellTestState_ . lastTestTime ( well_name ) = = ebosSimulator_ . time ( ) ) {
// The well was shut this timestep, we are most likely retrying
// a timestep without the well in question, after it caused
// repeated timestep cuts. It should therefore not be opened,
// even if it was new or received new targets this report step.
well_state_ . setEffectiveEventsOccurred ( w , false ) ;
} else {
wellTestState_ . openWell ( well_name ) ;
2018-12-14 10:04:59 +01:00
}
2018-11-17 23:36:31 +01:00
}
2019-07-31 16:15:41 +02:00
}
2018-11-17 23:30:27 +01:00
2019-07-31 16:15:41 +02:00
// TODO: should we do this for all kinds of closing reasons?
// something like wellTestState_.hasWell(well_name)?
2019-08-07 14:13:11 +02:00
bool wellIsStopped = false ;
2019-07-31 16:15:41 +02:00
if ( wellTestState_ . hasWellClosed ( well_name , WellTestConfig : : Reason : : ECONOMIC ) | |
wellTestState_ . hasWellClosed ( well_name , WellTestConfig : : Reason : : PHYSICAL ) ) {
if ( well_ecl . getAutomaticShutIn ( ) ) {
// shut wells are not added to the well container
2019-12-03 16:29:20 +01:00
well_state_ . shutWell ( w ) ;
2019-07-31 16:15:41 +02:00
continue ;
} else {
2019-08-07 14:13:11 +02:00
// stopped wells are added to the container but marked as stopped
2020-12-10 09:54:37 +01:00
well_state_ . stopWell ( w ) ;
2019-08-07 14:13:11 +02:00
wellIsStopped = true ;
2018-06-06 15:17:59 +02:00
}
}
2019-12-06 13:12:36 +01:00
// Due to ACTIONX the well might have been closed 'behind our back'.
const auto well_status = schedule ( ) . getWell ( well_name , time_step ) . getStatus ( ) ;
if ( well_status = = Well : : Status : : SHUT ) {
well_state_ . shutWell ( w ) ;
continue ;
}
2019-12-13 11:08:36 +01:00
// If a production well disallows crossflow and its
// (prediction type) rate control is zero, then it is effectively shut.
if ( ! well_ecl . getAllowCrossFlow ( ) & & well_ecl . isProducer ( ) & & well_ecl . predictionMode ( ) ) {
const auto & summaryState = ebosSimulator_ . vanguard ( ) . summaryState ( ) ;
auto prod_controls = well_ecl . productionControls ( summaryState ) ;
bool zero_rate_control = false ;
switch ( prod_controls . cmode ) {
case Well : : ProducerCMode : : ORAT :
zero_rate_control = ( prod_controls . oil_rate = = 0.0 ) ;
break ;
case Well : : ProducerCMode : : WRAT :
zero_rate_control = ( prod_controls . water_rate = = 0.0 ) ;
break ;
case Well : : ProducerCMode : : GRAT :
zero_rate_control = ( prod_controls . gas_rate = = 0.0 ) ;
break ;
case Well : : ProducerCMode : : LRAT :
zero_rate_control = ( prod_controls . liquid_rate = = 0.0 ) ;
break ;
case Well : : ProducerCMode : : RESV :
zero_rate_control = ( prod_controls . resv_rate = = 0.0 ) ;
break ;
default :
// Might still have zero rate controls, but is pressure controlled.
zero_rate_control = false ;
}
if ( zero_rate_control ) {
// Treat as shut, do not add to container.
local_deferredLogger . info ( " Well shut due to zero rate control and disallowing crossflow: " + well_ecl . name ( ) ) ;
well_state_ . shutWell ( w ) ;
continue ;
}
}
2019-12-06 13:12:36 +01:00
if ( well_status = = Well : : Status : : STOP ) {
2020-12-10 09:54:37 +01:00
well_state_ . stopWell ( w ) ;
2019-12-06 13:12:36 +01:00
wellIsStopped = true ;
}
2020-12-01 18:04:46 +01:00
well_container . emplace_back ( this - > createWellPointer ( w , time_step ) ) ;
2019-08-07 14:13:11 +02:00
if ( wellIsStopped )
well_container . back ( ) - > stopWell ( ) ;
2017-06-15 17:19:49 +02:00
}
}
2019-02-07 14:43:17 +01:00
2019-12-13 11:08:36 +01:00
// Collect log messages and print.
Opm : : DeferredLogger global_deferredLogger = gatherDeferredLogger ( local_deferredLogger ) ;
if ( terminal_output_ ) {
global_deferredLogger . logMessages ( ) ;
}
2017-08-21 10:23:42 +02:00
return well_container ;
2017-06-15 17:19:49 +02:00
}
2020-12-01 18:04:46 +01:00
template < typename TypeTag >
typename BlackoilWellModel < TypeTag > : : WellInterfacePtr
BlackoilWellModel < TypeTag > : :
createWellPointer ( const int wellID , const int time_step ) const
{
const auto is_multiseg = this - > wells_ecl_ [ wellID ] . isMultiSegment ( ) ;
if ( ! ( this - > param_ . use_multisegment_well_ & & is_multiseg ) ) {
return this - > template createTypedWellPointer < StandardWell < TypeTag > > ( wellID , time_step ) ;
}
else {
return this - > template createTypedWellPointer < MultisegmentWell < TypeTag > > ( wellID , time_step ) ;
}
}
template < typename TypeTag >
template < typename WellType >
std : : unique_ptr < WellType >
BlackoilWellModel < TypeTag > : :
createTypedWellPointer ( const int wellID , const int time_step ) const
{
// Use the pvtRegionIdx from the top cell
const auto & perf_data = this - > well_perf_data_ [ wellID ] ;
2020-11-27 18:22:23 +01:00
// Cater for case where local part might have no perforations.
2020-12-07 20:24:03 +01:00
const int pvtreg = perf_data . empty ( ) ?
0 : pvt_region_idx_ [ perf_data . front ( ) . cell_index ] ;
2020-11-27 18:22:23 +01:00
const auto & parallel_well_info = * local_parallel_well_info_ [ wellID ] ;
auto global_pvtreg = parallel_well_info . broadcastFirstPerforationValue ( pvtreg ) ;
2020-12-01 18:04:46 +01:00
return std : : make_unique < WellType > ( this - > wells_ecl_ [ wellID ] ,
2020-11-27 18:22:23 +01:00
parallel_well_info ,
2020-12-01 18:04:46 +01:00
time_step ,
this - > param_ ,
* this - > rateConverter_ ,
2020-11-27 18:22:23 +01:00
global_pvtreg ,
2020-12-01 18:04:46 +01:00
this - > numComponents ( ) ,
this - > numPhases ( ) ,
wellID ,
this - > well_state_ . firstPerfIndex ( ) [ wellID ] ,
perf_data ) ;
}
2018-10-31 14:56:56 +01:00
template < typename TypeTag >
typename BlackoilWellModel < TypeTag > : : WellInterfacePtr
BlackoilWellModel < TypeTag > : :
createWellForWellTest ( const std : : string & well_name ,
2019-02-07 14:43:17 +01:00
const int report_step ,
Opm : : DeferredLogger & deferred_logger ) const
2018-10-31 14:56:56 +01:00
{
// Finding the location of the well in wells_ecl
const int nw_wells_ecl = wells_ecl_ . size ( ) ;
int index_well_ecl = 0 ;
for ( ; index_well_ecl < nw_wells_ecl ; + + index_well_ecl ) {
2019-05-02 12:51:25 +02:00
if ( well_name = = wells_ecl_ [ index_well_ecl ] . name ( ) ) {
2018-10-31 14:56:56 +01:00
break ;
}
}
// It should be able to find in wells_ecl.
if ( index_well_ecl = = nw_wells_ecl ) {
2019-02-07 14:43:17 +01:00
OPM_DEFLOG_THROW ( std : : logic_error , " Could not find well " < < well_name < < " in wells_ecl " , deferred_logger ) ;
2018-10-31 14:56:56 +01:00
}
2020-12-01 18:04:46 +01:00
return this - > createWellPointer ( index_well_ecl , report_step ) ;
2018-10-31 14:56:56 +01:00
}
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-11-08 13:57:36 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2017-11-08 13:57:36 +01:00
assemble ( const int iterationIdx ,
2018-06-29 09:12:14 +02:00
const double dt )
2017-02-13 16:45:06 +01:00
{
2017-11-08 13:57:36 +01:00
2020-09-30 10:04:39 +02:00
Opm : : DeferredLogger local_deferredLogger ;
if ( this - > glift_debug ) {
2020-10-01 18:27:57 +02:00
const std : : string msg = fmt : : format (
" assemble() : iteration {} " , iterationIdx ) ;
gliftDebug ( msg , local_deferredLogger ) ;
2020-09-30 10:04:39 +02:00
}
2020-05-07 16:13:39 +02:00
last_report_ = SimulatorReportSingle ( ) ;
2020-09-07 15:05:02 +02:00
Dune : : Timer perfTimer ;
perfTimer . start ( ) ;
2017-11-08 13:57:36 +01:00
2017-11-08 15:48:30 +01:00
if ( ! wellsActive ( ) ) {
return ;
}
2019-02-03 08:13:11 +01:00
2017-11-08 13:57:36 +01:00
updatePerforationIntensiveQuantities ( ) ;
2017-02-13 16:45:06 +01:00
2019-02-07 14:43:17 +01:00
int exception_thrown = 0 ;
try {
if ( iterationIdx = = 0 ) {
calculateExplicitQuantities ( local_deferredLogger ) ;
2019-05-24 16:45:27 +02:00
prepareTimeStep ( local_deferredLogger ) ;
2019-02-07 14:43:17 +01:00
}
2020-02-10 15:16:09 +01:00
updateWellControls ( local_deferredLogger , /* check group controls */ true ) ;
2019-11-06 16:16:19 +01:00
2019-02-07 14:43:17 +01:00
// Set the well primary variables based on the value of well solutions
initPrimaryVariablesEvaluation ( ) ;
2017-02-13 16:45:06 +01:00
2019-05-24 16:45:27 +02:00
std : : vector < Scalar > B_avg ( numComponents ( ) , Scalar ( ) ) ;
computeAverageFormationFactor ( B_avg ) ;
2019-02-07 14:43:17 +01:00
if ( param_ . solve_welleq_initially_ & & iterationIdx = = 0 ) {
// solve the well equations as a pre-processing step
2019-02-27 14:47:31 +01:00
last_report_ = solveWellEq ( B_avg , dt , local_deferredLogger ) ;
2018-06-06 15:17:59 +02:00
2019-02-07 14:43:17 +01:00
if ( initial_step_ ) {
// update the explicit quantities to get the initial fluid distribution in the well correct.
calculateExplicitQuantities ( local_deferredLogger ) ;
2019-05-24 16:45:27 +02:00
prepareTimeStep ( local_deferredLogger ) ;
2019-02-27 14:47:31 +01:00
last_report_ = solveWellEq ( B_avg , dt , local_deferredLogger ) ;
2019-02-07 14:43:17 +01:00
initial_step_ = false ;
}
// TODO: should we update the explicit related here again, or even prepareTimeStep().
// basically, this is a more updated state from the solveWellEq based on fixed
// reservoir state, will tihs be a better place to inialize the explict information?
2018-03-23 12:56:19 +01:00
}
2020-09-30 10:04:39 +02:00
gliftDebug ( " assemble() : running assembleWellEq().. " , local_deferredLogger ) ;
well_state_ . enableGliftOptimization ( ) ;
2019-04-23 13:30:12 +02:00
assembleWellEq ( B_avg , dt , local_deferredLogger ) ;
2020-09-30 10:04:39 +02:00
well_state_ . disableGliftOptimization ( ) ;
2019-04-12 10:43:30 +02:00
2019-02-07 14:43:17 +01:00
} catch ( std : : exception & e ) {
exception_thrown = 1 ;
2019-02-03 08:13:11 +01:00
}
2019-02-07 14:43:17 +01:00
logAndCheckForExceptionsAndThrow ( local_deferredLogger , exception_thrown , " assemble() failed. " , terminal_output_ ) ;
2017-02-13 16:45:06 +01:00
2017-11-08 13:57:36 +01:00
last_report_ . converged = true ;
2020-09-07 15:05:02 +02:00
last_report_ . assemble_time_well + = perfTimer . stop ( ) ;
2017-02-13 16:45:06 +01:00
}
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-13 16:45:06 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2019-03-04 11:24:52 +01:00
assembleWellEq ( const std : : vector < Scalar > & B_avg , const double dt , Opm : : DeferredLogger & deferred_logger )
2017-02-13 16:45:06 +01:00
{
2018-06-06 15:17:59 +02:00
for ( auto & well : well_container_ ) {
2020-09-30 10:04:39 +02:00
well - > maybeDoGasLiftOptimization (
well_state_ , ebosSimulator_ , deferred_logger ) ;
2019-03-04 11:24:52 +01:00
well - > assembleWellEq ( ebosSimulator_ , B_avg , dt , well_state_ , deferred_logger ) ;
2017-07-21 14:21:17 +02:00
}
2017-02-13 17:07:34 +01:00
}
2018-11-05 15:54:48 +01:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
apply ( BVector & r ) const
{
if ( ! localWellsActive ( ) ) {
return ;
}
for ( auto & well : well_container_ ) {
well - > apply ( r ) ;
}
}
2017-02-13 17:07:34 +01:00
2017-07-21 14:21:17 +02:00
// Ax = A x - C D^-1 B x
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-13 17:07:34 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2017-06-07 14:23:43 +02:00
apply ( const BVector & x , BVector & Ax ) const
2017-02-13 17:07:34 +01:00
{
2017-07-21 15:30:34 +02:00
// TODO: do we still need localWellsActive()?
2018-02-26 15:47:25 +01:00
if ( ! localWellsActive ( ) ) {
2017-02-13 17:07:34 +01:00
return ;
}
2017-07-21 14:21:17 +02:00
for ( auto & well : well_container_ ) {
2018-03-02 20:47:04 +01:00
well - > apply ( x , Ax ) ;
2017-07-21 14:21:17 +02:00
}
2017-02-13 17:07:34 +01:00
}
2020-06-25 18:44:49 +02:00
# if HAVE_CUDA || HAVE_OPENCL
2020-03-13 14:21:59 +01:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
getWellContributions ( WellContributions & wellContribs ) const
{
2020-05-15 16:00:09 +02:00
// prepare for StandardWells
2020-03-19 11:08:11 +01:00
wellContribs . setBlockSize ( StandardWell < TypeTag > : : numEq , StandardWell < TypeTag > : : numStaticWellEq ) ;
2020-09-24 16:34:46 -03:00
2020-03-13 14:21:59 +01:00
for ( unsigned int i = 0 ; i < well_container_ . size ( ) ; i + + ) {
auto & well = well_container_ [ i ] ;
std : : shared_ptr < StandardWell < TypeTag > > derived = std : : dynamic_pointer_cast < StandardWell < TypeTag > > ( well ) ;
2020-05-15 16:00:09 +02:00
if ( derived ) {
unsigned int numBlocks ;
derived - > getNumBlocks ( numBlocks ) ;
wellContribs . addNumBlocks ( numBlocks ) ;
}
2020-03-13 14:21:59 +01:00
}
2020-05-15 16:00:09 +02:00
// allocate memory for data from StandardWells
2020-03-18 15:08:48 +01:00
wellContribs . alloc ( ) ;
2020-05-15 16:00:09 +02:00
2020-03-13 14:21:59 +01:00
for ( unsigned int i = 0 ; i < well_container_ . size ( ) ; i + + ) {
auto & well = well_container_ [ i ] ;
2020-05-15 16:00:09 +02:00
// maybe WellInterface could implement addWellContribution()
auto derived_std = std : : dynamic_pointer_cast < StandardWell < TypeTag > > ( well ) ;
if ( derived_std ) {
derived_std - > addWellContribution ( wellContribs ) ;
2020-03-19 16:06:49 +01:00
} else {
2020-05-15 16:00:09 +02:00
auto derived_ms = std : : dynamic_pointer_cast < MultisegmentWell < TypeTag > > ( well ) ;
if ( derived_ms ) {
derived_ms - > addWellContribution ( wellContribs ) ;
} else {
OpmLog : : warning ( " Warning unknown type of well " ) ;
}
2020-03-19 16:06:49 +01:00
}
2020-03-13 14:21:59 +01:00
}
}
2020-03-18 17:48:28 +01:00
# endif
2017-02-13 17:07:34 +01:00
2017-07-21 14:21:17 +02:00
// Ax = Ax - alpha * C D^-1 B x
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-13 17:07:34 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2017-06-07 14:23:43 +02:00
applyScaleAdd ( const Scalar alpha , const BVector & x , BVector & Ax ) const
2017-02-13 17:07:34 +01:00
{
2018-02-26 15:47:25 +01:00
if ( ! localWellsActive ( ) ) {
2017-02-13 17:07:34 +01:00
return ;
}
if ( scaleAddRes_ . size ( ) ! = Ax . size ( ) ) {
scaleAddRes_ . resize ( Ax . size ( ) ) ;
}
scaleAddRes_ = 0.0 ;
2017-07-21 14:21:17 +02:00
// scaleAddRes_ = - C D^-1 B x
2017-02-13 17:07:34 +01:00
apply ( x , scaleAddRes_ ) ;
2017-07-21 14:21:17 +02:00
// Ax = Ax + alpha * scaleAddRes_
2017-02-13 17:07:34 +01:00
Ax . axpy ( alpha , scaleAddRes_ ) ;
}
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-13 17:07:34 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2017-11-08 13:57:36 +01:00
recoverWellSolutionAndUpdateWellState ( const BVector & x )
2017-02-13 17:07:34 +01:00
{
2019-02-07 14:43:17 +01:00
Opm : : DeferredLogger local_deferredLogger ;
2017-11-08 13:57:36 +01:00
2019-02-07 14:43:17 +01:00
int exception_thrown = 0 ;
try {
if ( localWellsActive ( ) ) {
for ( auto & well : well_container_ ) {
well - > recoverWellSolutionAndUpdateWellState ( x , well_state_ , local_deferredLogger ) ;
}
}
} catch ( std : : exception & e ) {
exception_thrown = 1 ;
2017-02-13 17:07:34 +01:00
}
2019-02-07 14:43:17 +01:00
logAndCheckForExceptionsAndThrow ( local_deferredLogger , exception_thrown , " recoverWellSolutionAndUpdateWellState() failed. " , terminal_output_ ) ;
2017-02-13 17:07:34 +01:00
}
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-13 17:07:34 +01:00
bool
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2017-02-13 17:07:34 +01:00
wellsActive ( ) const
{
return wells_active_ ;
}
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-13 17:07:34 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2017-02-13 17:07:34 +01:00
setWellsActive ( const bool wells_active )
{
wells_active_ = wells_active ;
}
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-13 17:07:34 +01:00
bool
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2017-02-13 17:07:34 +01:00
localWellsActive ( ) const
{
2019-11-01 15:11:21 +01:00
return numLocalWells ( ) > 0 ;
2017-02-13 17:07:34 +01:00
}
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-13 17:07:34 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2017-08-21 15:41:25 +02:00
initPrimaryVariablesEvaluation ( ) const
2017-02-13 17:07:34 +01:00
{
2017-07-25 10:15:27 +02:00
for ( auto & well : well_container_ ) {
2017-08-21 15:41:25 +02:00
well - > initPrimaryVariablesEvaluation ( ) ;
2017-06-19 12:43:08 +02:00
}
2017-02-13 17:07:34 +01:00
}
2017-02-14 15:06:57 +01:00
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2020-05-07 16:13:39 +02:00
SimulatorReportSingle
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2019-03-04 11:24:52 +01:00
solveWellEq ( const std : : vector < Scalar > & B_avg , const double dt , Opm : : DeferredLogger & deferred_logger )
2017-02-14 11:34:03 +01:00
{
2017-11-08 13:57:36 +01:00
WellState well_state0 = well_state_ ;
2017-02-14 11:34:03 +01:00
2017-10-04 13:09:49 +02:00
const int max_iter = param_ . max_welleq_iter_ ;
2017-02-14 11:34:03 +01:00
int it = 0 ;
bool converged ;
2019-02-07 14:43:17 +01:00
int exception_thrown = 0 ;
2017-02-14 11:34:03 +01:00
do {
2019-02-07 14:43:17 +01:00
try {
2019-03-04 11:24:52 +01:00
assembleWellEq ( B_avg , dt , deferred_logger ) ;
2019-02-07 14:43:17 +01:00
} catch ( std : : exception & e ) {
exception_thrown = 1 ;
}
// We need to check on all processes, as getWellConvergence() below communicates on all processes.
logAndCheckForExceptionsAndThrow ( deferred_logger , exception_thrown , " solveWellEq() failed. " , terminal_output_ ) ;
2017-07-21 16:01:32 +02:00
2018-11-13 14:02:55 +01:00
const auto report = getWellConvergence ( B_avg ) ;
converged = report . converged ( ) ;
2017-02-14 11:34:03 +01:00
if ( converged ) {
break ;
}
2019-02-07 14:43:17 +01:00
try {
if ( localWellsActive ( ) )
{
for ( auto & well : well_container_ ) {
well - > solveEqAndUpdateWellState ( well_state_ , deferred_logger ) ;
}
2017-06-28 13:46:01 +02:00
}
2019-02-07 14:43:17 +01:00
// updateWellControls uses communication
// Therefore the following is executed if there
// are active wells anywhere in the global domain.
if ( wellsActive ( ) )
{
2020-02-10 15:16:09 +01:00
updateWellControls ( deferred_logger , /*don't switch group controls*/ false ) ;
2019-02-07 14:43:17 +01:00
initPrimaryVariablesEvaluation ( ) ;
}
} catch ( std : : exception & e ) {
exception_thrown = 1 ;
2017-04-12 17:37:34 +02:00
}
2019-02-07 14:43:17 +01:00
logAndCheckForExceptionsAndThrow ( deferred_logger , exception_thrown , " solveWellEq() failed. " , terminal_output_ ) ;
+ + it ;
2017-10-04 13:09:49 +02:00
} while ( it < max_iter ) ;
2017-02-14 11:34:03 +01:00
2019-02-07 14:43:17 +01:00
try {
if ( converged ) {
if ( terminal_output_ ) {
deferred_logger . debug ( " Well equation solution gets converged with " + std : : to_string ( it ) + " iterations " ) ;
}
} else {
if ( terminal_output_ ) {
deferred_logger . debug ( " Well equation solution failed in getting converged with " + std : : to_string ( it ) + " iterations " ) ;
}
well_state_ = well_state0 ;
updatePrimaryVariables ( deferred_logger ) ;
2017-03-08 14:02:00 +01:00
}
2019-02-07 14:43:17 +01:00
} catch ( std : : exception & e ) {
exception_thrown = 1 ;
2017-02-14 11:34:03 +01:00
}
2019-02-07 14:43:17 +01:00
logAndCheckForExceptionsAndThrow ( deferred_logger , exception_thrown , " solveWellEq() failed. " , terminal_output_ ) ;
2020-05-07 16:13:39 +02:00
SimulatorReportSingle report ;
2017-02-14 11:34:03 +01:00
report . converged = converged ;
report . total_well_iterations = it ;
return report ;
}
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2018-11-13 14:02:55 +01:00
ConvergenceReport
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2020-04-01 09:55:29 +02:00
getWellConvergence ( const std : : vector < Scalar > & B_avg , bool checkGroupConvergence ) const
2017-02-14 11:34:03 +01:00
{
2019-02-07 14:43:17 +01:00
Opm : : DeferredLogger local_deferredLogger ;
2018-11-13 14:02:55 +01:00
// Get global (from all processes) convergence report.
ConvergenceReport local_report ;
2017-06-28 11:15:04 +02:00
for ( const auto & well : well_container_ ) {
2018-11-17 23:14:51 +01:00
if ( well - > isOperable ( ) ) {
2019-08-07 14:13:11 +02:00
local_report + = well - > getWellConvergence ( well_state_ , B_avg , local_deferredLogger ) ;
2018-11-17 23:14:51 +01:00
}
2017-08-22 14:49:30 +02:00
}
2019-02-07 14:43:17 +01:00
Opm : : DeferredLogger global_deferredLogger = gatherDeferredLogger ( local_deferredLogger ) ;
if ( terminal_output_ ) {
global_deferredLogger . logMessages ( ) ;
}
2018-11-13 14:02:55 +01:00
ConvergenceReport report = gatherConvergenceReport ( local_report ) ;
2017-08-22 14:49:30 +02:00
2018-11-13 14:02:55 +01:00
// Log debug messages for NaN or too large residuals.
2019-02-03 08:13:11 +01:00
if ( terminal_output_ ) {
for ( const auto & f : report . wellFailures ( ) ) {
if ( f . severity ( ) = = ConvergenceReport : : Severity : : NotANumber ) {
OpmLog : : debug ( " NaN residual found with phase " + std : : to_string ( f . phase ( ) ) + " for well " + f . wellName ( ) ) ;
} else if ( f . severity ( ) = = ConvergenceReport : : Severity : : TooLarge ) {
OpmLog : : debug ( " Too large residual found with phase " + std : : to_string ( f . phase ( ) ) + " for well " + f . wellName ( ) ) ;
}
2017-03-24 12:12:06 +01:00
}
}
2020-04-08 10:41:20 +02:00
2020-04-01 09:55:29 +02:00
if ( checkGroupConvergence ) {
const int reportStepIdx = ebosSimulator_ . episodeIndex ( ) ;
const Group & fieldGroup = schedule ( ) . getGroup ( " FIELD " , reportStepIdx ) ;
bool violated = checkGroupConstraints ( fieldGroup , global_deferredLogger ) ;
report . setGroupConverged ( ! violated ) ;
}
2018-11-13 14:02:55 +01:00
return report ;
2017-02-14 11:34:03 +01:00
}
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-14 11:34:03 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2019-02-07 14:43:17 +01:00
calculateExplicitQuantities ( Opm : : DeferredLogger & deferred_logger ) const
2017-02-14 11:34:03 +01:00
{
2018-11-17 23:14:51 +01:00
// TODO: checking isOperable() ?
for ( auto & well : well_container_ ) {
2019-02-07 14:43:17 +01:00
well - > calculateExplicitQuantities ( ebosSimulator_ , well_state_ , deferred_logger ) ;
2018-11-17 23:14:51 +01:00
}
2017-02-14 11:34:03 +01:00
}
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-14 11:34:03 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2020-02-10 15:16:09 +01:00
updateWellControls ( Opm : : DeferredLogger & deferred_logger , const bool checkGroupControls )
2017-02-14 11:34:03 +01:00
{
2019-01-21 08:26:28 +01:00
// Even if there are no wells active locally, we cannot
// return as the DeferredLogger uses global communication.
// For no well active globally we simply return.
2017-04-12 17:37:34 +02:00
if ( ! wellsActive ( ) ) return ;
2017-02-14 11:34:03 +01:00
2020-02-10 15:16:09 +01:00
updateAndCommunicateGroupData ( ) ;
2020-05-15 11:21:32 +02:00
updateNetworkPressures ( ) ;
2020-02-10 15:16:09 +01:00
std : : set < std : : string > switched_wells ;
std : : set < std : : string > switched_groups ;
if ( checkGroupControls ) {
// Check group individual constraints.
updateGroupIndividualControls ( deferred_logger , switched_groups ) ;
// Check group's constraints from higher levels.
updateGroupHigherControls ( deferred_logger , switched_groups ) ;
updateAndCommunicateGroupData ( ) ;
2019-11-22 12:29:47 +01:00
2020-02-10 15:16:09 +01:00
// Check wells' group constraints and communicate.
for ( const auto & well : well_container_ ) {
const auto mode = WellInterface < TypeTag > : : IndividualOrGroup : : Group ;
const bool changed = well - > updateWellControl ( ebosSimulator_ , mode , well_state_ , deferred_logger ) ;
if ( changed ) {
switched_wells . insert ( well - > name ( ) ) ;
}
}
updateAndCommunicateGroupData ( ) ;
2019-08-07 14:13:11 +02:00
}
2020-02-10 15:16:09 +01:00
// Check individual well constraints and communicate.
2017-06-28 11:15:04 +02:00
for ( const auto & well : well_container_ ) {
2020-02-10 15:16:09 +01:00
if ( switched_wells . count ( well - > name ( ) ) ) {
continue ;
}
const auto mode = WellInterface < TypeTag > : : IndividualOrGroup : : Individual ;
well - > updateWellControl ( ebosSimulator_ , mode , well_state_ , deferred_logger ) ;
2019-01-18 14:04:30 +01:00
}
2020-02-10 15:16:09 +01:00
updateAndCommunicateGroupData ( ) ;
2019-11-22 12:29:47 +01:00
2020-02-10 15:16:09 +01:00
}
2020-05-15 11:21:32 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
updateNetworkPressures ( )
{
// Get the network and return if inactive.
const int reportStepIdx = ebosSimulator_ . episodeIndex ( ) ;
const auto & network = schedule ( ) . network ( reportStepIdx ) ;
if ( ! network . active ( ) ) {
return ;
}
2020-11-09 15:04:45 +01:00
node_pressures_ = WellGroupHelpers : : computeNetworkPressures (
network , well_state_ , * ( vfp_properties_ - > getProd ( ) ) , schedule ( ) , reportStepIdx ) ;
2020-05-15 11:21:32 +02:00
2020-10-21 09:50:31 +02:00
// Set the thp limits of wells
2020-05-15 11:21:32 +02:00
for ( auto & well : well_container_ ) {
2020-10-21 09:50:31 +02:00
// Producers only, since we so far only support the
// "extended" network model (properties defined by
// BRANPROP and NODEPROP) which only applies to producers.
2020-05-15 11:21:32 +02:00
if ( well - > isProducer ( ) ) {
const auto it = node_pressures_ . find ( well - > wellEcl ( ) . groupName ( ) ) ;
if ( it ! = node_pressures_ . end ( ) ) {
// The well belongs to a group with has a network pressure constraint,
// set the dynamic THP constraint of the well accordingly.
well - > setDynamicThpLimit ( it - > second ) ;
}
}
}
}
2020-02-10 15:16:09 +01:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
updateAndCommunicateGroupData ( )
{
const int reportStepIdx = ebosSimulator_ . episodeIndex ( ) ;
const Group & fieldGroup = schedule ( ) . getGroup ( " FIELD " , reportStepIdx ) ;
2021-01-06 15:10:05 +01:00
const int nupcol = schedule ( ) [ reportStepIdx ] . nupcol ( ) ;
2019-12-17 08:49:47 +01:00
const int iterationIdx = ebosSimulator_ . model ( ) . newtonMethod ( ) . numIterations ( ) ;
2020-02-10 15:16:09 +01:00
// This builds some necessary lookup structures, so it must be called
// before we copy to well_state_nupcol_.
const auto & comm = ebosSimulator_ . vanguard ( ) . grid ( ) . comm ( ) ;
well_state_ . updateGlobalIsGrup ( schedule ( ) , reportStepIdx , comm ) ;
2019-12-17 08:49:47 +01:00
if ( iterationIdx < nupcol ) {
well_state_nupcol_ = well_state_ ;
}
2019-11-22 12:29:47 +01:00
// the group target reduction rates needs to be update since wells may have swicthed to/from GRUP control
2020-02-10 15:16:09 +01:00
// Currently the group target reduction does not honor NUPCOL. TODO: is that true?
2019-12-12 09:22:37 +01:00
std : : vector < double > groupTargetReduction ( numPhases ( ) , 0.0 ) ;
2020-04-22 11:16:20 +02:00
WellGroupHelpers : : updateGroupTargetReduction ( fieldGroup , schedule ( ) , reportStepIdx , /*isInjector*/ false , phase_usage_ , * guideRate_ , well_state_nupcol_ , well_state_ , groupTargetReduction ) ;
2019-12-12 09:22:37 +01:00
std : : vector < double > groupTargetReductionInj ( numPhases ( ) , 0.0 ) ;
2020-04-22 11:16:20 +02:00
WellGroupHelpers : : updateGroupTargetReduction ( fieldGroup , schedule ( ) , reportStepIdx , /*isInjector*/ true , phase_usage_ , * guideRate_ , well_state_nupcol_ , well_state_ , groupTargetReductionInj ) ;
2019-12-12 09:22:37 +01:00
2019-12-16 13:48:48 +01:00
const double simulationTime = ebosSimulator_ . time ( ) ;
std : : vector < double > pot ( numPhases ( ) , 0.0 ) ;
2020-03-27 13:27:45 +01:00
WellGroupHelpers : : updateGuideRateForGroups ( fieldGroup , schedule ( ) , phase_usage_ , reportStepIdx , simulationTime , /*isInjector*/ false , well_state_ , comm , guideRate_ . get ( ) , pot ) ;
2019-12-16 13:48:48 +01:00
std : : vector < double > potInj ( numPhases ( ) , 0.0 ) ;
2020-03-27 13:27:45 +01:00
WellGroupHelpers : : updateGuideRateForGroups ( fieldGroup , schedule ( ) , phase_usage_ , reportStepIdx , simulationTime , /*isInjector*/ true , well_state_ , comm , guideRate_ . get ( ) , potInj ) ;
2019-12-17 08:49:47 +01:00
2019-12-12 09:22:37 +01:00
const auto & summaryState = ebosSimulator_ . vanguard ( ) . summaryState ( ) ;
2020-03-27 13:27:45 +01:00
WellGroupHelpers : : updateREINForGroups ( fieldGroup , schedule ( ) , reportStepIdx , phase_usage_ , summaryState , well_state_nupcol_ , well_state_ ) ;
WellGroupHelpers : : updateVREPForGroups ( fieldGroup , schedule ( ) , reportStepIdx , well_state_nupcol_ , well_state_ ) ;
2020-01-23 14:26:02 +01:00
2020-03-27 13:27:45 +01:00
WellGroupHelpers : : updateReservoirRatesInjectionGroups ( fieldGroup , schedule ( ) , reportStepIdx , well_state_nupcol_ , well_state_ ) ;
WellGroupHelpers : : updateGroupProductionRates ( fieldGroup , schedule ( ) , reportStepIdx , well_state_nupcol_ , well_state_ ) ;
2020-06-24 09:38:18 +02:00
// We use the rates from the privious time-step to reduce oscilations
WellGroupHelpers : : updateWellRates ( fieldGroup , schedule ( ) , reportStepIdx , previous_well_state_ , well_state_ ) ;
2020-11-16 10:36:44 +01:00
// Set ALQ for off-process wells to zero
for ( const auto & wname : schedule ( ) . wellNames ( reportStepIdx ) ) {
const bool is_producer = schedule ( ) . getWell ( wname , reportStepIdx ) . isProducer ( ) ;
const bool not_on_this_process = well_state_ . wellMap ( ) . count ( wname ) = = 0 ;
if ( is_producer & & not_on_this_process ) {
well_state_ . setALQ ( wname , 0.0 ) ;
}
}
2020-01-23 14:26:02 +01:00
well_state_ . communicateGroupRates ( comm ) ;
2019-12-17 08:49:47 +01:00
// compute wsolvent fraction for REIN wells
updateWsolvent ( fieldGroup , schedule ( ) , reportStepIdx , well_state_nupcol_ ) ;
2017-02-14 11:34:03 +01:00
}
2017-02-14 13:39:53 +01:00
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-14 13:39:53 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2018-06-28 13:47:10 +02:00
updateWellTestState ( const double & simulationTime , WellTestState & wellTestState ) const
2017-02-14 13:39:53 +01:00
{
2019-02-03 08:13:11 +01:00
Opm : : DeferredLogger local_deferredLogger ;
2017-07-26 11:01:26 +02:00
for ( const auto & well : well_container_ ) {
2019-02-03 08:13:11 +01:00
well - > updateWellTestState ( well_state_ , simulationTime , /*writeMessageToOPMLog=*/ true , wellTestState , local_deferredLogger ) ;
}
Opm : : DeferredLogger global_deferredLogger = gatherDeferredLogger ( local_deferredLogger ) ;
if ( terminal_output_ ) {
global_deferredLogger . logMessages ( ) ;
2017-07-26 11:01:26 +02:00
}
2017-02-14 13:39:53 +01:00
}
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-14 13:39:53 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2019-08-07 14:13:11 +02:00
computeWellPotentials ( std : : vector < double > & well_potentials , const int reportStepIdx , Opm : : DeferredLogger & deferred_logger )
2017-02-14 13:39:53 +01:00
{
// number of wells and phases
2019-11-01 15:11:21 +01:00
const int nw = numLocalWells ( ) ;
2017-11-08 13:57:36 +01:00
const int np = numPhases ( ) ;
2017-03-23 11:36:49 +01:00
well_potentials . resize ( nw * np , 0.0 ) ;
2019-04-23 13:30:12 +02:00
auto well_state_copy = well_state_ ;
// average B factors are required for the convergence checking of well equations
// Note: this must be done on all processes, even those with
// no wells needing testing, otherwise we will have locking.
std : : vector < Scalar > B_avg ( numComponents ( ) , Scalar ( ) ) ;
computeAverageFormationFactor ( B_avg ) ;
2018-11-16 16:03:23 +01:00
const Opm : : SummaryConfig & summaryConfig = ebosSimulator_ . vanguard ( ) . summaryConfig ( ) ;
2020-02-17 08:39:21 +01:00
const bool write_restart_file = ebosSimulator_ . vanguard ( ) . schedule ( ) . restart ( ) . getWriteRestartFile ( reportStepIdx ) ;
2019-02-07 14:43:17 +01:00
int exception_thrown = 0 ;
2020-12-11 08:26:52 +01:00
for ( const auto & well : well_container_ ) {
const bool needed_for_summary = ( ( summaryConfig . hasSummaryKey ( " WWPI: " + well - > name ( ) ) | |
summaryConfig . hasSummaryKey ( " WOPI: " + well - > name ( ) ) | |
summaryConfig . hasSummaryKey ( " WGPI: " + well - > name ( ) ) ) & & well - > isInjector ( ) ) | |
( ( summaryConfig . hasSummaryKey ( " WWPP: " + well - > name ( ) ) | |
summaryConfig . hasSummaryKey ( " WOPP: " + well - > name ( ) ) | |
summaryConfig . hasSummaryKey ( " WGPP: " + well - > name ( ) ) ) & & well - > isProducer ( ) ) ;
bool needPotentialsForGuideRate = true ; //eclWell.getGuideRatePhase() == Well::GuideRateTarget::UNDEFINED;
if ( write_restart_file | | needed_for_summary | | needPotentialsForGuideRate )
{
try {
2019-02-07 14:43:17 +01:00
std : : vector < double > potentials ;
2019-04-23 13:30:12 +02:00
well - > computeWellPotentials ( ebosSimulator_ , B_avg , well_state_copy , potentials , deferred_logger ) ;
2019-02-07 14:43:17 +01:00
// putting the sucessfully calculated potentials to the well_potentials
for ( int p = 0 ; p < np ; + + p ) {
well_potentials [ well - > indexOfWell ( ) * np + p ] = std : : abs ( potentials [ p ] ) ;
}
2020-12-11 08:26:52 +01:00
} catch ( std : : exception & e ) {
exception_thrown = 1 ;
2018-11-07 14:53:43 +01:00
}
2020-12-11 08:26:52 +01:00
}
2019-02-07 14:43:17 +01:00
}
logAndCheckForExceptionsAndThrow ( deferred_logger , exception_thrown , " computeWellPotentials() failed. " , terminal_output_ ) ;
2018-11-07 14:53:43 +01:00
// Store it in the well state
well_state_ . wellPotentials ( ) = well_potentials ;
2017-02-14 15:06:57 +01:00
}
2020-10-09 13:38:33 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
calculateProductivityIndexValues ( DeferredLogger & deferred_logger )
{
if ( ! this - > localWellsActive ( ) ) {
return ;
}
for ( const auto & wellPtr : this - > well_container_ ) {
wellPtr - > updateProductivityIndex ( this - > ebosSimulator_ ,
2020-11-27 14:50:08 +01:00
this - > prod_index_calc_ [ wellPtr - > indexOfWell ( ) ] ,
2020-10-09 13:38:33 +02:00
this - > well_state_ ,
deferred_logger ) ;
}
}
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-03-16 16:39:05 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2019-05-24 16:45:27 +02:00
prepareTimeStep ( Opm : : DeferredLogger & deferred_logger )
2017-03-16 16:39:05 +01:00
{
2019-02-07 14:43:17 +01:00
int exception_thrown = 0 ;
try {
for ( const auto & well : well_container_ ) {
2019-05-24 16:45:27 +02:00
well - > checkWellOperability ( ebosSimulator_ , well_state_ , deferred_logger ) ;
2017-09-04 13:54:41 +02:00
}
2019-02-07 14:43:17 +01:00
// since the controls are all updated, we should update well_state accordingly
for ( const auto & well : well_container_ ) {
const int w = well - > indexOfWell ( ) ;
if ( ! well - > isOperable ( ) ) continue ;
2018-06-06 15:17:59 +02:00
2019-02-07 14:43:17 +01:00
if ( well_state_ . effectiveEventsOccurred ( w ) ) {
2019-08-07 14:13:11 +02:00
well - > updateWellStateWithTarget ( ebosSimulator_ , well_state_ , deferred_logger ) ;
2019-02-07 14:43:17 +01:00
}
// there is no new well control change input within a report step,
// so next time step, the well does not consider to have effective events anymore
// TODO: if we can know whether this is the first time step within the report step,
// we do not need to set it to false
// TODO: we should do this at the end of the time step in case we will need it within
// this time step somewhere
if ( well_state_ . effectiveEventsOccurred ( w ) ) {
well_state_ . setEffectiveEventsOccurred ( w , false ) ;
}
} // end of for (const auto& well : well_container_)
updatePrimaryVariables ( deferred_logger ) ;
} catch ( std : : exception & e ) {
exception_thrown = 1 ;
}
logAndCheckForExceptionsAndThrow ( deferred_logger , exception_thrown , " prepareTimestep() failed. " , terminal_output_ ) ;
2017-09-04 13:54:41 +02:00
}
2017-11-08 15:48:30 +01:00
template < typename TypeTag >
const typename BlackoilWellModel < TypeTag > : : WellState &
BlackoilWellModel < TypeTag > : :
wellState ( ) const { return well_state_ ; }
2017-02-14 15:06:57 +01:00
2017-11-08 15:48:30 +01:00
template < typename TypeTag >
const typename BlackoilWellModel < TypeTag > : : WellState &
BlackoilWellModel < TypeTag > : :
wellState ( const WellState & well_state OPM_UNUSED ) const { return wellState ( ) ; }
2017-02-14 15:06:57 +01:00
2017-05-03 13:34:15 +02:00
template < typename TypeTag >
2017-02-14 15:06:57 +01:00
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2019-08-07 14:13:11 +02:00
calculateEfficiencyFactors ( const int reportStepIdx )
2017-02-14 15:06:57 +01:00
{
if ( ! localWellsActive ( ) ) {
return ;
}
2018-06-06 15:17:59 +02:00
for ( auto & well : well_container_ ) {
2019-11-13 23:16:11 +01:00
const Well & wellEcl = well - > wellEcl ( ) ;
2019-08-07 14:13:11 +02:00
double well_efficiency_factor = wellEcl . getEfficiencyFactor ( ) ;
2020-03-27 13:27:45 +01:00
WellGroupHelpers : : accumulateGroupEfficiencyFactor ( schedule ( ) . getGroup ( wellEcl . groupName ( ) , reportStepIdx ) , schedule ( ) , reportStepIdx , well_efficiency_factor ) ;
2018-06-06 15:17:59 +02:00
well - > setWellEfficiencyFactor ( well_efficiency_factor ) ;
2017-02-14 15:06:57 +01:00
}
}
2017-06-07 09:29:31 +02:00
template < typename TypeTag >
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2018-08-16 11:51:36 +02:00
setupCartesianToCompressed_ ( const int * global_cell , int number_of_cartesian_cells )
2017-06-07 09:29:31 +02:00
{
2018-08-16 11:51:36 +02:00
cartesian_to_compressed_ . resize ( number_of_cartesian_cells , - 1 ) ;
2017-06-07 09:29:31 +02:00
if ( global_cell ) {
2020-11-10 11:54:19 +01:00
auto elemIt = ebosSimulator_ . gridView ( ) . template begin < /*codim=*/ 0 > ( ) ;
2020-10-05 20:02:13 +02:00
for ( unsigned i = 0 ; i < local_num_cells_ ; + + i ) {
2020-11-10 11:54:19 +01:00
// Skip perforations in the overlap/ghost for distributed wells.
if ( elemIt - > partitionType ( ) = = Dune : : InteriorEntity )
{
assert ( ebosSimulator_ . gridView ( ) . indexSet ( ) . index ( * elemIt ) = = static_cast < int > ( i ) ) ;
cartesian_to_compressed_ [ global_cell [ i ] ] = i ;
}
+ + elemIt ;
2017-06-07 09:29:31 +02:00
}
}
else {
2020-10-05 20:02:13 +02:00
for ( unsigned i = 0 ; i < local_num_cells_ ; + + i ) {
2018-08-16 11:51:36 +02:00
cartesian_to_compressed_ [ i ] = i ;
2017-06-07 09:29:31 +02:00
}
}
}
template < typename TypeTag >
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2019-02-07 14:43:17 +01:00
computeRepRadiusPerfLength ( const Grid & grid , Opm : : DeferredLogger & deferred_logger )
2017-06-07 09:29:31 +02:00
{
2017-07-31 16:42:26 +02:00
for ( const auto & well : well_container_ ) {
2019-02-07 14:43:17 +01:00
well - > computeRepRadiusPerfLength ( grid , cartesian_to_compressed_ , deferred_logger ) ;
2017-06-07 09:29:31 +02:00
}
}
2017-05-09 08:21:51 +02:00
2017-06-23 12:24:50 +02:00
template < typename TypeTag >
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2019-02-27 14:47:31 +01:00
computeAverageFormationFactor ( std : : vector < Scalar > & B_avg ) const
2017-06-23 12:24:50 +02:00
{
2018-02-01 16:27:42 +01:00
const auto & grid = ebosSimulator_ . vanguard ( ) . grid ( ) ;
2017-06-23 12:24:50 +02:00
const auto & gridView = grid . leafGridView ( ) ;
2017-11-08 13:57:36 +01:00
ElementContext elemCtx ( ebosSimulator_ ) ;
2017-06-23 12:24:50 +02:00
const auto & elemEndIt = gridView . template end < /*codim=*/ 0 , Dune : : Interior_Partition > ( ) ;
for ( auto elemIt = gridView . template begin < /*codim=*/ 0 , Dune : : Interior_Partition > ( ) ;
elemIt ! = elemEndIt ; + + elemIt )
{
elemCtx . updatePrimaryStencil ( * elemIt ) ;
elemCtx . updatePrimaryIntensiveQuantities ( /*timeIdx=*/ 0 ) ;
const auto & intQuants = elemCtx . intensiveQuantities ( /*spaceIdx=*/ 0 , /*timeIdx=*/ 0 ) ;
const auto & fs = intQuants . fluidState ( ) ;
2017-12-04 09:14:08 +01:00
for ( unsigned phaseIdx = 0 ; phaseIdx < FluidSystem : : numPhases ; + + phaseIdx )
2017-06-23 12:24:50 +02:00
{
2017-12-04 09:14:08 +01:00
if ( ! FluidSystem : : phaseIsActive ( phaseIdx ) ) {
continue ;
}
const unsigned compIdx = Indices : : canonicalToActiveComponentIndex ( FluidSystem : : solventComponentIndex ( phaseIdx ) ) ;
auto & B = B_avg [ compIdx ] ;
2017-06-23 12:24:50 +02:00
2017-12-04 09:14:08 +01:00
B + = 1 / fs . invB ( phaseIdx ) . value ( ) ;
2017-06-23 12:24:50 +02:00
}
if ( has_solvent_ ) {
2017-06-27 15:16:22 +02:00
auto & B = B_avg [ solventSaturationIdx ] ;
2017-06-23 12:24:50 +02:00
B + = 1 / intQuants . solventInverseFormationVolumeFactor ( ) . value ( ) ;
}
}
// compute global average
grid . comm ( ) . sum ( B_avg . data ( ) , B_avg . size ( ) ) ;
for ( auto & bval : B_avg )
{
2020-10-05 20:02:13 +02:00
bval / = global_num_cells_ ;
2017-06-23 12:24:50 +02:00
}
}
2017-08-08 10:44:10 +02:00
template < typename TypeTag >
void
2017-09-26 10:52:05 +02:00
BlackoilWellModel < TypeTag > : :
2019-02-07 14:43:17 +01:00
updatePrimaryVariables ( Opm : : DeferredLogger & deferred_logger )
2017-08-08 10:44:10 +02:00
{
for ( const auto & well : well_container_ ) {
2019-02-07 14:43:17 +01:00
well - > updatePrimaryVariables ( well_state_ , deferred_logger ) ;
2017-11-08 13:57:36 +01:00
}
}
2017-11-08 15:48:30 +01:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : : extractLegacyCellPvtRegionIndex_ ( )
{
2018-02-01 16:27:42 +01:00
const auto & grid = ebosSimulator_ . vanguard ( ) . grid ( ) ;
2017-11-08 15:48:30 +01:00
const auto & eclProblem = ebosSimulator_ . problem ( ) ;
const unsigned numCells = grid . size ( /*codim=*/ 0 ) ;
pvt_region_idx_ . resize ( numCells ) ;
for ( unsigned cellIdx = 0 ; cellIdx < numCells ; + + cellIdx ) {
pvt_region_idx_ [ cellIdx ] =
eclProblem . pvtRegionIndex ( cellIdx ) ;
}
}
// The number of components in the model.
template < typename TypeTag >
int
BlackoilWellModel < TypeTag > : : numComponents ( ) const
{
2019-11-01 14:58:10 +01:00
if ( wellsActive ( ) & & numPhases ( ) < 3 ) {
2019-10-09 15:24:23 +02:00
return numPhases ( ) ;
2017-11-08 15:48:30 +01:00
}
int numComp = FluidSystem : : numComponents ;
if ( has_solvent_ ) {
numComp + + ;
}
return numComp ;
}
template < typename TypeTag >
int
2019-11-01 15:11:21 +01:00
BlackoilWellModel < TypeTag > : : numLocalWells ( ) const
2017-11-08 15:48:30 +01:00
{
2019-10-23 09:09:45 +02:00
return wells_ecl_ . size ( ) ;
2017-11-08 15:48:30 +01:00
}
template < typename TypeTag >
int
2019-10-23 09:09:45 +02:00
BlackoilWellModel < TypeTag > : : numPhases ( ) const
2017-11-08 15:48:30 +01:00
{
2019-10-23 09:09:45 +02:00
return phase_usage_ . num_phases ;
2017-11-08 15:48:30 +01:00
}
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : : extractLegacyDepth_ ( )
{
2018-02-01 16:27:42 +01:00
const auto & grid = ebosSimulator_ . vanguard ( ) . grid ( ) ;
2017-11-08 15:48:30 +01:00
const unsigned numCells = grid . size ( /*codim=*/ 0 ) ;
depth_ . resize ( numCells ) ;
for ( unsigned cellIdx = 0 ; cellIdx < numCells ; + + cellIdx ) {
2019-12-11 22:04:40 +01:00
depth_ [ cellIdx ] = Opm : : UgGridHelpers : : cellCenterDepth ( grid , cellIdx ) ;
2017-11-08 15:48:30 +01:00
}
}
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
updatePerforationIntensiveQuantities ( ) {
ElementContext elemCtx ( ebosSimulator_ ) ;
const auto & gridView = ebosSimulator_ . gridView ( ) ;
const auto & elemEndIt = gridView . template end < /*codim=*/ 0 , Dune : : Interior_Partition > ( ) ;
for ( auto elemIt = gridView . template begin < /*codim=*/ 0 , Dune : : Interior_Partition > ( ) ;
elemIt ! = elemEndIt ;
+ + elemIt )
{
2018-11-16 16:02:47 +01:00
2017-11-08 15:48:30 +01:00
elemCtx . updatePrimaryStencil ( * elemIt ) ;
2018-11-16 16:02:47 +01:00
int elemIdx = elemCtx . globalSpaceIndex ( 0 , 0 ) ;
if ( ! is_cell_perforated_ [ elemIdx ] ) {
continue ;
}
2017-11-08 15:48:30 +01:00
elemCtx . updatePrimaryIntensiveQuantities ( /*timeIdx=*/ 0 ) ;
}
}
2017-11-08 13:57:36 +01:00
2018-11-26 12:09:04 +01:00
// convert well data from opm-common to well state from opm-core
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
wellsToState ( const data : : Wells & wells ,
2020-09-20 22:16:07 +02:00
const data : : GroupAndNetworkValues & grpNwrkValues ,
2019-03-22 09:41:29 +01:00
const PhaseUsage & phases ,
const bool handle_ms_well ,
2019-06-25 21:53:37 +02:00
WellStateFullyImplicitBlackoil & state ) const
2019-03-22 09:41:29 +01:00
{
2020-08-28 08:41:48 +02:00
using GPMode = Group : : ProductionCMode ;
using GIMode = Group : : InjectionCMode ;
2018-11-26 12:09:04 +01:00
using rt = data : : Rates : : opt ;
const auto np = phases . num_phases ;
std : : vector < rt > phs ( np ) ;
if ( phases . phase_used [ BlackoilPhases : : Aqua ] ) {
phs . at ( phases . phase_pos [ BlackoilPhases : : Aqua ] ) = rt : : wat ;
}
if ( phases . phase_used [ BlackoilPhases : : Liquid ] ) {
phs . at ( phases . phase_pos [ BlackoilPhases : : Liquid ] ) = rt : : oil ;
}
if ( phases . phase_used [ BlackoilPhases : : Vapour ] ) {
phs . at ( phases . phase_pos [ BlackoilPhases : : Vapour ] ) = rt : : gas ;
}
for ( const auto & wm : state . wellMap ( ) ) {
const auto well_index = wm . second [ 0 ] ;
const auto & well = wells . at ( wm . first ) ;
state . bhp ( ) [ well_index ] = well . bhp ;
state . temperature ( ) [ well_index ] = well . temperature ;
2019-08-07 14:13:11 +02:00
2020-02-07 19:08:14 +01:00
if ( well . current_control . isProducer ) {
state . currentProductionControls ( ) [ well_index ] = well . current_control . prod ;
}
else {
state . currentInjectionControls ( ) [ well_index ] = well . current_control . inj ;
}
2019-08-07 14:13:11 +02:00
2018-11-26 12:09:04 +01:00
const auto wellrate_index = well_index * np ;
for ( size_t i = 0 ; i < phs . size ( ) ; + + i ) {
assert ( well . rates . has ( phs [ i ] ) ) ;
state . wellRates ( ) [ wellrate_index + i ] = well . rates . get ( phs [ i ] ) ;
}
2019-09-30 15:22:31 +02:00
const auto perforation_pressure = [ ] ( const data : : Connection & comp ) {
return comp . pressure ;
} ;
const auto perforation_reservoir_rate = [ ] ( const data : : Connection & comp ) {
return comp . reservoir_rate ;
} ;
std : : transform ( well . connections . begin ( ) ,
well . connections . end ( ) ,
state . perfPress ( ) . begin ( ) + wm . second [ 1 ] ,
perforation_pressure ) ;
std : : transform ( well . connections . begin ( ) ,
well . connections . end ( ) ,
state . perfRates ( ) . begin ( ) + wm . second [ 1 ] ,
perforation_reservoir_rate ) ;
int local_comp_index = 0 ;
for ( const data : : Connection & comp : well . connections ) {
const int global_comp_index = wm . second [ 1 ] + local_comp_index ;
for ( int phase_index = 0 ; phase_index < np ; + + phase_index ) {
state . perfPhaseRates ( ) [ global_comp_index * np + phase_index ] = comp . rates . get ( phs [ phase_index ] ) ;
}
+ + local_comp_index ;
}
2019-03-22 09:41:29 +01:00
if ( handle_ms_well & & ! well . segments . empty ( ) ) {
// we need the well_ecl_ information
const std : : string & well_name = wm . first ;
2019-11-13 23:16:11 +01:00
const Well & well_ecl = getWellEcl ( well_name ) ;
2019-03-22 09:41:29 +01:00
2019-05-02 12:51:25 +02:00
const WellSegments & segment_set = well_ecl . getSegments ( ) ;
2019-03-22 09:41:29 +01:00
const int top_segment_index = state . topSegmentIndex ( well_index ) ;
const auto & segments = well . segments ;
// \Note: eventually we need to hanlde the situations that some segments are shut
2019-04-30 10:01:38 +02:00
assert ( 0u + segment_set . size ( ) = = segments . size ( ) ) ;
2019-03-22 09:41:29 +01:00
for ( const auto & segment : segments ) {
const int segment_index = segment_set . segmentNumberToIndex ( segment . first ) ;
// recovering segment rates and pressure from the restart values
2020-04-17 00:36:13 +02:00
const auto pres_idx = Opm : : data : : SegmentPressures : : Value : : Pressure ;
state . segPress ( ) [ top_segment_index + segment_index ] = segment . second . pressures [ pres_idx ] ;
2019-03-22 09:41:29 +01:00
const auto & segment_rates = segment . second . rates ;
for ( int p = 0 ; p < np ; + + p ) {
state . segRates ( ) [ ( top_segment_index + segment_index ) * np + p ] = segment_rates . get ( phs [ p ] ) ;
}
}
}
}
2020-08-28 08:41:48 +02:00
2020-09-20 22:16:07 +02:00
for ( const auto & [ group , value ] : grpNwrkValues . groupData ) {
2020-08-28 08:41:48 +02:00
const auto cpc = value . currentControl . currentProdConstraint ;
const auto cgi = value . currentControl . currentGasInjectionConstraint ;
const auto cwi = value . currentControl . currentWaterInjectionConstraint ;
if ( cpc ! = GPMode : : NONE ) {
state . setCurrentProductionGroupControl ( group , cpc ) ;
}
if ( cgi ! = GIMode : : NONE ) {
state . setCurrentInjectionGroupControl ( Phase : : GAS , group , cgi ) ;
}
if ( cwi ! = GIMode : : NONE ) {
state . setCurrentInjectionGroupControl ( Phase : : WATER , group , cwi ) ;
}
}
2019-03-22 09:41:29 +01:00
}
template < typename TypeTag >
bool
BlackoilWellModel < TypeTag > : :
2019-10-23 09:09:45 +02:00
anyMSWellOpenLocal ( ) const
2019-03-22 09:41:29 +01:00
{
2019-10-23 09:09:45 +02:00
for ( const auto & well : wells_ecl_ ) {
if ( well . isMultiSegment ( ) ) {
return true ;
2019-03-22 09:41:29 +01:00
}
}
2019-10-23 09:09:45 +02:00
return false ;
2019-03-22 09:41:29 +01:00
}
template < typename TypeTag >
2019-11-13 23:16:11 +01:00
const Well &
2019-03-22 09:41:29 +01:00
BlackoilWellModel < TypeTag > : :
getWellEcl ( const std : : string & well_name ) const
{
2019-04-04 15:36:35 +02:00
// finding the iterator of the well in wells_ecl
auto well_ecl = std : : find_if ( wells_ecl_ . begin ( ) ,
wells_ecl_ . end ( ) ,
2019-11-13 23:16:11 +01:00
[ & well_name ] ( const Well & elem ) - > bool {
2019-05-02 12:51:25 +02:00
return elem . name ( ) = = well_name ;
2019-04-04 15:36:35 +02:00
} ) ;
2019-03-22 09:41:29 +01:00
2019-04-04 15:36:35 +02:00
assert ( well_ecl ! = wells_ecl_ . end ( ) ) ;
return * well_ecl ;
2018-11-26 12:09:04 +01:00
}
2019-09-23 15:15:55 +02:00
template < typename TypeTag >
typename BlackoilWellModel < TypeTag > : : WellInterfacePtr
BlackoilWellModel < TypeTag > : :
getWell ( const std : : string & well_name ) const
{
// finding the iterator of the well in wells_ecl
auto well = std : : find_if ( well_container_ . begin ( ) ,
well_container_ . end ( ) ,
[ & well_name ] ( const WellInterfacePtr & elem ) - > bool {
return elem - > name ( ) = = well_name ;
} ) ;
assert ( well ! = well_container_ . end ( ) ) ;
return * well ;
}
2020-02-10 15:16:09 +01:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
updateGroupIndividualControls ( Opm : : DeferredLogger & deferred_logger , std : : set < std : : string > & switched_groups )
{
const int reportStepIdx = ebosSimulator_ . episodeIndex ( ) ;
2020-04-27 10:28:24 +02:00
2021-01-06 15:10:05 +01:00
const int nupcol = schedule ( ) [ reportStepIdx ] . nupcol ( ) ;
2020-04-27 10:28:24 +02:00
const int iterationIdx = ebosSimulator_ . model ( ) . newtonMethod ( ) . numIterations ( ) ;
// don't switch group control when iterationIdx > nupcol
// to avoid oscilations between group controls
if ( iterationIdx > nupcol )
return ;
2020-02-10 15:16:09 +01:00
const Group & fieldGroup = schedule ( ) . getGroup ( " FIELD " , reportStepIdx ) ;
2020-03-24 09:24:45 +01:00
updateGroupIndividualControl ( fieldGroup , deferred_logger , switched_groups ) ;
2020-02-10 15:16:09 +01:00
}
2019-08-07 14:13:11 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
2020-03-24 09:24:45 +01:00
updateGroupIndividualControl ( const Group & group , Opm : : DeferredLogger & deferred_logger , std : : set < std : : string > & switched_groups ) {
2019-08-07 14:13:11 +02:00
const int reportStepIdx = ebosSimulator_ . episodeIndex ( ) ;
2020-02-10 15:16:09 +01:00
const bool skip = switched_groups . count ( group . name ( ) ) ;
if ( ! skip & & group . isInjectionGroup ( ) )
2019-08-07 14:13:11 +02:00
{
2020-01-23 11:20:06 +01:00
const Phase all [ ] = { Phase : : WATER , Phase : : OIL , Phase : : GAS } ;
for ( Phase phase : all ) {
if ( ! group . hasInjectionControl ( phase ) ) {
2020-02-11 09:03:40 +01:00
continue ;
2020-01-23 09:05:37 +01:00
}
2020-04-08 10:41:20 +02:00
Group : : InjectionCMode newControl = checkGroupInjectionConstraints ( group , phase ) ;
2020-03-24 09:24:45 +01:00
if ( newControl ! = Group : : InjectionCMode : : NONE )
2020-01-16 09:52:03 +01:00
{
2020-03-24 09:24:45 +01:00
switched_groups . insert ( group . name ( ) ) ;
2020-04-08 10:41:20 +02:00
actionOnBrokenConstraints ( group , newControl , phase , deferred_logger ) ;
2020-02-11 09:03:40 +01:00
}
2020-03-24 09:24:45 +01:00
}
2020-04-08 10:41:20 +02:00
}
if ( ! skip & & group . isProductionGroup ( ) ) {
2020-03-24 09:24:45 +01:00
Group : : ProductionCMode newControl = checkGroupProductionConstraints ( group , deferred_logger ) ;
const auto & summaryState = ebosSimulator_ . vanguard ( ) . summaryState ( ) ;
const auto controls = group . productionControls ( summaryState ) ;
if ( newControl ! = Group : : ProductionCMode : : NONE )
{
switched_groups . insert ( group . name ( ) ) ;
2020-04-08 10:41:20 +02:00
actionOnBrokenConstraints ( group , controls . exceed_action , newControl , deferred_logger ) ;
2020-03-24 09:24:45 +01:00
}
}
2019-12-12 09:22:37 +01:00
2020-03-24 09:24:45 +01:00
// call recursively down the group hiearchy
for ( const std : : string & groupName : group . groups ( ) ) {
updateGroupIndividualControl ( schedule ( ) . getGroup ( groupName , reportStepIdx ) , deferred_logger , switched_groups ) ;
}
}
2019-08-07 14:13:11 +02:00
2020-03-24 09:24:45 +01:00
template < typename TypeTag >
bool
BlackoilWellModel < TypeTag > : :
checkGroupConstraints ( const Group & group , Opm : : DeferredLogger & deferred_logger ) const {
2019-12-12 09:22:37 +01:00
2020-03-24 09:24:45 +01:00
const int reportStepIdx = ebosSimulator_ . episodeIndex ( ) ;
2020-04-08 10:41:20 +02:00
if ( group . isInjectionGroup ( ) ) {
2020-03-24 09:24:45 +01:00
const Phase all [ ] = { Phase : : WATER , Phase : : OIL , Phase : : GAS } ;
for ( Phase phase : all ) {
if ( ! group . hasInjectionControl ( phase ) ) {
continue ;
2020-02-11 09:03:40 +01:00
}
2020-04-08 10:41:20 +02:00
Group : : InjectionCMode newControl = checkGroupInjectionConstraints ( group , phase ) ;
if ( newControl ! = Group : : InjectionCMode : : NONE ) {
2020-03-24 09:24:45 +01:00
return true ;
2019-08-07 14:13:11 +02:00
}
2020-03-24 09:24:45 +01:00
}
2020-04-08 10:41:20 +02:00
}
if ( group . isProductionGroup ( ) ) {
2020-03-24 09:24:45 +01:00
Group : : ProductionCMode newControl = checkGroupProductionConstraints ( group , deferred_logger ) ;
if ( newControl ! = Group : : ProductionCMode : : NONE )
{
return true ;
}
}
2019-11-26 11:15:09 +01:00
2020-03-24 09:24:45 +01:00
// call recursively down the group hiearchy
bool violated = false ;
for ( const std : : string & groupName : group . groups ( ) ) {
violated = violated | | checkGroupConstraints ( schedule ( ) . getGroup ( groupName , reportStepIdx ) , deferred_logger ) ;
}
return violated ;
}
2020-04-08 10:41:20 +02:00
2020-03-24 09:24:45 +01:00
template < typename TypeTag >
Group : : ProductionCMode
BlackoilWellModel < TypeTag > : :
checkGroupProductionConstraints ( const Group & group , Opm : : DeferredLogger & deferred_logger ) const {
2019-11-26 11:15:09 +01:00
2020-03-24 09:24:45 +01:00
const int reportStepIdx = ebosSimulator_ . episodeIndex ( ) ;
const auto & summaryState = ebosSimulator_ . vanguard ( ) . summaryState ( ) ;
const auto & comm = ebosSimulator_ . vanguard ( ) . grid ( ) . comm ( ) ;
const auto & well_state = well_state_ ;
2019-11-26 11:15:09 +01:00
2020-03-24 09:24:45 +01:00
const auto controls = group . productionControls ( summaryState ) ;
const Group : : ProductionCMode & currentControl = well_state . currentProductionGroupControl ( group . name ( ) ) ;
2019-11-26 11:15:09 +01:00
2020-03-24 09:24:45 +01:00
if ( group . has_control ( Group : : ProductionCMode : : ORAT ) )
{
if ( currentControl ! = Group : : ProductionCMode : : ORAT )
{
double current_rate = 0.0 ;
2020-03-27 13:27:45 +01:00
current_rate + = WellGroupHelpers : : sumWellRates ( group , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Liquid ] , false ) ;
2020-02-11 09:03:40 +01:00
2020-03-24 09:24:45 +01:00
// sum over all nodes
current_rate = comm . sum ( current_rate ) ;
2019-11-26 11:15:09 +01:00
2020-03-24 09:24:45 +01:00
if ( controls . oil_target < current_rate ) {
return Group : : ProductionCMode : : ORAT ;
}
2020-02-11 09:03:40 +01:00
}
2019-11-26 13:36:45 +01:00
}
2020-03-24 09:24:45 +01:00
if ( group . has_control ( Group : : ProductionCMode : : WRAT ) )
{
if ( currentControl ! = Group : : ProductionCMode : : WRAT )
2019-08-07 14:13:11 +02:00
{
2019-12-12 09:22:37 +01:00
2020-03-24 09:24:45 +01:00
double current_rate = 0.0 ;
2020-03-27 13:27:45 +01:00
current_rate + = WellGroupHelpers : : sumWellRates ( group , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Aqua ] , false ) ;
2019-12-12 09:22:37 +01:00
2020-03-24 09:24:45 +01:00
// sum over all nodes
current_rate = comm . sum ( current_rate ) ;
if ( controls . water_target < current_rate ) {
return Group : : ProductionCMode : : WRAT ;
2020-01-16 09:52:03 +01:00
}
2019-08-07 14:13:11 +02:00
}
2020-03-24 09:24:45 +01:00
}
if ( group . has_control ( Group : : ProductionCMode : : GRAT ) )
{
if ( currentControl ! = Group : : ProductionCMode : : GRAT )
2019-08-07 14:13:11 +02:00
{
2020-03-24 09:24:45 +01:00
double current_rate = 0.0 ;
2020-03-27 13:27:45 +01:00
current_rate + = WellGroupHelpers : : sumWellRates ( group , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Vapour ] , false ) ;
2020-02-11 09:03:40 +01:00
2020-03-24 09:24:45 +01:00
// sum over all nodes
current_rate = comm . sum ( current_rate ) ;
if ( controls . gas_target < current_rate ) {
return Group : : ProductionCMode : : GRAT ;
}
}
}
if ( group . has_control ( Group : : ProductionCMode : : LRAT ) )
{
if ( currentControl ! = Group : : ProductionCMode : : LRAT )
{
double current_rate = 0.0 ;
2020-03-27 13:27:45 +01:00
current_rate + = WellGroupHelpers : : sumWellRates ( group , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Liquid ] , false ) ;
current_rate + = WellGroupHelpers : : sumWellRates ( group , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Aqua ] , false ) ;
2019-08-07 14:13:11 +02:00
2020-03-24 09:24:45 +01:00
// sum over all nodes
current_rate = comm . sum ( current_rate ) ;
2019-12-12 09:22:37 +01:00
2020-03-24 09:24:45 +01:00
if ( controls . liquid_target < current_rate ) {
return Group : : ProductionCMode : : LRAT ;
2019-08-07 14:13:11 +02:00
}
}
2020-03-24 09:24:45 +01:00
}
if ( group . has_control ( Group : : ProductionCMode : : CRAT ) )
{
OPM_DEFLOG_THROW ( std : : runtime_error , " Group " + group . name ( ) + " CRAT control for production groups not implemented " , deferred_logger ) ;
}
if ( group . has_control ( Group : : ProductionCMode : : RESV ) )
{
if ( currentControl ! = Group : : ProductionCMode : : RESV )
2019-08-07 14:13:11 +02:00
{
2020-03-24 09:24:45 +01:00
double current_rate = 0.0 ;
2020-03-27 13:27:45 +01:00
current_rate + = WellGroupHelpers : : sumWellResRates ( group , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Aqua ] , true ) ;
current_rate + = WellGroupHelpers : : sumWellResRates ( group , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Liquid ] , true ) ;
current_rate + = WellGroupHelpers : : sumWellResRates ( group , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Vapour ] , true ) ;
2019-12-12 09:22:37 +01:00
2020-03-24 09:24:45 +01:00
// sum over all nodes
current_rate = comm . sum ( current_rate ) ;
if ( controls . resv_target < current_rate ) {
return Group : : ProductionCMode : : RESV ;
2019-08-07 14:13:11 +02:00
}
}
2020-03-24 09:24:45 +01:00
}
if ( group . has_control ( Group : : ProductionCMode : : PRBL ) )
{
OPM_DEFLOG_THROW ( std : : runtime_error , " Group " + group . name ( ) + " PRBL control for production groups not implemented " , deferred_logger ) ;
2020-04-08 10:41:20 +02:00
}
2020-03-24 09:24:45 +01:00
return Group : : ProductionCMode : : NONE ;
}
2020-04-08 10:41:20 +02:00
2020-03-24 09:24:45 +01:00
template < typename TypeTag >
Group : : InjectionCMode
BlackoilWellModel < TypeTag > : :
2020-04-08 10:41:20 +02:00
checkGroupInjectionConstraints ( const Group & group , const Phase & phase ) const {
2020-03-24 09:24:45 +01:00
const int reportStepIdx = ebosSimulator_ . episodeIndex ( ) ;
const auto & summaryState = ebosSimulator_ . vanguard ( ) . summaryState ( ) ;
const auto & comm = ebosSimulator_ . vanguard ( ) . grid ( ) . comm ( ) ;
const auto & well_state = well_state_ ;
int phasePos ;
if ( phase = = Phase : : GAS & & phase_usage_ . phase_used [ BlackoilPhases : : Vapour ] )
phasePos = phase_usage_ . phase_pos [ BlackoilPhases : : Vapour ] ;
else if ( phase = = Phase : : OIL & & phase_usage_ . phase_used [ BlackoilPhases : : Liquid ] )
phasePos = phase_usage_ . phase_pos [ BlackoilPhases : : Liquid ] ;
else if ( phase = = Phase : : WATER & & phase_usage_ . phase_used [ BlackoilPhases : : Aqua ] )
phasePos = phase_usage_ . phase_pos [ BlackoilPhases : : Aqua ] ;
else
OPM_THROW ( std : : runtime_error , " Unknown phase " ) ;
const auto & controls = group . injectionControls ( phase , summaryState ) ;
const Group : : InjectionCMode & currentControl = well_state . currentInjectionGroupControl ( phase , group . name ( ) ) ;
if ( controls . has_control ( Group : : InjectionCMode : : RATE ) )
{
if ( currentControl ! = Group : : InjectionCMode : : RATE )
2019-08-07 14:13:11 +02:00
{
2020-03-24 09:24:45 +01:00
double current_rate = 0.0 ;
2020-03-27 13:27:45 +01:00
current_rate + = WellGroupHelpers : : sumWellRates ( group , schedule ( ) , well_state , reportStepIdx , phasePos , /*isInjector*/ true ) ;
2019-12-12 09:22:37 +01:00
2020-03-24 09:24:45 +01:00
// sum over all nodes
current_rate = comm . sum ( current_rate ) ;
2019-12-12 09:22:37 +01:00
2020-03-24 09:24:45 +01:00
if ( controls . surface_max_rate < current_rate ) {
return Group : : InjectionCMode : : RATE ;
2019-08-07 14:13:11 +02:00
}
}
2020-03-24 09:24:45 +01:00
}
if ( controls . has_control ( Group : : InjectionCMode : : RESV ) )
{
if ( currentControl ! = Group : : InjectionCMode : : RESV )
2019-08-07 14:13:11 +02:00
{
2020-03-24 09:24:45 +01:00
double current_rate = 0.0 ;
2020-03-27 13:27:45 +01:00
current_rate + = WellGroupHelpers : : sumWellResRates ( group , schedule ( ) , well_state , reportStepIdx , phasePos , /*isInjector*/ true ) ;
2020-03-24 09:24:45 +01:00
// sum over all nodes
current_rate = comm . sum ( current_rate ) ;
2019-08-07 14:13:11 +02:00
2020-03-24 09:24:45 +01:00
if ( controls . resv_max_rate < current_rate ) {
return Group : : InjectionCMode : : RESV ;
}
2019-08-07 14:13:11 +02:00
}
2020-03-24 09:24:45 +01:00
}
if ( controls . has_control ( Group : : InjectionCMode : : REIN ) )
{
if ( currentControl ! = Group : : InjectionCMode : : REIN )
2019-08-07 14:13:11 +02:00
{
2020-03-24 09:24:45 +01:00
double production_Rate = 0.0 ;
const Group & groupRein = schedule ( ) . getGroup ( controls . reinj_group , reportStepIdx ) ;
2020-03-27 13:27:45 +01:00
production_Rate + = WellGroupHelpers : : sumWellRates ( groupRein , schedule ( ) , well_state , reportStepIdx , phasePos , /*isInjector*/ false ) ;
2019-08-07 14:13:11 +02:00
2020-03-24 09:24:45 +01:00
// sum over all nodes
production_Rate = comm . sum ( production_Rate ) ;
2019-12-12 09:22:37 +01:00
2020-03-24 09:24:45 +01:00
double current_rate = 0.0 ;
2020-03-27 13:27:45 +01:00
current_rate + = WellGroupHelpers : : sumWellRates ( group , schedule ( ) , well_state , reportStepIdx , phasePos , /*isInjector*/ true ) ;
2020-03-24 09:24:45 +01:00
// sum over all nodes
current_rate = comm . sum ( current_rate ) ;
2019-08-07 14:13:11 +02:00
2020-03-24 09:24:45 +01:00
if ( controls . target_reinj_fraction * production_Rate < current_rate ) {
return Group : : InjectionCMode : : REIN ;
}
2019-08-07 14:13:11 +02:00
}
2020-03-24 09:24:45 +01:00
}
if ( controls . has_control ( Group : : InjectionCMode : : VREP ) )
{
if ( currentControl ! = Group : : InjectionCMode : : VREP )
2019-08-07 14:13:11 +02:00
{
2020-03-24 09:24:45 +01:00
double voidage_rate = 0.0 ;
const Group & groupVoidage = schedule ( ) . getGroup ( controls . voidage_group , reportStepIdx ) ;
2020-03-27 13:27:45 +01:00
voidage_rate + = WellGroupHelpers : : sumWellResRates ( groupVoidage , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Aqua ] , false ) ;
voidage_rate + = WellGroupHelpers : : sumWellResRates ( groupVoidage , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Liquid ] , false ) ;
voidage_rate + = WellGroupHelpers : : sumWellResRates ( groupVoidage , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Vapour ] , false ) ;
2020-01-16 09:52:03 +01:00
2020-03-24 09:24:45 +01:00
// sum over all nodes
voidage_rate = comm . sum ( voidage_rate ) ;
2019-08-07 14:13:11 +02:00
2020-03-24 09:24:45 +01:00
double total_rate = 0.0 ;
2020-03-27 13:27:45 +01:00
total_rate + = WellGroupHelpers : : sumWellResRates ( group , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Aqua ] , true ) ;
total_rate + = WellGroupHelpers : : sumWellResRates ( group , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Liquid ] , true ) ;
total_rate + = WellGroupHelpers : : sumWellResRates ( group , schedule ( ) , well_state , reportStepIdx , phase_usage_ . phase_pos [ BlackoilPhases : : Vapour ] , true ) ;
2019-08-07 14:13:11 +02:00
2020-03-24 09:24:45 +01:00
// sum over all nodes
total_rate = comm . sum ( total_rate ) ;
if ( controls . target_void_fraction * voidage_rate < total_rate ) {
return Group : : InjectionCMode : : VREP ;
}
}
2019-12-16 13:53:03 +01:00
}
2020-04-24 15:25:38 +02:00
return Group : : InjectionCMode : : NONE ;
}
2019-08-07 14:13:11 +02:00
2020-04-24 15:25:38 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
2020-05-04 15:56:34 +02:00
checkGconsaleLimits ( const Group & group , WellState & well_state , Opm : : DeferredLogger & deferred_logger ) const
2020-04-24 15:25:38 +02:00
{
const int reportStepIdx = ebosSimulator_ . episodeIndex ( ) ;
// call recursively down the group hiearchy
for ( const std : : string & groupName : group . groups ( ) ) {
2020-05-04 15:56:34 +02:00
checkGconsaleLimits ( schedule ( ) . getGroup ( groupName , reportStepIdx ) , well_state , deferred_logger ) ;
2020-04-24 15:25:38 +02:00
}
2019-08-07 14:13:11 +02:00
2020-04-24 15:25:38 +02:00
// only for groups with gas injection controls
if ( ! group . hasInjectionControl ( Phase : : GAS ) ) {
return ;
}
2020-03-24 09:24:45 +01:00
2020-04-24 15:25:38 +02:00
// check if gconsale is used for this group
if ( ! schedule ( ) . gConSale ( reportStepIdx ) . has ( group . name ( ) ) )
return ;
2020-03-24 09:24:45 +01:00
2020-05-04 15:56:34 +02:00
std : : ostringstream ss ;
2020-04-24 15:25:38 +02:00
const auto & summaryState = ebosSimulator_ . vanguard ( ) . summaryState ( ) ;
const auto & comm = ebosSimulator_ . vanguard ( ) . grid ( ) . comm ( ) ;
2020-03-24 09:24:45 +01:00
2020-04-24 15:25:38 +02:00
const auto & gconsale = schedule ( ) . gConSale ( reportStepIdx ) . get ( group . name ( ) , summaryState ) ;
2020-05-04 15:56:34 +02:00
const Group : : ProductionCMode & oldProductionControl = well_state . currentProductionGroupControl ( group . name ( ) ) ;
2020-04-24 15:25:38 +02:00
int gasPos = phase_usage_ . phase_pos [ BlackoilPhases : : Vapour ] ;
2020-05-04 15:56:34 +02:00
double production_rate = WellGroupHelpers : : sumWellRates ( group , schedule ( ) , well_state , reportStepIdx , gasPos , /*isInjector*/ false ) ;
double injection_rate = WellGroupHelpers : : sumWellRates ( group , schedule ( ) , well_state , reportStepIdx , gasPos , /*isInjector*/ true ) ;
2020-04-08 10:41:20 +02:00
2020-04-24 15:25:38 +02:00
// sum over all nodes
2020-05-04 15:56:34 +02:00
injection_rate = comm . sum ( injection_rate ) ;
production_rate = comm . sum ( production_rate ) ;
double sales_rate = production_rate - injection_rate ;
double production_target = gconsale . sales_target + injection_rate ;
2020-04-24 15:25:38 +02:00
// add import rate and substract consumption rate for group for gas
if ( schedule ( ) . gConSump ( reportStepIdx ) . has ( group . name ( ) ) ) {
const auto & gconsump = schedule ( ) . gConSump ( reportStepIdx ) . get ( group . name ( ) , summaryState ) ;
if ( phase_usage_ . phase_used [ BlackoilPhases : : Vapour ] ) {
sales_rate + = gconsump . import_rate ;
sales_rate - = gconsump . consumption_rate ;
2020-05-04 15:56:34 +02:00
production_target - = gconsump . import_rate ;
production_target + = gconsump . consumption_rate ;
2020-04-24 15:25:38 +02:00
}
2020-03-24 09:24:45 +01:00
}
2020-04-24 15:25:38 +02:00
if ( sales_rate > gconsale . max_sales_rate ) {
2020-05-04 15:56:34 +02:00
switch ( gconsale . max_proc ) {
case GConSale : : MaxProcedure : : NONE : {
if ( oldProductionControl ! = Group : : ProductionCMode : : GRAT & & oldProductionControl ! = Group : : ProductionCMode : : NONE ) {
ss < < " Group sales exceed maximum limit, but the action is NONE for " + group . name ( ) + " . Nothing happens " ;
}
break ;
}
case GConSale : : MaxProcedure : : CON : {
OPM_DEFLOG_THROW ( std : : runtime_error , " Group " + group . name ( ) + " GCONSALE exceed limit CON not implemented " , deferred_logger ) ;
break ;
}
case GConSale : : MaxProcedure : : CON_P : {
OPM_DEFLOG_THROW ( std : : runtime_error , " Group " + group . name ( ) + " GCONSALE exceed limit CON_P not implemented " , deferred_logger ) ;
break ;
}
case GConSale : : MaxProcedure : : WELL : {
OPM_DEFLOG_THROW ( std : : runtime_error , " Group " + group . name ( ) + " GCONSALE exceed limit WELL not implemented " , deferred_logger ) ;
break ;
}
case GConSale : : MaxProcedure : : PLUG : {
OPM_DEFLOG_THROW ( std : : runtime_error , " Group " + group . name ( ) + " GCONSALE exceed limit PLUG not implemented " , deferred_logger ) ;
break ;
}
case GConSale : : MaxProcedure : : MAXR : {
OPM_DEFLOG_THROW ( std : : runtime_error , " Group " + group . name ( ) + " GCONSALE exceed limit MAXR not implemented " , deferred_logger ) ;
break ;
}
case GConSale : : MaxProcedure : : END : {
OPM_DEFLOG_THROW ( std : : runtime_error , " Group " + group . name ( ) + " GCONSALE exceed limit END not implemented " , deferred_logger ) ;
break ;
}
case GConSale : : MaxProcedure : : RATE : {
well_state . setCurrentProductionGroupControl ( group . name ( ) , Group : : ProductionCMode : : GRAT ) ;
ss < < " Maximum GCONSALE limit violated for " < < group . name ( ) < < " . The group is switched from " ;
ss < < Group : : ProductionCMode2String ( oldProductionControl ) < < " to " < < Group : : ProductionCMode2String ( Group : : ProductionCMode : : GRAT ) ;
ss < < " and limited by the maximum sales rate after consumption and import are considered " ;
well_state . setCurrentGroupGratTargetFromSales ( group . name ( ) , production_target ) ;
break ;
}
default :
throw ( " Invalid procedure for maximum rate limit selected for group " + group . name ( ) ) ;
}
2020-04-24 15:25:38 +02:00
}
if ( sales_rate < gconsale . min_sales_rate ) {
2020-05-04 15:56:34 +02:00
const Group : : ProductionCMode & currentProductionControl = well_state . currentProductionGroupControl ( group . name ( ) ) ;
if ( currentProductionControl = = Group : : ProductionCMode : : GRAT ) {
ss < < " Group " + group . name ( ) + " has sale rate less then minimum permitted value and is under GRAT control. \n " ;
ss < < " The GRAT is increased to meet the sales minimum rate. \n " ;
well_state . setCurrentGroupGratTargetFromSales ( group . name ( ) , production_target ) ;
//} else if () {//TODO add action for WGASPROD
//} else if () {//TODO add action for drilling queue
} else {
ss < < " Group " + group . name ( ) + " has sale rate less then minimum permitted value but cannot increase the group production rate \n " ;
ss < < " or adjust gas production using WGASPROD or drill new wells to meet the sales target. \n " ;
ss < < " Note that WGASPROD and drilling queues are not implemented in Flow. No action is taken. \n " ;
}
2020-04-24 15:25:38 +02:00
}
if ( gconsale . sales_target < 0.0 ) {
2020-05-04 15:56:34 +02:00
OPM_DEFLOG_THROW ( std : : runtime_error , " Group " + group . name ( ) + " has sale rate target less then zero. Not implemented in Flow " , deferred_logger ) ;
}
auto cc = Dune : : MPIHelper : : getCollectiveCommunication ( ) ;
if ( cc . size ( ) > 1 ) {
ss < < " on rank " < < cc . rank ( ) ;
2020-04-24 15:25:38 +02:00
}
2020-05-04 15:56:34 +02:00
if ( ! ss . str ( ) . empty ( ) )
deferred_logger . info ( ss . str ( ) ) ;
2020-04-24 15:25:38 +02:00
2019-08-07 14:13:11 +02:00
}
2020-04-24 15:25:38 +02:00
2019-08-07 14:13:11 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
2020-04-08 10:41:20 +02:00
actionOnBrokenConstraints ( const Group & group , const Group : : ExceedAction & exceed_action , const Group : : ProductionCMode & newControl , Opm : : DeferredLogger & deferred_logger ) {
2019-08-07 14:13:11 +02:00
2019-12-11 22:04:40 +01:00
auto & well_state = well_state_ ;
2020-08-21 23:14:14 +02:00
const Group : : ProductionCMode oldControl = well_state . currentProductionGroupControl ( group . name ( ) ) ;
2019-08-07 14:13:11 +02:00
std : : ostringstream ss ;
2019-10-16 15:27:40 +02:00
2019-08-07 14:13:11 +02:00
switch ( exceed_action ) {
2019-11-13 23:16:11 +01:00
case Group : : ExceedAction : : NONE : {
2019-11-25 10:11:36 +01:00
if ( oldControl ! = newControl & & oldControl ! = Group : : ProductionCMode : : NONE ) {
2020-05-14 17:22:52 +02:00
ss < < " Group production exceed action is NONE for group " + group . name ( ) + " . Nothing happens. " ;
2019-11-25 10:11:36 +01:00
}
2019-08-07 14:13:11 +02:00
break ;
}
2019-11-13 23:16:11 +01:00
case Group : : ExceedAction : : CON : {
2019-08-07 14:13:11 +02:00
OPM_DEFLOG_THROW ( std : : runtime_error , " Group " + group . name ( ) + " GroupProductionExceedLimit CON not implemented " , deferred_logger ) ;
break ;
}
2019-11-13 23:16:11 +01:00
case Group : : ExceedAction : : CON_PLUS : {
2019-08-07 14:13:11 +02:00
OPM_DEFLOG_THROW ( std : : runtime_error , " Group " + group . name ( ) + " GroupProductionExceedLimit CON_PLUS not implemented " , deferred_logger ) ;
break ;
}
2019-11-13 23:16:11 +01:00
case Group : : ExceedAction : : WELL : {
2019-08-07 14:13:11 +02:00
OPM_DEFLOG_THROW ( std : : runtime_error , " Group " + group . name ( ) + " GroupProductionExceedLimit WELL not implemented " , deferred_logger ) ;
break ;
}
2019-11-13 23:16:11 +01:00
case Group : : ExceedAction : : PLUG : {
2019-08-07 14:13:11 +02:00
OPM_DEFLOG_THROW ( std : : runtime_error , " Group " + group . name ( ) + " GroupProductionExceedLimit PLUG not implemented " , deferred_logger ) ;
break ;
}
2019-11-13 23:16:11 +01:00
case Group : : ExceedAction : : RATE : {
2019-10-16 15:27:40 +02:00
if ( oldControl ! = newControl ) {
well_state . setCurrentProductionGroupControl ( group . name ( ) , newControl ) ;
2020-05-14 17:22:52 +02:00
ss < < " Switching production control mode for group " < < group . name ( )
< < " from " < < Group : : ProductionCMode2String ( oldControl )
< < " to " < < Group : : ProductionCMode2String ( newControl ) ;
2019-10-16 15:27:40 +02:00
}
2019-08-07 14:13:11 +02:00
break ;
}
default :
throw ( " Invalid procedure for maximum rate limit selected for group " + group . name ( ) ) ;
}
auto cc = Dune : : MPIHelper : : getCollectiveCommunication ( ) ;
if ( cc . size ( ) > 1 ) {
ss < < " on rank " < < cc . rank ( ) ;
}
2019-10-16 15:27:40 +02:00
if ( ! ss . str ( ) . empty ( ) )
deferred_logger . info ( ss . str ( ) ) ;
2019-08-07 14:13:11 +02:00
}
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
2020-04-08 10:41:20 +02:00
actionOnBrokenConstraints ( const Group & group , const Group : : InjectionCMode & newControl , const Phase & controlPhase , Opm : : DeferredLogger & deferred_logger ) {
2019-08-07 14:13:11 +02:00
auto & well_state = well_state_ ;
2020-08-21 23:14:14 +02:00
const Group : : InjectionCMode oldControl = well_state . currentInjectionGroupControl ( controlPhase , group . name ( ) ) ;
2019-10-16 15:27:40 +02:00
2019-08-07 14:13:11 +02:00
std : : ostringstream ss ;
2019-10-16 15:27:40 +02:00
if ( oldControl ! = newControl ) {
2019-11-13 23:16:11 +01:00
const std : : string from = Group : : InjectionCMode2String ( oldControl ) ;
2020-05-14 17:22:52 +02:00
ss < < " Switching injection control mode for group " < < group . name ( )
< < " from " < < Group : : InjectionCMode2String ( oldControl )
< < " to " < < Group : : InjectionCMode2String ( newControl ) ;
2019-10-16 15:27:40 +02:00
auto cc = Dune : : MPIHelper : : getCollectiveCommunication ( ) ;
if ( cc . size ( ) > 1 ) {
ss < < " on rank " < < cc . rank ( ) ;
}
2020-01-23 09:05:37 +01:00
well_state . setCurrentInjectionGroupControl ( controlPhase , group . name ( ) , newControl ) ;
2019-08-07 14:13:11 +02:00
}
2019-10-16 15:27:40 +02:00
if ( ! ss . str ( ) . empty ( ) )
deferred_logger . info ( ss . str ( ) ) ;
2019-08-07 14:13:11 +02:00
}
2020-02-10 15:16:09 +01:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
updateGroupHigherControls ( Opm : : DeferredLogger & deferred_logger , std : : set < std : : string > & switched_groups )
{
const int reportStepIdx = ebosSimulator_ . episodeIndex ( ) ;
const Group & fieldGroup = schedule ( ) . getGroup ( " FIELD " , reportStepIdx ) ;
checkGroupHigherConstraints ( fieldGroup , deferred_logger , switched_groups ) ;
}
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
checkGroupHigherConstraints ( const Group & group , Opm : : DeferredLogger & deferred_logger , std : : set < std : : string > & switched_groups )
{
2020-03-27 13:27:45 +01:00
// Set up coefficients for RESV <-> surface rate conversion.
2020-02-10 15:16:09 +01:00
// Use the pvtRegionIdx from the top cell of the first well.
// TODO fix this!
// This is only used for converting RESV rates.
// What is the proper approach?
2020-11-17 11:47:51 +01:00
const auto & comm = ebosSimulator_ . vanguard ( ) . grid ( ) . comm ( ) ;
2020-03-27 13:27:45 +01:00
const int fipnum = 0 ;
2020-11-17 17:07:58 +01:00
int pvtreg = well_perf_data_ . empty ( ) | | well_perf_data_ [ 0 ] . empty ( )
2020-02-10 15:16:09 +01:00
? pvt_region_idx_ [ 0 ]
: pvt_region_idx_ [ well_perf_data_ [ 0 ] [ 0 ] . cell_index ] ;
2020-11-17 11:47:51 +01:00
if ( comm . size ( ) > 1 )
{
// Just like in the sequential case the pvtregion is determined
// by the first cell of the first well. What is the first well
// is decided by the order in the Schedule using Well::seqIndex()
int firstWellIndex = well_perf_data_ . empty ( ) ?
std : : numeric_limits < int > : : max ( ) : wells_ecl_ [ 0 ] . seqIndex ( ) ;
auto regIndexPair = std : : make_pair ( pvtreg , firstWellIndex ) ;
std : : vector < decltype ( regIndexPair ) > pairs ( comm . size ( ) ) ;
comm . allgather ( & regIndexPair , 1 , pairs . data ( ) ) ;
pvtreg = std : : min_element ( pairs . begin ( ) , pairs . end ( ) ,
[ ] ( const auto & p1 , const auto & p2 ) { return p1 . second < p2 . second ; } )
- > first ;
}
2020-03-27 13:27:45 +01:00
std : : vector < double > resv_coeff ( phase_usage_ . num_phases , 0.0 ) ;
rateConverter_ - > calcCoeff ( fipnum , pvtreg , resv_coeff ) ;
2020-02-10 15:16:09 +01:00
const int reportStepIdx = ebosSimulator_ . episodeIndex ( ) ;
const auto & summaryState = ebosSimulator_ . vanguard ( ) . summaryState ( ) ;
std : : vector < double > rates ( phase_usage_ . num_phases , 0.0 ) ;
2020-03-24 19:51:11 +01:00
const bool skip = switched_groups . count ( group . name ( ) ) | | group . name ( ) = = " FIELD " ;
2020-02-10 15:16:09 +01:00
if ( ! skip & & group . isInjectionGroup ( ) ) {
// Obtain rates for group.
for ( int phasePos = 0 ; phasePos < phase_usage_ . num_phases ; + + phasePos ) {
2020-03-27 13:27:45 +01:00
const double local_current_rate = WellGroupHelpers : : sumWellRates (
2020-02-10 15:16:09 +01:00
group , schedule ( ) , well_state_ , reportStepIdx , phasePos , /* isInjector */ true ) ;
// Sum over all processes
rates [ phasePos ] = comm . sum ( local_current_rate ) ;
}
const Phase all [ ] = { Phase : : WATER , Phase : : OIL , Phase : : GAS } ;
for ( Phase phase : all ) {
// Check higher up only if under individual (not FLD) control.
const Group : : InjectionCMode & currentControl = well_state_ . currentInjectionGroupControl ( phase , group . name ( ) ) ;
if ( currentControl ! = Group : : InjectionCMode : : FLD ) {
const Group & parentGroup = schedule ( ) . getGroup ( group . parent ( ) , reportStepIdx ) ;
2020-03-27 13:27:45 +01:00
const std : : pair < bool , double > changed = WellGroupHelpers : : checkGroupConstraintsInj (
2020-02-10 15:16:09 +01:00
group . name ( ) ,
group . parent ( ) ,
parentGroup ,
well_state_ ,
reportStepIdx ,
guideRate_ . get ( ) ,
rates . data ( ) ,
phase ,
phase_usage_ ,
group . getGroupEfficiencyFactor ( ) ,
schedule ( ) ,
summaryState ,
2020-03-27 13:27:45 +01:00
resv_coeff ,
2020-02-10 15:16:09 +01:00
deferred_logger ) ;
2020-03-24 09:24:45 +01:00
if ( changed . first ) {
2020-02-10 15:16:09 +01:00
switched_groups . insert ( group . name ( ) ) ;
2020-04-08 10:41:20 +02:00
actionOnBrokenConstraints ( group , Group : : InjectionCMode : : FLD , phase , deferred_logger ) ;
2020-02-10 15:16:09 +01:00
}
}
}
}
if ( ! skip & & group . isProductionGroup ( ) ) {
// Obtain rates for group.
for ( int phasePos = 0 ; phasePos < phase_usage_ . num_phases ; + + phasePos ) {
2020-03-27 13:27:45 +01:00
const double local_current_rate = WellGroupHelpers : : sumWellRates (
2020-02-10 15:16:09 +01:00
group , schedule ( ) , well_state_ , reportStepIdx , phasePos , /* isInjector */ false ) ;
// Sum over all processes
rates [ phasePos ] = - comm . sum ( local_current_rate ) ;
}
// Check higher up only if under individual (not FLD) control.
const Group : : ProductionCMode & currentControl = well_state_ . currentProductionGroupControl ( group . name ( ) ) ;
if ( currentControl ! = Group : : ProductionCMode : : FLD ) {
const Group & parentGroup = schedule ( ) . getGroup ( group . parent ( ) , reportStepIdx ) ;
2020-03-27 13:27:45 +01:00
const std : : pair < bool , double > changed = WellGroupHelpers : : checkGroupConstraintsProd (
2020-02-10 15:16:09 +01:00
group . name ( ) ,
group . parent ( ) ,
parentGroup ,
well_state_ ,
reportStepIdx ,
guideRate_ . get ( ) ,
rates . data ( ) ,
phase_usage_ ,
group . getGroupEfficiencyFactor ( ) ,
schedule ( ) ,
summaryState ,
2020-03-27 13:27:45 +01:00
resv_coeff ,
2020-02-10 15:16:09 +01:00
deferred_logger ) ;
2020-03-24 09:24:45 +01:00
if ( changed . first ) {
2020-02-10 15:16:09 +01:00
switched_groups . insert ( group . name ( ) ) ;
const auto exceed_action = group . productionControls ( summaryState ) . exceed_action ;
2020-04-08 10:41:20 +02:00
actionOnBrokenConstraints ( group , exceed_action , Group : : ProductionCMode : : FLD , deferred_logger ) ;
2020-02-10 15:16:09 +01:00
}
}
}
// call recursively down the group hiearchy
for ( const std : : string & groupName : group . groups ( ) ) {
checkGroupHigherConstraints ( schedule ( ) . getGroup ( groupName , reportStepIdx ) , deferred_logger , switched_groups ) ;
}
}
2020-10-29 23:30:09 +01:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
runWellPIScaling ( const int timeStepIdx , DeferredLogger & local_deferredLogger )
{
if ( this - > last_run_wellpi_ . has_value ( ) & & ( * this - > last_run_wellpi_ = = timeStepIdx ) ) {
// We've already run WELPI scaling for this report step. Most
// common for the very first report step. Don't redo WELPI scaling.
return ;
}
auto hasWellPIEvent = [ this , timeStepIdx ] ( const int well_index ) - > bool
{
2021-01-10 21:46:45 +01:00
return this - > schedule ( ) [ timeStepIdx ]
. wellgroup_events ( ) . hasEvent ( this - > wells_ecl_ [ well_index ] . name ( ) ,
ScheduleEvents : : Events : : WELL_PRODUCTIVITY_INDEX ) ;
2020-10-29 23:30:09 +01:00
} ;
auto getWellPI = [ this ] ( const int well_index ) - > double
{
const auto & pu = this - > phase_usage_ ;
const auto np = this - > numPhases ( ) ;
const auto * pi = & this - > well_state_ . productivityIndex ( ) [ np * well_index + 0 ] ;
const auto preferred = this - > wells_ecl_ [ well_index ] . getPreferredPhase ( ) ;
switch ( preferred ) { // Should really have LIQUID = OIL + WATER here too...
case Phase : : WATER :
return pu . phase_used [ BlackoilPhases : : PhaseIndex : : Aqua ]
? pi [ pu . phase_pos [ BlackoilPhases : : PhaseIndex : : Aqua ] ]
: 0.0 ;
case Phase : : OIL :
return pu . phase_used [ BlackoilPhases : : PhaseIndex : : Liquid ]
? pi [ pu . phase_pos [ BlackoilPhases : : PhaseIndex : : Liquid ] ]
: 0.0 ;
case Phase : : GAS :
return pu . phase_used [ BlackoilPhases : : PhaseIndex : : Vapour ]
? pi [ pu . phase_pos [ BlackoilPhases : : PhaseIndex : : Vapour ] ]
: 0.0 ;
default :
throw std : : invalid_argument {
" Unsupported preferred phase " +
std : : to_string ( static_cast < int > ( preferred ) )
} ;
}
} ;
auto getWellPIScalingFactor = [ this ] ( const int well_index ,
const double newWellPI ) - > double
{
return this - > wells_ecl_ [ well_index ] . getWellPIScalingFactor ( newWellPI ) ;
} ;
auto rescaleWellPI =
[ this , timeStepIdx ] ( const int well_index ,
const double scalingFactor ) - > void
{
{
const auto & wname = this - > wells_ecl_ [ well_index ] . name ( ) ;
auto & schedule = this - > ebosSimulator_ . vanguard ( ) . schedule ( ) ; // Mutable
schedule . applyWellProdIndexScaling ( wname , timeStepIdx , scalingFactor ) ;
this - > wells_ecl_ [ well_index ] = schedule . getWell ( wname , timeStepIdx ) ;
}
const auto & well = this - > wells_ecl_ [ well_index ] ;
auto & pd = this - > well_perf_data_ [ well_index ] ;
auto pdIter = pd . begin ( ) ;
for ( const auto & conn : well . getConnections ( ) ) {
if ( conn . state ( ) ! = Connection : : State : : SHUT ) {
pdIter - > connection_transmissibility_factor = conn . CF ( ) ;
+ + pdIter ;
}
}
this - > well_state_ . resetConnectionTransFactors ( well_index , pd ) ;
this - > prod_index_calc_ [ well_index ] . reInit ( well ) ;
} ;
// Minimal well setup to compute PI/II values
{
auto saved_previous_well_state = this - > previous_well_state_ ;
this - > previous_well_state_ = this - > well_state_ ;
well_container_ = createWellContainer ( timeStepIdx ) ;
2020-12-08 12:51:25 +01:00
std : : vector < Scalar > B_avg ( numComponents ( ) , Scalar ( ) ) ;
// we don't plan to iterate so just passing trivial B_avg
// for now
2020-10-29 23:30:09 +01:00
for ( auto & well : well_container_ ) {
2020-12-08 12:51:25 +01:00
well - > init ( & phase_usage_ , depth_ , gravity_ , local_num_cells_ , B_avg ) ;
2020-10-29 23:30:09 +01:00
}
std : : fill ( is_cell_perforated_ . begin ( ) , is_cell_perforated_ . end ( ) , false ) ;
for ( auto & well : well_container_ ) {
well - > updatePerforatedCell ( is_cell_perforated_ ) ;
}
this - > calculateProductivityIndexValues ( local_deferredLogger ) ;
this - > previous_well_state_ = std : : move ( saved_previous_well_state ) ;
}
const auto nw = this - > numLocalWells ( ) ;
for ( auto wellID = 0 * nw ; wellID < nw ; + + wellID ) {
if ( hasWellPIEvent ( wellID ) ) {
const auto newWellPI = getWellPI ( wellID ) ;
const auto scalingFactor = getWellPIScalingFactor ( wellID , newWellPI ) ;
rescaleWellPI ( wellID , scalingFactor ) ;
}
}
this - > last_run_wellpi_ = timeStepIdx ;
}
2019-09-23 15:15:55 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
2019-11-13 23:16:11 +01:00
updateWsolvent ( const Group & group , const Schedule & schedule , const int reportStepIdx , const WellStateFullyImplicitBlackoil & wellState ) {
2019-09-23 15:15:55 +02:00
for ( const std : : string & groupName : group . groups ( ) ) {
2019-11-13 23:16:11 +01:00
const Group & groupTmp = schedule . getGroup ( groupName , reportStepIdx ) ;
2019-09-23 15:15:55 +02:00
updateWsolvent ( groupTmp , schedule , reportStepIdx , wellState ) ;
}
if ( group . isProductionGroup ( ) )
return ;
2020-01-23 09:05:37 +01:00
const Group : : InjectionCMode & currentGroupControl = wellState . currentInjectionGroupControl ( Phase : : GAS , group . name ( ) ) ;
2019-11-13 23:16:11 +01:00
if ( currentGroupControl = = Group : : InjectionCMode : : REIN ) {
2019-09-23 15:15:55 +02:00
int gasPos = phase_usage_ . phase_pos [ BlackoilPhases : : Vapour ] ;
2020-05-04 09:16:42 +02:00
const auto & summaryState = ebosSimulator_ . vanguard ( ) . summaryState ( ) ;
const auto & controls = group . injectionControls ( Phase : : GAS , summaryState ) ;
const Group & groupRein = schedule . getGroup ( controls . reinj_group , reportStepIdx ) ;
double gasProductionRate = WellGroupHelpers : : sumWellRates ( groupRein , schedule , wellState , reportStepIdx , gasPos , /*isInjector*/ false ) ;
double solventProductionRate = WellGroupHelpers : : sumSolventRates ( groupRein , schedule , wellState , reportStepIdx , /*isInjector*/ false ) ;
2019-09-23 15:15:55 +02:00
2019-12-12 09:22:37 +01:00
const auto & comm = ebosSimulator_ . vanguard ( ) . grid ( ) . comm ( ) ;
solventProductionRate = comm . sum ( solventProductionRate ) ;
gasProductionRate = comm . sum ( gasProductionRate ) ;
2019-09-23 15:15:55 +02:00
double wsolvent = 0.0 ;
if ( std : : abs ( gasProductionRate ) > 1e-6 )
wsolvent = solventProductionRate / gasProductionRate ;
setWsolvent ( group , schedule , reportStepIdx , wsolvent ) ;
}
}
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
2019-11-13 23:16:11 +01:00
setWsolvent ( const Group & group , const Schedule & schedule , const int reportStepIdx , double wsolvent ) {
2019-09-23 15:15:55 +02:00
for ( const std : : string & groupName : group . groups ( ) ) {
2019-11-13 23:16:11 +01:00
const Group & groupTmp = schedule . getGroup ( groupName , reportStepIdx ) ;
2019-09-23 15:15:55 +02:00
setWsolvent ( groupTmp , schedule , reportStepIdx , wsolvent ) ;
}
for ( const std : : string & wellName : group . wells ( ) ) {
2019-11-13 23:16:11 +01:00
const auto & wellTmp = schedule . getWell ( wellName , reportStepIdx ) ;
if ( wellTmp . getStatus ( ) = = Well : : Status : : SHUT )
2019-09-23 15:15:55 +02:00
continue ;
auto well = getWell ( wellName ) ;
well - > setWsolvent ( wsolvent ) ;
}
}
2020-10-20 00:10:27 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
assignWellGuideRates ( data : : Wells & wsrpt ) const
{
for ( const auto & well : this - > wells_ecl_ ) {
auto xwPos = wsrpt . find ( well . name ( ) ) ;
if ( xwPos = = wsrpt . end ( ) ) { // No well results. Unexpected.
continue ;
}
xwPos - > second . guide_rates = this - > getGuideRateValues ( well ) ;
}
}
2020-10-29 23:16:31 +01:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
assignShutConnections ( data : : Wells & wsrpt ) const
{
for ( const auto & well : this - > wells_ecl_ ) {
auto xwPos = wsrpt . find ( well . name ( ) ) ;
if ( xwPos = = wsrpt . end ( ) ) { // No well results. Unexpected.
continue ;
}
auto & xcon = xwPos - > second . connections ;
for ( const auto & conn : well . getConnections ( ) ) {
if ( conn . state ( ) ! = Connection : : State : : SHUT ) {
continue ;
}
auto & xc = xcon . emplace_back ( ) ;
xc . index = conn . global_index ( ) ;
xc . pressure = xc . reservoir_rate = 0.0 ;
xc . effective_Kh = conn . Kh ( ) ;
xc . trans_factor = conn . CF ( ) ;
}
}
}
2020-09-20 22:16:07 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
assignGroupValues ( const int reportStepIdx ,
const Schedule & sched ,
std : : map < std : : string , data : : GroupData > & gvalues ) const
{
const auto groupGuideRates =
this - > calculateAllGroupGuiderates ( reportStepIdx , sched ) ;
for ( const auto & gname : sched . groupNames ( reportStepIdx ) ) {
const auto & grup = sched . getGroup ( gname , reportStepIdx ) ;
auto & gdata = gvalues [ gname ] ;
this - > assignGroupControl ( grup , gdata ) ;
this - > assignGroupGuideRates ( grup , groupGuideRates , gdata ) ;
}
}
2020-05-15 11:21:32 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
2020-10-21 13:32:12 +02:00
assignNodeValues ( std : : map < std : : string , data : : NodeData > & nodevalues ) const
2020-05-15 11:21:32 +02:00
{
nodevalues . clear ( ) ;
for ( const auto & [ node , pressure ] : node_pressures_ ) {
nodevalues . emplace ( node , data : : NodeData { pressure } ) ;
}
}
2020-07-20 21:38:30 +02:00
template < typename TypeTag >
std : : unordered_map < std : : string , data : : GroupGuideRates >
BlackoilWellModel < TypeTag > : :
calculateAllGroupGuiderates ( const int reportStepIdx , const Schedule & sched ) const
{
auto gr = std : : unordered_map < std : : string , data : : GroupGuideRates > { } ;
auto up = std : : vector < std : : string > { } ;
// Start at well level, accumulate contributions towards root of
// group tree (FIELD group).
for ( const auto & wname : sched . wellNames ( reportStepIdx ) ) {
if ( ! ( this - > well_state_ . hasWellRates ( wname ) & &
this - > guideRate_ - > has ( wname ) ) )
{
continue ;
}
const auto & well = sched . getWell ( wname , reportStepIdx ) ;
const auto & parent = well . groupName ( ) ;
if ( parent = = " FIELD " ) {
// Well parented directly to "FIELD". Inadvisable and
// unexpected, but nothing to do about that here. Just skip
// this guide rate contribution.
continue ;
}
auto & grval = well . isInjector ( )
? gr [ parent ] . injection
: gr [ parent ] . production ;
grval + = this - > getGuideRateValues ( well ) ;
up . push_back ( parent ) ;
}
// Propagate accumulated guide rates up towards root of group tree.
// Override accumulation if there is a GUIDERAT specification that
// applies to a group.
std : : sort ( up . begin ( ) , up . end ( ) ) ;
auto start = 0 * up . size ( ) ;
auto u = std : : unique ( up . begin ( ) , up . end ( ) ) ;
auto nu = std : : distance ( up . begin ( ) , u ) ;
while ( nu > 0 ) {
const auto ntot = up . size ( ) ;
for ( auto gi = 0 * nu ; gi < nu ; + + gi ) {
const auto & gname = up [ start + gi ] ;
const auto & group = sched . getGroup ( gname , reportStepIdx ) ;
if ( this - > guideRate_ - > has ( gname ) ) {
gr [ gname ] . production = this - > getGuideRateValues ( group ) ;
}
const auto parent = group . parent ( ) ;
if ( parent = = " FIELD " ) { continue ; }
gr [ parent ] . injection + = gr [ gname ] . injection ;
gr [ parent ] . production + = gr [ gname ] . production ;
up . push_back ( parent ) ;
}
start = ntot ;
auto begin = up . begin ( ) + ntot ;
std : : sort ( begin , up . end ( ) ) ;
u = std : : unique ( begin , up . end ( ) ) ;
nu = std : : distance ( begin , u ) ;
}
return gr ;
}
2020-07-01 13:52:02 +02:00
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
assignGroupControl ( const Group & group , data : : GroupData & gdata ) const
{
const auto & gname = group . name ( ) ;
const auto grup_type = group . getGroupType ( ) ;
auto & cgc = gdata . currentControl ;
cgc . currentProdConstraint =
: : Opm : : Group : : ProductionCMode : : NONE ;
cgc . currentGasInjectionConstraint =
cgc . currentWaterInjectionConstraint =
: : Opm : : Group : : InjectionCMode : : NONE ;
if ( this - > well_state_ . hasProductionGroupControl ( gname ) ) {
cgc . currentProdConstraint = this - > well_state_
. currentProductionGroupControl ( gname ) ;
}
if ( ( grup_type = = : : Opm : : Group : : GroupType : : INJECTION ) | |
( grup_type = = : : Opm : : Group : : GroupType : : MIXED ) )
{
if ( this - > well_state_ . hasInjectionGroupControl ( : : Opm : : Phase : : WATER , gname ) ) {
cgc . currentWaterInjectionConstraint = this - > well_state_
. currentInjectionGroupControl ( : : Opm : : Phase : : WATER , gname ) ;
}
if ( this - > well_state_ . hasInjectionGroupControl ( : : Opm : : Phase : : GAS , gname ) ) {
cgc . currentGasInjectionConstraint = this - > well_state_
. currentInjectionGroupControl ( : : Opm : : Phase : : GAS , gname ) ;
}
}
}
2018-11-26 12:09:04 +01:00
2020-07-20 21:38:30 +02:00
template < typename TypeTag >
data : : GuideRateValue
BlackoilWellModel < TypeTag > : :
getGuideRateValues ( const Well & well ) const
{
auto grval = data : : GuideRateValue { } ;
assert ( this - > guideRate_ ! = nullptr ) ;
const auto & wname = well . name ( ) ;
if ( ! this - > well_state_ . hasWellRates ( wname ) ) {
// No flow rates for 'wname' -- might be before well comes
// online (e.g., for the initial condition before simulation
// starts).
return grval ;
}
if ( ! this - > guideRate_ - > has ( wname ) ) {
// No guiderates exist for 'wname'.
return grval ;
}
const auto qs = WellGroupHelpers : :
getRateVector ( this - > well_state_ , this - > phase_usage_ , wname ) ;
this - > getGuideRateValues ( qs , well . isInjector ( ) , wname , grval ) ;
return grval ;
}
template < typename TypeTag >
data : : GuideRateValue
BlackoilWellModel < TypeTag > : :
getGuideRateValues ( const Group & group ) const
{
auto grval = data : : GuideRateValue { } ;
assert ( this - > guideRate_ ! = nullptr ) ;
const auto & gname = group . name ( ) ;
if ( ! this - > well_state_ . hasProductionGroupRates ( gname ) ) {
// No flow rates for 'gname' -- might be before group comes
// online (e.g., for the initial condition before simulation
// starts).
return grval ;
}
if ( ! this - > guideRate_ - > has ( gname ) ) {
// No guiderates exist for 'gname'.
return grval ;
}
const auto qs = WellGroupHelpers : :
getProductionGroupRateVector ( this - > well_state_ , this - > phase_usage_ , gname ) ;
const auto is_inj = false ; // This procedure only applies to G*PGR.
this - > getGuideRateValues ( qs , is_inj , gname , grval ) ;
return grval ;
}
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
getGuideRateValues ( const GuideRate : : RateVector & qs ,
const bool is_inj ,
const std : : string & wgname ,
data : : GuideRateValue & grval ) const
{
auto getGR = [ this , & wgname , & qs ] ( const GuideRateModel : : Target t )
{
return this - > guideRate_ - > get ( wgname , t , qs ) ;
} ;
// Note: GuideRate does currently (2020-07-20) not support Target::RES.
grval . set ( data : : GuideRateValue : : Item : : Gas ,
getGR ( GuideRateModel : : Target : : GAS ) ) ;
grval . set ( data : : GuideRateValue : : Item : : Water ,
getGR ( GuideRateModel : : Target : : WAT ) ) ;
if ( ! is_inj ) {
// Producer. Extract "all" guiderate values.
grval . set ( data : : GuideRateValue : : Item : : Oil ,
getGR ( GuideRateModel : : Target : : OIL ) ) ;
}
}
template < typename TypeTag >
void
BlackoilWellModel < TypeTag > : :
assignGroupGuideRates ( const Group & group ,
const std : : unordered_map < std : : string , data : : GroupGuideRates > & groupGuideRates ,
data : : GroupData & gdata ) const
{
auto & prod = gdata . guideRates . production ; prod . clear ( ) ;
auto & inj = gdata . guideRates . injection ; inj . clear ( ) ;
auto xgrPos = groupGuideRates . find ( group . name ( ) ) ;
if ( ( xgrPos = = groupGuideRates . end ( ) ) | |
! this - > guideRate_ - > has ( group . name ( ) ) )
{
// No guiderates defined for this group.
return ;
}
const auto & xgr = xgrPos - > second ;
prod = xgr . production ;
inj = xgr . injection ;
}
2017-02-13 16:45:06 +01:00
} // namespace Opm