2017-06-15 11:34:07 +02:00
/*
2017-08-03 16:45:59 +02:00
Copyright 2017 SINTEF Digital , Mathematics and Cybernetics .
2017-06-15 11:34:07 +02:00
Copyright 2017 Statoil ASA .
2018-06-06 15:17:59 +02:00
Copyright 2018 IRIS
2017-06-15 11:34:07 +02: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/>.
*/
2024-02-23 08:45:25 +01:00
// Improve IDE experience
# ifndef OPM_WELLINTERFACE_HEADER_INCLUDED
# include <config.h>
# define OPM_WELLINTERFACE_IMPL_HEADER_INCLUDED
# include <opm/simulators/wells/WellInterface.hpp>
# endif
2022-12-13 12:54:27 +01:00
# include <opm/common/Exceptions.hpp>
2021-12-14 08:30:15 +01:00
# include <opm/input/eclipse/Schedule/ScheduleTypes.hpp>
2023-08-23 08:35:26 +02:00
# include <opm/input/eclipse/Schedule/Well/WDFAC.hpp>
2023-11-10 16:58:18 +01:00
2019-05-07 13:06:02 +02:00
# include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
2023-11-10 16:58:18 +01:00
2021-05-28 11:27:15 +02:00
# include <opm/simulators/wells/GroupState.hpp>
2020-03-27 13:27:45 +01:00
# include <opm/simulators/wells/TargetCalculator.hpp>
2022-10-19 09:55:14 +02:00
# include <opm/simulators/wells/WellBhpThpCalculator.hpp>
2023-03-22 15:10:00 +01:00
# include <opm/simulators/wells/WellHelpers.hpp>
2017-06-15 11:34:07 +02:00
2021-05-25 12:57:11 +02:00
# include <dune/common/version.hh>
2024-10-10 16:14:07 +02:00
# include <algorithm>
2024-02-20 14:37:48 +01:00
# include <cassert>
2023-08-15 09:32:10 +02:00
# include <cstddef>
2023-11-10 16:58:18 +01:00
# include <utility>
# include <fmt/format.h>
2023-08-15 09:32:10 +02:00
2017-06-15 11:34:07 +02:00
namespace Opm
{
2017-06-19 14:49:49 +02:00
template < typename TypeTag >
WellInterface < TypeTag > : :
2019-10-23 09:09:45 +02:00
WellInterface ( const Well & well ,
2024-02-20 15:35:13 +01:00
const ParallelWellInfo < Scalar > & pw_info ,
2019-10-23 09:09:45 +02:00
const int time_step ,
2017-11-23 08:37:30 +01:00
const ModelParameters & param ,
const RateConverterType & rate_converter ,
2017-11-30 16:31:48 +01:00
const int pvtRegionIdx ,
2019-10-23 09:09:45 +02:00
const int num_components ,
const int num_phases ,
const int index_of_well ,
2024-02-20 14:37:48 +01:00
const std : : vector < PerforationData < Scalar > > & perf_data )
2024-02-22 15:17:09 +01:00
: WellInterfaceIndices < FluidSystem , Indices > ( well ,
pw_info ,
time_step ,
2024-09-25 15:08:04 +02:00
param ,
2024-02-22 15:17:09 +01:00
rate_converter ,
pvtRegionIdx ,
num_components ,
num_phases ,
index_of_well ,
perf_data )
2017-06-15 11:34:07 +02:00
{
2021-05-11 23:03:33 +02:00
connectionRates_ . resize ( this - > number_of_perforations_ ) ;
2018-11-15 14:37:01 +01:00
2021-05-11 12:28:10 +02:00
if constexpr ( has_solvent | | has_zFraction ) {
if ( well . isInjector ( ) ) {
2021-05-11 23:03:33 +02:00
auto injectorType = this - > well_ecl_ . injectorType ( ) ;
2021-05-11 12:28:10 +02:00
if ( injectorType = = InjectorType : : GAS ) {
2021-05-11 23:03:33 +02:00
this - > wsolvent_ = this - > well_ecl_ . getSolventFraction ( ) ;
2021-05-11 12:28:10 +02:00
}
2019-09-23 15:15:55 +02:00
}
}
2017-06-15 11:34:07 +02:00
}
2017-06-19 14:49:49 +02:00
template < typename TypeTag >
2017-06-15 11:34:07 +02:00
void
2017-06-19 14:49:49 +02:00
WellInterface < TypeTag > : :
2017-06-15 11:34:07 +02:00
init ( const PhaseUsage * phase_usage_arg ,
2024-02-20 09:05:09 +01:00
const std : : vector < Scalar > & /* depth_arg */ ,
const Scalar gravity_arg ,
const std : : vector < Scalar > & B_avg ,
2022-04-12 08:44:52 +02:00
const bool changed_to_open_this_step )
2017-06-15 11:34:07 +02:00
{
2021-05-11 23:03:33 +02:00
this - > phase_usage_ = phase_usage_arg ;
this - > gravity_ = gravity_arg ;
2020-11-27 07:57:55 +01:00
B_avg_ = B_avg ;
2022-04-12 08:44:52 +02:00
this - > changed_to_open_this_step_ = changed_to_open_this_step ;
2017-06-15 11:34:07 +02:00
}
2017-06-19 16:46:06 +02:00
2017-07-25 12:10:13 +02:00
template < typename TypeTag >
2024-02-20 09:05:09 +01:00
typename WellInterface < TypeTag > : : Scalar
2017-07-25 12:10:13 +02:00
WellInterface < TypeTag > : :
wpolymer ( ) const
{
2021-05-11 12:28:10 +02:00
if constexpr ( has_polymer ) {
2023-01-12 15:15:42 +01:00
return this - > wpolymer_ ( ) ;
2017-07-25 12:10:13 +02:00
}
2021-05-11 12:28:10 +02:00
return 0.0 ;
2017-06-23 10:58:46 +02:00
}
2017-07-24 14:48:57 +02:00
2019-07-04 09:50:08 +02:00
template < typename TypeTag >
2024-02-20 09:05:09 +01:00
typename WellInterface < TypeTag > : : Scalar
2019-07-04 09:50:08 +02:00
WellInterface < TypeTag > : :
wfoam ( ) const
{
2021-05-11 12:28:10 +02:00
if constexpr ( has_foam ) {
2023-01-12 15:15:42 +01:00
return this - > wfoam_ ( ) ;
2019-07-04 09:50:08 +02:00
}
2021-05-11 12:28:10 +02:00
return 0.0 ;
2019-07-04 09:50:08 +02:00
}
2019-11-07 09:39:42 +01:00
template < typename TypeTag >
2024-02-20 09:05:09 +01:00
typename WellInterface < TypeTag > : : Scalar
2019-11-07 09:39:42 +01:00
WellInterface < TypeTag > : :
wsalt ( ) const
{
2021-05-11 12:28:10 +02:00
if constexpr ( has_brine ) {
2023-01-12 15:15:42 +01:00
return this - > wsalt_ ( ) ;
2019-11-07 09:39:42 +01:00
}
2021-05-11 12:28:10 +02:00
return 0.0 ;
2019-11-07 09:39:42 +01:00
}
2021-10-06 19:32:35 +02:00
template < typename TypeTag >
2024-02-20 09:05:09 +01:00
typename WellInterface < TypeTag > : : Scalar
2021-10-06 19:32:35 +02:00
WellInterface < TypeTag > : :
wmicrobes ( ) const
{
if constexpr ( has_micp ) {
2023-01-12 15:15:42 +01:00
return this - > wmicrobes_ ( ) ;
2021-10-06 19:32:35 +02:00
}
return 0.0 ;
}
template < typename TypeTag >
2024-02-20 09:05:09 +01:00
typename WellInterface < TypeTag > : : Scalar
2021-10-06 19:32:35 +02:00
WellInterface < TypeTag > : :
woxygen ( ) const
{
if constexpr ( has_micp ) {
2023-01-12 15:15:42 +01:00
return this - > woxygen_ ( ) ;
2021-10-06 19:32:35 +02: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 >
2024-02-20 09:05:09 +01:00
typename WellInterface < TypeTag > : : Scalar
2021-10-06 19:32:35 +02:00
WellInterface < TypeTag > : :
wurea ( ) const
{
if constexpr ( has_micp ) {
2023-01-12 15:15:42 +01:00
return this - > wurea_ ( ) ;
2021-10-06 19:32:35 +02:00
}
return 0.0 ;
}
2019-11-07 09:39:42 +01:00
2017-09-05 09:42:44 +02:00
template < typename TypeTag >
2020-02-10 15:16:09 +01:00
bool
2017-09-05 09:42:44 +02:00
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
updateWellControl ( const Simulator & simulator ,
2020-02-10 15:16:09 +01:00
const IndividualOrGroup iog ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2024-02-17 18:13:46 +01:00
const GroupState < Scalar > & group_state ,
2021-05-12 08:50:04 +02:00
DeferredLogger & deferred_logger ) /* const */
2017-09-05 09:42:44 +02:00
{
2024-03-19 12:50:34 +01:00
if ( stoppedOrZeroRateTarget ( simulator , well_state , deferred_logger ) ) {
2020-02-10 15:16:09 +01:00
return false ;
2019-10-07 11:54:57 +02:00
}
2018-11-16 09:00:11 +01:00
2024-02-06 11:55:07 +01:00
const auto & summaryState = simulator . vanguard ( ) . summaryState ( ) ;
const auto & schedule = simulator . vanguard ( ) . schedule ( ) ;
2021-05-11 23:03:33 +02:00
const auto & well = this - > well_ecl_ ;
2021-08-04 12:03:36 +02:00
auto & ws = well_state . well ( this - > index_of_well_ ) ;
2019-08-07 14:13:11 +02:00
std : : string from ;
if ( well . isInjector ( ) ) {
2023-01-18 09:58:57 +01:00
from = WellInjectorCMode2String ( ws . injection_cmode ) ;
2019-08-07 14:13:11 +02:00
} else {
2023-01-18 09:58:57 +01:00
from = WellProducerCMode2String ( ws . production_cmode ) ;
2017-09-05 09:42:44 +02:00
}
2024-09-25 15:08:04 +02:00
bool oscillating = std : : count ( this - > well_control_log_ . begin ( ) , this - > well_control_log_ . end ( ) , from ) > = this - > param_ . max_number_of_well_switches_ ;
2021-04-15 08:14:52 +02:00
if ( oscillating ) {
// only output frist time
2024-09-25 15:08:04 +02:00
bool output = std : : count ( this - > well_control_log_ . begin ( ) , this - > well_control_log_ . end ( ) , from ) = = this - > param_ . max_number_of_well_switches_ ;
2021-04-15 08:14:52 +02:00
if ( output ) {
std : : ostringstream ss ;
2023-11-20 08:29:24 +01:00
ss < < " The control mode for well " < < this - > name ( )
2021-04-15 08:14:52 +02:00
< < " is oscillating \n "
< < " We don't allow for more than "
2024-09-25 15:08:04 +02:00
< < this - > param_ . max_number_of_well_switches_
2021-04-15 08:14:52 +02:00
< < " 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 09:47:22 +02:00
this - > well_control_log_ . push_back ( from ) ;
2021-04-15 08:14:52 +02:00
}
return false ;
}
2020-02-10 15:16:09 +01:00
bool changed = false ;
if ( iog = = IndividualOrGroup : : Individual ) {
2022-02-23 11:11:11 +01:00
changed = this - > checkIndividualConstraints ( ws , summaryState , deferred_logger ) ;
2020-02-10 15:16:09 +01:00
} else if ( iog = = IndividualOrGroup : : Group ) {
2021-05-11 23:03:33 +02:00
changed = this - > checkGroupConstraints ( well_state , group_state , schedule , summaryState , deferred_logger ) ;
2020-02-10 15:16:09 +01:00
} else {
assert ( iog = = IndividualOrGroup : : Both ) ;
2021-05-11 23:03:33 +02:00
changed = this - > checkConstraints ( well_state , group_state , schedule , summaryState , deferred_logger ) ;
2020-02-10 15:16:09 +01:00
}
2024-02-06 11:55:07 +01:00
Parallel : : Communication cc = simulator . vanguard ( ) . grid ( ) . comm ( ) ;
2017-09-05 09:42:44 +02:00
// checking whether control changed
2019-08-07 14:13:11 +02:00
if ( changed ) {
std : : string to ;
if ( well . isInjector ( ) ) {
2023-01-18 09:58:57 +01:00
to = WellInjectorCMode2String ( ws . injection_cmode ) ;
2019-08-07 14:13:11 +02:00
} else {
2023-01-18 09:58:57 +01:00
to = WellProducerCMode2String ( ws . production_cmode ) ;
2019-08-07 14:13:11 +02:00
}
2019-01-18 14:04:30 +01:00
std : : ostringstream ss ;
2021-05-11 23:03:33 +02:00
ss < < " Switching control mode for well " < < this - > name ( )
2019-08-07 14:13:11 +02:00
< < " from " < < from
< < " to " < < to ;
2019-01-21 08:26:28 +01:00
if ( cc . size ( ) > 1 ) {
2019-01-18 15:48:05 +01:00
ss < < " on rank " < < cc . rank ( ) ;
}
2021-10-08 09:47:22 +02:00
deferred_logger . debug ( ss . str ( ) ) ;
this - > well_control_log_ . push_back ( from ) ;
2024-02-06 11:55:07 +01:00
updateWellStateWithTarget ( simulator , group_state , well_state , deferred_logger ) ;
2024-03-19 12:50:34 +01:00
updatePrimaryVariables ( simulator , well_state , deferred_logger ) ;
2017-09-05 09:42:44 +02:00
}
2020-02-10 15:16:09 +01:00
return changed ;
2017-09-05 09:42:44 +02:00
}
2023-08-16 12:50:06 +02:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
updateWellControlAndStatusLocalIteration ( const Simulator & simulator ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2024-02-17 18:13:46 +01:00
const GroupState < Scalar > & group_state ,
2023-09-25 13:07:39 +02:00
const Well : : InjectionControls & inj_controls ,
const Well : : ProductionControls & prod_controls ,
2024-02-20 09:05:09 +01:00
const Scalar wqTotal ,
2023-10-26 17:28:05 +02:00
DeferredLogger & deferred_logger ,
2023-10-30 20:58:09 +01:00
const bool fixed_control ,
const bool fixed_status )
2023-08-16 12:50:06 +02:00
{
2024-02-06 11:55:07 +01:00
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
2024-10-03 11:36:17 +02:00
const auto & schedule = simulator . vanguard ( ) . schedule ( ) ;
2024-05-29 09:52:43 +02:00
auto & ws = well_state . well ( this - > index_of_well_ ) ;
std : : string from ;
if ( this - > isInjector ( ) ) {
from = WellInjectorCMode2String ( ws . injection_cmode ) ;
} else {
from = WellProducerCMode2String ( ws . production_cmode ) ;
}
2024-09-25 15:08:04 +02:00
const bool oscillating = std : : count ( this - > well_control_log_ . begin ( ) , this - > well_control_log_ . end ( ) , from ) > = this - > param_ . max_number_of_well_switches_ ;
2023-12-07 12:15:40 +01:00
2024-05-29 09:52:43 +02:00
if ( oscillating | | this - > wellUnderZeroRateTarget ( simulator , well_state , deferred_logger ) | | ! ( this - > well_ecl_ . getStatus ( ) = = WellStatus : : OPEN ) ) {
2023-08-16 12:50:06 +02:00
return false ;
}
2017-09-05 09:42:44 +02:00
2024-02-20 09:05:09 +01:00
const Scalar sgn = this - > isInjector ( ) ? 1.0 : - 1.0 ;
2023-08-16 12:50:06 +02:00
if ( ! this - > wellIsStopped ( ) ) {
2023-10-30 20:58:09 +01:00
if ( wqTotal * sgn < = 0.0 & & ! fixed_status ) {
2023-08-16 12:50:06 +02:00
this - > stopWell ( ) ;
return true ;
} else {
bool changed = false ;
2023-10-30 20:58:09 +01:00
if ( ! fixed_control ) {
2024-10-03 11:36:17 +02:00
// Changing to group controls here may lead to inconsistencies in the group handling which in turn
// may result in excessive back and forth switching. However, we currently allow this by default.
// The switch check_group_constraints_inner_well_iterations_ is a temporary solution.
2024-09-19 12:58:47 +02:00
2024-10-03 11:36:17 +02:00
const bool hasGroupControl = this - > isInjector ( ) ? inj_controls . hasControl ( Well : : InjectorCMode : : GRUP ) :
prod_controls . hasControl ( Well : : ProducerCMode : : GRUP ) ;
2024-10-23 09:08:37 +02:00
bool isGroupControl = ws . production_cmode = = Well : : ProducerCMode : : GRUP | | ws . injection_cmode = = Well : : InjectorCMode : : GRUP ;
if ( ! ( isGroupControl & & ! this - > param_ . check_group_constraints_inner_well_iterations_ ) ) {
changed = this - > checkIndividualConstraints ( ws , summary_state , deferred_logger , inj_controls , prod_controls ) ;
}
2024-10-08 15:48:05 +02:00
if ( hasGroupControl & & this - > param_ . check_group_constraints_inner_well_iterations_ ) {
2024-10-03 11:36:17 +02:00
changed = changed | | this - > checkGroupConstraints ( well_state , group_state , schedule , summary_state , deferred_logger ) ;
}
2023-09-25 13:07:39 +02:00
2023-10-26 17:28:05 +02: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 11:55:07 +01:00
updateWellStateWithTarget ( simulator , group_state , well_state , deferred_logger ) ;
2023-10-26 17:28:05 +02:00
} else {
ws . thp = this - > getTHPConstraint ( summary_state ) ;
2024-03-19 12:50:34 +01:00
}
updatePrimaryVariables ( simulator , well_state , deferred_logger ) ;
2023-10-09 11:14:48 +02:00
}
2023-08-16 12:50:06 +02:00
}
return changed ;
}
2023-10-30 20:58:09 +01:00
} else if ( ! fixed_status ) {
2023-08-16 12:50:06 +02:00
// well is stopped, check if current bhp allows reopening
2024-02-20 09:05:09 +01:00
const Scalar bhp = well_state . well ( this - > index_of_well_ ) . bhp ;
Scalar prod_limit = prod_controls . bhp_limit ;
Scalar inj_limit = inj_controls . bhp_limit ;
2023-08-16 12:50:06 +02:00
const bool has_thp = this - > wellHasTHPConstraints ( summary_state ) ;
if ( has_thp ) {
2024-02-20 09:05:09 +01:00
std : : vector < Scalar > rates ( this - > num_components_ ) ;
2023-08-16 12:50:06 +02:00
if ( this - > isInjector ( ) ) {
2024-02-20 09:05:09 +01:00
const Scalar bhp_thp = WellBhpThpCalculator ( * this ) .
calculateBhpFromThp ( well_state , rates ,
this - > well_ecl_ ,
summary_state ,
this - > getRefDensity ( ) ,
deferred_logger ) ;
inj_limit = std : : min ( bhp_thp , static_cast < Scalar > ( inj_controls . bhp_limit ) ) ;
2023-08-16 12:50:06 +02:00
} else {
2024-02-20 09:05:09 +01:00
// if the well can operate, it must at least be able to produce
// at the lowest bhp of the bhp-curve (explicit fractions)
const Scalar bhp_min = WellBhpThpCalculator ( * this ) .
calculateMinimumBhpFromThp ( well_state ,
this - > well_ecl_ ,
summary_state ,
this - > getRefDensity ( ) ) ;
prod_limit = std : : max ( bhp_min , static_cast < Scalar > ( prod_controls . bhp_limit ) ) ;
2023-08-16 12:50:06 +02:00
}
}
2024-02-20 09:05:09 +01:00
const Scalar bhp_diff = ( this - > isInjector ( ) ) ? inj_limit - bhp : bhp - prod_limit ;
2023-08-16 12:50:06 +02:00
if ( bhp_diff > 0 ) {
this - > openWell ( ) ;
2023-10-30 20:58:09 +01:00
well_state . well ( this - > index_of_well_ ) . bhp = ( this - > isInjector ( ) ) ? inj_limit : prod_limit ;
2023-10-26 17:28:05 +02:00
if ( has_thp ) {
well_state . well ( this - > index_of_well_ ) . thp = this - > getTHPConstraint ( summary_state ) ;
}
2023-08-16 12:50:06 +02:00
return true ;
} else {
return false ;
}
2023-10-30 20:58:09 +01:00
} else {
return false ;
2023-08-16 12:50:06 +02:00
}
}
2017-09-05 09:42:44 +02:00
2018-10-31 14:56:56 +01:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2021-03-18 14:49:52 +01:00
wellTesting ( const Simulator & simulator ,
2021-08-24 09:00:06 +02:00
const double simulation_time ,
2024-02-17 18:13:46 +01:00
/* const */ WellState < Scalar > & well_state ,
2024-02-17 18:13:46 +01:00
const GroupState < Scalar > & group_state ,
2018-11-23 23:46:56 +01:00
WellTestState & well_test_state ,
2021-05-12 08:50:04 +02:00
DeferredLogger & deferred_logger )
2018-10-31 14:56:56 +01:00
{
2021-08-24 09:00:06 +02:00
deferred_logger . info ( " well " + this - > name ( ) + " is being tested " ) ;
2018-11-23 13:11:48 +01:00
2024-02-17 18:13:46 +01:00
WellState < Scalar > well_state_copy = well_state ;
2021-08-05 10:57:15 +02:00
auto & ws = well_state_copy . well ( this - > indexOfWell ( ) ) ;
2023-12-07 12:15:40 +01:00
2021-06-10 15:09:05 +02:00
updateWellStateWithTarget ( simulator , group_state , well_state_copy , deferred_logger ) ;
2019-06-26 22:40:33 +02:00
calculateExplicitQuantities ( simulator , well_state_copy , deferred_logger ) ;
2024-03-19 12:50:34 +01:00
updatePrimaryVariables ( simulator , well_state_copy , deferred_logger ) ;
2018-10-31 14:56:56 +01:00
initPrimaryVariablesEvaluation ( ) ;
2022-10-10 08:14:42 +02:00
if ( this - > isProducer ( ) ) {
2023-12-07 12:15:40 +01: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 08:14:42 +02:00
}
2018-10-31 14:56:56 +01: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 09:32:10 +02:00
const std : : size_t original_number_closed_completions = welltest_state_temp . num_closed_completions ( ) ;
2021-08-24 09:00:06 +02: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 08:14:42 +02:00
2021-08-24 09:00:06 +02:00
updateWellOperability ( simulator , well_state_copy , deferred_logger ) ;
2021-09-29 16:01:16 +02:00
if ( ! this - > isOperableAndSolvable ( ) ) {
2021-08-24 09:00:06 +02:00
const auto msg = fmt : : format ( " WTEST: Well {} is not operable (physical) " , this - > name ( ) ) ;
deferred_logger . debug ( msg ) ;
return ;
}
2024-02-20 09:05:09 +01:00
std : : vector < Scalar > potentials ;
2021-06-30 12:56:18 +02:00
try {
computeWellPotentials ( simulator , well_state_copy , potentials , deferred_logger ) ;
} catch ( const std : : exception & e ) {
2024-02-20 09:05:09 +01:00
const std : : string msg = fmt : : format ( " well {}: computeWellPotentials() "
" failed during testing for re-opening: " ,
this - > name ( ) , e . what ( ) ) ;
2021-06-30 12:56:18 +02:00
deferred_logger . info ( msg ) ;
2021-08-24 09:00:06 +02:00
return ;
2021-06-30 12:56:18 +02:00
}
const int np = well_state_copy . numPhases ( ) ;
for ( int p = 0 ; p < np ; + + p ) {
2024-02-20 09:05:09 +01:00
ws . well_potentials [ p ] = std : : max ( Scalar { 0.0 } , potentials [ p ] ) ;
2021-06-30 12:56:18 +02:00
}
2024-06-12 13:17:54 +02:00
const bool under_zero_target = this - > wellUnderZeroGroupRateTarget ( simulator , well_state_copy , deferred_logger ) ;
2024-02-20 09:05:09 +01:00
this - > updateWellTestState ( well_state_copy . well ( this - > indexOfWell ( ) ) ,
simulation_time ,
/*writeMessageToOPMLog=*/ false ,
2024-06-12 13:17:54 +02:00
under_zero_target ,
2024-02-20 09:05:09 +01:00
welltest_state_temp ,
deferred_logger ) ;
2021-05-11 23:03:33 +02:00
this - > closeCompletions ( welltest_state_temp ) ;
2018-10-31 14:56:56 +01: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 14:00:23 +02:00
if ( welltest_state_temp . num_closed_wells ( ) > 0 | |
( original_number_closed_completions = = welltest_state_temp . num_closed_completions ( ) ) ) {
2018-10-31 14:56:56 +01:00
testWell = false ; // this terminates the while loop
}
}
// update wellTestState if the well test succeeds
2021-10-07 15:18:53 +02:00
if ( ! welltest_state_temp . well_is_closed ( this - > name ( ) ) ) {
2021-10-07 15:30:10 +02:00
well_test_state . open_well ( this - > name ( ) ) ;
2021-08-24 09:00:06 +02:00
std : : string msg = std : : string ( " well " ) + this - > name ( ) + std : : string ( " is re-opened " ) ;
2019-02-03 08:13:11 +01:00
deferred_logger . info ( msg ) ;
2018-10-31 14:56:56 +01:00
// also reopen completions
2021-05-11 23:03:33 +02:00
for ( auto & completion : this - > well_ecl_ . getCompletions ( ) ) {
2021-10-08 10:40:38 +02:00
if ( ! welltest_state_temp . completion_is_closed ( this - > name ( ) , completion . first ) )
well_test_state . open_completion ( this - > name ( ) , completion . first ) ;
2018-10-31 14:56:56 +01:00
}
2021-09-21 10:32:56 +02:00
// set the status of the well_state to open
ws . open ( ) ;
2021-06-30 12:56:18 +02:00
well_state = well_state_copy ;
2018-10-31 14:56:56 +01:00
}
2017-07-26 11:01:26 +02:00
}
2017-07-31 16:42:26 +02:00
2021-08-24 09:00:06 +02:00
2020-06-09 23:35:58 +02:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
iterateWellEquations ( const Simulator & simulator ,
2020-06-09 23:35:58 +02:00
const double dt ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2024-02-17 18:13:46 +01:00
const GroupState < Scalar > & group_state ,
2021-05-12 08:50:04 +02:00
DeferredLogger & deferred_logger )
2020-06-09 23:35:58 +02:00
{
2024-02-06 11:55:07 +01:00
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
2021-05-11 23:03:33 +02: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 11:27:44 +02:00
bool converged = false ;
try {
2023-09-25 13:07:39 +02:00
// TODO: the following two functions will be refactored to be one to reduce the code duplication
2023-08-16 12:50:06 +02:00
if ( ! this - > param_ . local_well_solver_control_switching_ ) {
2024-02-06 11:55:07 +01:00
converged = this - > iterateWellEqWithControl ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2023-08-16 12:50:06 +02:00
} else {
2023-11-16 16:02:53 +01:00
if ( this - > param_ . use_implicit_ipr_ & & this - > well_ecl_ . isProducer ( ) & & this - > wellHasTHPConstraints ( summary_state ) & & ( this - > well_ecl_ . getStatus ( ) = = WellStatus : : OPEN ) ) {
2024-02-06 11:55:07 +01:00
converged = solveWellWithTHPConstraint ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2023-10-26 17:28:05 +02:00
} else {
2024-02-06 11:55:07 +01:00
converged = this - > iterateWellEqWithSwitching ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2023-10-26 17:28:05 +02:00
}
2023-08-16 12:50:06 +02:00
}
2023-12-07 12:15:40 +01:00
2022-12-13 12:54:27 +01:00
} catch ( NumericalProblem & e ) {
2021-10-05 11:27:44 +02: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 23:35:58 +02:00
}
2023-10-26 17:28:05 +02:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
solveWellWithTHPConstraint ( const Simulator & simulator ,
2023-11-07 17:29:19 +01:00
const double dt ,
const Well : : InjectionControls & inj_controls ,
2024-02-17 18:13:46 +01:00
const Well : : ProductionControls & prod_controls ,
WellState < Scalar > & well_state ,
2024-02-17 18:13:46 +01:00
const GroupState < Scalar > & group_state ,
2023-11-07 17:29:19 +01:00
DeferredLogger & deferred_logger )
2023-10-26 17:28:05 +02:00
{
2024-02-06 11:55:07 +01:00
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
2023-10-26 17:28:05 +02:00
bool is_operable = true ;
2023-11-07 17:29:19 +01: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 11:42:48 +01:00
if ( this - > wellIsStopped ( ) ) {
this - > openWell ( ) ;
2024-02-06 11:55:07 +01:00
auto bhp_target = estimateOperableBhp ( simulator , dt , well_state , summary_state , deferred_logger ) ;
2023-11-06 11:42:48 +01:00
if ( ! bhp_target . has_value ( ) ) {
2023-11-08 21:12:31 +01: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 11:42:48 +01:00
is_operable = false ;
2023-11-07 17:29:19 +01:00
// solve with zero rates
2024-02-06 11:55:07 +01:00
solveWellWithZeroRate ( simulator , dt , well_state , deferred_logger ) ;
2023-11-07 17:29:19 +01:00
this - > stopWell ( ) ;
2023-11-06 11:42:48 +01:00
} else {
2023-11-07 17:29:19 +01:00
// solve well with the estimated target bhp (or limit)
2023-11-08 21:12:31 +01:00
ws . thp = this - > getTHPConstraint ( summary_state ) ;
2024-02-20 09:05:09 +01:00
const Scalar bhp = std : : max ( bhp_target . value ( ) ,
static_cast < Scalar > ( prod_controls . bhp_limit ) ) ;
2024-02-06 11:55:07 +01:00
solveWellWithBhp ( simulator , dt , bhp , well_state , deferred_logger ) ;
2023-11-06 11:42:48 +01:00
}
2023-11-07 17:29:19 +01:00
}
// solve well-equation
if ( is_operable ) {
2024-02-06 11:55:07 +01:00
converged = this - > iterateWellEqWithSwitching ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2023-11-06 11:42:48 +01:00
}
2023-11-07 17:29:19 +01:00
const bool isThp = ws . production_cmode = = Well : : ProducerCMode : : THP ;
// check stability of solution under thp-control
2024-03-19 12:50:34 +01:00
if ( converged & & ! stoppedOrZeroRateTarget ( simulator , well_state , deferred_logger ) & & isThp ) {
2023-12-07 12:15:40 +01:00
auto rates = well_state . well ( this - > index_of_well_ ) . surface_rates ;
this - > adaptRatesForVFP ( rates ) ;
2024-02-06 11:55:07 +01:00
this - > updateIPRImplicit ( simulator , well_state , deferred_logger ) ;
2023-12-07 12:15:40 +01:00
bool is_stable = WellBhpThpCalculator ( * this ) . isStableSolution ( well_state , this - > well_ecl_ , rates , summary_state ) ;
if ( ! is_stable ) {
2024-03-19 12:50:34 +01:00
// solution converged to an unstable point!
2023-12-07 12:15:40 +01:00
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
2024-02-20 09:05:09 +01:00
const Scalar reltol = 1e-3 ;
const Scalar cur_bhp = ws . bhp ;
2023-12-07 12:15:40 +01:00
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 11:55:07 +01:00
solveWellWithBhp ( simulator , dt , bhp_stable . value ( ) , well_state , deferred_logger ) ;
2023-12-07 12:15:40 +01:00
// re-solve with hopefully good initial guess
ws . thp = this - > getTHPConstraint ( summary_state ) ;
2024-02-06 11:55:07 +01:00
converged = this - > iterateWellEqWithSwitching ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2023-12-07 12:15:40 +01:00
}
}
2023-11-07 17:29:19 +01:00
}
2023-12-07 12:15:40 +01:00
2023-11-07 17:29:19 +01:00
if ( ! converged ) {
2023-10-26 17:28:05 +02:00
// Well did not converge, switch to explicit fractions
this - > operability_status_ . use_vfpexplicit = true ;
2023-10-30 20:58:09 +01:00
this - > openWell ( ) ;
2024-02-06 11:55:07 +01:00
auto bhp_target = estimateOperableBhp ( simulator , dt , well_state , summary_state , deferred_logger ) ;
2023-10-26 17:28:05 +02:00
if ( ! bhp_target . has_value ( ) ) {
// well can't operate using explicit fractions
is_operable = false ;
2024-03-19 12:50:34 +01:00
// solve with zero rate
2024-02-06 11:55:07 +01:00
converged = solveWellWithZeroRate ( simulator , dt , well_state , deferred_logger ) ;
2023-11-07 17:29:19 +01:00
this - > stopWell ( ) ;
2024-03-19 12:50:34 +01:00
} else {
2023-11-07 17:29:19 +01:00
// solve well with the estimated target bhp (or limit)
2024-02-20 09:05:09 +01:00
const Scalar bhp = std : : max ( bhp_target . value ( ) ,
static_cast < Scalar > ( prod_controls . bhp_limit ) ) ;
2024-02-06 11:55:07 +01:00
solveWellWithBhp ( simulator , dt , bhp , well_state , deferred_logger ) ;
2023-11-07 17:29:19 +01:00
ws . thp = this - > getTHPConstraint ( summary_state ) ;
2024-02-20 09:05:09 +01:00
converged = this - > iterateWellEqWithSwitching ( simulator , dt ,
inj_controls ,
prod_controls ,
well_state ,
group_state ,
deferred_logger ) ;
2023-10-26 17:28:05 +02:00
}
}
2023-11-07 17:29:19 +01:00
// update operability
2023-11-08 21:12:31 +01:00
is_operable = is_operable & & ! this - > wellIsStopped ( ) ;
2023-11-07 17:29:19 +01: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 17:28:05 +02:00
return converged ;
}
template < typename TypeTag >
2024-02-20 09:05:09 +01:00
std : : optional < typename WellInterface < TypeTag > : : Scalar >
2023-10-26 17:28:05 +02:00
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
estimateOperableBhp ( const Simulator & simulator ,
2023-10-26 17:28:05 +02:00
const double dt ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2023-10-26 17:28:05 +02:00
const SummaryState & summary_state ,
DeferredLogger & deferred_logger )
2024-03-19 12:50:34 +01:00
{
// Given an unconverged well or closed well, estimate an operable bhp (if any)
2023-10-26 17:28:05 +02:00
// Get minimal bhp from vfp-curve
2024-02-20 09:05:09 +01:00
Scalar bhp_min = WellBhpThpCalculator ( * this ) . calculateMinimumBhpFromThp ( well_state , this - > well_ecl_ , summary_state , this - > getRefDensity ( ) ) ;
2023-10-26 17:28:05 +02:00
// Solve
2024-02-06 11:55:07 +01:00
const bool converged = solveWellWithBhp ( simulator , dt , bhp_min , well_state , deferred_logger ) ;
2023-11-08 21:12:31 +01:00
if ( ! converged | | this - > wellIsStopped ( ) ) {
2023-10-26 17:28:05 +02:00
return std : : nullopt ;
}
2024-02-06 11:55:07 +01:00
this - > updateIPRImplicit ( simulator , well_state , deferred_logger ) ;
2023-10-26 17:28:05 +02:00
auto rates = well_state . well ( this - > index_of_well_ ) . surface_rates ;
this - > adaptRatesForVFP ( rates ) ;
2023-11-27 17:07:38 +01:00
return WellBhpThpCalculator ( * this ) . estimateStableBhp ( well_state , this - > well_ecl_ , rates , this - > getRefDensity ( ) , summary_state ) ;
2024-03-19 12:50:34 +01:00
}
2023-10-26 17:28:05 +02:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
solveWellWithBhp ( const Simulator & simulator ,
2023-10-26 17:28:05 +02:00
const double dt ,
2024-02-20 09:05:09 +01:00
const Scalar bhp ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2023-10-26 17:28:05 +02:00
DeferredLogger & deferred_logger )
2024-03-19 12:50:34 +01:00
{
2024-02-17 18:13:46 +01:00
// Solve a well using single bhp-constraint (but close if not operable under this)
auto group_state = GroupState < Scalar > ( ) ; // empty group
2023-10-26 17:28:05 +02:00
auto inj_controls = Well : : InjectionControls ( 0 ) ;
2024-03-19 12:50:34 +01:00
auto prod_controls = Well : : ProductionControls ( 0 ) ;
2023-10-26 17:28:05 +02:00
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 ;
2024-03-19 12:50:34 +01:00
ws . injection_cmode = Well : : InjectorCMode : : BHP ;
2023-10-26 17:28:05 +02:00
} else {
prod_controls . addControl ( Well : : ProducerCMode : : BHP ) ;
prod_controls . bhp_limit = bhp ;
prod_controls . cmode = Well : : ProducerCMode : : BHP ;
2024-03-19 12:50:34 +01:00
ws . production_cmode = Well : : ProducerCMode : : BHP ;
2023-10-26 17:28:05 +02:00
}
// update well-state
ws . bhp = bhp ;
2024-03-19 12:50:34 +01:00
// solve
2024-02-06 11:55:07 +01:00
const bool converged = this - > iterateWellEqWithSwitching ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger , /*fixed_control*/ true ) ;
2023-10-26 17:28:05 +02:00
ws . injection_cmode = cmode_inj ;
ws . production_cmode = cmode_prod ;
return converged ;
2024-03-19 12:50:34 +01:00
}
2020-06-09 23:35:58 +02:00
2023-10-30 20:58:09 +01:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
solveWellWithZeroRate ( const Simulator & simulator ,
2023-10-30 20:58:09 +01:00
const double dt ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2023-10-30 20:58:09 +01:00
DeferredLogger & deferred_logger )
2024-03-19 12:50:34 +01:00
{
2023-10-30 20:58:09 +01:00
// Solve a well as stopped
const auto well_status_orig = this - > wellStatus_ ;
this - > stopWell ( ) ;
2024-03-19 12:50:34 +01:00
2024-02-17 18:13:46 +01:00
auto group_state = GroupState < Scalar > ( ) ; // empty group
2023-10-30 20:58:09 +01:00
auto inj_controls = Well : : InjectionControls ( 0 ) ;
2024-03-19 12:50:34 +01:00
auto prod_controls = Well : : ProductionControls ( 0 ) ;
2024-02-06 11:55:07 +01: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 20:58:09 +01:00
this - > wellStatus_ = well_status_orig ;
return converged ;
2024-03-19 12:50:34 +01:00
}
2023-10-30 20:58:09 +01:00
2018-06-21 14:40:04 +02:00
template < typename TypeTag >
2021-08-24 09:00:06 +02:00
bool
2018-11-22 15:44:09 +01:00
WellInterface < TypeTag > : :
2024-02-17 18:13:46 +01:00
solveWellForTesting ( const Simulator & simulator ,
WellState < Scalar > & well_state ,
2024-02-17 18:13:46 +01:00
const GroupState < Scalar > & group_state ,
2021-05-12 08:50:04 +02:00
DeferredLogger & deferred_logger )
2018-06-21 14:40:04 +02:00
{
2018-11-22 15:44:09 +01:00
// keep a copy of the original well state
2024-02-17 18:13:46 +01:00
const WellState < Scalar > well_state0 = well_state ;
2024-02-06 11:55:07 +01:00
const double dt = simulator . timeStepSize ( ) ;
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
2022-02-17 08:58:38 +01:00
const bool has_thp_limit = this - > wellHasTHPConstraints ( summary_state ) ;
2023-03-02 23:17:03 +01:00
bool converged ;
if ( has_thp_limit ) {
2022-02-17 08:58:38 +01:00
well_state . well ( this - > indexOfWell ( ) ) . production_cmode = Well : : ProducerCMode : : THP ;
2023-03-02 23:17:03 +01:00
converged = gliftBeginTimeStepWellTestIterateWellEquations (
2024-02-06 11:55:07 +01:00
simulator , dt , well_state , group_state , deferred_logger ) ;
2023-03-02 23:17:03 +01:00
}
else {
2022-02-17 08:58:38 +01:00
well_state . well ( this - > indexOfWell ( ) ) . production_cmode = Well : : ProducerCMode : : BHP ;
2024-02-06 11:55:07 +01:00
converged = iterateWellEquations ( simulator , dt , well_state , group_state , deferred_logger ) ;
2023-03-02 23:17:03 +01:00
}
2018-06-21 14:40:04 +02:00
if ( converged ) {
2021-05-11 23:03:33 +02:00
deferred_logger . debug ( " WellTest: Well equation for well " + this - > name ( ) + " converged " ) ;
2021-08-24 09:00:06 +02:00
return true ;
2018-06-21 14:40:04 +02:00
}
2024-09-25 15:08:04 +02:00
const int max_iter = this - > param_ . max_welleq_iter_ ;
2021-08-24 09:00:06 +02:00
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 14:40:04 +02:00
}
2018-11-02 15:21:58 +01:00
2021-08-24 09:00:06 +02:00
2021-01-04 14:00:59 +01:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
solveWellEquation ( const Simulator & simulator ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2024-02-17 18:13:46 +01:00
const GroupState < Scalar > & group_state ,
2021-05-12 08:50:04 +02:00
DeferredLogger & deferred_logger )
2021-01-04 14:00:59 +01:00
{
2022-04-19 14:13:48 +02:00
if ( ! this - > isOperableAndSolvable ( ) & & ! this - > wellIsStopped ( ) )
2021-01-19 14:43:32 +01:00
return ;
2021-01-04 14:00:59 +01:00
// keep a copy of the original well state
2024-02-17 18:13:46 +01:00
const WellState < Scalar > well_state0 = well_state ;
2024-02-06 11:55:07 +01:00
const double dt = simulator . timeStepSize ( ) ;
bool converged = iterateWellEquations ( simulator , dt , well_state , group_state , deferred_logger ) ;
2022-10-06 08:33:42 +02: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 09:58:57 +01:00
this - > well_control_log_ . push_back ( WellInjectorCMode2String ( Well : : InjectorCMode : : THP ) ) ;
2022-10-06 08:33:42 +02:00
}
} else {
thp_control = ws . production_cmode = = Well : : ProducerCMode : : THP ;
if ( thp_control ) {
ws . production_cmode = Well : : ProducerCMode : : BHP ;
2023-01-18 09:58:57 +01:00
this - > well_control_log_ . push_back ( WellProducerCMode2String ( Well : : ProducerCMode : : THP ) ) ;
2022-10-06 08:33:42 +02: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 11:55:07 +01:00
converged = this - > iterateWellEquations ( simulator , dt , well_state , group_state , deferred_logger ) ;
2022-10-06 08:33:42 +02:00
}
}
2021-04-16 14:44:14 +02:00
if ( ! converged ) {
2024-09-25 15:08:04 +02:00
const int max_iter = this - > param_ . max_welleq_iter_ ;
2021-05-11 23:03:33 +02:00
deferred_logger . debug ( " Compute initial well solution for well " + this - > name ( ) + " . Failed to converge in "
2021-01-04 14:00:59 +01:00
+ std : : to_string ( max_iter ) + " iterations " ) ;
well_state = well_state0 ;
}
}
2021-05-10 09:41:18 +02:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
assembleWellEq ( const Simulator & simulator ,
2021-05-10 09:41:18 +02:00
const double dt ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2024-02-17 18:13:46 +01:00
const GroupState < Scalar > & group_state ,
2021-05-10 09:41:18 +02:00
DeferredLogger & deferred_logger )
{
2024-02-06 11:55:07 +01:00
prepareWellBeforeAssembling ( simulator , dt , well_state , group_state , deferred_logger ) ;
assembleWellEqWithoutIteration ( simulator , dt , well_state , group_state , deferred_logger ) ;
2022-10-20 11:03:29 +02:00
}
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
assembleWellEqWithoutIteration ( const Simulator & simulator ,
2022-10-20 11:03:29 +02:00
const double dt ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2024-02-17 18:13:46 +01:00
const GroupState < Scalar > & group_state ,
2022-10-20 11:03:29 +02:00
DeferredLogger & deferred_logger )
{
2024-02-06 11:55:07 +01:00
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
2022-10-20 11:03:29 +02: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 11:55:07 +01:00
assembleWellEqWithoutIteration ( simulator , dt , inj_controls , prod_controls , well_state , group_state , deferred_logger ) ;
2022-10-20 11:03:29 +02:00
}
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
prepareWellBeforeAssembling ( const Simulator & simulator ,
2022-10-20 11:03:29 +02:00
const double dt ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2024-02-17 18:13:46 +01:00
const GroupState < Scalar > & group_state ,
2022-10-20 11:03:29 +02:00
DeferredLogger & deferred_logger )
{
2021-09-29 16:01:16 +02:00
const bool old_well_operable = this - > operability_status_ . isOperableAndSolvable ( ) ;
2021-09-21 11:34:19 +02:00
2024-09-25 15:08:04 +02:00
if ( this - > param_ . check_well_operability_iter_ )
2024-02-06 11:55:07 +01:00
checkWellOperability ( simulator , well_state , deferred_logger ) ;
2021-05-10 09:41:18 +02:00
2021-06-04 10:51:15 +02:00
// only use inner well iterations for the first newton iterations.
2024-02-06 11:55:07 +01:00
const int iteration_idx = simulator . model ( ) . newtonMethod ( ) . numIterations ( ) ;
2024-09-25 15:08:04 +02:00
if ( iteration_idx < this - > param_ . max_niter_inner_well_iter_ | | this - > well_ecl_ . isMultiSegment ( ) ) {
2024-10-10 12:44:24 +02:00
const auto & ws = well_state . well ( this - > indexOfWell ( ) ) ;
const auto pmode_orig = ws . production_cmode ;
const auto imode_orig = ws . injection_cmode ;
2024-10-10 16:14:07 +02:00
const bool nonzero_rate_original =
std : : any_of ( ws . surface_rates . begin ( ) ,
ws . surface_rates . begin ( ) + well_state . numPhases ( ) ,
[ ] ( Scalar rate ) { return rate ! = Scalar ( 0.0 ) ; } ) ;
2024-10-10 12:44:24 +02:00
2021-09-21 11:34:19 +02:00
this - > operability_status_ . solvable = true ;
2024-02-06 11:55:07 +01:00
bool converged = this - > iterateWellEquations ( simulator , dt , well_state , group_state , deferred_logger ) ;
2021-04-16 14:44:14 +02:00
2024-10-10 12:44:24 +02:00
if ( converged ) {
const bool zero_target = this - > wellUnderZeroRateTarget ( simulator , well_state , deferred_logger ) ;
2024-10-10 16:14:07 +02:00
if ( this - > wellIsStopped ( ) & & ! zero_target & & nonzero_rate_original ) {
2024-10-10 12:44:24 +02:00
// Well had non-zero rate, but was stopped during local well-solve. We re-open the well
// for the next global iteration, but if the zero rate persists, it will be stopped.
// This logic is introduced to prevent/ameliorate stopped/revived oscillations
this - > operability_status_ . resetOperability ( ) ;
this - > openWell ( ) ;
deferred_logger . debug ( " " + this - > name ( ) + " is re-opened after being stopped during local solve " ) ;
}
// Add debug info for switched controls
if ( ws . production_cmode ! = pmode_orig | | ws . injection_cmode ! = imode_orig ) {
std : : string from , to ;
if ( this - > isInjector ( ) ) {
from = WellInjectorCMode2String ( imode_orig ) ;
to = WellInjectorCMode2String ( ws . injection_cmode ) ;
} else {
from = WellProducerCMode2String ( pmode_orig ) ;
to = WellProducerCMode2String ( ws . production_cmode ) ;
}
deferred_logger . debug ( " " + this - > name ( ) + " switched from " + from + " to " + to + " during local solve " ) ;
}
} else {
// unsolvable wells are treated as not operable and will not be solved for in this iteration.
2024-09-25 15:08:04 +02:00
if ( this - > param_ . shut_unsolvable_wells_ )
2021-09-21 11:34:19 +02:00
this - > operability_status_ . solvable = false ;
}
2021-04-16 14:44:14 +02:00
}
2021-11-22 11:27:45 +00:00
if ( this - > operability_status_ . has_negative_potentials ) {
2021-11-18 11:57:16 +00:00
auto well_state_copy = well_state ;
2024-02-20 09:05:09 +01:00
std : : vector < Scalar > potentials ;
2021-11-18 11:57:16 +00:00
try {
2024-02-06 11:55:07 +01:00
computeWellPotentials ( simulator , well_state_copy , potentials , deferred_logger ) ;
2021-11-18 11:57:16 +00:00
} catch ( const std : : exception & e ) {
2024-02-20 09:05:09 +01:00
const std : : string msg = fmt : : format ( " well {}: computeWellPotentials() failed "
" during attempt to recompute potentials for well: " ,
this - > name ( ) , e . what ( ) ) ;
2021-11-18 11:57:16 +00:00
deferred_logger . info ( msg ) ;
2021-11-22 11:27:45 +00:00
this - > operability_status_ . has_negative_potentials = true ;
2021-11-18 11:57:16 +00:00
}
auto & ws = well_state . well ( this - > indexOfWell ( ) ) ;
const int np = well_state . numPhases ( ) ;
for ( int p = 0 ; p < np ; + + p ) {
2024-02-20 09:05:09 +01:00
ws . well_potentials [ p ] = std : : max ( Scalar { 0.0 } , potentials [ p ] ) ;
2021-11-18 11:57:16 +00:00
}
}
this - > changed_to_open_this_step_ = false ;
2021-09-29 16:01:16 +02:00
const bool well_operable = this - > operability_status_ . isOperableAndSolvable ( ) ;
2022-04-19 14:13:48 +02:00
2021-04-16 14:44:14 +02:00
if ( ! well_operable & & old_well_operable ) {
2023-10-01 13:16:48 +02:00
deferred_logger . info ( " well " + this - > name ( ) + " gets STOPPED during iteration " ) ;
this - > stopWell ( ) ;
changed_to_stopped_this_step_ = true ;
2021-04-16 14:44:14 +02: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 11:57:16 +00:00
this - > changed_to_open_this_step_ = true ;
2021-05-10 09:41:18 +02:00
}
}
2018-11-02 15:21:58 +01:00
template < typename TypeTag >
void
2018-11-15 14:37:01 +01:00
WellInterface < TypeTag > : : addCellRates ( RateVector & rates , int cellIdx ) const
{
2022-04-19 14:13:48 +02:00
if ( ! this - > isOperableAndSolvable ( ) & & ! this - > wellIsStopped ( ) )
2022-04-05 14:42:27 +02:00
return ;
2021-05-11 23:03:33 +02:00
for ( int perfIdx = 0 ; perfIdx < this - > number_of_perforations_ ; + + perfIdx ) {
if ( this - > cells ( ) [ perfIdx ] = = cellIdx ) {
2018-11-15 14:37:01 +01:00
for ( int i = 0 ; i < RateVector : : dimension ; + + i ) {
rates [ i ] + = connectionRates_ [ perfIdx ] [ i ] ;
}
}
}
}
2018-11-14 13:18:48 +01:00
template < typename TypeTag >
typename WellInterface < TypeTag > : : Scalar
2024-02-20 09:05:09 +01:00
WellInterface < TypeTag > : : volumetricSurfaceRateForConnection ( int cellIdx , int phaseIdx ) const
{
2021-05-11 23:03:33 +02:00
for ( int perfIdx = 0 ; perfIdx < this - > number_of_perforations_ ; + + perfIdx ) {
if ( this - > cells ( ) [ perfIdx ] = = cellIdx ) {
2018-11-14 13:18:48 +01:00
const unsigned activeCompIdx = Indices : : canonicalToActiveComponentIndex ( FluidSystem : : solventComponentIndex ( phaseIdx ) ) ;
return connectionRates_ [ perfIdx ] [ activeCompIdx ] . value ( ) ;
}
}
2019-02-07 14:43:17 +01:00
// this is not thread safe
2021-05-11 23:03:33 +02:00
OPM_THROW ( std : : invalid_argument , " The well with name " + this - > name ( )
2018-11-14 13:18:48 +01:00
+ " does not perforate cell " + std : : to_string ( cellIdx ) ) ;
return 0.0 ;
}
2018-11-02 15:21:58 +01:00
2020-11-27 07:57:55 +01:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
checkWellOperability ( const Simulator & simulator ,
2024-02-17 18:13:46 +01:00
const WellState < Scalar > & well_state ,
2021-05-12 08:50:04 +02:00
DeferredLogger & deferred_logger )
2020-11-27 07:57:55 +01:00
{
2024-09-25 15:08:04 +02:00
if ( ! this - > param_ . check_well_operability_ ) {
2020-11-27 07:57:55 +01:00
return ;
}
if ( this - > wellIsStopped ( ) & & ! changed_to_stopped_this_step_ ) {
return ;
}
2024-02-06 11:55:07 +01:00
updateWellOperability ( simulator , well_state , deferred_logger ) ;
2022-02-22 15:25:10 +01:00
if ( ! this - > operability_status_ . isOperableAndSolvable ( ) ) {
2022-04-08 10:15:13 +02:00
this - > operability_status_ . use_vfpexplicit = true ;
2022-02-22 15:25:10 +01:00
deferred_logger . debug ( " EXPLICIT_LOOKUP_VFP " ,
" well not operable, trying with explicit vfp lookup: " + this - > name ( ) ) ;
2024-02-06 11:55:07 +01:00
updateWellOperability ( simulator , well_state , deferred_logger ) ;
2022-02-22 15:25:10 +01:00
}
2021-04-16 14:44:14 +02:00
}
2020-11-27 07:57:55 +01:00
2023-03-02 23:17:03 +01:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-20 09:05:09 +01:00
gliftBeginTimeStepWellTestIterateWellEquations ( const Simulator & simulator ,
const double dt ,
WellState < Scalar > & well_state ,
const GroupState < Scalar > & group_state ,
DeferredLogger & deferred_logger )
2023-03-02 23:17:03 +01:00
{
const auto & well_name = this - > name ( ) ;
2024-02-06 11:55:07 +01:00
assert ( this - > wellHasTHPConstraints ( simulator . vanguard ( ) . summaryState ( ) ) ) ;
const auto & schedule = simulator . vanguard ( ) . schedule ( ) ;
auto report_step_idx = simulator . episodeIndex ( ) ;
2023-03-02 23:17:03 +01:00
const auto & glo = schedule . glo ( report_step_idx ) ;
2023-12-07 12:15:40 +01:00
if ( glo . active ( ) & & glo . has_well ( well_name ) ) {
const auto increment = glo . gaslift_increment ( ) ;
2023-04-13 11:08:30 +02:00
auto alq = well_state . getALQ ( well_name ) ;
bool converged ;
while ( alq > 0 ) {
well_state . setALQ ( well_name , alq ) ;
if ( ( converged =
2024-02-06 11:55:07 +01:00
iterateWellEquations ( simulator , dt , well_state , group_state , deferred_logger ) ) )
2023-04-13 11:08:30 +02:00
{
return converged ;
}
alq - = increment ;
2023-03-02 23:17:03 +01:00
}
2023-04-13 11:08:30 +02:00
return false ;
}
else {
2024-02-06 11:55:07 +01:00
return iterateWellEquations ( simulator , dt , well_state , group_state , deferred_logger ) ;
2023-03-02 23:17:03 +01:00
}
}
2022-03-27 07:57:31 +02:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
gliftBeginTimeStepWellTestUpdateALQ ( const Simulator & simulator ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2022-03-27 07:57:31 +02:00
DeferredLogger & deferred_logger )
{
2024-02-06 11:55:07 +01:00
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
2022-03-27 07:57:31 +02: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 11:55:07 +01:00
const auto & schedule = simulator . vanguard ( ) . schedule ( ) ;
const auto report_step_idx = simulator . episodeIndex ( ) ;
2022-03-27 07:57:31 +02: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 ( ) ;
2024-02-20 09:05:09 +01:00
Scalar max_alq ;
2022-03-27 07:57:31 +02:00
if ( max_alq_optional ) {
max_alq = * max_alq_optional ;
}
else {
2023-12-07 12:15:40 +01:00
const auto & well_ecl = this - > wellEcl ( ) ;
2022-03-27 07:57:31 +02: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 07:57:55 +01:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
updateWellOperability ( const Simulator & simulator ,
2024-02-17 18:13:46 +01:00
const WellState < Scalar > & well_state ,
2021-05-12 08:50:04 +02:00
DeferredLogger & deferred_logger )
2023-12-07 12:15:40 +01:00
{
2023-08-16 12:50:06 +02:00
if ( this - > param_ . local_well_solver_control_switching_ ) {
2024-02-06 11:55:07 +01:00
const bool success = updateWellOperabilityFromWellEq ( simulator , well_state , deferred_logger ) ;
2023-08-16 12:50:06 +02: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 16:01:16 +02:00
this - > operability_status_ . resetOperability ( ) ;
2020-11-27 07:57:55 +01:00
2022-10-17 15:22:04 +02:00
bool thp_controlled = this - > isInjector ( ) ? well_state . well ( this - > index_of_well_ ) . injection_cmode = = Well : : InjectorCMode : : THP :
2021-10-01 13:47:53 +02:00
well_state . well ( this - > index_of_well_ ) . production_cmode = = Well : : ProducerCMode : : THP ;
2022-10-17 15:22:04 +02:00
bool bhp_controlled = this - > isInjector ( ) ? well_state . well ( this - > index_of_well_ ) . injection_cmode = = Well : : InjectorCMode : : BHP :
2021-10-01 13:47:53 +02:00
well_state . well ( this - > index_of_well_ ) . production_cmode = = Well : : ProducerCMode : : BHP ;
2020-12-08 09:51:26 +01:00
// Operability checking is not free
// Only check wells under BHP and THP control
2022-10-17 15:22:04 +02:00
bool check_thp = thp_controlled | | this - > operability_status_ . thp_limit_violated_but_not_switched ;
if ( check_thp | | bhp_controlled ) {
2024-02-06 11:55:07 +01:00
updateIPR ( simulator , deferred_logger ) ;
checkOperabilityUnderBHPLimit ( well_state , simulator , deferred_logger ) ;
2020-12-08 09:51:26 +01:00
}
// we do some extra checking for wells under THP control.
2022-08-19 10:48:56 +02:00
if ( check_thp ) {
2024-02-06 11:55:07 +01:00
checkOperabilityUnderTHPLimit ( simulator , well_state , deferred_logger ) ;
2020-11-27 07:57:55 +01:00
}
}
2018-11-17 23:14:51 +01:00
2023-08-16 12:50:06 +02:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
updateWellOperabilityFromWellEq ( const Simulator & simulator ,
2024-02-17 18:13:46 +01:00
const WellState < Scalar > & well_state ,
2023-08-16 12:50:06 +02:00
DeferredLogger & deferred_logger )
{
2023-09-25 13:07:39 +02:00
// only makes sense if we're using this parameter is true
2023-08-16 12:50:06 +02:00
assert ( this - > param_ . local_well_solver_control_switching_ ) ;
this - > operability_status_ . resetOperability ( ) ;
2024-02-17 18:13:46 +01:00
WellState < Scalar > well_state_copy = well_state ;
2024-02-06 11:55:07 +01:00
const auto & group_state = simulator . problem ( ) . wellModel ( ) . groupState ( ) ;
const double dt = simulator . timeStepSize ( ) ;
2023-08-16 12:50:06 +02:00
// equations should be converged at this stage, so only one it is needed
2024-02-06 11:55:07 +01:00
bool converged = iterateWellEquations ( simulator , dt , well_state_copy , group_state , deferred_logger ) ;
2023-08-16 12:50:06 +02:00
return converged ;
2023-12-07 12:15:40 +01:00
}
2018-11-17 23:14:51 +01:00
2021-04-26 09:31:29 +02:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
updateWellStateWithTarget ( const Simulator & simulator ,
2024-02-17 18:13:46 +01:00
const GroupState < Scalar > & group_state ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2021-05-12 08:50:04 +02:00
DeferredLogger & deferred_logger ) const
2021-04-26 09:31:29 +02:00
{
// only bhp and wellRates are used to initilize the primaryvariables for standard wells
2021-05-11 23:03:33 +02:00
const auto & well = this - > well_ecl_ ;
const int well_index = this - > index_of_well_ ;
2021-08-03 20:05:14 +02:00
auto & ws = well_state . well ( well_index ) ;
2021-05-11 23:03:33 +02:00
const auto & pu = this - > phaseUsage ( ) ;
2021-04-26 09:31:29 +02:00
const int np = well_state . numPhases ( ) ;
2024-02-06 11:55:07 +01:00
const auto & summaryState = simulator . vanguard ( ) . summaryState ( ) ;
const auto & schedule = simulator . vanguard ( ) . schedule ( ) ;
2021-04-26 09:31:29 +02:00
if ( this - > wellIsStopped ( ) ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 11:49:03 +02:00
ws . surface_rates [ p ] = 0 ;
2021-04-26 09:31:29 +02:00
}
2021-08-03 20:05:14 +02:00
ws . thp = 0 ;
2021-04-26 09:31:29 +02: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 23:03:33 +02:00
OPM_DEFLOG_THROW ( std : : runtime_error , " Expected WATER, OIL or GAS as type for injectors " + this - > name ( ) , deferred_logger ) ;
2021-04-26 09:31:29 +02:00
}
2021-08-04 12:03:36 +02:00
const auto current = ws . injection_cmode ;
2021-04-26 09:31:29 +02:00
2024-02-20 09:05:09 +01:00
switch ( current ) {
2021-04-26 09:31:29 +02:00
case Well : : InjectorCMode : : RATE :
{
2022-04-21 11:03:11 +02: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 09:31:29 +02:00
break ;
}
case Well : : InjectorCMode : : RESV :
{
2024-02-20 09:05:09 +01:00
std : : vector < Scalar > convert_coeff ( this - > number_of_phases_ , 1.0 ) ;
2021-05-11 23:03:33 +02:00
this - > rateConverter_ . calcCoeff ( /*fipreg*/ 0 , this - > pvtRegionIdx_ , convert_coeff ) ;
2024-02-20 09:05:09 +01:00
const Scalar coeff = convert_coeff [ phasePos ] ;
2021-08-24 11:49:03 +02:00
ws . surface_rates [ phasePos ] = controls . reservoir_rate / coeff ;
2021-04-26 09:31:29 +02:00
break ;
}
case Well : : InjectorCMode : : THP :
{
2021-08-24 11:49:03 +02:00
auto rates = ws . surface_rates ;
2024-02-20 09:05:09 +01:00
Scalar bhp = WellBhpThpCalculator ( * this ) . calculateBhpFromThp ( well_state ,
2022-10-19 09:55:14 +02:00
rates ,
well ,
summaryState ,
this - > getRefDensity ( ) ,
deferred_logger ) ;
2021-08-03 20:05:14 +02:00
ws . bhp = bhp ;
2022-09-14 11:16:13 +02:00
ws . thp = this - > getTHPConstraint ( summaryState ) ;
2021-04-26 09:31:29 +02:00
// if the total rates are negative or zero
// we try to provide a better intial well rate
// using the well potentials
2024-02-20 09:05:09 +01:00
Scalar total_rate = std : : accumulate ( rates . begin ( ) , rates . end ( ) , 0.0 ) ;
2021-08-24 11:49:03 +02:00
if ( total_rate < = 0.0 )
ws . surface_rates = ws . well_potentials ;
2021-04-26 09:31:29 +02:00
break ;
}
case Well : : InjectorCMode : : BHP :
{
2021-08-03 20:05:14 +02:00
ws . bhp = controls . bhp_limit ;
2024-02-20 09:05:09 +01:00
Scalar total_rate = 0.0 ;
2021-04-26 09:31:29 +02:00
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 11:49:03 +02:00
total_rate + = ws . surface_rates [ p ] ;
2021-04-26 09:31:29 +02: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 11:49:03 +02:00
if ( total_rate < = 0.0 )
ws . surface_rates = ws . well_potentials ;
2021-04-26 09:31:29 +02:00
break ;
}
case Well : : InjectorCMode : : GRUP :
{
2021-06-10 15:09:05 +02:00
assert ( well . isAvailableForGroupControl ( ) ) ;
const auto & group = schedule . getGroup ( well . groupName ( ) , this - > currentStep ( ) ) ;
2024-02-20 09:05:09 +01:00
const Scalar efficiencyFactor = well . getEfficiencyFactor ( ) ;
std : : optional < Scalar > target =
2021-06-10 15:09:05 +02:00
this - > getGroupInjectionTargetRate ( group ,
well_state ,
group_state ,
schedule ,
summaryState ,
injectorType ,
efficiencyFactor ,
deferred_logger ) ;
if ( target )
2021-08-24 11:49:03 +02:00
ws . surface_rates [ phasePos ] = * target ;
2021-04-26 09:31:29 +02:00
break ;
}
case Well : : InjectorCMode : : CMODE_UNDEFINED :
{
2021-05-11 23:03:33 +02:00
OPM_DEFLOG_THROW ( std : : runtime_error , " Well control must be specified for well " + this - > name ( ) , deferred_logger ) ;
2021-04-26 09:31:29 +02:00
}
}
2022-09-16 10:50:12 +02: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.
2024-02-20 09:05:09 +01:00
ws . surface_rates [ phasePos ] = std : : max ( Scalar { 1.e-7 } , ws . surface_rates [ phasePos ] ) ;
2023-09-06 11:46:20 +02:00
if ( ws . bhp = = 0. ) {
ws . bhp = controls . bhp_limit ;
}
2021-04-26 09:31:29 +02:00
}
//Producer
else
{
2021-08-04 12:03:36 +02:00
const auto current = ws . production_cmode ;
2021-04-26 09:31:29 +02:00
const auto & controls = well . productionControls ( summaryState ) ;
switch ( current ) {
case Well : : ProducerCMode : : ORAT :
{
2024-02-20 09:05:09 +01:00
Scalar current_rate = - ws . surface_rates [ pu . phase_pos [ Oil ] ] ;
2021-04-26 09:31:29 +02: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 11:49:03 +02:00
ws . surface_rates [ p ] * = controls . oil_rate / current_rate ;
2021-04-26 09:31:29 +02:00
}
} else {
2024-02-20 09:05:09 +01:00
const std : : vector < Scalar > fractions = initialWellRateFractions ( simulator , well_state ) ;
2021-04-26 09:31:29 +02:00
double control_fraction = fractions [ pu . phase_pos [ Oil ] ] ;
if ( control_fraction ! = 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 11:49:03 +02:00
ws . surface_rates [ p ] = - fractions [ p ] * controls . oil_rate / control_fraction ;
2021-04-26 09:31:29 +02:00
}
}
2021-04-26 09:31:29 +02:00
}
break ;
}
case Well : : ProducerCMode : : WRAT :
{
2024-02-20 09:05:09 +01:00
Scalar current_rate = - ws . surface_rates [ pu . phase_pos [ Water ] ] ;
2021-04-26 09:31:29 +02: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 11:49:03 +02:00
ws . surface_rates [ p ] * = controls . water_rate / current_rate ;
2021-04-26 09:31:29 +02:00
}
} else {
2024-02-20 09:05:09 +01:00
const std : : vector < Scalar > fractions = initialWellRateFractions ( simulator , well_state ) ;
const Scalar control_fraction = fractions [ pu . phase_pos [ Water ] ] ;
2021-04-26 09:31:29 +02:00
if ( control_fraction ! = 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2024-02-20 09:05:09 +01:00
ws . surface_rates [ p ] = - fractions [ p ] * controls . water_rate / control_fraction ;
2021-04-26 09:31:29 +02:00
}
}
2021-04-26 09:31:29 +02:00
}
break ;
}
case Well : : ProducerCMode : : GRAT :
{
2024-02-20 09:05:09 +01:00
Scalar current_rate = - ws . surface_rates [ pu . phase_pos [ Gas ] ] ;
2021-04-26 09:31:29 +02: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 11:49:03 +02:00
ws . surface_rates [ p ] * = controls . gas_rate / current_rate ;
2021-04-26 09:31:29 +02:00
}
} else {
2024-02-20 09:05:09 +01:00
const std : : vector < Scalar > fractions = initialWellRateFractions ( simulator , well_state ) ;
const Scalar control_fraction = fractions [ pu . phase_pos [ Gas ] ] ;
2021-04-26 09:31:29 +02:00
if ( control_fraction ! = 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2024-02-20 09:05:09 +01:00
ws . surface_rates [ p ] = - fractions [ p ] * controls . gas_rate / control_fraction ;
2021-04-26 09:31:29 +02:00
}
}
2021-04-26 09:31:29 +02:00
}
2021-04-26 09:31:29 +02:00
2021-04-26 09:31:29 +02:00
break ;
}
case Well : : ProducerCMode : : LRAT :
{
2024-02-20 09:05:09 +01:00
Scalar current_rate = - ws . surface_rates [ pu . phase_pos [ Water ] ]
- ws . surface_rates [ pu . phase_pos [ Oil ] ] ;
2021-04-26 09:31:29 +02: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 11:49:03 +02:00
ws . surface_rates [ p ] * = controls . liquid_rate / current_rate ;
2021-04-26 09:31:29 +02:00
}
} else {
2024-02-20 09:05:09 +01:00
const std : : vector < Scalar > fractions = initialWellRateFractions ( simulator , well_state ) ;
const Scalar control_fraction = fractions [ pu . phase_pos [ Water ] ] + fractions [ pu . phase_pos [ Oil ] ] ;
2021-04-26 09:31:29 +02:00
if ( control_fraction ! = 0.0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 11:49:03 +02:00
ws . surface_rates [ p ] = - fractions [ p ] * controls . liquid_rate / control_fraction ;
2021-04-26 09:31:29 +02:00
}
}
2021-04-26 09:31:29 +02:00
}
break ;
}
case Well : : ProducerCMode : : CRAT :
{
2022-12-20 12:49:59 +01:00
OPM_DEFLOG_THROW ( std : : runtime_error ,
fmt : : format ( " CRAT control not supported, well {} " , this - > name ( ) ) ,
deferred_logger ) ;
2021-04-26 09:31:29 +02:00
}
case Well : : ProducerCMode : : RESV :
{
2024-02-20 09:05:09 +01:00
std : : vector < Scalar > convert_coeff ( this - > number_of_phases_ , 1.0 ) ;
2023-03-24 10:36:06 +01:00
this - > rateConverter_ . calcCoeff ( /*fipreg*/ 0 , this - > pvtRegionIdx_ , ws . surface_rates , convert_coeff ) ;
2024-02-20 09:05:09 +01:00
Scalar total_res_rate = 0.0 ;
2021-04-26 09:31:29 +02:00
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 11:49:03 +02:00
total_res_rate - = ws . surface_rates [ p ] * convert_coeff [ p ] ;
2021-04-26 09:31:29 +02:00
}
if ( controls . prediction_mode ) {
2021-04-26 09:31:29 +02: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 11:49:03 +02:00
ws . surface_rates [ p ] * = controls . resv_rate / total_res_rate ;
2021-04-26 09:31:29 +02:00
}
} else {
2024-02-20 09:05:09 +01:00
const std : : vector < Scalar > fractions = initialWellRateFractions ( simulator , well_state ) ;
2021-04-26 09:31:29 +02:00
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 11:49:03 +02:00
ws . surface_rates [ p ] = - fractions [ p ] * controls . resv_rate / convert_coeff [ p ] ;
2021-04-26 09:31:29 +02:00
}
2021-04-26 09:31:29 +02:00
}
} else {
2024-02-20 09:05:09 +01:00
std : : vector < Scalar > hrates ( this - > number_of_phases_ , 0. ) ;
2021-04-26 09:31:29 +02: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 ;
}
2024-02-20 09:05:09 +01:00
std : : vector < Scalar > hrates_resv ( this - > number_of_phases_ , 0. ) ;
2021-05-11 23:03:33 +02:00
this - > rateConverter_ . calcReservoirVoidageRates ( /*fipreg*/ 0 , this - > pvtRegionIdx_ , hrates , hrates_resv ) ;
2024-02-20 09:05:09 +01:00
Scalar target = std : : accumulate ( hrates_resv . begin ( ) , hrates_resv . end ( ) , 0.0 ) ;
2021-04-26 09:31:29 +02: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 11:49:03 +02:00
ws . surface_rates [ p ] * = target / total_res_rate ;
2021-04-26 09:31:29 +02:00
}
} else {
2024-02-20 09:05:09 +01:00
const std : : vector < Scalar > fractions = initialWellRateFractions ( simulator , well_state ) ;
2021-04-26 09:31:29 +02:00
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 11:49:03 +02:00
ws . surface_rates [ p ] = - fractions [ p ] * target / convert_coeff [ p ] ;
2021-04-26 09:31:29 +02:00
}
2021-04-26 09:31:29 +02:00
}
}
break ;
}
case Well : : ProducerCMode : : BHP :
{
2021-08-03 20:05:14 +02:00
ws . bhp = controls . bhp_limit ;
2024-02-20 09:05:09 +01:00
Scalar total_rate = 0.0 ;
2021-04-26 09:31:29 +02:00
for ( int p = 0 ; p < np ; + + p ) {
2021-08-24 11:49:03 +02:00
total_rate - = ws . surface_rates [ p ] ;
2021-04-26 09:31:29 +02: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 11:49:03 +02:00
ws . surface_rates [ p ] = - ws . well_potentials [ p ] ;
2021-04-26 09:31:29 +02:00
}
}
2021-04-26 09:31:29 +02:00
break ;
}
case Well : : ProducerCMode : : THP :
{
2024-02-06 11:55:07 +01:00
const bool update_success = updateWellStateWithTHPTargetProd ( simulator , well_state , deferred_logger ) ;
2023-01-30 12:41:45 +01: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 ) ;
2024-02-20 09:05:09 +01:00
const Scalar bhp = WellBhpThpCalculator ( * this ) . calculateBhpFromThp (
2023-01-30 12:41:45 +01:00
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
2024-02-20 09:05:09 +01:00
const Scalar total_rate = - std : : accumulate ( rates . begin ( ) , rates . end ( ) , 0.0 ) ;
2023-01-30 12:41:45 +01:00
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 09:31:29 +02:00
}
}
break ;
2021-04-26 09:31:29 +02:00
}
case Well : : ProducerCMode : : GRUP :
{
2021-06-10 15:09:05 +02:00
assert ( well . isAvailableForGroupControl ( ) ) ;
const auto & group = schedule . getGroup ( well . groupName ( ) , this - > currentStep ( ) ) ;
2024-02-20 09:05:09 +01:00
const Scalar efficiencyFactor = well . getEfficiencyFactor ( ) ;
Scalar scale = this - > getGroupProductionTargetRate ( group ,
well_state ,
group_state ,
schedule ,
summaryState ,
efficiencyFactor ,
deferred_logger ) ;
2021-06-10 15:09:05 +02: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 11:49:03 +02:00
ws . surface_rates [ p ] * = scale ;
2021-06-10 15:09:05 +02:00
}
2022-04-25 08:09:54 +02:00
ws . trivial_target = false ;
} else {
ws . trivial_target = true ;
2021-06-10 15:09:05 +02:00
}
2021-04-26 09:31:29 +02:00
break ;
}
case Well : : ProducerCMode : : CMODE_UNDEFINED :
case Well : : ProducerCMode : : NONE :
{
2021-05-11 23:03:33 +02:00
OPM_DEFLOG_THROW ( std : : runtime_error , " Well control must be specified for well " + this - > name ( ) , deferred_logger ) ;
2021-04-26 09:31:29 +02:00
break ;
2024-02-20 09:05:09 +01:00
}
2021-04-26 09:31:29 +02:00
} // end of switch
2023-09-06 11:46:20 +02:00
if ( ws . bhp = = 0. ) {
ws . bhp = controls . bhp_limit ;
}
2021-04-26 09:31:29 +02:00
}
}
2024-02-28 15:38:19 +01:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-03-19 12:50:34 +01:00
wellUnderZeroRateTarget ( const Simulator & simulator ,
const WellState < Scalar > & well_state ,
DeferredLogger & deferred_logger ) const
2024-02-28 15:38:19 +01:00
{
2024-03-19 12:50:34 +01:00
// Check if well is under zero rate control, either directly or from group
2024-06-12 13:17:54 +02:00
const bool isGroupControlled = this - > wellUnderGroupControl ( well_state . well ( this - > index_of_well_ ) ) ;
2024-02-28 15:38:19 +01:00
if ( ! isGroupControlled ) {
2024-03-19 12:50:34 +01:00
// well is not under group control, check "individual" version
const auto & summaryState = simulator . vanguard ( ) . summaryState ( ) ;
return this - > wellUnderZeroRateTargetIndividual ( summaryState , well_state ) ;
2024-02-28 15:38:19 +01:00
} else {
2024-06-12 13:17:54 +02:00
return this - > wellUnderZeroGroupRateTarget ( simulator , well_state , deferred_logger , isGroupControlled ) ;
}
}
template < typename TypeTag >
bool
WellInterface < TypeTag > : : wellUnderZeroGroupRateTarget ( const Simulator & simulator ,
const WellState < Scalar > & well_state ,
DeferredLogger & deferred_logger ,
const std : : optional < bool > group_control ) const
{
// Check if well is under zero rate target from group
const bool isGroupControlled = group_control . value_or ( this - > wellUnderGroupControl ( well_state . well ( this - > index_of_well_ ) ) ) ;
if ( isGroupControlled ) {
2024-03-19 12:50:34 +01:00
const auto & summaryState = simulator . vanguard ( ) . summaryState ( ) ;
2024-02-28 15:38:19 +01:00
const auto & group_state = simulator . problem ( ) . wellModel ( ) . groupState ( ) ;
2024-03-19 12:50:34 +01:00
const auto & schedule = simulator . vanguard ( ) . schedule ( ) ;
2024-06-12 13:17:54 +02:00
return this - > zeroGroupRateTarget ( summaryState , schedule , well_state , group_state , deferred_logger ) ;
2024-02-28 15:38:19 +01:00
}
2024-06-12 13:17:54 +02:00
return false ;
2024-02-28 15:38:19 +01:00
}
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-03-19 12:50:34 +01:00
stoppedOrZeroRateTarget ( const Simulator & simulator ,
const WellState < Scalar > & well_state ,
DeferredLogger & deferred_logger ) const
2024-02-28 15:38:19 +01:00
{
2024-07-12 15:58:05 +02:00
// Check if well is stopped or under zero rate control, either
// directly or from group.
return this - > wellIsStopped ( )
| | this - > wellUnderZeroRateTarget ( simulator , well_state , deferred_logger ) ;
2024-02-28 15:38:19 +01:00
}
2024-03-19 12:50:34 +01:00
2021-04-26 09:31:29 +02:00
template < typename TypeTag >
2024-02-20 09:05:09 +01:00
std : : vector < typename WellInterface < TypeTag > : : Scalar >
2021-04-26 09:31:29 +02:00
WellInterface < TypeTag > : :
2024-02-17 18:13:46 +01:00
initialWellRateFractions ( const Simulator & simulator ,
const WellState < Scalar > & well_state ) const
2021-04-26 09:31:29 +02:00
{
2021-05-11 23:03:33 +02:00
const int np = this - > number_of_phases_ ;
2024-02-20 09:05:09 +01:00
std : : vector < Scalar > scaling_factor ( np ) ;
2021-08-05 10:57:15 +02:00
const auto & ws = well_state . well ( this - > index_of_well_ ) ;
2021-04-26 09:31:29 +02:00
2024-02-20 09:05:09 +01:00
Scalar total_potentials = 0.0 ;
2021-04-26 09:31:29 +02:00
for ( int p = 0 ; p < np ; + + p ) {
2021-08-05 10:57:15 +02:00
total_potentials + = ws . well_potentials [ p ] ;
2021-04-26 09:31:29 +02:00
}
if ( total_potentials > 0 ) {
for ( int p = 0 ; p < np ; + + p ) {
2021-08-05 10:57:15 +02:00
scaling_factor [ p ] = ws . well_potentials [ p ] / total_potentials ;
2021-04-26 09:31:29 +02: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
2024-02-20 09:05:09 +01:00
Scalar total_tw = 0 ;
2021-05-11 23:03:33 +02:00
const int nperf = this - > number_of_perforations_ ;
2021-04-26 09:31:29 +02:00
for ( int perf = 0 ; perf < nperf ; + + perf ) {
2021-05-11 23:03:33 +02:00
total_tw + = this - > well_index_ [ perf ] ;
2021-04-26 09:31:29 +02:00
}
for ( int perf = 0 ; perf < nperf ; + + perf ) {
2021-05-11 23:03:33 +02:00
const int cell_idx = this - > well_cells_ [ perf ] ;
2024-02-06 11:55:07 +01:00
const auto & intQuants = simulator . model ( ) . intensiveQuantities ( cell_idx , /*timeIdx=*/ 0 ) ;
2021-04-26 09:31:29 +02:00
const auto & fs = intQuants . fluidState ( ) ;
2024-02-20 09:05:09 +01:00
const Scalar well_tw_fraction = this - > well_index_ [ perf ] / total_tw ;
Scalar total_mobility = 0.0 ;
2021-04-26 09:31:29 +02:00
for ( int p = 0 ; p < np ; + + p ) {
2024-02-06 12:36:45 +01:00
int modelPhaseIdx = this - > flowPhaseToModelPhaseIdx ( p ) ;
total_mobility + = fs . invB ( modelPhaseIdx ) . value ( ) * intQuants . mobility ( modelPhaseIdx ) . value ( ) ;
2021-04-26 09:31:29 +02:00
}
for ( int p = 0 ; p < np ; + + p ) {
2024-02-06 12:36:45 +01: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 09:31:29 +02:00
}
}
return scaling_factor ;
}
2018-11-17 23:14:51 +01:00
2020-02-10 15:16:09 +01:00
2020-10-15 14:15:05 +02:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
updateWellStateRates ( const Simulator & simulator ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2020-10-15 14:15:05 +02:00
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 11:49:03 +02:00
auto & ws = well_state . well ( this - > index_of_well_ ) ;
2020-10-15 14:15:05 +02:00
int nonzero_rate_index = - 1 ;
2024-02-20 09:05:09 +01:00
const Scalar floating_point_error_epsilon = 1e-14 ;
2021-05-11 23:03:33 +02:00
for ( int p = 0 ; p < this - > number_of_phases_ ; + + p ) {
2022-04-25 08:08:37 +02:00
if ( std : : abs ( ws . surface_rates [ p ] ) > floating_point_error_epsilon ) {
2020-10-15 14:15:05 +02:00
if ( nonzero_rate_index = = - 1 ) {
nonzero_rate_index = p ;
} else {
// More than one nonzero rate.
return ;
}
}
}
2022-03-23 12:29:40 +01:00
// Calculate the rates that follow from the current primary variables.
2024-02-20 09:05:09 +01:00
std : : vector < Scalar > well_q_s = computeCurrentWellRates ( simulator , deferred_logger ) ;
2022-03-23 12:29:40 +01:00
2020-10-15 14:15:05 +02:00
if ( nonzero_rate_index = = - 1 ) {
// No nonzero rates.
2022-03-23 12:29:40 +01:00
// Use the computed rate directly
for ( int p = 0 ; p < this - > number_of_phases_ ; + + p ) {
2024-02-06 12:12:13 +01:00
ws . surface_rates [ p ] = well_q_s [ this - > flowPhaseToModelCompIdx ( p ) ] ;
2022-03-23 12:29:40 +01:00
}
2020-10-15 14:15:05 +02:00
return ;
}
// Set the currently-zero phase flows to be nonzero in proportion to well_q_s.
2024-02-20 09:05:09 +01:00
const Scalar initial_nonzero_rate = ws . surface_rates [ nonzero_rate_index ] ;
2024-02-06 12:12:13 +01:00
const int comp_idx_nz = this - > flowPhaseToModelCompIdx ( nonzero_rate_index ) ;
2023-06-20 23:14:50 +02: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 12:12:13 +01:00
const int comp_idx = this - > flowPhaseToModelCompIdx ( p ) ;
2024-02-20 09:05:09 +01:00
Scalar & rate = ws . surface_rates [ p ] ;
2023-06-20 23:14:50 +02:00
rate = ( initial_nonzero_rate / well_q_s [ comp_idx_nz ] ) * ( well_q_s [ comp_idx ] ) ;
}
2020-10-15 14:15:05 +02:00
}
}
}
2023-05-12 11:57:48 +02:00
2023-08-23 08:35:26 +02:00
template < typename TypeTag >
2024-02-20 09:05:09 +01:00
std : : vector < typename WellInterface < TypeTag > : : Scalar >
2023-08-23 08:35:26 +02:00
WellInterface < TypeTag > : :
2024-02-17 18:13:46 +01:00
wellIndex ( const int perf ,
const IntensiveQuantities & intQuants ,
2024-02-20 09:05:09 +01:00
const Scalar trans_mult ,
const SingleWellState < Scalar > & ws ) const
2023-11-10 16:58:18 +01:00
{
// Add a Forchheimer term to the gas phase CTF if the run uses
// either of the WDFAC or the WDFACCOR keywords.
2024-10-15 14:11:34 +02:00
if ( static_cast < std : : size_t > ( perf ) > = this - > well_cells_ . size ( ) ) {
OPM_THROW ( std : : invalid_argument , " The perforation index exceeds the size of the local containers - possibly wellIndex was called with a global instead of a local perforation index! " ) ;
}
2023-11-10 16:58:18 +01:00
auto wi = std : : vector < Scalar >
( this - > num_components_ , this - > well_index_ [ perf ] * trans_mult ) ;
if constexpr ( ! Indices : : gasEnabled ) {
2023-11-14 12:45:25 +01:00
return wi ;
2023-08-23 08:35:26 +02:00
}
2023-11-10 16:58:18 +01:00
const auto & wdfac = this - > well_ecl_ . getWDFAC ( ) ;
if ( ! wdfac . useDFactor ( ) | | ( this - > well_index_ [ perf ] = = 0.0 ) ) {
2023-11-14 12:45:25 +01:00
return wi ;
2023-08-23 08:35:26 +02:00
}
2024-02-20 09:05:09 +01:00
const Scalar d = this - > computeConnectionDFactor ( perf , intQuants , ws ) ;
2023-11-10 16:58:18 +01:00
if ( d < 1.0e-15 ) {
2023-12-14 11:22:38 +01:00
return wi ;
}
2023-12-20 14:20:45 +01: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 14:06:23 +01:00
const auto & connection = this - > well_ecl_ . getConnections ( ) [ ws . perf_data . ecl_index [ perf ] ] ;
2024-02-20 09:05:09 +01:00
const Scalar Kh = connection . Kh ( ) ;
const Scalar scaling = 3.141592653589 * Kh * connection . wpimult ( ) ;
2023-11-14 12:45:25 +01:00
const unsigned gas_comp_idx = Indices : : canonicalToActiveComponentIndex ( FluidSystem : : gasCompIdx ) ;
2023-12-14 11:22:38 +01:00
2024-02-20 09:05:09 +01:00
const Scalar connection_pressure = ws . perf_data . pressure [ perf ] ;
const Scalar cell_pressure = getValue ( intQuants . fluidState ( ) . pressure ( FluidSystem : : gasPhaseIdx ) ) ;
const Scalar drawdown = cell_pressure - connection_pressure ;
const Scalar invB = getValue ( intQuants . fluidState ( ) . invB ( FluidSystem : : gasPhaseIdx ) ) ;
const Scalar mob_g = getValue ( intQuants . mobility ( FluidSystem : : gasPhaseIdx ) ) * invB ;
const Scalar a = d ;
const Scalar b = 2 * scaling / wi [ gas_comp_idx ] ;
const Scalar c = - 2 * scaling * mob_g * drawdown ;
2023-12-20 14:20:45 +01:00
2024-02-20 09:05:09 +01:00
Scalar consistent_Q = - 1.0e20 ;
2023-12-20 14:20:45 +01:00
// Find and check negative solutions (a --> -a)
2024-02-20 09:05:09 +01:00
const Scalar r2n = b * b + 4 * a * c ;
2023-12-20 14:20:45 +01:00
if ( r2n > = 0 ) {
2024-02-20 09:05:09 +01:00
const Scalar rn = std : : sqrt ( r2n ) ;
const Scalar xn1 = ( b - rn ) * 0.5 / a ;
2023-12-20 14:20:45 +01:00
if ( xn1 < = 0 ) {
2023-12-20 15:06:01 +01:00
consistent_Q = xn1 ;
2023-12-20 14:20:45 +01:00
}
2024-02-20 09:05:09 +01:00
const Scalar xn2 = ( b + rn ) * 0.5 / a ;
2023-12-20 15:06:01 +01:00
if ( xn2 < = 0 & & xn2 > consistent_Q ) {
consistent_Q = xn2 ;
2023-12-20 14:20:45 +01:00
}
2023-12-14 11:22:38 +01:00
}
2023-12-20 14:20:45 +01:00
// Find and check positive solutions
2023-12-20 15:06:01 +01:00
consistent_Q * = - 1 ;
2024-02-20 09:05:09 +01:00
const Scalar r2p = b * b - 4 * a * c ;
2023-12-20 14:20:45 +01:00
if ( r2p > = 0 ) {
2024-02-20 09:05:09 +01:00
const Scalar rp = std : : sqrt ( r2p ) ;
const Scalar xp1 = ( rp - b ) * 0.5 / a ;
2023-12-20 14:20:45 +01:00
if ( xp1 > 0 & & xp1 < consistent_Q ) {
2023-12-20 15:06:01 +01:00
consistent_Q = xp1 ;
2023-12-20 14:20:45 +01:00
}
2024-02-20 09:05:09 +01:00
const Scalar xp2 = - ( rp + b ) * 0.5 / a ;
2023-12-20 14:20:45 +01:00
if ( xp2 > 0 & & xp2 < consistent_Q ) {
consistent_Q = xp2 ;
}
2023-12-15 00:57:49 +01:00
}
2023-12-20 14:20:45 +01:00
wi [ gas_comp_idx ] = 1.0 / ( 1.0 / ( trans_mult * this - > well_index_ [ perf ] ) + ( consistent_Q / 2 * d / scaling ) ) ;
2023-11-10 16:58:18 +01:00
2023-11-14 12:45:25 +01:00
return wi ;
2023-08-23 08:35:26 +02:00
}
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-17 18:13:46 +01:00
updateConnectionDFactor ( const Simulator & simulator ,
2024-02-20 09:05:09 +01:00
SingleWellState < Scalar > & ws ) const
2023-11-10 16:58:18 +01:00
{
if ( ! this - > well_ecl_ . getWDFAC ( ) . useDFactor ( ) ) {
2023-08-23 08:35:26 +02:00
return ;
}
2023-11-10 16:58:18 +01:00
auto & d_factor = ws . perf_data . connection_d_factor ;
2023-08-23 08:35:26 +02: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 16:58:18 +01:00
d_factor [ perf ] = this - > computeConnectionDFactor ( perf , intQuants , ws ) ;
2023-08-23 08:35:26 +02:00
}
}
2023-11-14 14:06:23 +01:00
template < typename TypeTag >
2024-02-20 09:05:09 +01:00
typename WellInterface < TypeTag > : : Scalar
2023-11-14 14:06:23 +01:00
WellInterface < TypeTag > : :
2024-02-17 18:13:46 +01:00
computeConnectionDFactor ( const int perf ,
const IntensiveQuantities & intQuants ,
2024-02-20 09:05:09 +01:00
const SingleWellState < Scalar > & ws ) const
2023-11-10 16:58:18 +01:00
{
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
2024-02-20 09:05:09 +01:00
const Scalar rv_sat = gasPvt . saturatedOilVaporizationFactor
2023-11-10 16:58:18 +01:00
( 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 14:06:23 +01:00
}
2023-08-23 08:35:26 +02:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
2024-02-17 18:13:46 +01:00
updateConnectionTransmissibilityFactor ( const Simulator & simulator ,
2024-02-20 09:05:09 +01:00
SingleWellState < Scalar > & ws ) const
2023-11-10 16:58:18 +01:00
{
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 18:15:23 +01:00
auto & tmult = ws . perf_data . connection_compaction_tmult ;
auto & ctf = ws . perf_data . connection_transmissibility_factor ;
2023-11-10 16:58:18 +01:00
2023-08-23 08:35:26 +02:00
for ( int perf = 0 ; perf < this - > number_of_perforations_ ; + + perf ) {
const int cell_idx = this - > well_cells_ [ perf ] ;
2023-11-10 16:58:18 +01:00
const auto & intQuants = simulator . model ( )
. intensiveQuantities ( cell_idx , /*timeIdx=*/ 0 ) ;
2023-11-10 18:15:23 +01:00
tmult [ perf ] = simulator . problem ( )
2023-11-10 16:58:18 +01:00
. template wellTransMultiplier < double > ( intQuants , cell_idx ) ;
2023-11-10 18:15:23 +01:00
ctf [ perf ] = connCF ( perf ) * tmult [ perf ] ;
2023-08-23 08:35:26 +02:00
}
}
2021-11-30 10:48:02 +00:00
template < typename TypeTag >
typename WellInterface < TypeTag > : : Eval
WellInterface < TypeTag > : : getPerfCellPressure ( const typename WellInterface < TypeTag > : : FluidState & fs ) const
{
2022-10-26 10:59:58 +02:00
if constexpr ( Indices : : oilEnabled ) {
return fs . pressure ( FluidSystem : : oilPhaseIdx ) ;
2023-09-06 13:46:18 +02:00
} else if constexpr ( Indices : : gasEnabled ) {
2022-10-26 10:59:58 +02:00
return fs . pressure ( FluidSystem : : gasPhaseIdx ) ;
2023-09-06 13:46:18 +02:00
} else {
return fs . pressure ( FluidSystem : : waterPhaseIdx ) ;
2021-11-30 10:48:02 +00:00
}
}
2023-03-22 15:10:00 +01:00
2023-05-12 10:02:05 +02:00
template < typename TypeTag >
template < class Value , class Callback >
void
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
getMobility ( const Simulator & simulator ,
2023-05-12 10:02:05 +02: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 > { } ;
}
} ;
2024-10-15 14:11:34 +02:00
if ( static_cast < std : : size_t > ( perf ) > = this - > well_cells_ . size ( ) ) {
OPM_THROW ( std : : invalid_argument , " The perforation index exceeds the size of the local containers - possibly getMobility was called with a global instead of a local perforation index! " ) ;
}
2023-05-12 10:02:05 +02:00
const int cell_idx = this - > well_cells_ [ perf ] ;
assert ( int ( mob . size ( ) ) = = this - > num_components_ ) ;
2024-02-06 11:55:07 +01:00
const auto & intQuants = simulator . model ( ) . intensiveQuantities ( cell_idx , /*timeIdx=*/ 0 ) ;
const auto & materialLawManager = simulator . problem ( ) . materialLawManager ( ) ;
2023-05-12 10:02:05 +02: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 15:56:16 +02:00
2023-07-07 12:22:17 +02:00
if ( this - > isInjector ( ) & & ! this - > inj_fc_multiplier_ . empty ( ) ) {
2023-07-06 15:56:16 +02: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 10:02:05 +02:00
}
2023-05-12 11:57:48 +02:00
template < typename TypeTag >
bool
WellInterface < TypeTag > : :
2024-02-06 11:55:07 +01:00
updateWellStateWithTHPTargetProd ( const Simulator & simulator ,
2024-02-17 18:13:46 +01:00
WellState < Scalar > & well_state ,
2023-05-12 11:57:48 +02:00
DeferredLogger & deferred_logger ) const
{
2024-02-06 11:55:07 +01:00
const auto & summary_state = simulator . vanguard ( ) . summaryState ( ) ;
2023-05-12 11:57:48 +02:00
auto bhp_at_thp_limit = computeBhpAtThpLimitProdWithAlq (
2024-10-23 09:09:09 +02:00
simulator , summary_state , this - > getALQ ( well_state ) , deferred_logger , /*iterate_if_no_solution */ true ) ;
2023-05-12 11:57:48 +02:00
if ( bhp_at_thp_limit ) {
2024-02-20 09:05:09 +01:00
std : : vector < Scalar > rates ( this - > number_of_phases_ , 0.0 ) ;
2023-05-12 11:57:48 +02:00
if ( thp_update_iterations ) {
2024-02-06 11:55:07 +01:00
computeWellRatesWithBhpIterations ( simulator , * bhp_at_thp_limit ,
2023-05-12 11:57:48 +02:00
rates , deferred_logger ) ;
} else {
2024-02-06 11:55:07 +01:00
computeWellRatesWithBhp ( simulator , * bhp_at_thp_limit ,
2023-05-12 11:57:48 +02: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 15:29:53 +02:00
template < typename TypeTag >
void
WellInterface < TypeTag > : :
computeConnLevelProdInd ( const FluidState & fs ,
2024-02-20 09:05:09 +01:00
const std : : function < Scalar ( const Scalar ) > & connPICalc ,
2023-05-12 15:29:53 +02:00
const std : : vector < Scalar > & mobility ,
2024-02-20 09:05:09 +01:00
Scalar * connPI ) const
2023-05-12 15:29:53 +02:00
{
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 12:12:13 +01:00
mobility [ this - > flowPhaseToModelCompIdx ( p ) ]
2024-02-06 12:36:45 +01:00
* fs . invB ( this - > flowPhaseToModelPhaseIdx ( p ) ) . value ( ) ;
2023-05-12 15:29:53 +02: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 ,
2024-02-20 09:05:09 +01:00
const std : : function < Scalar ( const Scalar ) > & connIICalc ,
2023-05-12 15:29:53 +02:00
const std : : vector < Scalar > & mobility ,
2024-02-20 09:05:09 +01:00
Scalar * connII ,
2023-05-12 15:29:53 +02:00
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 12:36:45 +01:00
connII [ phase_pos ] = connIICalc ( mt * fs . invB ( this - > flowPhaseToModelPhaseIdx ( phase_pos ) ) . value ( ) ) ;
2023-05-12 15:29:53 +02:00
}
2020-02-10 15:16:09 +01:00
} // namespace Opm