2017-06-15 04:34:07 -05:00
/*
2017-08-03 09:45:59 -05:00
Copyright 2017 SINTEF Digital , Mathematics and Cybernetics .
2017-06-15 04:34:07 -05:00
Copyright 2017 Statoil ASA .
2018-06-06 08:17:59 -05:00
Copyright 2018 IRIS
2017-06-15 04:34:07 -05:00
This file is part of the Open Porous Media project ( OPM ) .
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/>.
*/
2022-12-13 05:54:27 -06:00
# include <opm/common/Exceptions.hpp>
2021-12-14 01:30:15 -06:00
# include <opm/input/eclipse/Schedule/ScheduleTypes.hpp>
2019-05-07 06:06:02 -05:00
# include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
2021-05-28 04:27:15 -05:00
# include <opm/simulators/wells/GroupState.hpp>
2020-03-27 07:27:45 -05:00
# include <opm/simulators/wells/TargetCalculator.hpp>
2022-10-19 02:55:14 -05:00
# include <opm/simulators/wells/WellBhpThpCalculator.hpp>
2017-06-15 04:34:07 -05:00
2021-05-25 05:57:11 -05:00
# include <dune/common/version.hh>
2022-12-20 05:49:59 -06:00
# include <fmt/format.h>
2017-06-15 04:34:07 -05:00
namespace Opm
{
2017-06-19 07:49:49 -05:00
template < typename TypeTag >
WellInterface < TypeTag > : :
2019-10-23 02:09:45 -05:00
WellInterface ( const Well & well ,
2020-10-09 08:09:28 -05:00
const ParallelWellInfo & pw_info ,
2019-10-23 02:09:45 -05:00
const int time_step ,
2017-11-23 01:37:30 -06:00
const ModelParameters & param ,
const RateConverterType & rate_converter ,
2017-11-30 09:31:48 -06:00
const int pvtRegionIdx ,
2019-10-23 02:09:45 -05:00
const int num_components ,
const int num_phases ,
const int index_of_well ,
const std : : vector < PerforationData > & perf_data )
2021-05-31 07:31:56 -05:00
: WellInterfaceIndices < FluidSystem , Indices , Scalar > ( well ,
pw_info ,
time_step ,
rate_converter ,
pvtRegionIdx ,
num_components ,
num_phases ,
index_of_well ,
perf_data )
2019-05-02 05:51:25 -05:00
, param_ ( param )
2017-06-15 04:34:07 -05:00
{
2021-05-11 16:03:33 -05:00
connectionRates_ . resize ( this - > number_of_perforations_ ) ;
2018-11-15 07:37:01 -06:00
2021-05-11 05:28:10 -05:00
if constexpr ( has_solvent | | has_zFraction ) {
if ( well . isInjector ( ) ) {
2021-05-11 16:03:33 -05:00
auto injectorType = this - > well_ecl_ . injectorType ( ) ;
2021-05-11 05:28:10 -05:00
if ( injectorType = = InjectorType : : GAS ) {
2021-05-11 16:03:33 -05:00
this - > wsolvent_ = this - > well_ecl_ . getSolventFraction ( ) ;
2021-05-11 05:28:10 -05:00
}
2019-09-23 08:15:55 -05:00
}
}
2017-06-15 04:34:07 -05:00
}
2017-06-19 07:49:49 -05:00
template < typename TypeTag >
2017-06-15 04:34:07 -05:00
void
2017-06-19 07:49:49 -05:00
WellInterface < TypeTag > : :
2017-06-15 04:34:07 -05:00
init ( const PhaseUsage * phase_usage_arg ,
2017-08-01 09:11:55 -05:00
const std : : vector < double > & /* depth_arg */ ,
2017-06-21 07:07:11 -05:00
const double gravity_arg ,
2020-11-27 00:57:55 -06:00
const int /* num_cells */ ,
2022-04-12 01:44:52 -05:00
const std : : vector < Scalar > & B_avg ,
const bool changed_to_open_this_step )
2017-06-15 04:34:07 -05:00
{
2021-05-11 16:03:33 -05:00
this - > phase_usage_ = phase_usage_arg ;
this - > gravity_ = gravity_arg ;
2020-11-27 00:57:55 -06:00
B_avg_ = B_avg ;
2022-04-12 01:44:52 -05:00
this - > changed_to_open_this_step_ = changed_to_open_this_step ;
2017-06-15 04:34:07 -05:00
}
2017-06-19 09:46:06 -05:00
2017-07-25 05:10:13 -05:00
template < typename TypeTag >
double
WellInterface < TypeTag > : :
wpolymer ( ) const
{
2021-05-11 05:28:10 -05:00
if constexpr ( has_polymer ) {
2023-01-12 08:15:42 -06:00
return this - > wpolymer_ ( ) ;
2017-07-25 05:10:13 -05:00
}
2021-05-11 05:28:10 -05:00
return 0.0 ;
2017-06-23 03:58:46 -05:00
}
2017-07-24 07:48:57 -05:00
2019-07-04 02:50:08 -05:00
template < typename TypeTag >
double
WellInterface < TypeTag > : :
wfoam ( ) const
{
2021-05-11 05:28:10 -05:00
if constexpr ( has_foam ) {
2023-01-12 08:15:42 -06:00
return this - > wfoam_ ( ) ;
2019-07-04 02:50:08 -05:00
}
2021-05-11 05:28:10 -05:00
return 0.0 ;
2019-07-04 02:50:08 -05:00
}
2019-11-07 02:39:42 -06:00
template < typename TypeTag >
double
WellInterface < TypeTag > : :
wsalt ( ) const
{
2021-05-11 05:28:10 -05:00
if constexpr ( has_brine ) {
2023-01-12 08:15:42 -06:00
return this - > wsalt_ ( ) ;
2019-11-07 02:39:42 -06:00
}
2021-05-11 05:28:10 -05:00
return 0.0 ;
2019-11-07 02:39:42 -06:00
}
2021-10-06 12:32:35 -05:00
template < typename TypeTag >
double
WellInterface < TypeTag > : :
wmicrobes ( ) const
{
if constexpr ( has_micp ) {
2023-01-12 08:15:42 -06:00
return this - > wmicrobes_ ( ) ;
2021-10-06 12:32:35 -05:00
}
return 0.0 ;
}
template < typename TypeTag >
double
WellInterface < TypeTag > : :
woxygen ( ) const
{
if constexpr ( has_micp ) {
2023-01-12 08:15:42 -06:00
return this - > woxygen_ ( ) ;
2021-10-06 12:32:35 -05:00
}
return 0.0 ;
}
// The urea injection concentration is scaled down by a factor of 10, since its value
// can be much bigger than 1 (not doing this slows the simulations). The
// corresponding values are scaled accordingly in blackoilmicpmodules.hh when computing
// the reactions and also when writing the output files (vtk and eclipse format, i.e.,
// vtkblackoilmicpmodule.hh and ecloutputblackoilmodel.hh respectively).
template < typename TypeTag >
double
WellInterface < TypeTag > : :
wurea ( ) const
{
if constexpr ( has_micp ) {
2023-01-12 08:15:42 -06:00
return this - > wurea_ ( ) ;
2021-10-06 12:32:35 -05:00
}
return 0.0 ;
}
2019-11-07 02:39:42 -06:00
2017-09-05 02:42:44 -05:00
template < typename TypeTag >
2020-02-10 08:16:09 -06:00
bool
2017-09-05 02:42:44 -05:00
WellInterface < TypeTag > : :
2019-05-10 07:56:20 -05:00
updateWellControl ( const Simulator & ebos_simulator ,
2020-02-10 08:16:09 -06:00
const IndividualOrGroup iog ,
2018-11-15 03:08:03 -06:00
WellState & well_state ,
2021-04-22 10:31:21 -05:00
const GroupState & group_state ,
2021-05-12 01:50:04 -05:00
DeferredLogger & deferred_logger ) /* const */
2017-09-05 02:42:44 -05:00
{
2022-09-19 01:33:57 -05:00
if ( this - > wellIsStopped ( ) ) {
2020-02-10 08:16:09 -06:00
return false ;
2019-10-07 04:54:57 -05:00
}
2018-11-16 02:00:11 -06:00
2019-08-07 07:13:11 -05:00
const auto & summaryState = ebos_simulator . vanguard ( ) . summaryState ( ) ;
2020-02-10 08:16:09 -06:00
const auto & schedule = ebos_simulator . vanguard ( ) . schedule ( ) ;
2021-05-11 16:03:33 -05:00
const auto & well = this - > well_ecl_ ;
2021-08-04 05:03:36 -05:00
auto & ws = well_state . well ( this - > index_of_well_ ) ;
2019-08-07 07:13:11 -05:00
std : : string from ;
if ( well . isInjector ( ) ) {
2023-01-18 02:58:57 -06:00
from = WellInjectorCMode2String ( ws . injection_cmode ) ;
2019-08-07 07:13:11 -05:00
} else {
2023-01-18 02:58:57 -06:00
from = WellProducerCMode2String ( ws . production_cmode ) ;
2017-09-05 02:42:44 -05:00
}
2021-10-08 02:47:22 -05:00
bool oscillating = std : : count ( this - > well_control_log_ . begin ( ) , this - > well_control_log_ . end ( ) , from ) > = param_ . max_number_of_well_switches_ ;
2021-04-15 01:14:52 -05:00
if ( oscillating ) {
// only output frist time
2021-10-08 02:47:22 -05:00
bool output = std : : count ( this - > well_control_log_ . begin ( ) , this - > well_control_log_ . end ( ) , from ) = = param_ . max_number_of_well_switches_ ;
2021-04-15 01:14:52 -05:00
if ( output ) {
std : : ostringstream ss ;
ss < < " The control model for well " < < this - > name ( )
< < " is oscillating \n "
< < " We don't allow for more than "
< < param_ . max_number_of_well_switches_
< < " switches. The control is kept at " < < from ;
deferred_logger . info ( ss . str ( ) ) ;
// add one more to avoid outputting the same info again
2021-10-08 02:47:22 -05:00
this - > well_control_log_ . push_back ( from ) ;
2021-04-15 01:14:52 -05:00
}
return false ;
}
2020-02-10 08:16:09 -06:00
bool changed = false ;
if ( iog = = IndividualOrGroup : : Individual ) {
2022-02-23 04:11:11 -06:00
changed = this - > checkIndividualConstraints ( ws , summaryState , deferred_logger ) ;
2020-02-10 08:16:09 -06:00
} else if ( iog = = IndividualOrGroup : : Group ) {
2021-05-11 16:03:33 -05:00
changed = this - > checkGroupConstraints ( well_state , group_state , schedule , summaryState , deferred_logger ) ;
2020-02-10 08:16:09 -06:00
} else {
assert ( iog = = IndividualOrGroup : : Both ) ;
2021-05-11 16:03:33 -05:00
changed = this - > checkConstraints ( well_state , group_state , schedule , summaryState , deferred_logger ) ;
2020-02-10 08:16:09 -06:00
}
2021-05-25 05:57:11 -05:00
Parallel : : Communication cc = ebos_simulator . vanguard ( ) . grid ( ) . comm ( ) ;
2017-09-05 02:42:44 -05:00
// checking whether control changed
2019-08-07 07:13:11 -05:00
if ( changed ) {
std : : string to ;
if ( well . isInjector ( ) ) {
2023-01-18 02:58:57 -06:00
to = WellInjectorCMode2String ( ws . injection_cmode ) ;
2019-08-07 07:13:11 -05:00
} else {
2023-01-18 02:58:57 -06:00
to = WellProducerCMode2String ( ws . production_cmode ) ;
2019-08-07 07:13:11 -05:00
}
2019-01-18 07:04:30 -06:00
std : : ostringstream ss ;
2021-05-11 16:03:33 -05:00
ss < < " Switching control mode for well " < < this - > name ( )
2019-08-07 07:13:11 -05:00
< < " from " < < from
< < " to " < < to ;
2019-01-21 01:26:28 -06:00
if ( cc . size ( ) > 1 ) {
2019-01-18 08:48:05 -06:00
ss < < " on rank " < < cc . rank ( ) ;
}
2021-10-08 02:47:22 -05:00
deferred_logger . debug ( ss . str ( ) ) ;
this - > well_control_log_ . push_back ( from ) ;
2021-06-10 08:09:05 -05:00
updateWellStateWithTarget ( ebos_simulator , group_state , well_state , deferred_logger ) ;
2019-02-07 07:43:17 -06:00
updatePrimaryVariables ( well_state , deferred_logger ) ;
2017-09-05 02:42:44 -05:00
}
2020-02-10 08:16:09 -06:00
return changed ;
2017-09-05 02:42:44 -05:00
}
2018-10-31 08:56:56 -05:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2021-03-18 08:49:52 -05:00
wellTesting ( const Simulator & simulator ,
2021-08-24 02:00:06 -05:00
const double simulation_time ,
2018-11-22 08:44:09 -06:00
/* const */ WellState & well_state ,
2021-04-22 10:31:21 -05:00
const GroupState & group_state ,
2018-11-23 16:46:56 -06:00
WellTestState & well_test_state ,
2021-05-12 01:50:04 -05:00
DeferredLogger & deferred_logger )
2018-10-31 08:56:56 -05:00
{
2021-08-24 02:00:06 -05:00
deferred_logger . info ( " well " + this - > name ( ) + " is being tested " ) ;
2018-11-23 06:11:48 -06:00
2018-10-31 08:56:56 -05:00
WellState well_state_copy = well_state ;
2021-08-05 03:57:15 -05:00
auto & ws = well_state_copy . well ( this - > indexOfWell ( ) ) ;
2018-10-31 08:56:56 -05:00
2021-06-10 08:09:05 -05:00
updateWellStateWithTarget ( simulator , group_state , well_state_copy , deferred_logger ) ;
2019-06-26 15:40:33 -05:00
calculateExplicitQuantities ( simulator , well_state_copy , deferred_logger ) ;
2019-02-07 07:43:17 -06:00
updatePrimaryVariables ( well_state_copy , deferred_logger ) ;
2018-10-31 08:56:56 -05:00
initPrimaryVariablesEvaluation ( ) ;
2022-10-10 01:14:42 -05:00
if ( this - > isProducer ( ) ) {
gliftBeginTimeStepWellTestUpdateALQ ( simulator , well_state_copy , deferred_logger ) ;
}
2018-10-31 08:56:56 -05:00
WellTestState welltest_state_temp ;
bool testWell = true ;
// if a well is closed because all completions are closed, we need to check each completion
// individually. We first open all completions, then we close one by one by calling updateWellTestState
// untill the number of closed completions do not increase anymore.
while ( testWell ) {
2021-10-06 07:00:23 -05:00
const size_t original_number_closed_completions = welltest_state_temp . num_closed_completions ( ) ;
2021-08-24 02:00:06 -05:00
bool converged = solveWellForTesting ( simulator , well_state_copy , group_state , deferred_logger ) ;
if ( ! converged ) {
const auto msg = fmt : : format ( " WTEST: Well {} is not solvable (physical) " , this - > name ( ) ) ;
deferred_logger . debug ( msg ) ;
return ;
}
2022-10-10 01:14:42 -05:00
2021-08-24 02:00:06 -05:00
updateWellOperability ( simulator , well_state_copy , deferred_logger ) ;
2021-09-29 09:01:16 -05:00
if ( ! this - > isOperableAndSolvable ( ) ) {
2021-08-24 02:00:06 -05:00
const auto msg = fmt : : format ( " WTEST: Well {} is not operable (physical) " , this - > name ( ) ) ;
deferred_logger . debug ( msg ) ;
return ;
}
2021-06-30 05:56:18 -05:00
std : : vector < double > potentials ;
try {
computeWellPotentials ( simulator , well_state_copy , potentials , deferred_logger ) ;
} catch ( const std : : exception & e ) {
const std : : string msg = std : : string ( " well " ) + this - > name ( ) + std : : string ( " : computeWellPotentials() failed during testing for re-opening: " ) + e . what ( ) ;
deferred_logger . info ( msg ) ;
2021-08-24 02:00:06 -05:00
return ;
2021-06-30 05:56:18 -05:00
}
const int np = well_state_copy . numPhases ( ) ;
for ( int p = 0 ; p < np ; + + p ) {
2021-11-18 05:57:16 -06:00
ws . well_potentials [ p ] = std : : max ( 0.0 , potentials [ p ] ) ;
2021-06-30 05:56:18 -05:00
}
2021-09-20 04:16:32 -05:00
this - > updateWellTestState ( well_state_copy . well ( this - > indexOfWell ( ) ) , simulation_time , /*writeMessageToOPMLog=*/ false , welltest_state_temp , deferred_logger ) ;
2021-05-11 16:03:33 -05:00
this - > closeCompletions ( welltest_state_temp ) ;
2018-10-31 08:56:56 -05:00
// Stop testing if the well is closed or shut due to all completions shut
// Also check if number of completions has increased. If the number of closed completions do not increased
// we stop the testing.
// TODO: it can be tricky here, if the well is shut/closed due to other reasons
2021-10-06 07:00:23 -05:00
if ( welltest_state_temp . num_closed_wells ( ) > 0 | |
( original_number_closed_completions = = welltest_state_temp . num_closed_completions ( ) ) ) {
2018-10-31 08:56:56 -05:00
testWell = false ; // this terminates the while loop
}
}
// update wellTestState if the well test succeeds
2021-10-07 08:18:53 -05:00
if ( ! welltest_state_temp . well_is_closed ( this - > name ( ) ) ) {
2021-10-07 08:30:10 -05:00
well_test_state . open_well ( this - > name ( ) ) ;
2021-08-24 02:00:06 -05:00
std : : string msg = std : : string ( " well " ) + this - > name ( ) + std : : string ( " is re-opened " ) ;
2019-02-03 01:13:11 -06:00
deferred_logger . info ( msg ) ;
2018-10-31 08:56:56 -05:00
// also reopen completions
2021-05-11 16:03:33 -05:00
for ( auto & completion : this - > well_ecl_ . getCompletions ( ) ) {
2021-10-08 03:40:38 -05:00
if ( ! welltest_state_temp . completion_is_closed ( this - > name ( ) , completion . first ) )
well_test_state . open_completion ( this - > name ( ) , completion . first ) ;
2018-10-31 08:56:56 -05:00
}
2021-09-21 03:32:56 -05:00
// set the status of the well_state to open
ws . open ( ) ;
2021-06-30 05:56:18 -05:00
well_state = well_state_copy ;
2018-10-31 08:56:56 -05:00
}
2017-07-26 04:01:26 -05:00
}
2017-07-31 09:42:26 -05:00
2021-08-24 02:00:06 -05:00
2020-06-09 16:35:58 -05:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
iterateWellEquations ( const Simulator & ebosSimulator ,
const double dt ,
WellState & well_state ,
2021-04-22 10:31:21 -05:00
const GroupState & group_state ,
2021-05-12 01:50:04 -05:00
DeferredLogger & deferred_logger )
2020-06-09 16:35:58 -05:00
{
const auto & summary_state = ebosSimulator . vanguard ( ) . summaryState ( ) ;
2021-05-11 16:03:33 -05:00
const auto inj_controls = this - > well_ecl_ . isInjector ( ) ? this - > well_ecl_ . injectionControls ( summary_state ) : Well : : InjectionControls ( 0 ) ;
const auto prod_controls = this - > well_ecl_ . isProducer ( ) ? this - > well_ecl_ . productionControls ( summary_state ) : Well : : ProductionControls ( 0 ) ;
2021-10-05 04:27:44 -05:00
bool converged = false ;
try {
converged = this - > iterateWellEqWithControl ( ebosSimulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2022-12-13 05:54:27 -06:00
} catch ( NumericalProblem & e ) {
2021-10-05 04:27:44 -05:00
const std : : string msg = " Inner well iterations failed for well " + this - > name ( ) + " Treat the well as unconverged. " ;
deferred_logger . warning ( " INNER_ITERATION_FAILED " , msg ) ;
converged = false ;
}
return converged ;
2020-06-09 16:35:58 -05:00
}
2018-06-21 07:40:04 -05:00
template < typename TypeTag >
2021-08-24 02:00:06 -05:00
bool
2018-11-22 08:44:09 -06:00
WellInterface < TypeTag > : :
2021-04-22 10:31:21 -05:00
solveWellForTesting ( const Simulator & ebosSimulator , WellState & well_state , const GroupState & group_state ,
2021-05-12 01:50:04 -05:00
DeferredLogger & deferred_logger )
2018-06-21 07:40:04 -05:00
{
2018-11-22 08:44:09 -06:00
// keep a copy of the original well state
const WellState well_state0 = well_state ;
2020-06-02 07:54:45 -05:00
const double dt = ebosSimulator . timeStepSize ( ) ;
2022-02-17 01:58:38 -06:00
const auto & summary_state = ebosSimulator . vanguard ( ) . summaryState ( ) ;
const bool has_thp_limit = this - > wellHasTHPConstraints ( summary_state ) ;
if ( has_thp_limit )
well_state . well ( this - > indexOfWell ( ) ) . production_cmode = Well : : ProducerCMode : : THP ;
else
well_state . well ( this - > indexOfWell ( ) ) . production_cmode = Well : : ProducerCMode : : BHP ;
2021-04-22 10:31:21 -05:00
const bool converged = iterateWellEquations ( ebosSimulator , dt , well_state , group_state , deferred_logger ) ;
2018-06-21 07:40:04 -05:00
if ( converged ) {
2021-05-11 16:03:33 -05:00
deferred_logger . debug ( " WellTest: Well equation for well " + this - > name ( ) + " converged " ) ;
2021-08-24 02:00:06 -05:00
return true ;
2018-06-21 07:40:04 -05:00
}
2021-08-24 02:00:06 -05:00
const int max_iter = param_ . max_welleq_iter_ ;
deferred_logger . debug ( " WellTest: Well equation for well " + this - > name ( ) + " failed converging in "
+ std : : to_string ( max_iter ) + " iterations " ) ;
well_state = well_state0 ;
return false ;
2018-06-21 07:40:04 -05:00
}
2018-11-02 09:21:58 -05:00
2021-08-24 02:00:06 -05:00
2021-01-04 07:00:59 -06:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2021-01-07 04:20:50 -06:00
solveWellEquation ( const Simulator & ebosSimulator ,
WellState & well_state ,
2021-04-22 10:31:21 -05:00
const GroupState & group_state ,
2021-05-12 01:50:04 -05:00
DeferredLogger & deferred_logger )
2021-01-04 07:00:59 -06:00
{
2022-04-19 07:13:48 -05:00
if ( ! this - > isOperableAndSolvable ( ) & & ! this - > wellIsStopped ( ) )
2021-01-19 07:43:32 -06:00
return ;
2021-01-04 07:00:59 -06:00
// keep a copy of the original well state
const WellState well_state0 = well_state ;
const double dt = ebosSimulator . timeStepSize ( ) ;
2022-10-06 01:33:42 -05:00
bool converged = iterateWellEquations ( ebosSimulator , dt , well_state , group_state , deferred_logger ) ;
// Newly opened wells with THP control sometimes struggles to
// converge due to bad initial guess. Or due to the simple fact
// that the well needs to change to another control.
// We therefore try to solve the well with BHP control to get
// an better initial guess.
// If the well is supposed to operate under THP control
// "updateWellControl" will switch it back to THP later.
if ( ! converged ) {
auto & ws = well_state . well ( this - > indexOfWell ( ) ) ;
bool thp_control = false ;
if ( this - > well_ecl_ . isInjector ( ) ) {
thp_control = ws . injection_cmode = = Well : : InjectorCMode : : THP ;
if ( thp_control ) {
ws . injection_cmode = Well : : InjectorCMode : : BHP ;
2023-01-18 02:58:57 -06:00
this - > well_control_log_ . push_back ( WellInjectorCMode2String ( Well : : InjectorCMode : : THP ) ) ;
2022-10-06 01:33:42 -05:00
}
} else {
thp_control = ws . production_cmode = = Well : : ProducerCMode : : THP ;
if ( thp_control ) {
ws . production_cmode = Well : : ProducerCMode : : BHP ;
2023-01-18 02:58:57 -06:00
this - > well_control_log_ . push_back ( WellProducerCMode2String ( Well : : ProducerCMode : : THP ) ) ;
2022-10-06 01:33:42 -05:00
}
}
if ( thp_control ) {
const std : : string msg = std : : string ( " The newly opened well " ) + this - > name ( )
+ std : : string ( " with THP control did not converge during inner iterations, we try again with bhp control " ) ;
deferred_logger . debug ( msg ) ;
converged = this - > iterateWellEquations ( ebosSimulator , dt , well_state , group_state , deferred_logger ) ;
}
}
2021-04-16 07:44:14 -05:00
if ( ! converged ) {
2021-01-04 07:00:59 -06:00
const int max_iter = param_ . max_welleq_iter_ ;
2021-05-11 16:03:33 -05:00
deferred_logger . debug ( " Compute initial well solution for well " + this - > name ( ) + " . Failed to converge in "
2021-01-04 07:00:59 -06:00
+ std : : to_string ( max_iter ) + " iterations " ) ;
well_state = well_state0 ;
}
}
2021-05-10 02:41:18 -05:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
assembleWellEq ( const Simulator & ebosSimulator ,
const double dt ,
WellState & well_state ,
const GroupState & group_state ,
DeferredLogger & deferred_logger )
{
2021-09-29 09:01:16 -05:00
const bool old_well_operable = this - > operability_status_ . isOperableAndSolvable ( ) ;
2021-09-21 04:34:19 -05:00
2021-09-29 09:01:16 -05:00
if ( param_ . check_well_operability_iter_ )
2021-09-21 04:34:19 -05:00
checkWellOperability ( ebosSimulator , well_state , deferred_logger ) ;
2021-05-10 02:41:18 -05:00
2021-06-04 03:51:15 -05:00
// only use inner well iterations for the first newton iterations.
const int iteration_idx = ebosSimulator . model ( ) . newtonMethod ( ) . numIterations ( ) ;
2022-01-20 15:39:38 -06:00
if ( iteration_idx < param_ . max_niter_inner_well_iter_ | | this - > well_ecl_ . isMultiSegment ( ) ) {
2021-09-21 04:34:19 -05:00
this - > operability_status_ . solvable = true ;
bool converged = this - > iterateWellEquations ( ebosSimulator , dt , well_state , group_state , deferred_logger ) ;
2021-04-16 07:44:14 -05:00
2021-09-21 04:34:19 -05:00
// unsolvable wells are treated as not operable and will not be solved for in this iteration.
if ( ! converged ) {
2021-10-14 06:14:38 -05:00
if ( param_ . shut_unsolvable_wells_ )
2021-09-21 04:34:19 -05:00
this - > operability_status_ . solvable = false ;
}
2021-04-16 07:44:14 -05:00
}
2021-11-22 05:27:45 -06:00
if ( this - > operability_status_ . has_negative_potentials ) {
2021-11-18 05:57:16 -06:00
auto well_state_copy = well_state ;
std : : vector < double > potentials ;
try {
computeWellPotentials ( ebosSimulator , well_state_copy , potentials , deferred_logger ) ;
} catch ( const std : : exception & e ) {
2021-11-23 06:56:03 -06:00
const std : : string msg = std : : string ( " well " ) + this - > name ( ) + std : : string ( " : computeWellPotentials() failed during attempt to recompute potentials for well : " ) + e . what ( ) ;
2021-11-18 05:57:16 -06:00
deferred_logger . info ( msg ) ;
2021-11-22 05:27:45 -06:00
this - > operability_status_ . has_negative_potentials = true ;
2021-11-18 05:57:16 -06:00
}
auto & ws = well_state . well ( this - > indexOfWell ( ) ) ;
const int np = well_state . numPhases ( ) ;
for ( int p = 0 ; p < np ; + + p ) {
ws . well_potentials [ p ] = std : : max ( 0.0 , potentials [ p ] ) ;
}
}
this - > changed_to_open_this_step_ = false ;
2021-09-29 09:01:16 -05:00
const bool well_operable = this - > operability_status_ . isOperableAndSolvable ( ) ;
2022-04-19 07:13:48 -05:00
2021-04-16 07:44:14 -05:00
if ( ! well_operable & & old_well_operable ) {
if ( this - > well_ecl_ . getAutomaticShutIn ( ) ) {
deferred_logger . info ( " well " + this - > name ( ) + " gets SHUT during iteration " ) ;
} else {
if ( ! this - > wellIsStopped ( ) ) {
deferred_logger . info ( " well " + this - > name ( ) + " gets STOPPED during iteration " ) ;
this - > stopWell ( ) ;
changed_to_stopped_this_step_ = true ;
}
}
} else if ( well_operable & & ! old_well_operable ) {
deferred_logger . info ( " well " + this - > name ( ) + " gets REVIVED during iteration " ) ;
this - > openWell ( ) ;
changed_to_stopped_this_step_ = false ;
2021-11-18 05:57:16 -06:00
this - > changed_to_open_this_step_ = true ;
2021-05-10 02:41:18 -05:00
}
const auto & summary_state = ebosSimulator . vanguard ( ) . summaryState ( ) ;
2021-05-11 16:03:33 -05:00
const auto inj_controls = this - > well_ecl_ . isInjector ( ) ? this - > well_ecl_ . injectionControls ( summary_state ) : Well : : InjectionControls ( 0 ) ;
const auto prod_controls = this - > well_ecl_ . isProducer ( ) ? this - > well_ecl_ . productionControls ( summary_state ) : Well : : ProductionControls ( 0 ) ;
2021-05-10 02:41:18 -05:00
assembleWellEqWithoutIteration ( ebosSimulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
}
2018-11-02 09:21:58 -05:00
template < typename TypeTag >
void
2018-11-15 07:37:01 -06:00
WellInterface < TypeTag > : : addCellRates ( RateVector & rates , int cellIdx ) const
{
2022-04-19 07:13:48 -05:00
if ( ! this - > isOperableAndSolvable ( ) & & ! this - > wellIsStopped ( ) )
2022-04-05 07:42:27 -05:00
return ;
2021-05-11 16:03:33 -05:00
for ( int perfIdx = 0 ; perfIdx < this - > number_of_perforations_ ; + + perfIdx ) {
if ( this - > cells ( ) [ perfIdx ] = = cellIdx ) {
2018-11-15 07:37:01 -06:00
for ( int i = 0 ; i < RateVector : : dimension ; + + i ) {
rates [ i ] + = connectionRates_ [ perfIdx ] [ i ] ;
}
}
}
}
2018-11-14 06:18:48 -06:00
template < typename TypeTag >
typename WellInterface < TypeTag > : : Scalar
WellInterface < TypeTag > : : volumetricSurfaceRateForConnection ( int cellIdx , int phaseIdx ) const {
2021-05-11 16:03:33 -05:00
for ( int perfIdx = 0 ; perfIdx < this - > number_of_perforations_ ; + + perfIdx ) {
if ( this - > cells ( ) [ perfIdx ] = = cellIdx ) {
2018-11-14 06:18:48 -06:00
const unsigned activeCompIdx = Indices : : canonicalToActiveComponentIndex ( FluidSystem : : solventComponentIndex ( phaseIdx ) ) ;
return connectionRates_ [ perfIdx ] [ activeCompIdx ] . value ( ) ;
}
}
2019-02-07 07:43:17 -06:00
// this is not thread safe
2021-05-11 16:03:33 -05:00
OPM_THROW ( std : : invalid_argument , " The well with name " + this - > name ( )
2018-11-14 06:18:48 -06:00
+ " does not perforate cell " + std : : to_string ( cellIdx ) ) ;
return 0.0 ;
}
2018-11-02 09:21:58 -05:00
2020-11-27 00:57:55 -06:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
checkWellOperability ( const Simulator & ebos_simulator ,
const WellState & well_state ,
2021-05-12 01:50:04 -05:00
DeferredLogger & deferred_logger )
2020-11-27 00:57:55 -06:00
{
2021-09-29 09:01:16 -05:00
if ( ! param_ . check_well_operability_ ) {
2020-11-27 00:57:55 -06:00
return ;
}
if ( this - > wellIsStopped ( ) & & ! changed_to_stopped_this_step_ ) {
return ;
}
updateWellOperability ( ebos_simulator , well_state , deferred_logger ) ;
2022-02-22 08:25:10 -06:00
if ( ! this - > operability_status_ . isOperableAndSolvable ( ) ) {
2022-04-08 03:15:13 -05:00
this - > operability_status_ . use_vfpexplicit = true ;
2022-02-22 08:25:10 -06:00
deferred_logger . debug ( " EXPLICIT_LOOKUP_VFP " ,
" well not operable, trying with explicit vfp lookup: " + this - > name ( ) ) ;
updateWellOperability ( ebos_simulator , well_state , deferred_logger ) ;
}
2021-04-16 07:44:14 -05:00
}
2020-11-27 00:57:55 -06:00
2022-03-27 00:57:31 -05:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
gliftBeginTimeStepWellTestUpdateALQ ( const Simulator & ebos_simulator ,
WellState & well_state ,
DeferredLogger & deferred_logger )
{
const auto & summary_state = ebos_simulator . vanguard ( ) . summaryState ( ) ;
const auto & well_name = this - > name ( ) ;
if ( ! this - > wellHasTHPConstraints ( summary_state ) ) {
const std : : string msg = fmt : : format ( " GLIFT WTEST: Well {} does not have THP constraints " , well_name ) ;
deferred_logger . info ( msg ) ;
return ;
}
const auto & well_ecl = this - > wellEcl ( ) ;
const auto & schedule = ebos_simulator . vanguard ( ) . schedule ( ) ;
auto report_step_idx = ebos_simulator . episodeIndex ( ) ;
const auto & glo = schedule . glo ( report_step_idx ) ;
if ( ! glo . has_well ( well_name ) ) {
const std : : string msg = fmt : : format (
" GLIFT WTEST: Well {} : Gas Lift not activated: "
" WLIFTOPT is probably missing. Skipping. " , well_name ) ;
deferred_logger . info ( msg ) ;
return ;
}
const auto & gl_well = glo . well ( well_name ) ;
auto & max_alq_optional = gl_well . max_rate ( ) ;
double max_alq ;
if ( max_alq_optional ) {
max_alq = * max_alq_optional ;
}
else {
const auto & controls = well_ecl . productionControls ( summary_state ) ;
const auto & table = this - > vfpProperties ( ) - > getProd ( ) - > getTable ( controls . vfp_table_number ) ;
const auto & alq_values = table . getALQAxis ( ) ;
max_alq = alq_values . back ( ) ;
}
well_state . setALQ ( well_name , max_alq ) ;
const std : : string msg = fmt : : format (
" GLIFT WTEST: Well {} : Setting ALQ to max value: {} " ,
well_name , max_alq ) ;
deferred_logger . info ( msg ) ;
}
2020-11-27 00:57:55 -06:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
updateWellOperability ( const Simulator & ebos_simulator ,
const WellState & well_state ,
2021-05-12 01:50:04 -05:00
DeferredLogger & deferred_logger )
2020-11-27 00:57:55 -06:00
{
2021-09-29 09:01:16 -05:00
this - > operability_status_ . resetOperability ( ) ;
2020-11-27 00:57:55 -06:00
2022-10-17 08:22:04 -05:00
bool thp_controlled = this - > isInjector ( ) ? well_state . well ( this - > index_of_well_ ) . injection_cmode = = Well : : InjectorCMode : : THP :
2021-10-01 06:47:53 -05:00
well_state . well ( this - > index_of_well_ ) . production_cmode = = Well : : ProducerCMode : : THP ;
2022-10-17 08:22:04 -05:00
bool bhp_controlled = this - > isInjector ( ) ? well_state . well ( this - > index_of_well_ ) . injection_cmode = = Well : : InjectorCMode : : BHP :
2021-10-01 06:47:53 -05:00
well_state . well ( this - > index_of_well_ ) . production_cmode = = Well : : ProducerCMode : : BHP ;
2020-12-08 02:51:26 -06:00
// Operability checking is not free
// Only check wells under BHP and THP control
2022-10-17 08:22:04 -05:00
bool check_thp = thp_controlled | | this - > operability_status_ . thp_limit_violated_but_not_switched ;
if ( check_thp | | bhp_controlled ) {
2020-12-08 02:51:26 -06:00
updateIPR ( ebos_simulator , deferred_logger ) ;
2021-10-01 06:47:53 -05:00
checkOperabilityUnderBHPLimit ( well_state , ebos_simulator , deferred_logger ) ;
2020-12-08 02:51:26 -06:00
}
// we do some extra checking for wells under THP control.
2022-08-19 03:48:56 -05:00
if ( check_thp ) {
2021-10-01 06:47:53 -05:00
checkOperabilityUnderTHPLimit ( ebos_simulator , well_state , deferred_logger ) ;
2020-11-27 00:57:55 -06:00
}
}
2018-11-17 16:14:51 -06:00
2021-04-26 02:31:29 -05:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
updateWellStateWithTarget ( const Simulator & ebos_simulator ,
2021-06-10 08:09:05 -05:00
const GroupState & group_state ,
2021-04-26 02:31:29 -05:00
WellState & well_state ,
2021-05-12 01:50:04 -05:00
DeferredLogger & deferred_logger ) const
2021-04-26 02:31:29 -05:00
{
// only bhp and wellRates are used to initilize the primaryvariables for standard wells
2021-05-11 16:03:33 -05:00
const auto & well = this - > well_ecl_ ;
const int well_index = this - > index_of_well_ ;
2021-08-03 13:05:14 -05:00
auto & ws = well_state . well ( well_index ) ;
2021-05-11 16:03:33 -05:00
const auto & pu = this - > phaseUsage ( ) ;
2021-04-26 02:31:29 -05:00
const int np = well_state . numPhases ( ) ;
const auto & summaryState = ebos_simulator . vanguard ( ) . summaryState ( ) ;
2021-06-10 08:09:05 -05:00
const auto & schedule = ebos_simulator . vanguard ( ) . schedule ( ) ;
2021-04-26 02:31:29 -05:00
if ( this - > wellIsStopped ( ) ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] = 0 ;
2021-04-26 02:31:29 -05:00
}
2021-08-03 13:05:14 -05:00
ws . thp = 0 ;
2021-04-26 02:31:29 -05:00
return ;
}
if ( this - > isInjector ( ) )
{
const auto & controls = well . injectionControls ( summaryState ) ;
InjectorType injectorType = controls . injector_type ;
int phasePos ;
switch ( injectorType ) {
case InjectorType : : WATER :
{
phasePos = pu . phase_pos [ BlackoilPhases : : Aqua ] ;
break ;
}
case InjectorType : : OIL :
{
phasePos = pu . phase_pos [ BlackoilPhases : : Liquid ] ;
break ;
}
case InjectorType : : GAS :
{
phasePos = pu . phase_pos [ BlackoilPhases : : Vapour ] ;
break ;
}
default :
2021-05-11 16:03:33 -05:00
OPM_DEFLOG_THROW ( std : : runtime_error , " Expected WATER, OIL or GAS as type for injectors " + this - > name ( ) , deferred_logger ) ;
2021-04-26 02:31:29 -05:00
}
2021-08-04 05:03:36 -05:00
const auto current = ws . injection_cmode ;
2021-04-26 02:31:29 -05:00
switch ( current ) {
case Well : : InjectorCMode : : RATE :
{
2022-04-21 04:03:11 -05:00
ws . surface_rates [ phasePos ] = ( 1.0 - this - > rsRvInj ( ) ) * controls . surface_rate ;
if ( this - > rsRvInj ( ) > 0 ) {
if ( injectorType = = InjectorType : : OIL & & FluidSystem : : phaseIsActive ( FluidSystem : : gasPhaseIdx ) ) {
ws . surface_rates [ pu . phase_pos [ BlackoilPhases : : Vapour ] ] = controls . surface_rate * this - > rsRvInj ( ) ;
} else if ( injectorType = = InjectorType : : GAS & & FluidSystem : : phaseIsActive ( FluidSystem : : oilPhaseIdx ) ) {
ws . surface_rates [ pu . phase_pos [ BlackoilPhases : : Liquid ] ] = controls . surface_rate * this - > rsRvInj ( ) ;
} else {
OPM_DEFLOG_THROW ( std : : runtime_error , " Expected OIL or GAS as type for injectors when RS/RV (item 10) is non-zero " + this - > name ( ) , deferred_logger ) ;
}
}
2021-04-26 02:31:29 -05:00
break ;
}
case Well : : InjectorCMode : : RESV :
{
2021-05-11 16:03:33 -05:00
std : : vector < double > convert_coeff ( this - > number_of_phases_ , 1.0 ) ;
this - > rateConverter_ . calcCoeff ( /*fipreg*/ 0 , this - > pvtRegionIdx_ , convert_coeff ) ;
2021-04-26 02:31:29 -05:00
const double coeff = convert_coeff [ phasePos ] ;
2021-08-24 04:49:03 -05:00
ws . surface_rates [ phasePos ] = controls . reservoir_rate / coeff ;
2021-04-26 02:31:29 -05:00
break ;
}
case Well : : InjectorCMode : : THP :
{
2021-08-24 04:49:03 -05:00
auto rates = ws . surface_rates ;
2022-10-19 02:55:14 -05:00
double bhp = WellBhpThpCalculator ( * this ) . calculateBhpFromThp ( well_state ,
rates ,
well ,
summaryState ,
this - > getRefDensity ( ) ,
deferred_logger ) ;
2021-08-03 13:05:14 -05:00
ws . bhp = bhp ;
2022-09-14 04:16:13 -05:00
ws . thp = this - > getTHPConstraint ( summaryState ) ;
2021-04-26 02:31:29 -05:00
// if the total rates are negative or zero
// we try to provide a better intial well rate
// using the well potentials
double total_rate = std : : accumulate ( rates . begin ( ) , rates . end ( ) , 0.0 ) ;
2021-08-24 04:49:03 -05:00
if ( total_rate < = 0.0 )
ws . surface_rates = ws . well_potentials ;
2021-04-26 02:31:29 -05:00
break ;
}
case Well : : InjectorCMode : : BHP :
{
2021-08-03 13:05:14 -05:00
ws . bhp = controls . bhp_limit ;
2021-04-26 02:31:29 -05:00
double total_rate = 0.0 ;
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
total_rate + = ws . surface_rates [ p ] ;
2021-04-26 02:31:29 -05:00
}
// if the total rates are negative or zero
// we try to provide a better intial well rate
// using the well potentials
2021-08-24 04:49:03 -05:00
if ( total_rate < = 0.0 )
ws . surface_rates = ws . well_potentials ;
2021-04-26 02:31:29 -05:00
break ;
}
case Well : : InjectorCMode : : GRUP :
{
2021-06-10 08:09:05 -05:00
assert ( well . isAvailableForGroupControl ( ) ) ;
const auto & group = schedule . getGroup ( well . groupName ( ) , this - > currentStep ( ) ) ;
const double efficiencyFactor = well . getEfficiencyFactor ( ) ;
std : : optional < double > target =
this - > getGroupInjectionTargetRate ( group ,
well_state ,
group_state ,
schedule ,
summaryState ,
injectorType ,
efficiencyFactor ,
deferred_logger ) ;
if ( target )
2021-08-24 04:49:03 -05:00
ws . surface_rates [ phasePos ] = * target ;
2021-04-26 02:31:29 -05:00
break ;
}
case Well : : InjectorCMode : : CMODE_UNDEFINED :
{
2021-05-11 16:03:33 -05:00
OPM_DEFLOG_THROW ( std : : runtime_error , " Well control must be specified for well " + this - > name ( ) , deferred_logger ) ;
2021-04-26 02:31:29 -05:00
}
}
2022-09-16 03:50:12 -05:00
// for wells with zero injection rate, if we assign exactly zero rate,
// we will have to assume some trivial composition in the wellbore.
// here, we use some small value (about 0.01 m^3/day ~= 1.e-7) to initialize
// the zero rate target, then we can use to retain the composition information
// within the wellbore from the previous result, and hopefully it is a good
// initial guess for the zero rate target.
ws . surface_rates [ phasePos ] = std : : max ( 1.e-7 , ws . surface_rates [ phasePos ] ) ;
2021-04-26 02:31:29 -05:00
}
//Producer
else
{
2021-08-04 05:03:36 -05:00
const auto current = ws . production_cmode ;
2021-04-26 02:31:29 -05:00
const auto & controls = well . productionControls ( summaryState ) ;
switch ( current ) {
case Well : : ProducerCMode : : ORAT :
{
2021-08-24 04:49:03 -05:00
double current_rate = - ws . surface_rates [ pu . phase_pos [ Oil ] ] ;
2021-04-26 02:31:29 -05:00
// for trivial rates or opposite direction we don't just scale the rates
// but use either the potentials or the mobility ratio to initial the well rates
if ( current_rate > 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] * = controls . oil_rate / current_rate ;
2021-04-26 02:31:29 -05:00
}
} else {
2021-06-04 03:41:07 -05:00
const std : : vector < double > fractions = initialWellRateFractions ( ebos_simulator , well_state ) ;
2021-04-26 02:31:29 -05:00
double control_fraction = fractions [ pu . phase_pos [ Oil ] ] ;
if ( control_fraction ! = 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] = - fractions [ p ] * controls . oil_rate / control_fraction ;
2021-04-26 02:31:29 -05:00
}
}
2021-04-26 02:31:29 -05:00
}
break ;
}
case Well : : ProducerCMode : : WRAT :
{
2021-08-24 04:49:03 -05:00
double current_rate = - ws . surface_rates [ pu . phase_pos [ Water ] ] ;
2021-04-26 02:31:29 -05:00
// for trivial rates or opposite direction we don't just scale the rates
// but use either the potentials or the mobility ratio to initial the well rates
if ( current_rate > 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] * = controls . water_rate / current_rate ;
2021-04-26 02:31:29 -05:00
}
} else {
2021-06-04 03:41:07 -05:00
const std : : vector < double > fractions = initialWellRateFractions ( ebos_simulator , well_state ) ;
2021-04-26 02:31:29 -05:00
double control_fraction = fractions [ pu . phase_pos [ Water ] ] ;
if ( control_fraction ! = 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] = - fractions [ p ] * controls . water_rate / control_fraction ;
2021-04-26 02:31:29 -05:00
}
}
2021-04-26 02:31:29 -05:00
}
break ;
}
case Well : : ProducerCMode : : GRAT :
{
2021-08-24 04:49:03 -05:00
double current_rate = - ws . surface_rates [ pu . phase_pos [ Gas ] ] ;
2021-04-26 02:31:29 -05:00
// or trivial rates or opposite direction we don't just scale the rates
// but use either the potentials or the mobility ratio to initial the well rates
if ( current_rate > 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] * = controls . gas_rate / current_rate ;
2021-04-26 02:31:29 -05:00
}
} else {
2021-06-04 03:41:07 -05:00
const std : : vector < double > fractions = initialWellRateFractions ( ebos_simulator , well_state ) ;
2021-04-26 02:31:29 -05:00
double control_fraction = fractions [ pu . phase_pos [ Gas ] ] ;
if ( control_fraction ! = 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] = - fractions [ p ] * controls . gas_rate / control_fraction ;
2021-04-26 02:31:29 -05:00
}
}
2021-04-26 02:31:29 -05:00
}
2021-04-26 02:31:29 -05:00
2021-04-26 02:31:29 -05:00
break ;
}
case Well : : ProducerCMode : : LRAT :
{
2021-08-24 04:49:03 -05:00
double current_rate = - ws . surface_rates [ pu . phase_pos [ Water ] ]
- ws . surface_rates [ pu . phase_pos [ Oil ] ] ;
2021-04-26 02:31:29 -05:00
// or trivial rates or opposite direction we don't just scale the rates
// but use either the potentials or the mobility ratio to initial the well rates
if ( current_rate > 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] * = controls . liquid_rate / current_rate ;
2021-04-26 02:31:29 -05:00
}
} else {
2021-06-04 03:41:07 -05:00
const std : : vector < double > fractions = initialWellRateFractions ( ebos_simulator , well_state ) ;
2021-04-26 02:31:29 -05:00
double control_fraction = fractions [ pu . phase_pos [ Water ] ] + fractions [ pu . phase_pos [ Oil ] ] ;
if ( control_fraction ! = 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] = - fractions [ p ] * controls . liquid_rate / control_fraction ;
2021-04-26 02:31:29 -05:00
}
}
2021-04-26 02:31:29 -05:00
}
break ;
}
case Well : : ProducerCMode : : CRAT :
{
2022-12-20 05:49:59 -06:00
OPM_DEFLOG_THROW ( std : : runtime_error ,
fmt : : format ( " CRAT control not supported, well {} " , this - > name ( ) ) ,
deferred_logger ) ;
2021-04-26 02:31:29 -05:00
}
case Well : : ProducerCMode : : RESV :
{
2021-05-11 16:03:33 -05:00
std : : vector < double > convert_coeff ( this - > number_of_phases_ , 1.0 ) ;
this - > rateConverter_ . calcCoeff ( /*fipreg*/ 0 , this - > pvtRegionIdx_ , convert_coeff ) ;
2021-04-26 02:31:29 -05:00
double total_res_rate = 0.0 ;
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
total_res_rate - = ws . surface_rates [ p ] * convert_coeff [ p ] ;
2021-04-26 02:31:29 -05:00
}
if ( controls . prediction_mode ) {
2021-04-26 02:31:29 -05:00
// or trivial rates or opposite direction we don't just scale the rates
// but use either the potentials or the mobility ratio to initial the well rates
if ( total_res_rate > 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] * = controls . resv_rate / total_res_rate ;
2021-04-26 02:31:29 -05:00
}
} else {
2021-06-04 03:41:07 -05:00
const std : : vector < double > fractions = initialWellRateFractions ( ebos_simulator , well_state ) ;
2021-04-26 02:31:29 -05:00
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] = - fractions [ p ] * controls . resv_rate / convert_coeff [ p ] ;
2021-04-26 02:31:29 -05:00
}
2021-04-26 02:31:29 -05:00
}
} else {
2021-05-11 16:03:33 -05:00
std : : vector < double > hrates ( this - > number_of_phases_ , 0. ) ;
2021-04-26 02:31:29 -05:00
if ( FluidSystem : : phaseIsActive ( FluidSystem : : waterPhaseIdx ) ) {
hrates [ pu . phase_pos [ Water ] ] = controls . water_rate ;
}
if ( FluidSystem : : phaseIsActive ( FluidSystem : : oilPhaseIdx ) ) {
hrates [ pu . phase_pos [ Oil ] ] = controls . oil_rate ;
}
if ( FluidSystem : : phaseIsActive ( FluidSystem : : gasPhaseIdx ) ) {
hrates [ pu . phase_pos [ Gas ] ] = controls . gas_rate ;
}
2021-05-11 16:03:33 -05:00
std : : vector < double > hrates_resv ( this - > number_of_phases_ , 0. ) ;
this - > rateConverter_ . calcReservoirVoidageRates ( /*fipreg*/ 0 , this - > pvtRegionIdx_ , hrates , hrates_resv ) ;
2021-04-26 02:31:29 -05:00
double target = std : : accumulate ( hrates_resv . begin ( ) , hrates_resv . end ( ) , 0.0 ) ;
2021-04-26 02:31:29 -05:00
// or trivial rates or opposite direction we don't just scale the rates
// but use either the potentials or the mobility ratio to initial the well rates
if ( total_res_rate > 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] * = target / total_res_rate ;
2021-04-26 02:31:29 -05:00
}
} else {
2021-06-04 03:41:07 -05:00
const std : : vector < double > fractions = initialWellRateFractions ( ebos_simulator , well_state ) ;
2021-04-26 02:31:29 -05:00
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] = - fractions [ p ] * target / convert_coeff [ p ] ;
2021-04-26 02:31:29 -05:00
}
2021-04-26 02:31:29 -05:00
}
}
break ;
}
case Well : : ProducerCMode : : BHP :
{
2021-08-03 13:05:14 -05:00
ws . bhp = controls . bhp_limit ;
2021-04-26 02:31:29 -05:00
double total_rate = 0.0 ;
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
total_rate - = ws . surface_rates [ p ] ;
2021-04-26 02:31:29 -05:00
}
// if the total rates are negative or zero
// we try to provide a better intial well rate
// using the well potentials
if ( total_rate < = 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] = - ws . well_potentials [ p ] ;
2021-04-26 02:31:29 -05:00
}
}
2021-04-26 02:31:29 -05:00
break ;
}
case Well : : ProducerCMode : : THP :
{
2023-01-30 05:41:45 -06:00
const bool update_success = updateWellStateWithTHPTargetProd ( ebos_simulator , well_state , deferred_logger ) ;
if ( ! update_success ) {
// the following is the original way of initializing well state with THP constraint
// keeping it for robust reason in case that it fails to get a bhp value with THP constraint
// more sophisticated design might be needed in the future
auto rates = ws . surface_rates ;
this - > adaptRatesForVFP ( rates ) ;
const double bhp = WellBhpThpCalculator ( * this ) . calculateBhpFromThp (
well_state , rates , well , summaryState , this - > getRefDensity ( ) , deferred_logger ) ;
ws . bhp = bhp ;
ws . thp = this - > getTHPConstraint ( summaryState ) ;
// if the total rates are negative or zero
// we try to provide a better initial well rate
// using the well potentials
const double total_rate = - std : : accumulate ( rates . begin ( ) , rates . end ( ) , 0.0 ) ;
if ( total_rate < = 0.0 ) {
for ( int p = 0 ; p < this - > number_of_phases_ ; + + p ) {
ws . surface_rates [ p ] = - ws . well_potentials [ p ] ;
}
2021-04-26 02:31:29 -05:00
}
}
break ;
2021-04-26 02:31:29 -05:00
}
case Well : : ProducerCMode : : GRUP :
{
2021-06-10 08:09:05 -05:00
assert ( well . isAvailableForGroupControl ( ) ) ;
const auto & group = schedule . getGroup ( well . groupName ( ) , this - > currentStep ( ) ) ;
const double efficiencyFactor = well . getEfficiencyFactor ( ) ;
double scale = this - > getGroupProductionTargetRate ( group ,
well_state ,
group_state ,
schedule ,
summaryState ,
2023-01-10 08:32:08 -06:00
efficiencyFactor ,
deferred_logger ) ;
2021-06-10 08:09:05 -05:00
// we don't want to scale with zero and get zero rates.
if ( scale > 0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 04:49:03 -05:00
ws . surface_rates [ p ] * = scale ;
2021-06-10 08:09:05 -05:00
}
2022-04-25 01:09:54 -05:00
ws . trivial_target = false ;
} else {
ws . trivial_target = true ;
2021-06-10 08:09:05 -05:00
}
2021-04-26 02:31:29 -05:00
break ;
}
case Well : : ProducerCMode : : CMODE_UNDEFINED :
case Well : : ProducerCMode : : NONE :
{
2021-05-11 16:03:33 -05:00
OPM_DEFLOG_THROW ( std : : runtime_error , " Well control must be specified for well " + this - > name ( ) , deferred_logger ) ;
2021-04-26 02:31:29 -05:00
}
break ;
} // end of switch
}
}
2021-04-26 02:31:29 -05:00
template < typename TypeTag >
std : : vector < double >
WellInterface < TypeTag > : :
2021-06-04 03:41:07 -05:00
initialWellRateFractions ( const Simulator & ebosSimulator , const WellState & well_state ) const
2021-04-26 02:31:29 -05:00
{
2021-05-11 16:03:33 -05:00
const int np = this - > number_of_phases_ ;
2021-04-26 02:31:29 -05:00
std : : vector < double > scaling_factor ( np ) ;
2021-08-05 03:57:15 -05:00
const auto & ws = well_state . well ( this - > index_of_well_ ) ;
2021-04-26 02:31:29 -05:00
double total_potentials = 0.0 ;
for ( int p = 0 ; p < np ; + + p ) {
2021-08-05 03:57:15 -05:00
total_potentials + = ws . well_potentials [ p ] ;
2021-04-26 02:31:29 -05:00
}
if ( total_potentials > 0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-05 03:57:15 -05:00
scaling_factor [ p ] = ws . well_potentials [ p ] / total_potentials ;
2021-04-26 02:31:29 -05:00
}
return scaling_factor ;
}
// if we don't have any potentials we weight it using the mobilites
// We only need approximation so we don't bother with the vapporized oil and dissolved gas
double total_tw = 0 ;
2021-05-11 16:03:33 -05:00
const int nperf = this - > number_of_perforations_ ;
2021-04-26 02:31:29 -05:00
for ( int perf = 0 ; perf < nperf ; + + perf ) {
2021-05-11 16:03:33 -05:00
total_tw + = this - > well_index_ [ perf ] ;
2021-04-26 02:31:29 -05:00
}
for ( int perf = 0 ; perf < nperf ; + + perf ) {
2021-05-11 16:03:33 -05:00
const int cell_idx = this - > well_cells_ [ perf ] ;
2021-04-26 02:31:29 -05:00
const auto & intQuants = * ( ebosSimulator . model ( ) . cachedIntensiveQuantities ( cell_idx , /*timeIdx=*/ 0 ) ) ;
const auto & fs = intQuants . fluidState ( ) ;
2021-05-11 16:03:33 -05:00
const double well_tw_fraction = this - > well_index_ [ perf ] / total_tw ;
2021-04-26 02:31:29 -05:00
double total_mobility = 0.0 ;
for ( int p = 0 ; p < np ; + + p ) {
2021-05-31 06:21:31 -05:00
int ebosPhaseIdx = this - > flowPhaseToEbosPhaseIdx ( p ) ;
2021-04-26 02:31:29 -05:00
total_mobility + = fs . invB ( ebosPhaseIdx ) . value ( ) * intQuants . mobility ( ebosPhaseIdx ) . value ( ) ;
}
for ( int p = 0 ; p < np ; + + p ) {
2021-05-31 06:21:31 -05:00
int ebosPhaseIdx = this - > flowPhaseToEbosPhaseIdx ( p ) ;
2021-04-26 02:31:29 -05:00
scaling_factor [ p ] + = well_tw_fraction * fs . invB ( ebosPhaseIdx ) . value ( ) * intQuants . mobility ( ebosPhaseIdx ) . value ( ) / total_mobility ;
}
}
return scaling_factor ;
}
2018-11-17 16:14:51 -06:00
2020-02-10 08:16:09 -06:00
2020-10-15 07:15:05 -05:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
updateWellStateRates ( const Simulator & ebosSimulator ,
WellState & well_state ,
DeferredLogger & deferred_logger ) const
{
// Check if the rates of this well only are single-phase, do nothing
// if more than one nonzero rate.
2021-08-24 04:49:03 -05:00
auto & ws = well_state . well ( this - > index_of_well_ ) ;
2020-10-15 07:15:05 -05:00
int nonzero_rate_index = - 1 ;
2022-04-25 01:08:37 -05:00
const double floating_point_error_epsilon = 1e-14 ;
2021-05-11 16:03:33 -05:00
for ( int p = 0 ; p < this - > number_of_phases_ ; + + p ) {
2022-04-25 01:08:37 -05:00
if ( std : : abs ( ws . surface_rates [ p ] ) > floating_point_error_epsilon ) {
2020-10-15 07:15:05 -05:00
if ( nonzero_rate_index = = - 1 ) {
nonzero_rate_index = p ;
} else {
// More than one nonzero rate.
return ;
}
}
}
2022-03-23 06:29:40 -05:00
// Calculate the rates that follow from the current primary variables.
std : : vector < double > well_q_s = computeCurrentWellRates ( ebosSimulator , deferred_logger ) ;
2020-10-15 07:15:05 -05:00
if ( nonzero_rate_index = = - 1 ) {
// No nonzero rates.
2022-03-23 06:29:40 -05:00
// Use the computed rate directly
for ( int p = 0 ; p < this - > number_of_phases_ ; + + p ) {
ws . surface_rates [ p ] = well_q_s [ this - > flowPhaseToEbosCompIdx ( p ) ] ;
}
2020-10-15 07:15:05 -05:00
return ;
}
// Set the currently-zero phase flows to be nonzero in proportion to well_q_s.
2021-08-24 04:49:03 -05:00
const double initial_nonzero_rate = ws . surface_rates [ nonzero_rate_index ] ;
2021-05-31 07:31:56 -05:00
const int comp_idx_nz = this - > flowPhaseToEbosCompIdx ( nonzero_rate_index ) ;
2021-05-11 16:03:33 -05:00
for ( int p = 0 ; p < this - > number_of_phases_ ; + + p ) {
2020-10-15 07:15:05 -05:00
if ( p ! = nonzero_rate_index ) {
2021-05-31 07:31:56 -05:00
const int comp_idx = this - > flowPhaseToEbosCompIdx ( p ) ;
2021-08-24 04:49:03 -05:00
double & rate = ws . surface_rates [ p ] ;
2020-10-15 07:15:05 -05:00
rate = ( initial_nonzero_rate / well_q_s [ comp_idx_nz ] ) * ( well_q_s [ comp_idx ] ) ;
}
}
}
2021-11-30 04:48:02 -06:00
template < typename TypeTag >
typename WellInterface < TypeTag > : : Eval
WellInterface < TypeTag > : : getPerfCellPressure ( const typename WellInterface < TypeTag > : : FluidState & fs ) const
{
2022-10-26 03:59:58 -05:00
if constexpr ( Indices : : oilEnabled ) {
return fs . pressure ( FluidSystem : : oilPhaseIdx ) ;
} else if constexpr ( Indices : : waterEnabled ) {
return fs . pressure ( FluidSystem : : waterPhaseIdx ) ;
2021-11-30 04:48:02 -06:00
} else {
2022-10-26 03:59:58 -05:00
return fs . pressure ( FluidSystem : : gasPhaseIdx ) ;
2021-11-30 04:48:02 -06:00
}
}
2020-02-10 08:16:09 -06:00
} // namespace Opm