2021-03-03 06:59:26 -06:00
|
|
|
/*
|
|
|
|
Copyright 2020 Equinor ASA.
|
|
|
|
|
|
|
|
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/>.
|
|
|
|
*/
|
|
|
|
|
2021-05-20 07:20:27 -05:00
|
|
|
namespace Opm {
|
2021-03-03 06:59:26 -06:00
|
|
|
|
|
|
|
template<typename TypeTag>
|
|
|
|
GasLiftSingleWell<TypeTag>::
|
2022-02-15 03:21:09 -06:00
|
|
|
GasLiftSingleWell(const WellInterface<TypeTag> &well,
|
2021-05-20 07:20:27 -05:00
|
|
|
const Simulator &ebos_simulator,
|
|
|
|
const SummaryState &summary_state,
|
|
|
|
DeferredLogger &deferred_logger,
|
2021-05-27 01:31:49 -05:00
|
|
|
WellState &well_state,
|
2021-06-23 08:46:33 -05:00
|
|
|
const GroupState &group_state,
|
2021-05-27 01:31:49 -05:00
|
|
|
GasLiftGroupInfo &group_info,
|
Improve debugging tools in gaslift code.
Introduces a gaslift debugging variable in ALQState in WellState. This
variable will persist between timesteps in contrast to when debugging
variables are defined in GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2.
Currently only an integer variable debug_counter is added to ALQState,
which can be used as follows: First debugging is switched on globally
for BlackOilWellModel, GasLiftSingleWell, GasLiftGroupState, and
GasLiftStage2 by setting glift_debug to a true value in BlackOilWellModelGeneric.
Then, the following debugging code can be added to e.g. one of
GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2 :
auto count = debugUpdateGlobalCounter_();
if (count == some_integer) {
displayDebugMessage_("stop here");
}
Here, the integer "some_integer" is determined typically by looking at
the debugging output of a previous run. This can be done since the
call to debugUpdateGlobalCounter_() will print out the current value
of the counter and then increment the counter by one. And it will be
easy to recognize these values in the debug ouput. If you find a place
in the output that looks suspect, just take a note of the counter
value in the output around that point and insert the value for
"some_integer", then after recompiling the code with the desired value
for "some_integer", it is now easy to set a breakpoint in GDB at the
line
displayDebugMessage_("stop here").
shown in the above snippet. This should improve the ability to quickly
to set a breakpoint in GDB around at a given time and point in the simulation.
2022-01-23 13:37:26 -06:00
|
|
|
GLiftSyncGroups &sync_groups,
|
2022-03-24 06:42:46 -05:00
|
|
|
const Parallel::Communication& comm,
|
Improve debugging tools in gaslift code.
Introduces a gaslift debugging variable in ALQState in WellState. This
variable will persist between timesteps in contrast to when debugging
variables are defined in GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2.
Currently only an integer variable debug_counter is added to ALQState,
which can be used as follows: First debugging is switched on globally
for BlackOilWellModel, GasLiftSingleWell, GasLiftGroupState, and
GasLiftStage2 by setting glift_debug to a true value in BlackOilWellModelGeneric.
Then, the following debugging code can be added to e.g. one of
GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2 :
auto count = debugUpdateGlobalCounter_();
if (count == some_integer) {
displayDebugMessage_("stop here");
}
Here, the integer "some_integer" is determined typically by looking at
the debugging output of a previous run. This can be done since the
call to debugUpdateGlobalCounter_() will print out the current value
of the counter and then increment the counter by one. And it will be
easy to recognize these values in the debug ouput. If you find a place
in the output that looks suspect, just take a note of the counter
value in the output around that point and insert the value for
"some_integer", then after recompiling the code with the desired value
for "some_integer", it is now easy to set a breakpoint in GDB at the
line
displayDebugMessage_("stop here").
shown in the above snippet. This should improve the ability to quickly
to set a breakpoint in GDB around at a given time and point in the simulation.
2022-01-23 13:37:26 -06:00
|
|
|
bool glift_debug
|
2021-05-27 01:31:49 -05:00
|
|
|
)
|
|
|
|
// The parent class GasLiftSingleWellGeneric contains all stuff
|
|
|
|
// that is not dependent on TypeTag
|
|
|
|
: GasLiftSingleWellGeneric(
|
|
|
|
deferred_logger,
|
|
|
|
well_state,
|
2021-06-23 08:46:33 -05:00
|
|
|
group_state,
|
2022-02-15 03:21:09 -06:00
|
|
|
well.wellEcl(),
|
2021-05-27 01:31:49 -05:00
|
|
|
summary_state,
|
|
|
|
group_info,
|
2022-02-15 03:21:09 -06:00
|
|
|
well.phaseUsage(),
|
2021-05-27 01:31:49 -05:00
|
|
|
ebos_simulator.vanguard().schedule(),
|
|
|
|
ebos_simulator.episodeIndex(),
|
Improve debugging tools in gaslift code.
Introduces a gaslift debugging variable in ALQState in WellState. This
variable will persist between timesteps in contrast to when debugging
variables are defined in GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2.
Currently only an integer variable debug_counter is added to ALQState,
which can be used as follows: First debugging is switched on globally
for BlackOilWellModel, GasLiftSingleWell, GasLiftGroupState, and
GasLiftStage2 by setting glift_debug to a true value in BlackOilWellModelGeneric.
Then, the following debugging code can be added to e.g. one of
GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2 :
auto count = debugUpdateGlobalCounter_();
if (count == some_integer) {
displayDebugMessage_("stop here");
}
Here, the integer "some_integer" is determined typically by looking at
the debugging output of a previous run. This can be done since the
call to debugUpdateGlobalCounter_() will print out the current value
of the counter and then increment the counter by one. And it will be
easy to recognize these values in the debug ouput. If you find a place
in the output that looks suspect, just take a note of the counter
value in the output around that point and insert the value for
"some_integer", then after recompiling the code with the desired value
for "some_integer", it is now easy to set a breakpoint in GDB at the
line
displayDebugMessage_("stop here").
shown in the above snippet. This should improve the ability to quickly
to set a breakpoint in GDB around at a given time and point in the simulation.
2022-01-23 13:37:26 -06:00
|
|
|
sync_groups,
|
2022-03-24 06:42:46 -05:00
|
|
|
comm,
|
Improve debugging tools in gaslift code.
Introduces a gaslift debugging variable in ALQState in WellState. This
variable will persist between timesteps in contrast to when debugging
variables are defined in GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2.
Currently only an integer variable debug_counter is added to ALQState,
which can be used as follows: First debugging is switched on globally
for BlackOilWellModel, GasLiftSingleWell, GasLiftGroupState, and
GasLiftStage2 by setting glift_debug to a true value in BlackOilWellModelGeneric.
Then, the following debugging code can be added to e.g. one of
GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2 :
auto count = debugUpdateGlobalCounter_();
if (count == some_integer) {
displayDebugMessage_("stop here");
}
Here, the integer "some_integer" is determined typically by looking at
the debugging output of a previous run. This can be done since the
call to debugUpdateGlobalCounter_() will print out the current value
of the counter and then increment the counter by one. And it will be
easy to recognize these values in the debug ouput. If you find a place
in the output that looks suspect, just take a note of the counter
value in the output around that point and insert the value for
"some_integer", then after recompiling the code with the desired value
for "some_integer", it is now easy to set a breakpoint in GDB at the
line
displayDebugMessage_("stop here").
shown in the above snippet. This should improve the ability to quickly
to set a breakpoint in GDB around at a given time and point in the simulation.
2022-01-23 13:37:26 -06:00
|
|
|
glift_debug
|
2021-05-27 01:31:49 -05:00
|
|
|
)
|
2021-05-20 07:20:27 -05:00
|
|
|
, ebos_simulator_{ebos_simulator}
|
2022-02-15 03:21:09 -06:00
|
|
|
, well_{well}
|
2021-03-03 06:59:26 -06:00
|
|
|
{
|
2021-05-20 07:20:27 -05:00
|
|
|
const auto& gl_well = *gl_well_;
|
2021-03-03 06:59:26 -06:00
|
|
|
if(useFixedAlq_(gl_well)) {
|
|
|
|
updateWellStateAlqFixedValue_(gl_well);
|
|
|
|
this->optimize_ = false; // lift gas supply is fixed
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
setAlqMaxRate_(gl_well);
|
|
|
|
this->optimize_ = true;
|
|
|
|
}
|
2021-05-20 07:20:27 -05:00
|
|
|
|
2022-02-17 02:15:56 -06:00
|
|
|
setupPhaseVariables_();
|
2021-03-03 06:59:26 -06:00
|
|
|
// get the alq value used for this well for the previous iteration (a
|
|
|
|
// nonlinear iteration in assemble() in BlackoilWellModel).
|
|
|
|
// If gas lift optimization has not been applied to this well yet, the
|
|
|
|
// default value is used.
|
|
|
|
this->orig_alq_ = this->well_state_.getALQ(this->well_name_);
|
|
|
|
if(this->optimize_) {
|
|
|
|
setAlqMinRate_(gl_well);
|
|
|
|
// NOTE: According to item 4 in WLIFTOPT, this value does not
|
|
|
|
// have to be positive.
|
|
|
|
// TODO: Does it make sense to have a negative value?
|
|
|
|
this->alpha_w_ = gl_well.weight_factor();
|
|
|
|
if (this->alpha_w_ <= 0 ) {
|
|
|
|
displayWarning_("Nonpositive value for alpha_w ignored");
|
|
|
|
this->alpha_w_ = 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: According to item 6 in WLIFTOPT:
|
|
|
|
// "If this value is greater than zero, the incremental gas rate will influence
|
|
|
|
// the calculation of the incremental gradient and may be used
|
|
|
|
// to discourage the allocation of lift gas to wells which produce more gas."
|
|
|
|
// TODO: Does this mean that we should ignore this value if it
|
|
|
|
// is negative?
|
|
|
|
this->alpha_g_ = gl_well.inc_weight_factor();
|
|
|
|
|
|
|
|
// TODO: adhoc value.. Should we keep max_iterations_ as a safety measure
|
|
|
|
// or does it not make sense to have it?
|
|
|
|
this->max_iterations_ = 1000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************
|
|
|
|
* Private methods in alphabetical order
|
|
|
|
****************************************/
|
|
|
|
|
|
|
|
template<typename TypeTag>
|
2022-01-25 15:05:46 -06:00
|
|
|
GasLiftSingleWellGeneric::BasicRates
|
2021-03-03 06:59:26 -06:00
|
|
|
GasLiftSingleWell<TypeTag>::
|
2022-01-25 15:05:46 -06:00
|
|
|
computeWellRates_( double bhp, bool bhp_is_limited, bool debug_output ) const
|
2021-03-03 06:59:26 -06:00
|
|
|
{
|
2022-02-17 02:15:56 -06:00
|
|
|
std::vector<double> potentials(NUM_PHASES, 0.0);
|
2022-02-15 03:21:09 -06:00
|
|
|
this->well_.computeWellRatesWithBhp(
|
2021-05-20 07:20:27 -05:00
|
|
|
this->ebos_simulator_, bhp, potentials, this->deferred_logger_);
|
|
|
|
if (debug_output) {
|
|
|
|
const std::string msg = fmt::format("computed well potentials given bhp {}, "
|
|
|
|
"oil: {}, gas: {}, water: {}", bhp,
|
|
|
|
-potentials[this->oil_pos_], -potentials[this->gas_pos_],
|
|
|
|
-potentials[this->water_pos_]);
|
2021-03-03 06:59:26 -06:00
|
|
|
displayDebugMessage_(msg);
|
|
|
|
}
|
2021-11-18 05:57:16 -06:00
|
|
|
|
2021-11-23 06:56:03 -06:00
|
|
|
for (auto& potential : potentials) {
|
|
|
|
potential = std::min(0.0, potential);
|
2021-11-18 05:57:16 -06:00
|
|
|
}
|
2022-01-25 15:05:46 -06:00
|
|
|
return {-potentials[this->oil_pos_],
|
|
|
|
-potentials[this->gas_pos_],
|
|
|
|
-potentials[this->water_pos_],
|
|
|
|
bhp_is_limited
|
|
|
|
};
|
2021-03-03 06:59:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename TypeTag>
|
|
|
|
std::optional<double>
|
|
|
|
GasLiftSingleWell<TypeTag>::
|
|
|
|
computeBhpAtThpLimit_(double alq) const
|
|
|
|
{
|
2022-02-15 03:21:09 -06:00
|
|
|
auto bhp_at_thp_limit = this->well_.computeBhpAtThpLimitProdWithAlq(
|
2021-03-03 06:59:26 -06:00
|
|
|
this->ebos_simulator_,
|
|
|
|
this->summary_state_,
|
2022-10-19 05:23:02 -05:00
|
|
|
alq,
|
|
|
|
this->deferred_logger_);
|
2021-03-03 06:59:26 -06:00
|
|
|
if (bhp_at_thp_limit) {
|
|
|
|
if (*bhp_at_thp_limit < this->controls_.bhp_limit) {
|
|
|
|
const std::string msg = fmt::format(
|
|
|
|
"Computed bhp ({}) from thp limit is below bhp limit ({}), (ALQ = {})."
|
|
|
|
" Using bhp limit instead",
|
|
|
|
*bhp_at_thp_limit, this->controls_.bhp_limit, alq);
|
|
|
|
displayDebugMessage_(msg);
|
|
|
|
bhp_at_thp_limit = this->controls_.bhp_limit;
|
|
|
|
}
|
|
|
|
//bhp_at_thp_limit = std::max(*bhp_at_thp_limit, this->controls_.bhp_limit);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const std::string msg = fmt::format(
|
|
|
|
"Failed in getting converged bhp potential from thp limit (ALQ = {})", alq);
|
|
|
|
displayDebugMessage_(msg);
|
|
|
|
}
|
|
|
|
return bhp_at_thp_limit;
|
|
|
|
}
|
|
|
|
|
2022-02-17 02:15:56 -06:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
GasLiftSingleWell<TypeTag>::
|
|
|
|
setupPhaseVariables_()
|
|
|
|
{
|
|
|
|
const auto& pu = this->phase_usage_;
|
|
|
|
bool num_phases_ok = (pu.num_phases == 3);
|
2022-03-07 08:58:11 -06:00
|
|
|
if (pu.num_phases == 2) {
|
2022-02-17 02:15:56 -06:00
|
|
|
// NOTE: We support two-phase oil-water flow, by setting the gas flow rate
|
|
|
|
// to zero. This is done by initializing the potential vector to zero:
|
|
|
|
//
|
|
|
|
// std::vector<double> potentials(NUM_PHASES, 0.0);
|
|
|
|
//
|
|
|
|
// see e.g. runOptimizeLoop_() in GasLiftSingleWellGeneric.cpp
|
|
|
|
// In addition the VFP calculations, e.g. to calculate BHP from THP
|
|
|
|
// has been adapted to the two-phase oil-water case, see the comment
|
|
|
|
// in WellInterfaceGeneric.cpp for the method adaptRatesForVFP() for
|
|
|
|
// more information.
|
2022-03-07 08:58:11 -06:00
|
|
|
if ( pu.phase_used[BlackoilPhases::Aqua] == 1
|
|
|
|
&& pu.phase_used[BlackoilPhases::Liquid] == 1
|
|
|
|
&& pu.phase_used[BlackoilPhases::Vapour] == 0)
|
|
|
|
{
|
|
|
|
num_phases_ok = true; // two-phase oil-water is also supported
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw std::logic_error("Two-phase gas lift optimization only supported"
|
|
|
|
" for oil and water");
|
|
|
|
}
|
2022-02-17 02:15:56 -06:00
|
|
|
}
|
|
|
|
assert(num_phases_ok);
|
|
|
|
this->oil_pos_ = pu.phase_pos[Oil];
|
|
|
|
this->gas_pos_ = pu.phase_pos[Gas];
|
|
|
|
this->water_pos_ = pu.phase_pos[Water];
|
|
|
|
}
|
|
|
|
|
2021-03-03 06:59:26 -06:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
GasLiftSingleWell<TypeTag>::
|
|
|
|
setAlqMaxRate_(const GasLiftOpt::Well &well)
|
|
|
|
{
|
|
|
|
auto& max_alq_optional = well.max_rate();
|
|
|
|
if (max_alq_optional) {
|
|
|
|
// NOTE: To prevent extrapolation of the VFP tables, any value
|
|
|
|
// entered here must not exceed the largest ALQ value in the well's VFP table.
|
|
|
|
this->max_alq_ = *max_alq_optional;
|
|
|
|
}
|
|
|
|
else { // i.e. WLIFTOPT, item 3 has been defaulted
|
|
|
|
// According to the manual for WLIFTOPT, item 3:
|
|
|
|
// The default value should be set to the largest ALQ
|
|
|
|
// value in the well's VFP table
|
2022-02-15 03:21:09 -06:00
|
|
|
const auto& table = well_.vfpProperties()->getProd()->getTable(
|
2021-03-03 06:59:26 -06:00
|
|
|
this->controls_.vfp_table_number);
|
|
|
|
const auto& alq_values = table.getALQAxis();
|
|
|
|
// Assume the alq_values are sorted in ascending order, so
|
|
|
|
// the last item should be the largest value:
|
|
|
|
this->max_alq_ = alq_values.back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-02 05:11:39 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
bool
|
|
|
|
GasLiftSingleWell<TypeTag>::
|
|
|
|
checkThpControl_() const
|
|
|
|
{
|
|
|
|
const int well_index = this->well_state_.index(this->well_name_).value();
|
|
|
|
const Well::ProducerCMode& control_mode =
|
|
|
|
this->well_state_.well(well_index).production_cmode;
|
|
|
|
bool thp_control = control_mode == Well::ProducerCMode::THP;
|
|
|
|
const WellInterfaceGeneric &well = getWell();
|
|
|
|
thp_control = thp_control || well.thpLimitViolatedButNotSwitched();
|
|
|
|
if (this->debug) {
|
|
|
|
if (!thp_control) {
|
|
|
|
displayDebugMessage_("Well is not under THP control, skipping iteration..");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return thp_control;
|
|
|
|
}
|
|
|
|
|
2021-03-03 06:59:26 -06:00
|
|
|
}
|