2021-05-27 01:31:49 -05:00
|
|
|
/*
|
|
|
|
Copyright 2021 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-06-22 02:52:22 -05:00
|
|
|
#include <config.h>
|
2021-05-27 01:31:49 -05:00
|
|
|
#include <opm/simulators/wells/GasLiftGroupInfo.hpp>
|
|
|
|
|
|
|
|
namespace Opm {
|
|
|
|
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
GasLiftGroupInfo(
|
|
|
|
GLiftEclWells &ecl_wells,
|
|
|
|
const Schedule &schedule,
|
|
|
|
const SummaryState &summary_state,
|
|
|
|
const int report_step_idx,
|
|
|
|
const int iteration_idx,
|
|
|
|
const PhaseUsage &phase_usage,
|
|
|
|
DeferredLogger &deferred_logger,
|
2021-06-22 02:52:22 -05:00
|
|
|
WellState &well_state,
|
2022-10-20 05:58:42 -05:00
|
|
|
const GroupState &group_state,
|
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
|
|
|
const Communication &comm,
|
|
|
|
bool glift_debug
|
2021-05-27 01:31:49 -05:00
|
|
|
) :
|
2022-10-20 05:58:42 -05:00
|
|
|
GasLiftCommon(well_state, group_state, deferred_logger, comm, glift_debug)
|
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
|
|
|
, ecl_wells_{ecl_wells}
|
|
|
|
, schedule_{schedule}
|
|
|
|
, summary_state_{summary_state}
|
|
|
|
, report_step_idx_{report_step_idx}
|
|
|
|
, iteration_idx_{iteration_idx}
|
|
|
|
, phase_usage_{phase_usage}
|
|
|
|
, glo_{schedule_.glo(report_step_idx_)}
|
2021-05-27 01:31:49 -05:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************
|
|
|
|
* Public methods in alphabetical order
|
|
|
|
****************************************/
|
|
|
|
|
|
|
|
double
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
alqRate(const std::string& group_name)
|
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
|
|
|
return group_rate.alq();
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
getGroupIdx(const std::string& group_name)
|
|
|
|
{
|
|
|
|
return this->group_idx_.at(group_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
double
|
|
|
|
GasLiftGroupInfo::
|
2021-12-21 16:24:58 -06:00
|
|
|
gasRate(const std::string& group_name) const
|
2021-05-27 01:31:49 -05:00
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
|
|
|
return group_rate.gasRate();
|
|
|
|
}
|
|
|
|
|
2022-11-03 03:02:41 -05:00
|
|
|
double
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
gasPotential(const std::string& group_name) const
|
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
|
|
|
return group_rate.gasPotential();
|
|
|
|
}
|
|
|
|
double
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
waterPotential(const std::string& group_name) const
|
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
|
|
|
return group_rate.waterPotential();
|
|
|
|
}
|
|
|
|
double
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
oilPotential(const std::string& group_name) const
|
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
|
|
|
return group_rate.oilPotential();
|
|
|
|
}
|
|
|
|
|
2021-05-27 01:31:49 -05:00
|
|
|
std::optional<double>
|
|
|
|
GasLiftGroupInfo::
|
2021-12-21 16:24:58 -06:00
|
|
|
gasTarget(const std::string& group_name) const
|
2021-05-27 01:31:49 -05:00
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
|
|
|
return group_rate.gasTarget();
|
|
|
|
}
|
|
|
|
|
2021-12-16 18:52:34 -06:00
|
|
|
double
|
|
|
|
GasLiftGroupInfo::
|
2021-12-21 16:24:58 -06:00
|
|
|
getRate(Rate rate_type, const std::string& group_name) const
|
2021-12-16 18:52:34 -06:00
|
|
|
{
|
|
|
|
switch (rate_type) {
|
|
|
|
case Rate::oil:
|
|
|
|
return oilRate(group_name);
|
|
|
|
case Rate::gas:
|
|
|
|
return gasRate(group_name);
|
|
|
|
case Rate::water:
|
|
|
|
return waterRate(group_name);
|
|
|
|
case Rate::liquid:
|
|
|
|
return oilRate(group_name) + waterRate(group_name);
|
|
|
|
default:
|
|
|
|
// Need this to avoid compiler warning : control reaches end of non-void function
|
|
|
|
throw std::runtime_error("This should not happen");
|
|
|
|
}
|
|
|
|
}
|
2022-11-03 03:02:41 -05:00
|
|
|
double
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
getPotential(Rate rate_type, const std::string& group_name) const
|
|
|
|
{
|
|
|
|
switch (rate_type) {
|
|
|
|
case Rate::oil:
|
|
|
|
return oilPotential(group_name);
|
|
|
|
case Rate::gas:
|
|
|
|
return gasPotential(group_name);
|
|
|
|
case Rate::water:
|
|
|
|
return waterPotential(group_name);
|
|
|
|
case Rate::liquid:
|
|
|
|
return oilPotential(group_name) + waterPotential(group_name);
|
|
|
|
default:
|
|
|
|
// Need this to avoid compiler warning : control reaches end of non-void function
|
|
|
|
throw std::runtime_error("This should not happen");
|
|
|
|
}
|
|
|
|
}
|
2021-12-16 18:52:34 -06:00
|
|
|
|
2021-11-04 07:12:05 -05:00
|
|
|
std::tuple<double, double, double, double>
|
2021-05-27 01:31:49 -05:00
|
|
|
GasLiftGroupInfo::
|
2021-12-21 16:24:58 -06:00
|
|
|
getRates(const int group_idx) const
|
2021-05-27 01:31:49 -05:00
|
|
|
{
|
|
|
|
const auto& group_name = groupIdxToName(group_idx);
|
|
|
|
auto& rates = this->group_rate_map_.at(group_name);
|
2021-11-04 07:12:05 -05:00
|
|
|
return std::make_tuple(rates.oilRate(), rates.gasRate(), rates.waterRate(), rates.alq());
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
|
|
|
|
2021-12-16 18:52:34 -06:00
|
|
|
std::optional<double>
|
|
|
|
GasLiftGroupInfo::
|
2021-12-21 16:24:58 -06:00
|
|
|
getTarget(Rate rate_type, const std::string& group_name) const
|
2021-12-16 18:52:34 -06:00
|
|
|
{
|
|
|
|
switch (rate_type) {
|
|
|
|
case Rate::oil:
|
|
|
|
return oilTarget(group_name);
|
|
|
|
case Rate::gas:
|
|
|
|
return gasTarget(group_name);
|
|
|
|
case Rate::water:
|
|
|
|
return waterTarget(group_name);
|
|
|
|
case Rate::liquid:
|
|
|
|
return liquidTarget(group_name);
|
|
|
|
default:
|
|
|
|
// Need this to avoid compiler warning : control reaches end of non-void function
|
|
|
|
throw std::runtime_error("This should not happen");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-27 01:31:49 -05:00
|
|
|
std::vector<std::pair<std::string,double>>&
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
getWellGroups(const std::string& well_name)
|
|
|
|
{
|
|
|
|
assert(this->well_group_map_.count(well_name) == 1);
|
|
|
|
return this->well_group_map_[well_name];
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string&
|
|
|
|
GasLiftGroupInfo::
|
2021-12-21 16:24:58 -06:00
|
|
|
groupIdxToName(int group_idx) const
|
2021-05-27 01:31:49 -05:00
|
|
|
{
|
|
|
|
const std::string *group_name = nullptr;
|
|
|
|
// TODO: An alternative to the below loop is to set up a reverse map from idx ->
|
|
|
|
// string, then we could in theory do faster lookup here..
|
|
|
|
for (const auto& [key, value] : this->group_idx_) {
|
|
|
|
if (value == group_idx) {
|
|
|
|
// NOTE: it is assumed that the mapping from name->idx is one-to-one
|
|
|
|
// so there can only be one idx with a given group name.
|
|
|
|
group_name = &key;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// the caller is responsible for providing a valid idx, so group_name
|
|
|
|
// cannot be nullptr here..
|
|
|
|
assert(group_name);
|
|
|
|
return *group_name;
|
|
|
|
}
|
|
|
|
|
2022-01-25 15:05:46 -06:00
|
|
|
bool
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
hasAnyTarget(const std::string& group_name) const
|
|
|
|
{
|
|
|
|
return oilTarget(group_name) || gasTarget(group_name)
|
|
|
|
|| waterTarget(group_name) || liquidTarget(group_name);
|
|
|
|
}
|
|
|
|
|
2021-05-27 01:31:49 -05:00
|
|
|
bool
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
hasWell(const std::string& well_name)
|
|
|
|
{
|
|
|
|
return this->well_group_map_.count(well_name) == 1;
|
|
|
|
}
|
|
|
|
|
2021-06-22 02:52:22 -05:00
|
|
|
void
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
initialize()
|
|
|
|
{
|
|
|
|
const auto& group = this->schedule_.getGroup("FIELD", this->report_step_idx_);
|
|
|
|
initializeGroupRatesRecursive_(group);
|
|
|
|
std::vector<std::string> group_names;
|
|
|
|
std::vector<double> group_efficiency;
|
|
|
|
initializeWell2GroupMapRecursive_(
|
|
|
|
group, group_names, group_efficiency, /*current efficiency=*/1.0);
|
|
|
|
}
|
2021-05-27 01:31:49 -05:00
|
|
|
|
|
|
|
std::optional<double>
|
|
|
|
GasLiftGroupInfo::
|
2021-12-21 16:24:58 -06:00
|
|
|
liquidTarget(const std::string &group_name) const
|
2021-05-27 01:31:49 -05:00
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
2021-12-21 16:24:58 -06:00
|
|
|
return group_rate.liquidTarget();
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<double>
|
|
|
|
GasLiftGroupInfo::
|
2021-12-21 16:24:58 -06:00
|
|
|
maxAlq(const std::string& group_name)
|
2021-05-27 01:31:49 -05:00
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
2021-12-21 16:24:58 -06:00
|
|
|
return group_rate.maxAlq();
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
|
|
|
|
2022-02-02 03:28:13 -06:00
|
|
|
std::optional<double>
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
maxTotalGasRate(const std::string& group_name)
|
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
|
|
|
return group_rate.maxTotalGasRate();
|
|
|
|
}
|
|
|
|
|
2021-11-04 07:12:05 -05:00
|
|
|
double
|
|
|
|
GasLiftGroupInfo::
|
2021-12-21 16:24:58 -06:00
|
|
|
oilRate(const std::string &group_name) const
|
2021-11-04 07:12:05 -05:00
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
2021-12-21 16:24:58 -06:00
|
|
|
return group_rate.oilRate();
|
2021-11-04 07:12:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<double>
|
|
|
|
GasLiftGroupInfo::
|
2021-12-21 16:24:58 -06:00
|
|
|
oilTarget(const std::string &group_name) const
|
2021-11-04 07:12:05 -05:00
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
2021-12-21 16:24:58 -06:00
|
|
|
return group_rate.oilTarget();
|
2021-11-04 07:12:05 -05:00
|
|
|
}
|
|
|
|
|
2021-12-16 18:52:34 -06:00
|
|
|
const std::string
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
rateToString(Rate rate) {
|
|
|
|
switch (rate) {
|
|
|
|
case Rate::oil:
|
|
|
|
return "oil";
|
|
|
|
case Rate::gas:
|
|
|
|
return "gas";
|
|
|
|
case Rate::water:
|
|
|
|
return "water";
|
|
|
|
case Rate::liquid:
|
|
|
|
return "liquid";
|
|
|
|
default:
|
|
|
|
throw std::runtime_error("This should not happen");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-21 16:24:58 -06:00
|
|
|
double
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
waterRate(const std::string &group_name) const
|
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
|
|
|
return group_rate.waterRate();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<double>
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
waterTarget(const std::string &group_name) const
|
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
|
|
|
return group_rate.waterTarget();
|
|
|
|
}
|
|
|
|
|
2021-05-27 01:31:49 -05:00
|
|
|
void
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
update(
|
2021-11-04 07:12:05 -05:00
|
|
|
const std::string &group_name, double delta_oil, double delta_gas, double delta_water, double delta_alq)
|
2021-05-27 01:31:49 -05:00
|
|
|
{
|
|
|
|
auto& group_rate = this->group_rate_map_.at(group_name);
|
2021-11-04 07:12:05 -05:00
|
|
|
group_rate.update(delta_oil, delta_gas, delta_water, delta_alq);
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
GasLiftGroupInfo::
|
2021-11-04 07:12:05 -05:00
|
|
|
updateRate(int idx, double oil_rate, double gas_rate, double water_rate, double alq)
|
2021-05-27 01:31:49 -05:00
|
|
|
{
|
|
|
|
const auto& group_name = groupIdxToName(idx);
|
|
|
|
auto& rates = this->group_rate_map_.at(group_name);
|
2021-11-04 07:12:05 -05:00
|
|
|
rates.assign(oil_rate, gas_rate, water_rate, alq);
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************
|
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
|
|
|
* Protected methods in alphabetical order
|
2021-05-27 01:31:49 -05:00
|
|
|
****************************************/
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
checkDoGasLiftOptimization_(const std::string &well_name)
|
|
|
|
{
|
|
|
|
if (this->well_state_.gliftCheckAlqOscillation(well_name)) {
|
|
|
|
displayDebugMessage_(
|
|
|
|
"further optimization skipped due to oscillation in ALQ", well_name);
|
|
|
|
return false;
|
|
|
|
}
|
2022-02-17 02:15:56 -06:00
|
|
|
auto itr = this->ecl_wells_.find(well_name);
|
|
|
|
if (itr == this->ecl_wells_.end()) {
|
|
|
|
// well_name is not present in the well_model's well container
|
2022-03-24 07:47:57 -05:00
|
|
|
//displayDebugMessage_("Could not find well in ecl_wells. Skipping.", well_name);
|
2022-02-17 02:15:56 -06:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const Well *well = (itr->second).first;
|
|
|
|
//assert(well); // Should never be nullptr
|
|
|
|
if (well->isInjector()) {
|
|
|
|
displayDebugMessage_("Injector well. Skipping", well_name);
|
|
|
|
return false;
|
|
|
|
}
|
2021-05-27 01:31:49 -05:00
|
|
|
if (this->optimize_only_thp_wells_) {
|
2022-02-17 02:15:56 -06:00
|
|
|
const int well_index = (itr->second).second;
|
|
|
|
const auto& ws = this->well_state_.well(well_index);
|
|
|
|
const Well::ProducerCMode& control_mode = ws.production_cmode;
|
|
|
|
if (control_mode != Well::ProducerCMode::THP ) {
|
|
|
|
displayDebugMessage_("Not THP control. Skipping.", well_name);
|
2021-05-27 01:31:49 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!checkNewtonIterationIdxOk_(well_name)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!this->glo_.has_well(well_name)) {
|
|
|
|
displayDebugMessage_(
|
2022-02-17 02:15:56 -06:00
|
|
|
"Gas Lift not activated: WLIFTOPT is probably missing. Skipping.", well_name);
|
2021-05-27 01:31:49 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto increment = this->glo_.gaslift_increment();
|
|
|
|
// NOTE: According to the manual: LIFTOPT, item 1, :
|
|
|
|
// "Increment size for lift gas injection rate. Lift gas is
|
|
|
|
// allocated to individual wells in whole numbers of the increment
|
|
|
|
// size. If gas lift optimization is no longer required, it can be
|
|
|
|
// turned off by entering a zero or negative number."
|
|
|
|
if (increment <= 0) {
|
|
|
|
if (this->debug) {
|
|
|
|
const std::string msg = fmt::format(
|
|
|
|
"Gas Lift switched off in LIFTOPT item 1 due to non-positive "
|
|
|
|
"value: {}", increment);
|
|
|
|
displayDebugMessage_(msg, well_name);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
checkNewtonIterationIdxOk_(const std::string &well_name)
|
|
|
|
{
|
|
|
|
if (this->glo_.all_newton()) {
|
|
|
|
const int nupcol = this->schedule_[this->report_step_idx_].nupcol();
|
|
|
|
if (this->debug) {
|
|
|
|
const std::string msg = fmt::format(
|
|
|
|
"LIFTOPT item4 == YES, it = {}, nupcol = {} --> GLIFT optimize = {}",
|
|
|
|
this->iteration_idx_,
|
|
|
|
nupcol,
|
|
|
|
((this->iteration_idx_ <= nupcol) ? "TRUE" : "FALSE"));
|
|
|
|
displayDebugMessage_(msg, well_name);
|
|
|
|
}
|
|
|
|
return this->iteration_idx_ <= nupcol;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (this->debug) {
|
|
|
|
const std::string msg = fmt::format(
|
|
|
|
"LIFTOPT item4 == NO, it = {} --> GLIFT optimize = {}",
|
|
|
|
this->iteration_idx_,
|
|
|
|
((this->iteration_idx_ == 1) ? "TRUE" : "FALSE"));
|
|
|
|
displayDebugMessage_(msg, well_name);
|
|
|
|
}
|
|
|
|
return this->iteration_idx_ == 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-24 06:42:46 -05:00
|
|
|
// This is called by each rank, but the value of "well_name" should be unique
|
|
|
|
// across ranks
|
2022-02-07 04:28:35 -06:00
|
|
|
void
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
debugDisplayWellContribution_(
|
|
|
|
const std::string& gr_name, const std::string& well_name,
|
|
|
|
double eff_factor,
|
|
|
|
double well_oil_rate, double well_gas_rate, double well_water_rate,
|
|
|
|
double well_alq,
|
|
|
|
double oil_rate, double gas_rate, double water_rate,
|
|
|
|
double alq
|
|
|
|
) const
|
|
|
|
{
|
|
|
|
const std::string msg = fmt::format("Group rate for {} : Well {} : "
|
|
|
|
"eff_factor = {}, oil_rate = {}, gas_rate = {}, water_rate = {}, "
|
|
|
|
"alq = {}, New group rates: oil = {}, gas = {}, water = {}, alq = {}",
|
|
|
|
gr_name, well_name, eff_factor, well_oil_rate, well_gas_rate,
|
|
|
|
well_water_rate, well_alq, oil_rate, gas_rate, water_rate, alq);
|
|
|
|
displayDebugMessage_(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
debugDisplayUpdatedGroupRates(
|
|
|
|
const std::string& name,
|
|
|
|
double oil_rate, double gas_rate, double water_rate, double alq) const
|
|
|
|
{
|
|
|
|
const std::string msg = fmt::format("Updated group info for {} : "
|
|
|
|
"oil_rate = {}, gas_rate = {}, water_rate = {}, alq = {}",
|
|
|
|
name, oil_rate, gas_rate, water_rate, alq);
|
2022-03-24 06:42:46 -05:00
|
|
|
displayDebugMessageOnRank0_(msg);
|
2022-02-07 04:28:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
debugEndInitializeGroup(const std::string& name) const
|
|
|
|
{
|
|
|
|
const std::string msg = fmt::format("Finished with group {} ...", name);
|
2022-03-24 06:42:46 -05:00
|
|
|
displayDebugMessageOnRank0_(msg);
|
2022-02-07 04:28:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
debugStartInitializeGroup(const std::string& name) const
|
|
|
|
{
|
|
|
|
const std::string msg = fmt::format("Initializing group {} ...", name);
|
2022-03-24 06:42:46 -05:00
|
|
|
displayDebugMessageOnRank0_(msg);
|
2022-02-07 04:28:35 -06:00
|
|
|
}
|
|
|
|
|
2021-05-27 01:31:49 -05:00
|
|
|
void
|
|
|
|
GasLiftGroupInfo::
|
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
|
|
|
displayDebugMessage_(const std::string &msg) const
|
2021-05-27 01:31:49 -05:00
|
|
|
{
|
|
|
|
if (this->debug) {
|
2022-03-24 06:42:46 -05:00
|
|
|
const std::string message = fmt::format("Init group info : {}", msg);
|
|
|
|
logMessage_(/*prefix=*/"GLIFT", message);
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
displayDebugMessage_(const std::string &msg, const std::string &well_name)
|
|
|
|
{
|
|
|
|
if (this->debug) {
|
2022-03-24 06:42:46 -05:00
|
|
|
const std::string message = fmt::format("Well {} : {}", well_name, msg);
|
|
|
|
displayDebugMessage_(message);
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-11-03 03:02:41 -05:00
|
|
|
std::tuple<double, double, double, double, double, double>
|
2021-05-27 01:31:49 -05:00
|
|
|
GasLiftGroupInfo::
|
2022-11-03 03:02:41 -05:00
|
|
|
getProducerWellRates_(const Well* well, int well_index)
|
2021-05-27 01:31:49 -05:00
|
|
|
{
|
|
|
|
const auto& pu = this->phase_usage_;
|
2021-08-24 04:49:03 -05:00
|
|
|
const auto& ws= this->well_state_.well(well_index);
|
2022-02-01 05:55:04 -06:00
|
|
|
const auto& wrate = ws.well_potentials;
|
2021-06-30 10:21:52 -05:00
|
|
|
|
2022-11-03 03:02:41 -05:00
|
|
|
const auto oil_pot = pu.phase_used[Oil]
|
2022-02-01 05:55:04 -06:00
|
|
|
? wrate[pu.phase_pos[Oil]]
|
2021-06-30 10:21:52 -05:00
|
|
|
: 0.0;
|
|
|
|
|
2022-11-03 03:02:41 -05:00
|
|
|
const auto gas_pot = pu.phase_used[Gas]
|
2022-02-01 05:55:04 -06:00
|
|
|
? wrate[pu.phase_pos[Gas]]
|
2021-06-30 10:21:52 -05:00
|
|
|
: 0.0;
|
|
|
|
|
2022-11-03 03:02:41 -05:00
|
|
|
const auto water_pot = pu.phase_used[Water]
|
2022-02-01 05:55:04 -06:00
|
|
|
? wrate[pu.phase_pos[Water]]
|
2021-11-04 07:12:05 -05:00
|
|
|
: 0.0;
|
|
|
|
|
2022-11-03 03:02:41 -05:00
|
|
|
const auto controls = well->productionControls(this->summary_state_);
|
|
|
|
double oil_rate = oil_pot;
|
|
|
|
if (controls.hasControl(Well::ProducerCMode::ORAT)) {
|
|
|
|
oil_rate = std::min(controls.oil_rate, oil_rate);
|
|
|
|
}
|
|
|
|
double gas_rate = gas_pot;
|
|
|
|
if (controls.hasControl(Well::ProducerCMode::GRAT)) {
|
|
|
|
gas_rate = std::min(controls.gas_rate, gas_rate);
|
|
|
|
}
|
|
|
|
double water_rate = water_pot;
|
|
|
|
if (controls.hasControl(Well::ProducerCMode::WRAT)) {
|
|
|
|
water_rate = std::min(controls.water_rate, water_rate);
|
|
|
|
}
|
|
|
|
if (controls.hasControl(Well::ProducerCMode::LRAT)) {
|
|
|
|
double liquid_rate = oil_rate + water_rate;
|
|
|
|
double liquid_rate_lim = std::min(controls.liquid_rate, liquid_rate);
|
|
|
|
water_rate = water_rate / liquid_rate * liquid_rate_lim;
|
|
|
|
oil_rate = oil_rate / liquid_rate * liquid_rate_lim;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {oil_rate, gas_rate, water_rate, oil_pot, gas_pot, water_pot};
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
|
|
|
|
2022-11-03 03:02:41 -05:00
|
|
|
std::tuple<double, double, double, double, double, double, double>
|
2021-06-22 02:52:22 -05:00
|
|
|
GasLiftGroupInfo::
|
|
|
|
initializeGroupRatesRecursive_(const Group &group)
|
|
|
|
{
|
2022-11-03 03:02:41 -05:00
|
|
|
std::array<double,7> rates{};
|
2022-02-07 04:28:35 -06:00
|
|
|
if (this->debug) debugStartInitializeGroup(group.name());
|
2022-11-03 03:02:41 -05:00
|
|
|
auto& [oil_rate, water_rate, gas_rate, oil_potential, water_potential, gas_potential, alq] = rates;
|
2021-06-22 02:52:22 -05:00
|
|
|
if (group.wellgroup()) {
|
|
|
|
for (const std::string& well_name : group.wells()) {
|
|
|
|
// NOTE: we cannot simply use:
|
|
|
|
//
|
|
|
|
// const auto &well =
|
|
|
|
// this->schedule_.getWell(well_name, this->report_step_idx_);
|
|
|
|
//
|
|
|
|
// since the well may not be active (present in the well container)
|
|
|
|
auto itr = this->ecl_wells_.find(well_name);
|
|
|
|
if (itr != this->ecl_wells_.end()) {
|
|
|
|
const Well *well = (itr->second).first;
|
|
|
|
assert(well); // Should never be nullptr
|
|
|
|
const int index = (itr->second).second;
|
|
|
|
if (well->isProducer()) {
|
2022-11-03 03:02:41 -05:00
|
|
|
auto [sw_oil_rate, sw_gas_rate, sw_water_rate, sw_oil_pot, sw_gas_pot, sw_water_pot] = getProducerWellRates_(well, index);
|
2021-06-22 02:52:22 -05:00
|
|
|
auto sw_alq = this->well_state_.getALQ(well_name);
|
|
|
|
double factor = well->getEfficiencyFactor();
|
|
|
|
oil_rate += (factor * sw_oil_rate);
|
|
|
|
gas_rate += (factor * sw_gas_rate);
|
2021-11-04 07:12:05 -05:00
|
|
|
water_rate += (factor * sw_water_rate);
|
2022-11-03 03:02:41 -05:00
|
|
|
oil_potential += (factor * sw_oil_pot);
|
|
|
|
gas_potential += (factor * sw_gas_pot);
|
|
|
|
water_potential += (factor * sw_water_pot);
|
|
|
|
|
2021-06-22 02:52:22 -05:00
|
|
|
alq += (factor * sw_alq);
|
2022-02-07 04:28:35 -06:00
|
|
|
if (this->debug) {
|
|
|
|
debugDisplayWellContribution_(
|
|
|
|
group.name(), well_name, factor,
|
2022-11-03 03:02:41 -05:00
|
|
|
sw_oil_pot, sw_gas_pot, sw_water_pot, sw_alq,
|
2022-02-07 04:28:35 -06:00
|
|
|
oil_rate, gas_rate, water_rate, alq
|
|
|
|
);
|
|
|
|
}
|
2021-06-22 02:52:22 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-09 05:51:49 -06:00
|
|
|
this->comm_.sum(rates.data(), rates.size());
|
2021-06-22 02:52:22 -05:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (const std::string& group_name : group.groups()) {
|
|
|
|
if (!this->schedule_.back().groups.has(group_name))
|
|
|
|
continue;
|
|
|
|
const Group& sub_group = this->schedule_.getGroup(
|
|
|
|
group_name, this->report_step_idx_);
|
2022-11-03 03:02:41 -05:00
|
|
|
auto [sg_oil_rate, sg_gas_rate, sg_water_rate,
|
|
|
|
sg_oil_pot, sg_gas_pot, sg_water_pot, sg_alq]
|
2021-06-22 02:52:22 -05:00
|
|
|
= initializeGroupRatesRecursive_(sub_group);
|
|
|
|
const auto gefac = sub_group.getGroupEfficiencyFactor();
|
|
|
|
oil_rate += (gefac * sg_oil_rate);
|
|
|
|
gas_rate += (gefac * sg_gas_rate);
|
2021-11-04 07:12:05 -05:00
|
|
|
water_rate += (gefac * sg_water_rate);
|
2022-11-03 03:02:41 -05:00
|
|
|
oil_potential += (gefac * sg_oil_pot);
|
|
|
|
gas_potential += (gefac * sg_gas_pot);
|
|
|
|
water_potential += (gefac * sg_water_pot);
|
2021-06-22 02:52:22 -05:00
|
|
|
alq += (gefac * sg_alq);
|
|
|
|
}
|
|
|
|
}
|
2022-02-07 04:28:35 -06:00
|
|
|
if (this->debug) debugEndInitializeGroup(group.name());
|
2021-11-04 07:12:05 -05:00
|
|
|
std::optional<double> oil_target, gas_target, water_target, liquid_target, max_total_gas, max_alq;
|
2021-06-22 02:52:22 -05:00
|
|
|
const auto controls = group.productionControls(this->summary_state_);
|
2021-11-04 07:12:05 -05:00
|
|
|
if (group.has_control(Group::ProductionCMode::LRAT)) {
|
|
|
|
liquid_target = controls.liquid_target;
|
|
|
|
}
|
2021-06-22 02:52:22 -05:00
|
|
|
if (group.has_control(Group::ProductionCMode::ORAT)) {
|
|
|
|
oil_target = controls.oil_target;
|
|
|
|
}
|
|
|
|
if (group.has_control(Group::ProductionCMode::GRAT)) {
|
|
|
|
gas_target = controls.gas_target;
|
|
|
|
}
|
2021-11-04 07:12:05 -05:00
|
|
|
if (group.has_control(Group::ProductionCMode::WRAT)) {
|
|
|
|
water_target = controls.water_target;
|
|
|
|
}
|
2021-06-22 02:52:22 -05:00
|
|
|
if (this->glo_.has_group(group.name())) {
|
|
|
|
const auto &gl_group = this->glo_.group(group.name());
|
|
|
|
max_alq = gl_group.max_lift_gas();
|
|
|
|
max_total_gas = gl_group.max_total_gas();
|
|
|
|
}
|
2021-11-04 07:12:05 -05:00
|
|
|
if (oil_target || liquid_target || water_target || gas_target || max_total_gas || max_alq) {
|
2021-06-22 02:52:22 -05:00
|
|
|
updateGroupIdxMap_(group.name());
|
2022-11-03 03:02:41 -05:00
|
|
|
if(oil_target)
|
|
|
|
oil_rate = std::min(oil_rate, *oil_target);
|
|
|
|
if(gas_target)
|
|
|
|
gas_rate = std::min(gas_rate, *gas_target);
|
|
|
|
if(water_target)
|
|
|
|
water_rate = std::min(water_rate, *water_target);
|
|
|
|
if(liquid_target) {
|
|
|
|
double liquid_rate = oil_rate + water_rate;
|
|
|
|
double liquid_rate_limited = std::min(liquid_rate, *liquid_target);
|
|
|
|
oil_rate = oil_rate / liquid_rate * liquid_rate_limited;
|
|
|
|
water_rate = water_rate / liquid_rate * liquid_rate_limited;
|
|
|
|
}
|
|
|
|
|
2021-06-22 02:52:22 -05:00
|
|
|
this->group_rate_map_.try_emplace(group.name(),
|
2022-11-03 03:02:41 -05:00
|
|
|
oil_rate, gas_rate, water_rate, alq,
|
|
|
|
oil_potential, gas_potential, water_potential,
|
|
|
|
oil_target, gas_target, water_target, liquid_target, max_total_gas, max_alq);
|
2022-02-07 04:28:35 -06:00
|
|
|
if (this->debug) {
|
|
|
|
debugDisplayUpdatedGroupRates(
|
|
|
|
group.name(), oil_rate, gas_rate, water_rate, alq);
|
|
|
|
}
|
2021-06-22 02:52:22 -05:00
|
|
|
}
|
2022-11-03 03:02:41 -05:00
|
|
|
return std::make_tuple(oil_rate, gas_rate, water_rate, oil_potential, gas_potential, water_potential, alq);
|
2021-06-22 02:52:22 -05:00
|
|
|
}
|
|
|
|
|
2021-05-27 01:31:49 -05:00
|
|
|
void
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
initializeWell2GroupMapRecursive_(
|
|
|
|
const Group &group,
|
|
|
|
std::vector<std::string> &group_names,
|
|
|
|
std::vector<double> &group_efficiency,
|
|
|
|
double cur_efficiency)
|
|
|
|
{
|
|
|
|
double gfac = group.getGroupEfficiencyFactor();
|
|
|
|
cur_efficiency = gfac * cur_efficiency;
|
|
|
|
for (auto &item : group_efficiency) {
|
|
|
|
item *= gfac;
|
|
|
|
}
|
|
|
|
if (this->group_rate_map_.count(group.name()) == 1) {
|
|
|
|
// extract the subset of groups that has limits or targets that can affect
|
|
|
|
// gas lift optimization.
|
|
|
|
group_names.push_back(group.name());
|
|
|
|
group_efficiency.push_back(gfac);
|
|
|
|
}
|
|
|
|
if (group.wellgroup()) {
|
|
|
|
for (const std::string& well_name : group.wells()) {
|
|
|
|
// TODO: can the same well be memember of two different groups
|
|
|
|
// (on the same recursion level) ?
|
|
|
|
assert(this->well_group_map_.count(well_name) == 0);
|
|
|
|
if (checkDoGasLiftOptimization_(well_name)) {
|
|
|
|
const auto &well = this->schedule_.getWell(
|
|
|
|
well_name, this->report_step_idx_);
|
|
|
|
double wfac = well.getEfficiencyFactor();
|
|
|
|
auto [itr, success] = this->well_group_map_.insert(
|
|
|
|
{well_name, /*empty vector*/ {}});
|
|
|
|
assert(success);
|
|
|
|
auto &vec = itr->second;
|
|
|
|
assert(group_names.size() == group_efficiency.size());
|
|
|
|
auto iter2 = group_efficiency.begin();
|
|
|
|
for (auto iter1 = group_names.begin();
|
|
|
|
iter1 != group_names.end(); ++iter1)
|
|
|
|
{
|
|
|
|
double efficiency = (*iter2) * wfac;
|
|
|
|
vec.emplace_back(/*group_name=*/*iter1, efficiency);
|
|
|
|
++iter2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (const std::string& group_name : group.groups()) {
|
|
|
|
if (!this->schedule_.back().groups.has(group_name))
|
|
|
|
continue;
|
|
|
|
const Group& sub_group = this->schedule_.getGroup(
|
|
|
|
group_name, this->report_step_idx_);
|
|
|
|
initializeWell2GroupMapRecursive_(
|
|
|
|
sub_group, group_names, group_efficiency, cur_efficiency);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (this->group_rate_map_.count(group.name()) == 1) {
|
|
|
|
group_names.pop_back();
|
|
|
|
group_efficiency.pop_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: It would be more efficient if the group idx map was build once
|
|
|
|
// per time step (or better: once per report step) and saved e.g. in
|
|
|
|
// the well state object, instead of rebuilding here for each of
|
|
|
|
// NUPCOL well iteration for each time step.
|
|
|
|
void
|
|
|
|
GasLiftGroupInfo::
|
|
|
|
updateGroupIdxMap_(const std::string &group_name)
|
|
|
|
{
|
|
|
|
if (this->group_idx_.count(group_name) == 0) {
|
|
|
|
//auto [itr, success] =
|
|
|
|
this->group_idx_.try_emplace(group_name, this->next_group_idx_);
|
|
|
|
this->next_group_idx_++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Opm
|