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>
2023-08-23 01:35:26 -05:00
# include <opm/input/eclipse/Schedule/Well/WDFAC.hpp>
2023-11-10 09:58:18 -06:00
2019-05-07 06:06:02 -05:00
# include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
2023-11-10 09:58:18 -06:00
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>
2023-03-22 09:10:00 -05:00
# include <opm/simulators/wells/WellHelpers.hpp>
2017-06-15 04:34:07 -05:00
2021-05-25 05:57:11 -05:00
# include <dune/common/version.hh>
2023-08-15 02:32:10 -05:00
# include <cstddef>
2023-11-10 09:58:18 -06:00
# include <utility>
# include <fmt/format.h>
2023-08-15 02:32:10 -05:00
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 > : :
2024-02-06 04:55:07 -06:00
updateWellControl ( const Simulator & 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
{
2024-02-06 04:55:07 -06:00
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
2023-03-29 07:33:05 -05:00
if ( this - > stopppedOrZeroRateTarget ( summary_state , well_state ) ) {
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
2024-02-06 04:55:07 -06:00
const auto & summaryState = simulator . vanguard ( ) . summaryState ( ) ;
const auto & schedule = 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 ;
2023-11-20 01:29:24 -06:00
ss < < " The control mode for well " < < this - > name ( )
2021-04-15 01:14:52 -05:00
< < " 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
}
2024-02-06 04:55:07 -06:00
Parallel : : Communication cc = 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 ) ;
2024-02-06 04:55:07 -06:00
updateWellStateWithTarget ( simulator , group_state , well_state , deferred_logger ) ;
2023-03-22 09:10:00 -05:00
updatePrimaryVariables ( summaryState , 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
}
2023-08-16 05:50:06 -05:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-06 04:55:07 -06:00
updateWellControlAndStatusLocalIteration ( const Simulator & simulator ,
2023-09-25 06:07:39 -05:00
WellState & well_state ,
const GroupState & group_state ,
const Well : : InjectionControls & inj_controls ,
const Well : : ProductionControls & prod_controls ,
const double wqTotal ,
2023-10-26 10:28:05 -05:00
DeferredLogger & deferred_logger ,
2023-10-30 14:58:09 -05:00
const bool fixed_control ,
const bool fixed_status )
2023-08-16 05:50:06 -05:00
{
2024-02-06 04:55:07 -06:00
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
const auto & schedule = simulator . vanguard ( ) . schedule ( ) ;
2023-12-07 05:15:40 -06:00
2023-08-16 05:50:06 -05:00
if ( this - > wellUnderZeroRateTarget ( summary_state , well_state ) | | ! ( this - > well_ecl_ . getStatus ( ) = = WellStatus : : OPEN ) ) {
return false ;
}
2017-09-05 02:42:44 -05:00
2023-08-16 05:50:06 -05:00
const double sgn = this - > isInjector ( ) ? 1.0 : - 1.0 ;
if ( ! this - > wellIsStopped ( ) ) {
2023-10-30 14:58:09 -05:00
if ( wqTotal * sgn < = 0.0 & & ! fixed_status ) {
2023-08-16 05:50:06 -05:00
this - > stopWell ( ) ;
return true ;
} else {
bool changed = false ;
2023-10-30 14:58:09 -05:00
if ( ! fixed_control ) {
2023-10-26 10:28:05 -05:00
auto & ws = well_state . well ( this - > index_of_well_ ) ;
const bool hasGroupControl = this - > isInjector ( ) ? inj_controls . hasControl ( Well : : InjectorCMode : : GRUP ) :
prod_controls . hasControl ( Well : : ProducerCMode : : GRUP ) ;
changed = this - > checkIndividualConstraints ( ws , summary_state , deferred_logger , inj_controls , prod_controls ) ;
if ( hasGroupControl ) {
changed = changed | | this - > checkGroupConstraints ( well_state , group_state , schedule , summary_state , deferred_logger ) ;
}
2023-09-25 06:07:39 -05:00
2023-10-26 10:28:05 -05:00
if ( changed ) {
const bool thp_controlled = this - > isInjector ( ) ? ws . injection_cmode = = Well : : InjectorCMode : : THP :
ws . production_cmode = = Well : : ProducerCMode : : THP ;
if ( ! thp_controlled ) {
// don't call for thp since this might trigger additional local solve
2024-02-06 04:55:07 -06:00
updateWellStateWithTarget ( simulator , group_state , well_state , deferred_logger ) ;
2023-10-26 10:28:05 -05:00
} else {
ws . thp = this - > getTHPConstraint ( summary_state ) ;
}
updatePrimaryVariables ( summary_state , well_state , deferred_logger ) ;
2023-10-09 04:14:48 -05:00
}
2023-08-16 05:50:06 -05:00
}
return changed ;
}
2023-10-30 14:58:09 -05:00
} else if ( ! fixed_status ) {
2023-08-16 05:50:06 -05:00
// well is stopped, check if current bhp allows reopening
const double bhp = well_state . well ( this - > index_of_well_ ) . bhp ;
double prod_limit = prod_controls . bhp_limit ;
double inj_limit = inj_controls . bhp_limit ;
const bool has_thp = this - > wellHasTHPConstraints ( summary_state ) ;
if ( has_thp ) {
std : : vector < double > rates ( this - > num_components_ ) ;
if ( this - > isInjector ( ) ) {
2023-10-26 10:28:05 -05:00
const double bhp_thp = WellBhpThpCalculator ( * this ) . calculateBhpFromThp ( well_state , rates , this - > well_ecl_ , summary_state , this - > getRefDensity ( ) , deferred_logger ) ;
2023-08-16 05:50:06 -05:00
inj_limit = std : : min ( bhp_thp , inj_controls . bhp_limit ) ;
} else {
2023-11-08 14:58:26 -06:00
// if the well can operate, it must at least be able to produce at the lowest bhp of the bhp-curve (explicit fractions)
2023-10-26 10:28:05 -05:00
const double bhp_min = WellBhpThpCalculator ( * this ) . calculateMinimumBhpFromThp ( well_state , this - > well_ecl_ , summary_state , this - > getRefDensity ( ) ) ;
prod_limit = std : : max ( bhp_min , prod_controls . bhp_limit ) ;
2023-08-16 05:50:06 -05:00
}
}
const double bhp_diff = ( this - > isInjector ( ) ) ? inj_limit - bhp : bhp - prod_limit ;
if ( bhp_diff > 0 ) {
this - > openWell ( ) ;
2023-10-30 14:58:09 -05:00
well_state . well ( this - > index_of_well_ ) . bhp = ( this - > isInjector ( ) ) ? inj_limit : prod_limit ;
2023-10-26 10:28:05 -05:00
if ( has_thp ) {
well_state . well ( this - > index_of_well_ ) . thp = this - > getTHPConstraint ( summary_state ) ;
}
2023-08-16 05:50:06 -05:00
return true ;
} else {
return false ;
}
2023-10-30 14:58:09 -05:00
} else {
return false ;
2023-08-16 05:50:06 -05:00
}
}
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 ( ) ) ;
2023-12-07 05:15:40 -06: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 ) ;
2023-03-22 09:10:00 -05:00
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
updatePrimaryVariables ( summary_state , 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 ( ) ) {
2023-12-07 05:15:40 -06:00
const auto & schedule = simulator . vanguard ( ) . schedule ( ) ;
const auto report_step = simulator . episodeIndex ( ) ;
const auto & glo = schedule . glo ( report_step ) ;
if ( glo . active ( ) ) {
gliftBeginTimeStepWellTestUpdateALQ ( simulator , well_state_copy , deferred_logger ) ;
}
2022-10-10 01:14:42 -05:00
}
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 ) {
2023-08-15 02:32:10 -05:00
const std : : 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 > : :
2024-02-06 04:55:07 -06:00
iterateWellEquations ( const Simulator & simulator ,
2020-06-09 16:35:58 -05:00
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
{
2024-02-06 04:55:07 -06:00
const auto & summary_state = simulator . 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 {
2023-09-25 06:07:39 -05:00
// TODO: the following two functions will be refactored to be one to reduce the code duplication
2023-08-16 05:50:06 -05:00
if ( ! this - > param_ . local_well_solver_control_switching_ ) {
2024-02-06 04:55:07 -06:00
converged = this - > iterateWellEqWithControl ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2023-08-16 05:50:06 -05:00
} else {
2023-11-16 09:02:53 -06:00
if ( this - > param_ . use_implicit_ipr_ & & this - > well_ecl_ . isProducer ( ) & & this - > wellHasTHPConstraints ( summary_state ) & & ( this - > well_ecl_ . getStatus ( ) = = WellStatus : : OPEN ) ) {
2024-02-06 04:55:07 -06:00
converged = solveWellWithTHPConstraint ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2023-10-26 10:28:05 -05:00
} else {
2024-02-06 04:55:07 -06:00
converged = this - > iterateWellEqWithSwitching ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2023-10-26 10:28:05 -05:00
}
2023-08-16 05:50:06 -05:00
}
2023-12-07 05:15:40 -06:00
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
}
2023-10-26 10:28:05 -05:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-06 04:55:07 -06:00
solveWellWithTHPConstraint ( const Simulator & simulator ,
2023-11-07 10:29:19 -06:00
const double dt ,
const Well : : InjectionControls & inj_controls ,
const Well : : ProductionControls & prod_controls ,
WellState & well_state ,
const GroupState & group_state ,
DeferredLogger & deferred_logger )
2023-10-26 10:28:05 -05:00
{
2024-02-06 04:55:07 -06:00
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
2023-10-26 10:28:05 -05:00
bool is_operable = true ;
2023-11-07 10:29:19 -06:00
bool converged = true ;
auto & ws = well_state . well ( this - > index_of_well_ ) ;
// if well is stopped, check if we can reopen
2023-11-06 04:42:48 -06:00
if ( this - > wellIsStopped ( ) ) {
this - > openWell ( ) ;
2024-02-06 04:55:07 -06:00
auto bhp_target = estimateOperableBhp ( simulator , dt , well_state , summary_state , deferred_logger ) ;
2023-11-06 04:42:48 -06:00
if ( ! bhp_target . has_value ( ) ) {
2023-11-08 14:12:31 -06:00
// no intersection with ipr
const auto msg = fmt : : format ( " estimateOperableBhp: Did not find operable BHP for well {} " , this - > name ( ) ) ;
deferred_logger . debug ( msg ) ;
2023-11-06 04:42:48 -06:00
is_operable = false ;
2023-11-07 10:29:19 -06:00
// solve with zero rates
2024-02-06 04:55:07 -06:00
solveWellWithZeroRate ( simulator , dt , well_state , deferred_logger ) ;
2023-11-07 10:29:19 -06:00
this - > stopWell ( ) ;
2023-11-06 04:42:48 -06:00
} else {
2023-11-07 10:29:19 -06:00
// solve well with the estimated target bhp (or limit)
2023-11-08 14:12:31 -06:00
ws . thp = this - > getTHPConstraint ( summary_state ) ;
2023-11-07 10:29:19 -06:00
const double bhp = std : : max ( bhp_target . value ( ) , prod_controls . bhp_limit ) ;
2024-02-06 04:55:07 -06:00
solveWellWithBhp ( simulator , dt , bhp , well_state , deferred_logger ) ;
2023-11-06 04:42:48 -06:00
}
2023-11-07 10:29:19 -06:00
}
// solve well-equation
if ( is_operable ) {
2024-02-06 04:55:07 -06:00
converged = this - > iterateWellEqWithSwitching ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2023-11-06 04:42:48 -06:00
}
2023-11-07 10:29:19 -06:00
const bool isThp = ws . production_cmode = = Well : : ProducerCMode : : THP ;
// check stability of solution under thp-control
2023-12-07 05:15:40 -06:00
if ( converged & & ! this - > stopppedOrZeroRateTarget ( summary_state , well_state ) & & isThp ) {
auto rates = well_state . well ( this - > index_of_well_ ) . surface_rates ;
this - > adaptRatesForVFP ( rates ) ;
2024-02-06 04:55:07 -06:00
this - > updateIPRImplicit ( simulator , well_state , deferred_logger ) ;
2023-12-07 05:15:40 -06:00
bool is_stable = WellBhpThpCalculator ( * this ) . isStableSolution ( well_state , this - > well_ecl_ , rates , summary_state ) ;
if ( ! is_stable ) {
// solution converged to an unstable point!
this - > operability_status_ . use_vfpexplicit = true ;
auto bhp_stable = WellBhpThpCalculator ( * this ) . estimateStableBhp ( well_state , this - > well_ecl_ , rates , this - > getRefDensity ( ) , summary_state ) ;
// if we find an intersection with a sufficiently lower bhp, re-solve equations
const double reltol = 1e-3 ;
const double cur_bhp = ws . bhp ;
if ( bhp_stable . has_value ( ) & & cur_bhp - bhp_stable . value ( ) > cur_bhp * reltol ) {
const auto msg = fmt : : format ( " Well {} converged to an unstable solution, re-solving " , this - > name ( ) ) ;
deferred_logger . debug ( msg ) ;
2024-02-06 04:55:07 -06:00
solveWellWithBhp ( simulator , dt , bhp_stable . value ( ) , well_state , deferred_logger ) ;
2023-12-07 05:15:40 -06:00
// re-solve with hopefully good initial guess
ws . thp = this - > getTHPConstraint ( summary_state ) ;
2024-02-06 04:55:07 -06:00
converged = this - > iterateWellEqWithSwitching ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2023-12-07 05:15:40 -06:00
}
}
2023-11-07 10:29:19 -06:00
}
2023-12-07 05:15:40 -06:00
2023-11-07 10:29:19 -06:00
if ( ! converged ) {
2023-10-26 10:28:05 -05:00
// Well did not converge, switch to explicit fractions
this - > operability_status_ . use_vfpexplicit = true ;
2023-10-30 14:58:09 -05:00
this - > openWell ( ) ;
2024-02-06 04:55:07 -06:00
auto bhp_target = estimateOperableBhp ( simulator , dt , well_state , summary_state , deferred_logger ) ;
2023-10-26 10:28:05 -05:00
if ( ! bhp_target . has_value ( ) ) {
// well can't operate using explicit fractions
is_operable = false ;
2023-11-07 10:29:19 -06:00
// solve with zero rate
2024-02-06 04:55:07 -06:00
converged = solveWellWithZeroRate ( simulator , dt , well_state , deferred_logger ) ;
2023-11-07 10:29:19 -06:00
this - > stopWell ( ) ;
2023-10-26 10:28:05 -05:00
} else {
2023-11-07 10:29:19 -06:00
// solve well with the estimated target bhp (or limit)
const double bhp = std : : max ( bhp_target . value ( ) , prod_controls . bhp_limit ) ;
2024-02-06 04:55:07 -06:00
solveWellWithBhp ( simulator , dt , bhp , well_state , deferred_logger ) ;
2023-11-07 10:29:19 -06:00
ws . thp = this - > getTHPConstraint ( summary_state ) ;
2024-02-06 04:55:07 -06:00
converged = this - > iterateWellEqWithSwitching ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2023-10-26 10:28:05 -05:00
}
}
2023-11-07 10:29:19 -06:00
// update operability
2023-11-08 14:12:31 -06:00
is_operable = is_operable & & ! this - > wellIsStopped ( ) ;
2023-11-07 10:29:19 -06:00
this - > operability_status_ . can_obtain_bhp_with_thp_limit = is_operable ;
this - > operability_status_ . obey_thp_limit_under_bhp_limit = is_operable ;
2023-10-26 10:28:05 -05:00
return converged ;
}
template < typename TypeTag >
std : : optional < double >
WellInterface < TypeTag > : :
2024-02-06 04:55:07 -06:00
estimateOperableBhp ( const Simulator & simulator ,
2023-10-26 10:28:05 -05:00
const double dt ,
WellState & well_state ,
const SummaryState & summary_state ,
DeferredLogger & deferred_logger )
{
// Given an unconverged well or closed well, estimate an operable bhp (if any)
// Get minimal bhp from vfp-curve
double bhp_min = WellBhpThpCalculator ( * this ) . calculateMinimumBhpFromThp ( well_state , this - > well_ecl_ , summary_state , this - > getRefDensity ( ) ) ;
// Solve
2024-02-06 04:55:07 -06:00
const bool converged = solveWellWithBhp ( simulator , dt , bhp_min , well_state , deferred_logger ) ;
2023-11-08 14:12:31 -06:00
if ( ! converged | | this - > wellIsStopped ( ) ) {
2023-10-26 10:28:05 -05:00
return std : : nullopt ;
}
2024-02-06 04:55:07 -06:00
this - > updateIPRImplicit ( simulator , well_state , deferred_logger ) ;
2023-10-26 10:28:05 -05:00
auto rates = well_state . well ( this - > index_of_well_ ) . surface_rates ;
this - > adaptRatesForVFP ( rates ) ;
2023-11-27 10:07:38 -06:00
return WellBhpThpCalculator ( * this ) . estimateStableBhp ( well_state , this - > well_ecl_ , rates , this - > getRefDensity ( ) , summary_state ) ;
2023-10-26 10:28:05 -05:00
}
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-06 04:55:07 -06:00
solveWellWithBhp ( const Simulator & simulator ,
2023-10-26 10:28:05 -05:00
const double dt ,
const double bhp ,
WellState & well_state ,
DeferredLogger & deferred_logger )
{
// Solve a well using single bhp-constraint (but close if not operable under this)
auto group_state = GroupState ( ) ; // empty group
auto inj_controls = Well : : InjectionControls ( 0 ) ;
auto prod_controls = Well : : ProductionControls ( 0 ) ;
auto & ws = well_state . well ( this - > index_of_well_ ) ;
auto cmode_inj = ws . injection_cmode ;
auto cmode_prod = ws . production_cmode ;
if ( this - > isInjector ( ) ) {
inj_controls . addControl ( Well : : InjectorCMode : : BHP ) ;
inj_controls . bhp_limit = bhp ;
inj_controls . cmode = Well : : InjectorCMode : : BHP ;
ws . injection_cmode = Well : : InjectorCMode : : BHP ;
} else {
prod_controls . addControl ( Well : : ProducerCMode : : BHP ) ;
prod_controls . bhp_limit = bhp ;
prod_controls . cmode = Well : : ProducerCMode : : BHP ;
ws . production_cmode = Well : : ProducerCMode : : BHP ;
}
// update well-state
ws . bhp = bhp ;
// solve
2024-02-06 04:55:07 -06:00
const bool converged = this - > iterateWellEqWithSwitching ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger , /*fixed_control*/ true ) ;
2023-10-26 10:28:05 -05:00
ws . injection_cmode = cmode_inj ;
ws . production_cmode = cmode_prod ;
return converged ;
}
2020-06-09 16:35:58 -05:00
2023-10-30 14:58:09 -05:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-06 04:55:07 -06:00
solveWellWithZeroRate ( const Simulator & simulator ,
2023-10-30 14:58:09 -05:00
const double dt ,
WellState & well_state ,
DeferredLogger & deferred_logger )
{
// Solve a well as stopped
const auto well_status_orig = this - > wellStatus_ ;
this - > stopWell ( ) ;
auto group_state = GroupState ( ) ; // empty group
auto inj_controls = Well : : InjectionControls ( 0 ) ;
auto prod_controls = Well : : ProductionControls ( 0 ) ;
2024-02-06 04:55:07 -06:00
const bool converged = this - > iterateWellEqWithSwitching ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger , /*fixed_control*/ true , /*fixed_status*/ true ) ;
2023-10-30 14:58:09 -05:00
this - > wellStatus_ = well_status_orig ;
return converged ;
}
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 > : :
2024-02-06 04:55:07 -06:00
solveWellForTesting ( const Simulator & simulator , 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 ;
2024-02-06 04:55:07 -06:00
const double dt = simulator . timeStepSize ( ) ;
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
2022-02-17 01:58:38 -06:00
const bool has_thp_limit = this - > wellHasTHPConstraints ( summary_state ) ;
2023-03-02 16:17:03 -06:00
bool converged ;
if ( has_thp_limit ) {
2022-02-17 01:58:38 -06:00
well_state . well ( this - > indexOfWell ( ) ) . production_cmode = Well : : ProducerCMode : : THP ;
2023-03-02 16:17:03 -06:00
converged = gliftBeginTimeStepWellTestIterateWellEquations (
2024-02-06 04:55:07 -06:00
simulator , dt , well_state , group_state , deferred_logger ) ;
2023-03-02 16:17:03 -06:00
}
else {
2022-02-17 01:58:38 -06:00
well_state . well ( this - > indexOfWell ( ) ) . production_cmode = Well : : ProducerCMode : : BHP ;
2024-02-06 04:55:07 -06:00
converged = iterateWellEquations ( simulator , dt , well_state , group_state , deferred_logger ) ;
2023-03-02 16:17:03 -06:00
}
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 > : :
2024-02-06 04:55:07 -06:00
solveWellEquation ( const Simulator & simulator ,
2021-01-07 04:20:50 -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 )
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 ;
2024-02-06 04:55:07 -06:00
const double dt = simulator . timeStepSize ( ) ;
bool converged = iterateWellEquations ( simulator , dt , well_state , group_state , deferred_logger ) ;
2022-10-06 01:33:42 -05:00
// 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 ) ;
2024-02-06 04:55:07 -06:00
converged = this - > iterateWellEquations ( simulator , dt , well_state , group_state , deferred_logger ) ;
2022-10-06 01:33:42 -05:00
}
}
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 > : :
2024-02-06 04:55:07 -06:00
assembleWellEq ( const Simulator & simulator ,
2021-05-10 02:41:18 -05:00
const double dt ,
WellState & well_state ,
const GroupState & group_state ,
DeferredLogger & deferred_logger )
{
2022-10-20 04:03:29 -05:00
2024-02-06 04:55:07 -06:00
prepareWellBeforeAssembling ( simulator , dt , well_state , group_state , deferred_logger ) ;
2022-10-20 04:03:29 -05:00
2024-02-06 04:55:07 -06:00
assembleWellEqWithoutIteration ( simulator , dt , well_state , group_state , deferred_logger ) ;
2022-10-20 04:03:29 -05:00
}
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-06 04:55:07 -06:00
assembleWellEqWithoutIteration ( const Simulator & simulator ,
2022-10-20 04:03:29 -05:00
const double dt ,
WellState & well_state ,
const GroupState & group_state ,
DeferredLogger & deferred_logger )
{
2024-02-06 04:55:07 -06:00
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
2022-10-20 04:03:29 -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 ) ;
// TODO: the reason to have inj_controls and prod_controls in the arguments, is that we want to change the control used for the well functions
// TODO: maybe we can use std::optional or pointers to simplify here
2024-02-06 04:55:07 -06:00
assembleWellEqWithoutIteration ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2022-10-20 04:03:29 -05:00
}
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-06 04:55:07 -06:00
prepareWellBeforeAssembling ( const Simulator & simulator ,
2022-10-20 04:03:29 -05:00
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_ )
2024-02-06 04:55:07 -06:00
checkWellOperability ( simulator , 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.
2024-02-06 04:55:07 -06:00
const int iteration_idx = simulator . 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 ;
2024-02-06 04:55:07 -06:00
bool converged = this - > iterateWellEquations ( simulator , 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 {
2024-02-06 04:55:07 -06:00
computeWellPotentials ( simulator , well_state_copy , potentials , deferred_logger ) ;
2021-11-18 05:57:16 -06:00
} 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 ) {
2023-10-01 06:16:48 -05:00
deferred_logger . info ( " well " + this - > name ( ) + " gets STOPPED during iteration " ) ;
this - > stopWell ( ) ;
changed_to_stopped_this_step_ = true ;
2021-04-16 07:44:14 -05:00
} 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
}
}
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 > : :
2024-02-06 04:55:07 -06:00
checkWellOperability ( const Simulator & simulator ,
2020-11-27 00:57:55 -06:00
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 ;
}
2024-02-06 04:55:07 -06:00
updateWellOperability ( 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 ( ) ) ;
2024-02-06 04:55:07 -06:00
updateWellOperability ( simulator , well_state , deferred_logger ) ;
2022-02-22 08:25:10 -06:00
}
2021-04-16 07:44:14 -05:00
}
2020-11-27 00:57:55 -06:00
2023-03-02 16:17:03 -06:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
gliftBeginTimeStepWellTestIterateWellEquations (
2024-02-06 04:55:07 -06:00
const Simulator & simulator ,
2023-03-02 16:17:03 -06:00
const double dt ,
WellState & well_state ,
const GroupState & group_state ,
DeferredLogger & deferred_logger )
{
const auto & well_name = this - > name ( ) ;
2024-02-06 04:55:07 -06:00
assert ( this - > wellHasTHPConstraints ( simulator . vanguard ( ) . summaryState ( ) ) ) ;
const auto & schedule = simulator . vanguard ( ) . schedule ( ) ;
auto report_step_idx = simulator . episodeIndex ( ) ;
2023-03-02 16:17:03 -06:00
const auto & glo = schedule . glo ( report_step_idx ) ;
2023-12-07 05:15:40 -06:00
if ( glo . active ( ) & & glo . has_well ( well_name ) ) {
const auto increment = glo . gaslift_increment ( ) ;
2023-04-13 04:08:30 -05:00
auto alq = well_state . getALQ ( well_name ) ;
bool converged ;
while ( alq > 0 ) {
well_state . setALQ ( well_name , alq ) ;
if ( ( converged =
2024-02-06 04:55:07 -06:00
iterateWellEquations ( simulator , dt , well_state , group_state , deferred_logger ) ) )
2023-04-13 04:08:30 -05:00
{
return converged ;
}
alq - = increment ;
2023-03-02 16:17:03 -06:00
}
2023-04-13 04:08:30 -05:00
return false ;
}
else {
2024-02-06 04:55:07 -06:00
return iterateWellEquations ( simulator , dt , well_state , group_state , deferred_logger ) ;
2023-03-02 16:17:03 -06:00
}
}
2022-03-27 00:57:31 -05:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-06 04:55:07 -06:00
gliftBeginTimeStepWellTestUpdateALQ ( const Simulator & simulator ,
2022-03-27 00:57:31 -05:00
WellState & well_state ,
DeferredLogger & deferred_logger )
{
2024-02-06 04:55:07 -06:00
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
2022-03-27 00:57:31 -05:00
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 ;
}
2024-02-06 04:55:07 -06:00
const auto & schedule = simulator . vanguard ( ) . schedule ( ) ;
const auto report_step_idx = simulator . episodeIndex ( ) ;
2022-03-27 00:57:31 -05:00
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 {
2023-12-07 05:15:40 -06:00
const auto & well_ecl = this - > wellEcl ( ) ;
2022-03-27 00:57:31 -05:00
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 > : :
2024-02-06 04:55:07 -06:00
updateWellOperability ( const Simulator & simulator ,
2020-11-27 00:57:55 -06:00
const WellState & well_state ,
2021-05-12 01:50:04 -05:00
DeferredLogger & deferred_logger )
2023-12-07 05:15:40 -06:00
{
2023-08-16 05:50:06 -05:00
if ( this - > param_ . local_well_solver_control_switching_ ) {
2024-02-06 04:55:07 -06:00
const bool success = updateWellOperabilityFromWellEq ( simulator , well_state , deferred_logger ) ;
2023-08-16 05:50:06 -05:00
if ( success ) {
return ;
} else {
deferred_logger . debug ( " Operability check using well equations did not converge for well "
+ this - > name ( ) + " , reverting to classical approach. " ) ;
}
}
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 ) {
2024-02-06 04:55:07 -06:00
updateIPR ( simulator , deferred_logger ) ;
checkOperabilityUnderBHPLimit ( well_state , 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 ) {
2024-02-06 04:55:07 -06:00
checkOperabilityUnderTHPLimit ( simulator , well_state , deferred_logger ) ;
2020-11-27 00:57:55 -06:00
}
}
2018-11-17 16:14:51 -06:00
2023-08-16 05:50:06 -05:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-06 04:55:07 -06:00
updateWellOperabilityFromWellEq ( const Simulator & simulator ,
2023-08-16 05:50:06 -05:00
const WellState & well_state ,
DeferredLogger & deferred_logger )
{
2023-09-25 06:07:39 -05:00
// only makes sense if we're using this parameter is true
2023-08-16 05:50:06 -05:00
assert ( this - > param_ . local_well_solver_control_switching_ ) ;
this - > operability_status_ . resetOperability ( ) ;
WellState well_state_copy = well_state ;
2024-02-06 04:55:07 -06:00
const auto & group_state = simulator . problem ( ) . wellModel ( ) . groupState ( ) ;
const double dt = simulator . timeStepSize ( ) ;
2023-08-16 05:50:06 -05:00
// equations should be converged at this stage, so only one it is needed
2024-02-06 04:55:07 -06:00
bool converged = iterateWellEquations ( simulator , dt , well_state_copy , group_state , deferred_logger ) ;
2023-08-16 05:50:06 -05:00
return converged ;
2023-12-07 05:15:40 -06:00
}
2018-11-17 16:14:51 -06:00
2021-04-26 02:31:29 -05:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-06 04:55:07 -06:00
updateWellStateWithTarget ( const Simulator & 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 ( ) ;
2024-02-06 04:55:07 -06:00
const auto & summaryState = simulator . vanguard ( ) . summaryState ( ) ;
const auto & schedule = 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 ] ) ;
2023-09-06 04:46:20 -05:00
if ( ws . bhp = = 0. ) {
ws . bhp = controls . bhp_limit ;
}
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 {
2024-02-06 04:55:07 -06:00
const std : : vector < double > fractions = initialWellRateFractions ( 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 {
2024-02-06 04:55:07 -06:00
const std : : vector < double > fractions = initialWellRateFractions ( 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 {
2024-02-06 04:55:07 -06:00
const std : : vector < double > fractions = initialWellRateFractions ( 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 {
2024-02-06 04:55:07 -06:00
const std : : vector < double > fractions = initialWellRateFractions ( 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 ) ;
2023-03-24 04:36:06 -05:00
this - > rateConverter_ . calcCoeff ( /*fipreg*/ 0 , this - > pvtRegionIdx_ , ws . surface_rates , 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 {
2024-02-06 04:55:07 -06:00
const std : : vector < double > fractions = initialWellRateFractions ( 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 {
2024-02-06 04:55:07 -06:00
const std : : vector < double > fractions = initialWellRateFractions ( 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 :
{
2024-02-06 04:55:07 -06:00
const bool update_success = updateWellStateWithTHPTargetProd ( simulator , well_state , deferred_logger ) ;
2023-01-30 05:41:45 -06:00
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
2023-09-06 04:46:20 -05:00
if ( ws . bhp = = 0. ) {
ws . bhp = controls . bhp_limit ;
}
2021-04-26 02:31:29 -05:00
}
}
2021-04-26 02:31:29 -05:00
template < typename TypeTag >
std : : vector < double >
WellInterface < TypeTag > : :
2024-02-06 04:55:07 -06:00
initialWellRateFractions ( const Simulator & simulator , 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 ] ;
2024-02-06 04:55:07 -06:00
const auto & intQuants = simulator . model ( ) . intensiveQuantities ( cell_idx , /*timeIdx=*/ 0 ) ;
2021-04-26 02:31:29 -05:00
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 ) {
2024-02-06 05:36:45 -06:00
int modelPhaseIdx = this - > flowPhaseToModelPhaseIdx ( p ) ;
total_mobility + = fs . invB ( modelPhaseIdx ) . value ( ) * intQuants . mobility ( modelPhaseIdx ) . value ( ) ;
2021-04-26 02:31:29 -05:00
}
for ( int p = 0 ; p < np ; + + p ) {
2024-02-06 05:36:45 -06:00
int modelPhaseIdx = this - > flowPhaseToModelPhaseIdx ( p ) ;
scaling_factor [ p ] + = well_tw_fraction * fs . invB ( modelPhaseIdx ) . value ( ) * intQuants . mobility ( modelPhaseIdx ) . value ( ) / total_mobility ;
2021-04-26 02:31:29 -05:00
}
}
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 > : :
2024-02-06 04:55:07 -06:00
updateWellStateRates ( const Simulator & simulator ,
2020-10-15 07:15:05 -05:00
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.
2024-02-06 04:55:07 -06:00
std : : vector < double > well_q_s = computeCurrentWellRates ( simulator , deferred_logger ) ;
2022-03-23 06:29:40 -05:00
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 ) {
2024-02-06 05:12:13 -06:00
ws . surface_rates [ p ] = well_q_s [ this - > flowPhaseToModelCompIdx ( p ) ] ;
2022-03-23 06:29:40 -05:00
}
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 ] ;
2024-02-06 05:12:13 -06:00
const int comp_idx_nz = this - > flowPhaseToModelCompIdx ( nonzero_rate_index ) ;
2023-06-20 16:14:50 -05:00
if ( std : : abs ( well_q_s [ comp_idx_nz ] ) > floating_point_error_epsilon ) {
for ( int p = 0 ; p < this - > number_of_phases_ ; + + p ) {
if ( p ! = nonzero_rate_index ) {
2024-02-06 05:12:13 -06:00
const int comp_idx = this - > flowPhaseToModelCompIdx ( p ) ;
2023-06-20 16:14:50 -05:00
double & rate = ws . surface_rates [ p ] ;
rate = ( initial_nonzero_rate / well_q_s [ comp_idx_nz ] ) * ( well_q_s [ comp_idx ] ) ;
}
2020-10-15 07:15:05 -05:00
}
}
}
2023-05-12 04:57:48 -05:00
2023-08-23 01:35:26 -05:00
template < typename TypeTag >
2023-11-14 05:45:25 -06:00
std : : vector < double >
2023-08-23 01:35:26 -05:00
WellInterface < TypeTag > : :
2023-11-10 09:58:18 -06:00
wellIndex ( const int perf ,
const IntensiveQuantities & intQuants ,
const double trans_mult ,
const SingleWellState & ws ) const
{
// Add a Forchheimer term to the gas phase CTF if the run uses
// either of the WDFAC or the WDFACCOR keywords.
2023-08-23 01:35:26 -05:00
2023-11-10 09:58:18 -06:00
auto wi = std : : vector < Scalar >
( this - > num_components_ , this - > well_index_ [ perf ] * trans_mult ) ;
if constexpr ( ! Indices : : gasEnabled ) {
2023-11-14 05:45:25 -06:00
return wi ;
2023-08-23 01:35:26 -05:00
}
2023-11-10 09:58:18 -06:00
const auto & wdfac = this - > well_ecl_ . getWDFAC ( ) ;
if ( ! wdfac . useDFactor ( ) | | ( this - > well_index_ [ perf ] = = 0.0 ) ) {
2023-11-14 05:45:25 -06:00
return wi ;
2023-08-23 01:35:26 -05:00
}
2023-11-10 09:58:18 -06:00
const double d = this - > computeConnectionDFactor ( perf , intQuants , ws ) ;
if ( d < 1.0e-15 ) {
2023-12-14 04:22:38 -06:00
return wi ;
}
2023-12-20 07:20:45 -06:00
// Solve quadratic equations for connection rates satisfying the ipr and the flow-dependent skin.
// If more than one solution, pick the one corresponding to lowest absolute rate (smallest skin).
2023-11-14 07:06:23 -06:00
const auto & connection = this - > well_ecl_ . getConnections ( ) [ ws . perf_data . ecl_index [ perf ] ] ;
2023-12-14 17:57:49 -06:00
const double Kh = connection . Kh ( ) ;
const double scaling = 3.141592653589 * Kh * connection . wpimult ( ) ;
2023-11-14 05:45:25 -06:00
const unsigned gas_comp_idx = Indices : : canonicalToActiveComponentIndex ( FluidSystem : : gasCompIdx ) ;
2023-12-14 04:22:38 -06:00
const double connection_pressure = ws . perf_data . pressure [ perf ] ;
const double cell_pressure = getValue ( intQuants . fluidState ( ) . pressure ( FluidSystem : : gasPhaseIdx ) ) ;
const double drawdown = cell_pressure - connection_pressure ;
const double invB = getValue ( intQuants . fluidState ( ) . invB ( FluidSystem : : gasPhaseIdx ) ) ;
const double mob_g = getValue ( intQuants . mobility ( FluidSystem : : gasPhaseIdx ) ) * invB ;
const double a = d ;
const double b = 2 * scaling / wi [ gas_comp_idx ] ;
2023-12-20 07:20:45 -06:00
const double c = - 2 * scaling * mob_g * drawdown ;
2023-12-20 08:06:01 -06:00
double consistent_Q = - 1.0e20 ;
2023-12-20 07:20:45 -06:00
// Find and check negative solutions (a --> -a)
const double r2n = b * b + 4 * a * c ;
if ( r2n > = 0 ) {
const double rn = std : : sqrt ( r2n ) ;
const double xn1 = ( b - rn ) * 0.5 / a ;
if ( xn1 < = 0 ) {
2023-12-20 08:06:01 -06:00
consistent_Q = xn1 ;
2023-12-20 07:20:45 -06:00
}
const double xn2 = ( b + rn ) * 0.5 / a ;
2023-12-20 08:06:01 -06:00
if ( xn2 < = 0 & & xn2 > consistent_Q ) {
consistent_Q = xn2 ;
2023-12-20 07:20:45 -06:00
}
2023-12-14 04:22:38 -06:00
}
2023-12-20 07:20:45 -06:00
// Find and check positive solutions
2023-12-20 08:06:01 -06:00
consistent_Q * = - 1 ;
2023-12-20 07:20:45 -06:00
const double r2p = b * b - 4 * a * c ;
if ( r2p > = 0 ) {
const double rp = std : : sqrt ( r2p ) ;
const double xp1 = ( rp - b ) * 0.5 / a ;
if ( xp1 > 0 & & xp1 < consistent_Q ) {
2023-12-20 08:06:01 -06:00
consistent_Q = xp1 ;
2023-12-20 07:20:45 -06:00
}
2023-12-20 08:06:01 -06:00
const double xp2 = - ( rp + b ) * 0.5 / a ;
2023-12-20 07:20:45 -06:00
if ( xp2 > 0 & & xp2 < consistent_Q ) {
consistent_Q = xp2 ;
}
2023-12-14 17:57:49 -06:00
}
2023-12-20 07:20:45 -06:00
wi [ gas_comp_idx ] = 1.0 / ( 1.0 / ( trans_mult * this - > well_index_ [ perf ] ) + ( consistent_Q / 2 * d / scaling ) ) ;
2023-11-10 09:58:18 -06:00
2023-11-14 05:45:25 -06:00
return wi ;
2023-08-23 01:35:26 -05:00
}
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2023-11-10 09:58:18 -06:00
updateConnectionDFactor ( const Simulator & simulator , SingleWellState & ws ) const
{
if ( ! this - > well_ecl_ . getWDFAC ( ) . useDFactor ( ) ) {
2023-08-23 01:35:26 -05:00
return ;
}
2023-11-10 09:58:18 -06:00
auto & d_factor = ws . perf_data . connection_d_factor ;
2023-08-23 01:35:26 -05:00
for ( int perf = 0 ; perf < this - > number_of_perforations_ ; + + perf ) {
const int cell_idx = this - > well_cells_ [ perf ] ;
const auto & intQuants = simulator . model ( ) . intensiveQuantities ( cell_idx , /*timeIdx=*/ 0 ) ;
2023-11-10 09:58:18 -06:00
d_factor [ perf ] = this - > computeConnectionDFactor ( perf , intQuants , ws ) ;
2023-08-23 01:35:26 -05:00
}
}
2023-11-14 07:06:23 -06:00
template < typename TypeTag >
double
WellInterface < TypeTag > : :
2023-11-10 09:58:18 -06:00
computeConnectionDFactor ( const int perf ,
const IntensiveQuantities & intQuants ,
const SingleWellState & ws ) const
{
auto rhoGS = [ regIdx = this - > pvtRegionIdx ( ) ] ( ) {
return FluidSystem : : referenceDensity ( FluidSystem : : gasPhaseIdx , regIdx ) ;
} ;
// Viscosity is evaluated at connection pressure.
auto gas_visc = [ connection_pressure = ws . perf_data . pressure [ perf ] ,
temperature = ws . temperature ,
regIdx = this - > pvtRegionIdx ( ) , & intQuants ] ( )
{
const auto rv = getValue ( intQuants . fluidState ( ) . Rv ( ) ) ;
const auto & gasPvt = FluidSystem : : gasPvt ( ) ;
// Note that rv here is from grid block with typically
// p_block > connection_pressure
// so we may very well have rv > rv_sat
const double rv_sat = gasPvt . saturatedOilVaporizationFactor
( regIdx , temperature , connection_pressure ) ;
if ( ! ( rv < rv_sat ) ) {
return gasPvt . saturatedViscosity ( regIdx , temperature ,
connection_pressure ) ;
}
return gasPvt . viscosity ( regIdx , temperature , connection_pressure ,
rv , getValue ( intQuants . fluidState ( ) . Rvw ( ) ) ) ;
} ;
const auto & connection = this - > well_ecl_ . getConnections ( )
[ ws . perf_data . ecl_index [ perf ] ] ;
return this - > well_ecl_ . getWDFAC ( ) . getDFactor ( rhoGS , gas_visc , connection ) ;
2023-11-14 07:06:23 -06:00
}
2023-08-23 01:35:26 -05:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2023-11-10 09:58:18 -06:00
updateConnectionTransmissibilityFactor ( const Simulator & simulator , SingleWellState & ws ) const
{
auto connCF = [ & connIx = std : : as_const ( ws . perf_data . ecl_index ) ,
& conns = this - > well_ecl_ . getConnections ( ) ]
( const int perf )
{
return conns [ connIx [ perf ] ] . CF ( ) ;
} ;
2023-11-10 11:15:23 -06:00
auto & tmult = ws . perf_data . connection_compaction_tmult ;
auto & ctf = ws . perf_data . connection_transmissibility_factor ;
2023-11-10 09:58:18 -06:00
2023-08-23 01:35:26 -05:00
for ( int perf = 0 ; perf < this - > number_of_perforations_ ; + + perf ) {
const int cell_idx = this - > well_cells_ [ perf ] ;
2023-11-10 09:58:18 -06:00
const auto & intQuants = simulator . model ( )
. intensiveQuantities ( cell_idx , /*timeIdx=*/ 0 ) ;
2023-11-10 11:15:23 -06:00
tmult [ perf ] = simulator . problem ( )
2023-11-10 09:58:18 -06:00
. template wellTransMultiplier < double > ( intQuants , cell_idx ) ;
2023-11-10 11:15:23 -06:00
ctf [ perf ] = connCF ( perf ) * tmult [ perf ] ;
2023-08-23 01:35:26 -05:00
}
}
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 ) ;
2023-09-06 06:46:18 -05:00
} else if constexpr ( Indices : : gasEnabled ) {
2022-10-26 03:59:58 -05:00
return fs . pressure ( FluidSystem : : gasPhaseIdx ) ;
2023-09-06 06:46:18 -05:00
} else {
return fs . pressure ( FluidSystem : : waterPhaseIdx ) ;
2021-11-30 04:48:02 -06:00
}
}
2023-03-22 09:10:00 -05:00
2023-05-12 03:02:05 -05:00
template < typename TypeTag >
template < class Value , class Callback >
void
WellInterface < TypeTag > : :
2024-02-06 04:55:07 -06:00
getMobility ( const Simulator & simulator ,
2023-05-12 03:02:05 -05:00
const int perf ,
std : : vector < Value > & mob ,
Callback & extendEval ,
[[maybe_unused]] DeferredLogger & deferred_logger ) const
{
auto relpermArray = [ ] ( )
{
if constexpr ( std : : is_same_v < Value , Scalar > ) {
return std : : array < Scalar , 3 > { } ;
} else {
return std : : array < Eval , 3 > { } ;
}
} ;
const int cell_idx = this - > well_cells_ [ perf ] ;
assert ( int ( mob . size ( ) ) = = this - > num_components_ ) ;
2024-02-06 04:55:07 -06:00
const auto & intQuants = simulator . model ( ) . intensiveQuantities ( cell_idx , /*timeIdx=*/ 0 ) ;
const auto & materialLawManager = simulator . problem ( ) . materialLawManager ( ) ;
2023-05-12 03:02:05 -05:00
// either use mobility of the perforation cell or calculate its own
// based on passing the saturation table index
const int satid = this - > saturation_table_number_ [ perf ] - 1 ;
const int satid_elem = materialLawManager - > satnumRegionIdx ( cell_idx ) ;
if ( satid = = satid_elem ) { // the same saturation number is used. i.e. just use the mobilty from the cell
for ( unsigned phaseIdx = 0 ; phaseIdx < FluidSystem : : numPhases ; + + phaseIdx ) {
if ( ! FluidSystem : : phaseIsActive ( phaseIdx ) ) {
continue ;
}
const unsigned activeCompIdx = Indices : : canonicalToActiveComponentIndex ( FluidSystem : : solventComponentIndex ( phaseIdx ) ) ;
mob [ activeCompIdx ] = extendEval ( intQuants . mobility ( phaseIdx ) ) ;
}
if constexpr ( has_solvent ) {
mob [ Indices : : contiSolventEqIdx ] = extendEval ( intQuants . solventMobility ( ) ) ;
}
} else {
const auto & paramsCell = materialLawManager - > connectionMaterialLawParams ( satid , cell_idx ) ;
auto relativePerms = relpermArray ( ) ;
MaterialLaw : : relativePermeabilities ( relativePerms , paramsCell , intQuants . fluidState ( ) ) ;
// reset the satnumvalue back to original
materialLawManager - > connectionMaterialLawParams ( satid_elem , cell_idx ) ;
// compute the mobility
for ( unsigned phaseIdx = 0 ; phaseIdx < FluidSystem : : numPhases ; + + phaseIdx ) {
if ( ! FluidSystem : : phaseIsActive ( phaseIdx ) ) {
continue ;
}
const unsigned activeCompIdx = Indices : : canonicalToActiveComponentIndex ( FluidSystem : : solventComponentIndex ( phaseIdx ) ) ;
mob [ activeCompIdx ] = extendEval ( relativePerms [ phaseIdx ] / intQuants . fluidState ( ) . viscosity ( phaseIdx ) ) ;
}
// this may not work if viscosity and relperms has been modified?
if constexpr ( has_solvent ) {
OPM_DEFLOG_THROW ( std : : runtime_error , " individual mobility for wells does not work in combination with solvent " , deferred_logger ) ;
}
}
2023-07-06 08:56:16 -05:00
2023-07-07 05:22:17 -05:00
if ( this - > isInjector ( ) & & ! this - > inj_fc_multiplier_ . empty ( ) ) {
2023-07-06 08:56:16 -05:00
const auto perf_ecl_index = this - > perforationData ( ) [ perf ] . ecl_index ;
const auto & connections = this - > well_ecl_ . getConnections ( ) ;
const auto & connection = connections [ perf_ecl_index ] ;
if ( connection . filterCakeActive ( ) ) {
for ( auto & val : mob ) {
val * = this - > inj_fc_multiplier_ [ perf ] ;
}
}
}
2023-05-12 03:02:05 -05:00
}
2023-05-12 04:57:48 -05:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-06 04:55:07 -06:00
updateWellStateWithTHPTargetProd ( const Simulator & simulator ,
2023-05-12 04:57:48 -05:00
WellState & well_state ,
DeferredLogger & deferred_logger ) const
{
2024-02-06 04:55:07 -06:00
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
2023-05-12 04:57:48 -05:00
auto bhp_at_thp_limit = computeBhpAtThpLimitProdWithAlq (
2024-02-06 04:55:07 -06:00
simulator , summary_state , this - > getALQ ( well_state ) , deferred_logger ) ;
2023-05-12 04:57:48 -05:00
if ( bhp_at_thp_limit ) {
std : : vector < double > rates ( this - > number_of_phases_ , 0.0 ) ;
if ( thp_update_iterations ) {
2024-02-06 04:55:07 -06:00
computeWellRatesWithBhpIterations ( simulator , * bhp_at_thp_limit ,
2023-05-12 04:57:48 -05:00
rates , deferred_logger ) ;
} else {
2024-02-06 04:55:07 -06:00
computeWellRatesWithBhp ( simulator , * bhp_at_thp_limit ,
2023-05-12 04:57:48 -05:00
rates , deferred_logger ) ;
}
auto & ws = well_state . well ( this - > name ( ) ) ;
ws . surface_rates = rates ;
ws . bhp = * bhp_at_thp_limit ;
ws . thp = this - > getTHPConstraint ( summary_state ) ;
return true ;
} else {
return false ;
}
}
2023-05-12 08:29:53 -05:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
computeConnLevelProdInd ( const FluidState & fs ,
const std : : function < double ( const double ) > & connPICalc ,
const std : : vector < Scalar > & mobility ,
double * connPI ) const
{
const auto & pu = this - > phaseUsage ( ) ;
const int np = this - > number_of_phases_ ;
for ( int p = 0 ; p < np ; + + p ) {
// Note: E100's notion of PI value phase mobility includes
// the reciprocal FVF.
const auto connMob =
2024-02-06 05:12:13 -06:00
mobility [ this - > flowPhaseToModelCompIdx ( p ) ]
2024-02-06 05:36:45 -06:00
* fs . invB ( this - > flowPhaseToModelPhaseIdx ( p ) ) . value ( ) ;
2023-05-12 08:29:53 -05:00
connPI [ p ] = connPICalc ( connMob ) ;
}
if ( FluidSystem : : phaseIsActive ( FluidSystem : : oilPhaseIdx ) & &
FluidSystem : : phaseIsActive ( FluidSystem : : gasPhaseIdx ) )
{
const auto io = pu . phase_pos [ Oil ] ;
const auto ig = pu . phase_pos [ Gas ] ;
const auto vapoil = connPI [ ig ] * fs . Rv ( ) . value ( ) ;
const auto disgas = connPI [ io ] * fs . Rs ( ) . value ( ) ;
connPI [ io ] + = vapoil ;
connPI [ ig ] + = disgas ;
}
}
template < typename TypeTag >
void
WellInterface < TypeTag > : :
computeConnLevelInjInd ( const FluidState & fs ,
const Phase preferred_phase ,
const std : : function < double ( const double ) > & connIICalc ,
const std : : vector < Scalar > & mobility ,
double * connII ,
DeferredLogger & deferred_logger ) const
{
// Assumes single phase injection
const auto & pu = this - > phaseUsage ( ) ;
auto phase_pos = 0 ;
if ( preferred_phase = = Phase : : GAS ) {
phase_pos = pu . phase_pos [ Gas ] ;
}
else if ( preferred_phase = = Phase : : OIL ) {
phase_pos = pu . phase_pos [ Oil ] ;
}
else if ( preferred_phase = = Phase : : WATER ) {
phase_pos = pu . phase_pos [ Water ] ;
}
else {
OPM_DEFLOG_THROW ( NotImplemented ,
fmt : : format ( " Unsupported Injector Type ({}) "
" for well {} during connection I.I. calculation " ,
static_cast < int > ( preferred_phase ) , this - > name ( ) ) ,
deferred_logger ) ;
}
const auto mt = std : : accumulate ( mobility . begin ( ) , mobility . end ( ) , 0.0 ) ;
2024-02-06 05:36:45 -06:00
connII [ phase_pos ] = connIICalc ( mt * fs . invB ( this - > flowPhaseToModelPhaseIdx ( phase_pos ) ) . value ( ) ) ;
2023-05-12 08:29:53 -05:00
}
2020-02-10 08:16:09 -06:00
} // namespace Opm