mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
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.
1690 lines
62 KiB
C++
1690 lines
62 KiB
C++
/*
|
|
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/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <opm/simulators/wells/GasLiftSingleWellGeneric.hpp>
|
|
|
|
#include <opm/input/eclipse/Schedule/GasLiftOpt.hpp>
|
|
#include <opm/input/eclipse/Schedule/Schedule.hpp>
|
|
|
|
#include <opm/simulators/utils/DeferredLogger.hpp>
|
|
#include <opm/simulators/wells/GasLiftWellState.hpp>
|
|
#include <opm/simulators/wells/WellState.hpp>
|
|
#include <opm/simulators/wells/GroupState.hpp>
|
|
|
|
|
|
#include <fmt/format.h>
|
|
|
|
#include <cassert>
|
|
#include <sstream>
|
|
|
|
namespace Opm
|
|
{
|
|
|
|
GasLiftSingleWellGeneric::GasLiftSingleWellGeneric(
|
|
DeferredLogger& deferred_logger,
|
|
WellState& well_state,
|
|
const GroupState& group_state,
|
|
const Well& ecl_well,
|
|
const SummaryState& summary_state,
|
|
GasLiftGroupInfo &group_info,
|
|
const Schedule& schedule,
|
|
const int report_step_idx,
|
|
GLiftSyncGroups &sync_groups,
|
|
bool glift_debug
|
|
) :
|
|
GasLiftCommon(well_state, deferred_logger, glift_debug)
|
|
, group_state_{group_state}
|
|
, ecl_well_{ecl_well}
|
|
, summary_state_{summary_state}
|
|
, group_info_{group_info}
|
|
, sync_groups_{sync_groups}
|
|
, controls_{ecl_well_.productionControls(summary_state_)}
|
|
, num_phases_{well_state_.numPhases()}
|
|
, debug_limit_increase_decrease_{false}
|
|
{
|
|
this->well_name_ = ecl_well_.name();
|
|
const GasLiftOpt& glo = schedule.glo(report_step_idx);
|
|
// NOTE: According to 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."
|
|
// NOTE: This condition was checked in doGasLiftOptimize() in StandardWell
|
|
// so it can be assumed that increment_ > 0
|
|
this->increment_ = glo.gaslift_increment();
|
|
assert( this->increment_ > 0);
|
|
// NOTE: The manual (see LIFTOPT, item 2) does not mention
|
|
// any default value or restrictions on the economic gradient.
|
|
// TODO: The value of the gradient would most likely be a positive
|
|
// number. Should we warn or fail on a negative value?
|
|
// A negative value for the economic gradient would mean that
|
|
// the oil production is decreasing with increased liftgas
|
|
// injection (which seems strange)
|
|
this->eco_grad_ = glo.min_eco_gradient();
|
|
gl_well_ = &glo.well(this->well_name_);
|
|
}
|
|
|
|
/****************************************
|
|
* Public methods in alphabetical order
|
|
****************************************/
|
|
// NOTE: Used from GasLiftStage2
|
|
std::optional<GasLiftSingleWellGeneric::GradInfo>
|
|
GasLiftSingleWellGeneric::
|
|
calcIncOrDecGradient(double oil_rate, double gas_rate, double alq, bool increase) const
|
|
{
|
|
auto [new_alq_opt, alq_is_limited] = addOrSubtractAlqIncrement_(alq, increase);
|
|
// TODO: What to do if ALQ is limited and new_alq != alq?
|
|
if (!new_alq_opt)
|
|
return std::nullopt;
|
|
double new_alq = *new_alq_opt;
|
|
if (auto bhp = computeBhpAtThpLimit_(new_alq)) {
|
|
auto new_bhp = getBhpWithLimit_(*bhp);
|
|
// TODO: What to do if BHP is limited?
|
|
std::vector<double> potentials(this->num_phases_, 0.0);
|
|
computeWellRates_(new_bhp.first, potentials);
|
|
auto [new_oil_rate, oil_is_limited] = getOilRateWithLimit_(potentials);
|
|
auto [new_gas_rate, gas_is_limited] = getGasRateWithLimit_(potentials);
|
|
if (!increase && new_oil_rate < 0 ) {
|
|
return std::nullopt;
|
|
}
|
|
auto grad = calcEcoGradient_(
|
|
oil_rate, new_oil_rate, gas_rate, new_gas_rate, increase);
|
|
return GradInfo(grad, new_oil_rate, oil_is_limited,
|
|
new_gas_rate, gas_is_limited, new_alq, alq_is_limited);
|
|
}
|
|
else {
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<GasLiftWellState>
|
|
GasLiftSingleWellGeneric::
|
|
runOptimize(const int iteration_idx)
|
|
{
|
|
std::unique_ptr<GasLiftWellState> state;
|
|
if (this->optimize_) {
|
|
if (this->debug_limit_increase_decrease_) {
|
|
state = runOptimize1_();
|
|
}
|
|
else {
|
|
state = runOptimize2_();
|
|
}
|
|
if (state) {
|
|
// NOTE: that state->increase() returns a std::optional<bool>, if
|
|
// this is std::nullopt it means that we was not able to change ALQ
|
|
// (either increase or decrease)
|
|
if (state->increase()) { // ALQ changed..
|
|
double alq = state->alq();
|
|
if (this->debug)
|
|
logSuccess_(alq, iteration_idx);
|
|
this->well_state_.setALQ(this->well_name_, alq);
|
|
}
|
|
}
|
|
}
|
|
return state;
|
|
}
|
|
|
|
/****************************************
|
|
* Protected methods in alphabetical order
|
|
****************************************/
|
|
|
|
std::pair<std::optional<double>, bool>
|
|
GasLiftSingleWellGeneric::
|
|
addOrSubtractAlqIncrement_(double alq, bool increase) const
|
|
{
|
|
bool limited = false;
|
|
double orig_alq = alq;
|
|
if (increase) {
|
|
alq += this->increment_;
|
|
// NOTE: if max_alq_ was defaulted in WLIFTOPT, item 3, it has
|
|
// already been set to the largest value in the VFP table in
|
|
// the contructor of GasLiftSingleWell
|
|
if (alq > this->max_alq_) {
|
|
alq = this->max_alq_;
|
|
limited = true;
|
|
}
|
|
}
|
|
else { // we are decreasing ALQ
|
|
alq -= this->increment_;
|
|
if (this->min_alq_ > 0) {
|
|
// According to WLIFTOPT item 5: If a positive value is
|
|
// specified (min_alq_), the well is allocated at least that amount
|
|
// of lift gas, unless the well is unable to flow with
|
|
// that rate of lift gas injection, or unless the well can
|
|
// already meet one of its own rate limits before
|
|
// receiving its minimum lift gas rate.
|
|
if (alq < this->min_alq_) {
|
|
alq = this->min_alq_;
|
|
limited = true;
|
|
}
|
|
}
|
|
else {
|
|
if (alq < 0) {
|
|
alq = 0.0;
|
|
limited = true;
|
|
}
|
|
}
|
|
}
|
|
std::optional<double> alq_opt {alq};
|
|
// If we were not able to change ALQ (to within rounding error), we
|
|
// return std::nullopt
|
|
if (limited && checkALQequal_(orig_alq,alq))
|
|
alq_opt = std::nullopt;
|
|
|
|
return {alq_opt, limited};
|
|
}
|
|
|
|
double
|
|
GasLiftSingleWellGeneric::
|
|
calcEcoGradient_(double oil_rate, double new_oil_rate, double gas_rate,
|
|
double new_gas_rate, bool increase) const
|
|
{
|
|
auto dqo = new_oil_rate - oil_rate;
|
|
auto dqg = new_gas_rate - gas_rate;
|
|
// TODO: Should the gas rate term in the denominator be subject to the constraint:
|
|
//
|
|
// alpha_g_ * dqg >= 0.0
|
|
//
|
|
// ?
|
|
auto gradient = (this->alpha_w_ * dqo) / (this->increment_ + this->alpha_g_*dqg);
|
|
// TODO: Should we do any error checks on the calculation of the
|
|
// gradient?
|
|
|
|
if (!increase) gradient = -gradient;
|
|
return gradient;
|
|
}
|
|
|
|
bool
|
|
GasLiftSingleWellGeneric::
|
|
checkALQequal_(double alq1, double alq2) const
|
|
{
|
|
return std::fabs(alq1-alq2) < (this->increment_*ALQ_EPSILON);
|
|
}
|
|
|
|
bool
|
|
GasLiftSingleWellGeneric::
|
|
checkGroupTargetsViolated(double delta_oil, double delta_gas, double delta_water) const
|
|
{
|
|
const auto &pairs =
|
|
this->group_info_.getWellGroups(this->well_name_);
|
|
for (const auto &[group_name, efficiency] : pairs) {
|
|
auto oil_target_opt = this->group_info_.oilTarget(group_name);
|
|
if (oil_target_opt) {
|
|
double oil_rate =
|
|
this->group_info_.oilRate(group_name) + efficiency * delta_oil;
|
|
if (oil_rate > *oil_target_opt) {
|
|
if (this->debug) {
|
|
const std::string msg = fmt::format(
|
|
"Group {} : oil rate {} exceeds oil target {}. Stopping iteration",
|
|
group_name, oil_rate, *oil_target_opt);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
auto gas_target_opt = this->group_info_.gasTarget(group_name);
|
|
if (gas_target_opt) {
|
|
double gas_rate =
|
|
this->group_info_.gasRate(group_name) + efficiency * delta_gas;
|
|
if (gas_rate > *gas_target_opt) {
|
|
if (this->debug) {
|
|
const std::string msg = fmt::format(
|
|
"Group {} : gas rate {} exceeds gas target {}. Stopping iteration",
|
|
group_name, gas_rate, *gas_target_opt);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
auto liquid_target_opt = this->group_info_.liquidTarget(group_name);
|
|
if (liquid_target_opt) {
|
|
double oil_rate =
|
|
this->group_info_.oilRate(group_name) + efficiency * delta_oil;
|
|
double water_rate =
|
|
this->group_info_.waterRate(group_name) + efficiency * delta_water;
|
|
double liquid_rate = oil_rate + water_rate;
|
|
if (liquid_rate > *liquid_target_opt) {
|
|
if (this->debug) {
|
|
const std::string msg = fmt::format(
|
|
"Group {} : liquid rate {} exceeds liquid target {}. Stopping iteration",
|
|
group_name, liquid_rate, *liquid_target_opt);
|
|
this->displayDebugMessage_(msg);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
auto water_target_opt = this->group_info_.waterTarget(group_name);
|
|
if (water_target_opt) {
|
|
double water_rate =
|
|
this->group_info_.waterRate(group_name) + efficiency * delta_water;
|
|
if (water_rate > *water_target_opt) {
|
|
if (this->debug) {
|
|
const std::string msg = fmt::format(
|
|
"Group {} : water rate {} exceeds water target {}. Stopping iteration",
|
|
group_name, water_rate, *water_target_opt);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
GasLiftSingleWellGeneric::
|
|
checkInitialALQmodified_(double alq, double initial_alq) const
|
|
{
|
|
if (checkALQequal_(alq,initial_alq)) {
|
|
return false;
|
|
}
|
|
else {
|
|
const std::string msg = fmt::format("initial ALQ changed from {} "
|
|
"to {} before iteration starts..", initial_alq, alq);
|
|
displayDebugMessage_(msg);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool
|
|
GasLiftSingleWellGeneric::
|
|
checkWellRatesViolated_(
|
|
std::vector<double>& potentials,
|
|
const std::function<bool(double, double, const std::string &)>& callback,
|
|
bool increase)
|
|
{
|
|
if (!increase) {
|
|
auto oil_rate = -potentials[this->oil_pos_];
|
|
if (oil_rate < 0) {
|
|
// The well is not flowing, and it will(?) not help to reduce lift
|
|
// gas further. Note that this assumes that the oil rates drops with
|
|
// decreasing lift gas.
|
|
displayDebugMessage_("Negative oil rate detected while descreasing "
|
|
"lift gas. Stopping iteration.");
|
|
return true;
|
|
}
|
|
}
|
|
// TODO: the below checks could probably be skipped if we are decreasing
|
|
// lift gas (provided we can assume that rates declines monotonically with
|
|
// decreasing lift gas).
|
|
if (this->controls_.hasControl(Well::ProducerCMode::ORAT)) {
|
|
auto oil_rate = -potentials[this->oil_pos_];
|
|
if (callback(oil_rate, this->controls_.oil_rate, "oil"))
|
|
return true;
|
|
}
|
|
if (this->controls_.hasControl(Well::ProducerCMode::WRAT)) {
|
|
auto water_rate = -potentials[this->water_pos_];
|
|
if (callback(water_rate, this->controls_.water_rate, "water"))
|
|
return true;
|
|
}
|
|
if (this->controls_.hasControl(Well::ProducerCMode::GRAT)) {
|
|
auto gas_rate = -potentials[this->gas_pos_];
|
|
if (callback(gas_rate, this->controls_.gas_rate, "gas"))
|
|
return true;
|
|
}
|
|
if (this->controls_.hasControl(Well::ProducerCMode::LRAT)) {
|
|
auto oil_rate = -potentials[this->oil_pos_];
|
|
auto water_rate = -potentials[this->water_pos_];
|
|
auto liq_rate = oil_rate + water_rate;
|
|
if (callback(liq_rate, this->controls_.liquid_rate, "liquid"))
|
|
return true;
|
|
}
|
|
// TODO: Also check RESV, see checkIndividualContraints() in
|
|
// WellInterface_impl.hpp
|
|
// TODO: Check group contraints?
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
GasLiftSingleWellGeneric::
|
|
computeInitialWellRates_(std::vector<double>& potentials)
|
|
{
|
|
|
|
if (auto bhp = computeBhpAtThpLimit_(this->orig_alq_); bhp) {
|
|
{
|
|
const std::string msg = fmt::format(
|
|
"computed initial bhp {} given thp limit and given alq {}",
|
|
*bhp, this->orig_alq_);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
computeWellRates_(*bhp, potentials);
|
|
{
|
|
const std::string msg = fmt::format(
|
|
"computed initial well potentials given bhp, "
|
|
"oil: {}, gas: {}, water: {}",
|
|
-potentials[this->oil_pos_],
|
|
-potentials[this->gas_pos_],
|
|
-potentials[this->water_pos_]);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
return true;
|
|
}
|
|
else {
|
|
displayDebugMessage_("Aborting optimization.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void
|
|
GasLiftSingleWellGeneric::
|
|
debugCheckNegativeGradient_(double grad, double alq, double new_alq,
|
|
double oil_rate, double new_oil_rate,
|
|
double gas_rate, double new_gas_rate, bool increase) const
|
|
{
|
|
{
|
|
const std::string msg = fmt::format("calculating gradient: "
|
|
"new_oil_rate = {}, oil_rate = {}, grad = {}", new_oil_rate, oil_rate, grad);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
if (grad < 0 ) {
|
|
const std::string msg = fmt::format("negative {} gradient detected ({}) : "
|
|
"alq: {}, new_alq: {}, "
|
|
"oil_rate: {}, new_oil_rate: {}, gas_rate: {}, new_gas_rate: {}",
|
|
(increase ? "incremental" : "decremental"),
|
|
grad, alq, new_alq, oil_rate, new_oil_rate, gas_rate, new_gas_rate);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
}
|
|
|
|
void
|
|
GasLiftSingleWellGeneric::
|
|
debugShowAlqIncreaseDecreaseCounts_()
|
|
{
|
|
auto inc_count = this->well_state_.gliftGetAlqIncreaseCount(this->well_name_);
|
|
auto dec_count = this->well_state_.gliftGetAlqDecreaseCount(this->well_name_);
|
|
const std::string msg =
|
|
fmt::format("ALQ increase/decrease count : {}/{}", inc_count, dec_count);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
|
|
void
|
|
GasLiftSingleWellGeneric::
|
|
debugShowBhpAlqTable_()
|
|
{
|
|
double alq = 0.0;
|
|
const std::string fmt_fmt1 {"{:^12s} {:^12s} {:^12s} {:^12s}"};
|
|
const std::string fmt_fmt2 {"{:>12.5g} {:>12.5g} {:>12.5g} {:>12.5g}"};
|
|
const std::string header = fmt::format(fmt_fmt1, "ALQ", "BHP", "oil", "gas");
|
|
displayDebugMessage_(header);
|
|
while (alq <= (this->max_alq_+this->increment_)) {
|
|
auto bhp_at_thp_limit = computeBhpAtThpLimit_(alq);
|
|
if (!bhp_at_thp_limit) {
|
|
const std::string msg = fmt::format("Failed to get converged potentials "
|
|
"for ALQ = {}. Skipping.", alq );
|
|
displayDebugMessage_(msg);
|
|
}
|
|
else {
|
|
std::vector<double> potentials(this->num_phases_, 0.0);
|
|
computeWellRates_(*bhp_at_thp_limit, potentials, /*debug_out=*/false);
|
|
auto oil_rate = -potentials[this->oil_pos_];
|
|
auto gas_rate = -potentials[this->gas_pos_];
|
|
const std::string msg = fmt::format(
|
|
fmt_fmt2, alq, *bhp_at_thp_limit, oil_rate, gas_rate);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
alq += this->increment_;
|
|
}
|
|
}
|
|
|
|
void
|
|
GasLiftSingleWellGeneric::
|
|
debugShowStartIteration_(double alq, bool increase, double oil_rate)
|
|
{
|
|
const std::string msg =
|
|
fmt::format("starting {} iteration, ALQ = {}, oilrate = {}",
|
|
(increase ? "increase" : "decrease"),
|
|
alq, oil_rate);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
|
|
void
|
|
GasLiftSingleWellGeneric::
|
|
debugShowTargets_()
|
|
{
|
|
if (this->controls_.hasControl(Well::ProducerCMode::ORAT)) {
|
|
auto target = this->controls_.oil_rate;
|
|
const std::string msg = fmt::format("has ORAT control with target {}", target);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
if (this->controls_.hasControl(Well::ProducerCMode::GRAT)) {
|
|
auto target = this->controls_.gas_rate;
|
|
const std::string msg = fmt::format("has GRAT control with target {}", target);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
if (this->controls_.hasControl(Well::ProducerCMode::LRAT)) {
|
|
auto target = this->controls_.liquid_rate;
|
|
const std::string msg = fmt::format("has LRAT control with target {}", target);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
}
|
|
|
|
void
|
|
GasLiftSingleWellGeneric::
|
|
displayDebugMessage_(const std::string& msg) const
|
|
{
|
|
|
|
if (this->debug) {
|
|
const std::string message = fmt::format(
|
|
" GLIFT (DEBUG) : Well {} : {}", this->well_name_, msg);
|
|
this->deferred_logger_.info(message);
|
|
}
|
|
}
|
|
|
|
void
|
|
GasLiftSingleWellGeneric::
|
|
displayWarning_(const std::string& msg)
|
|
{
|
|
const std::string message = fmt::format(
|
|
"GAS LIFT OPTIMIZATION, WELL {} : {}", this->well_name_, msg);
|
|
this->deferred_logger_.warning("WARNING", message);
|
|
}
|
|
|
|
std::pair<double, bool>
|
|
GasLiftSingleWellGeneric::
|
|
getBhpWithLimit_(double bhp) const
|
|
{
|
|
bool limited = false;
|
|
if (this->controls_.hasControl(Well::ProducerCMode::BHP)) {
|
|
auto limit = this->controls_.bhp_limit;
|
|
if (bhp < limit) {
|
|
bhp = limit;
|
|
limited = true;
|
|
}
|
|
}
|
|
return {bhp, limited};
|
|
}
|
|
|
|
// TODO: what if the gas_rate_target_ has been defaulted
|
|
// (i.e. value == 0, meaning: "No limit") but the
|
|
// oil_rate_target_ has not been defaulted ?
|
|
// If the new_oil_rate exceeds the oil_rate_target_ it is cut back,
|
|
// but the same cut-back will not happen for the new_gas_rate
|
|
// Seems like an inconsistency, since alq should in this
|
|
// case also be adjusted (to the smaller value that would
|
|
// give oil target rate) but then the gas rate would also be smaller?
|
|
// The effect of not reducing the gas rate (if it should be
|
|
// reduced?) is that a too large value is used in the
|
|
// computation of the economic gradient making the gradient
|
|
// smaller than it should be since the term appears in the denominator.
|
|
std::pair<double, bool>
|
|
GasLiftSingleWellGeneric::
|
|
getGasRateWithLimit_(const std::vector<double>& potentials) const
|
|
{
|
|
return getRateWithLimit_(Rate::gas, potentials);
|
|
}
|
|
|
|
// NOTE: If the computed oil rate is larger than the target
|
|
// rate of the well, we reduce it to the target rate. This
|
|
// will make the economic gradient smaller than it would be
|
|
// if we did not reduce the rate, and it is less
|
|
// likely that the current gas lift increment will be
|
|
// accepted.
|
|
// TODO: If it still is accepted, we should ideally reduce the alq
|
|
// also since we also reduced the rate. This might involve
|
|
// some sort of iteration though..
|
|
std::pair<double, bool>
|
|
GasLiftSingleWellGeneric::
|
|
getOilRateWithLimit_(const std::vector<double>& potentials) const
|
|
{
|
|
return getRateWithLimit_(Rate::oil, potentials);
|
|
}
|
|
|
|
std::pair<double, bool>
|
|
GasLiftSingleWellGeneric::
|
|
getWaterRateWithLimit_(const std::vector<double>& potentials) const
|
|
{
|
|
return getRateWithLimit_(Rate::water, potentials);
|
|
}
|
|
|
|
double
|
|
GasLiftSingleWellGeneric::
|
|
getRate_(Rate rate, const std::vector<double>& potentials) const
|
|
{
|
|
switch (rate) {
|
|
case Rate::oil:
|
|
return -potentials[this->oil_pos_];
|
|
case Rate::gas:
|
|
return -potentials[this->gas_pos_];
|
|
case Rate::water:
|
|
return -potentials[this->water_pos_];
|
|
case Rate::liquid:
|
|
return -potentials[this->oil_pos_] - potentials[this->water_pos_];
|
|
default:
|
|
// Need this to avoid compiler warning : control reaches end of non-void function
|
|
throw std::runtime_error("This should not happen");
|
|
}
|
|
}
|
|
|
|
double
|
|
GasLiftSingleWellGeneric::
|
|
getProductionTarget_(Rate rate) const
|
|
{
|
|
switch (rate) {
|
|
case Rate::oil:
|
|
return this->controls_.oil_rate;
|
|
case Rate::gas:
|
|
return this->controls_.gas_rate;
|
|
case Rate::water:
|
|
return this->controls_.water_rate;
|
|
case Rate::liquid:
|
|
return this->controls_.liquid_rate;
|
|
default:
|
|
// Need this to avoid compiler warning : control reaches end of non-void function
|
|
throw std::runtime_error("This should not happen");
|
|
}
|
|
}
|
|
|
|
std::pair<double, bool>
|
|
GasLiftSingleWellGeneric::
|
|
getRateWithLimit_(Rate rate_type, const std::vector<double>& potentials) const
|
|
{
|
|
double new_rate = getRate_(rate_type, potentials);
|
|
bool limited = false;
|
|
if (hasProductionControl_(rate_type)) {
|
|
auto target = getProductionTarget_(rate_type);
|
|
if (new_rate > target) {
|
|
new_rate = target;
|
|
limited = true;
|
|
const std::string msg = fmt::format("limiting {} rate to target: "
|
|
"computed rate: {}, target: {}",
|
|
GasLiftGroupInfo::rateToString(rate_type), new_rate, target);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
}
|
|
if ((rate_type == Rate::oil) || (rate_type == Rate::water)) {
|
|
double rate2;
|
|
if (rate_type == Rate::oil) {
|
|
rate2 = getRate_(Rate::water, potentials);
|
|
}
|
|
else {
|
|
rate2 = getRate_(Rate::oil, potentials);
|
|
}
|
|
double liq_rate = new_rate + rate2;
|
|
auto liq_target = getProductionTarget_(Rate::liquid);
|
|
if (liq_rate > liq_target) {
|
|
double fraction = new_rate / liq_rate;
|
|
// NOTE: since
|
|
// fraction * liq_rate = new_rate,
|
|
// we must have
|
|
// fraction * liq_target < new_rate
|
|
// since
|
|
// liq_target < liq_rate
|
|
// therefore new_rate will become less than it original was and
|
|
// limited = true.
|
|
new_rate = fraction * liq_target;
|
|
limited = true;
|
|
const std::string msg = fmt::format(
|
|
"limiting {} rate to {} due to LRAT target: "
|
|
"computed LRAT: {}, target LRAT: {}",
|
|
GasLiftGroupInfo::rateToString(rate_type), new_rate,
|
|
liq_rate, liq_target);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
}
|
|
return { new_rate, limited};
|
|
}
|
|
|
|
std::pair<double, bool>
|
|
GasLiftSingleWellGeneric::
|
|
getOilRateWithGroupLimit_(double new_oil_rate, double oil_rate) const
|
|
{
|
|
[[maybe_unused]] auto [rate, gr_name, efficiency]
|
|
= getRateWithGroupLimit_(Rate::oil, new_oil_rate, oil_rate);
|
|
bool limited = gr_name != nullptr;
|
|
return {rate, limited};
|
|
}
|
|
|
|
std::pair<double, bool>
|
|
GasLiftSingleWellGeneric::
|
|
getGasRateWithGroupLimit_(double new_gas_rate, double gas_rate) const
|
|
{
|
|
[[maybe_unused]] auto [rate, gr_name, efficiency]
|
|
= getRateWithGroupLimit_(Rate::gas, new_gas_rate, gas_rate);
|
|
bool limited = gr_name != nullptr;
|
|
return {rate, limited};
|
|
}
|
|
|
|
std::pair<double, bool>
|
|
GasLiftSingleWellGeneric::
|
|
getWaterRateWithGroupLimit_(double new_water_rate, double water_rate) const
|
|
{
|
|
[[maybe_unused]] auto [rate, gr_name, efficiency] = getRateWithGroupLimit_(
|
|
Rate::water, new_water_rate, water_rate);
|
|
bool limited = gr_name != nullptr;
|
|
return {rate, limited};
|
|
}
|
|
|
|
std::tuple<double, double, bool, bool>
|
|
GasLiftSingleWellGeneric::
|
|
getLiquidRateWithGroupLimit_(const double new_oil_rate, const double oil_rate,
|
|
const double new_water_rate, const double water_rate) const
|
|
{
|
|
auto liquid_rate = oil_rate + water_rate;
|
|
auto new_liquid_rate = new_oil_rate + new_water_rate;
|
|
auto [liquid_rate_limited, group_name, efficiency]
|
|
= getRateWithGroupLimit_(Rate::liquid, new_liquid_rate, liquid_rate);
|
|
bool limited = group_name != nullptr;
|
|
if (limited) {
|
|
// the oil, gas, and water cases can be handled directly by
|
|
// getRateWithGroupLimit_() above. However, for the liquid case
|
|
// we must do some postprocessing. I chose to include it here
|
|
// instead of cluttering up getRateWithGroupLimit_() with this
|
|
// special case.
|
|
double delta_water = new_water_rate - water_rate;
|
|
double delta_oil = new_oil_rate - oil_rate;
|
|
|
|
double gr_water_rate = this->group_info_.waterRate(*group_name);
|
|
double gr_oil_rate = this->group_info_.oilRate(*group_name);
|
|
|
|
// NOTE: these rates are too large according to the limited liquid rate
|
|
// but it does not matter since we are only using them to calculate
|
|
// the fraction of the liquid corresponding to the oil phase
|
|
double new_gr_water_rate = gr_water_rate + efficiency * delta_water;
|
|
double new_gr_oil_rate = gr_oil_rate + efficiency * delta_oil;
|
|
double new_gr_liquid_rate = new_gr_water_rate + new_gr_oil_rate;
|
|
|
|
double oil_fraction = new_gr_oil_rate / new_gr_liquid_rate;
|
|
double delta_liquid = liquid_rate_limited - liquid_rate;
|
|
auto limited_oil_rate = oil_rate + oil_fraction * delta_liquid;
|
|
auto limited_water_rate = water_rate + (1.0 - oil_fraction) * delta_liquid;
|
|
return {limited_oil_rate, limited_water_rate, limited, limited};
|
|
}
|
|
return {new_oil_rate, new_water_rate, limited, limited};
|
|
}
|
|
|
|
std::tuple<double, const std::string*, double>
|
|
GasLiftSingleWellGeneric::
|
|
getRateWithGroupLimit_(
|
|
Rate rate_type, const double new_rate, const double old_rate) const
|
|
{
|
|
const double delta_rate = new_rate - old_rate;
|
|
if (delta_rate > 0) {
|
|
// It is required that the production rate for a given group is
|
|
// is less than or equal to its target rate, see assert() below.
|
|
// Then it only makes sense to check if the group target is exceeded
|
|
// if delta_rate > 0
|
|
const auto &pairs =
|
|
this->group_info_.getWellGroups(this->well_name_);
|
|
double limited_rate = new_rate;
|
|
double gr_target, new_gr_rate, efficiency;
|
|
const std::string *group_name = nullptr;
|
|
for (const auto& [group_name_temp, efficiency_temp] : pairs) {
|
|
auto gr_target_opt = this->group_info_.getTarget(rate_type, group_name_temp);
|
|
if (gr_target_opt) {
|
|
double gr_target_temp = *gr_target_opt;
|
|
double gr_rate_temp =
|
|
this->group_info_.getRate(rate_type, group_name_temp);
|
|
assert(gr_rate_temp <= gr_target_temp);
|
|
double new_gr_rate_temp = gr_rate_temp + efficiency_temp * delta_rate;
|
|
if (new_gr_rate_temp > gr_target_temp) {
|
|
double limited_rate_temp =
|
|
old_rate + (gr_target_temp - gr_rate_temp) / efficiency_temp;
|
|
if (limited_rate_temp < limited_rate) {
|
|
group_name = &group_name_temp;
|
|
efficiency = efficiency_temp;
|
|
limited_rate = limited_rate_temp;
|
|
gr_target = gr_target_temp;
|
|
new_gr_rate = new_gr_rate_temp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (this->debug && group_name) {
|
|
const std::string msg = fmt::format(
|
|
"limiting {} rate from {} to {} to meet group target {} "
|
|
"for group {}. Computed group rate was: {}",
|
|
GasLiftGroupInfo::rateToString(rate_type),
|
|
new_rate, limited_rate, gr_target,
|
|
*group_name, new_gr_rate);
|
|
displayDebugMessage_(msg);
|
|
return { limited_rate, group_name, efficiency };
|
|
}
|
|
}
|
|
return { new_rate, /*group_name =*/nullptr, /*efficiency dummy value*/0.0 };
|
|
}
|
|
|
|
|
|
std::tuple<double,double,double, bool, bool,bool>
|
|
GasLiftSingleWellGeneric::
|
|
getInitialRatesWithLimit_(const std::vector<double>& potentials)
|
|
{
|
|
auto [oil_rate, oil_is_limited] = getOilRateWithLimit_(potentials);
|
|
auto [gas_rate, gas_is_limited] = getGasRateWithLimit_(potentials);
|
|
auto [water_rate, water_is_limited] = getWaterRateWithLimit_(potentials);
|
|
if (oil_is_limited) {
|
|
const std::string msg = fmt::format(
|
|
"initial oil rate was limited to: {}", oil_rate);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
if (gas_is_limited) {
|
|
const std::string msg = fmt::format(
|
|
"initial gas rate was limited to: {}", gas_rate);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
if (water_is_limited) {
|
|
const std::string msg = fmt::format(
|
|
"initial water rate was limited to: {}", water_rate);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
return std::make_tuple(oil_rate, gas_rate, water_rate, oil_is_limited, gas_is_limited, water_is_limited);
|
|
}
|
|
|
|
bool
|
|
GasLiftSingleWellGeneric::
|
|
hasProductionControl_(Rate rate) const
|
|
{
|
|
switch (rate) {
|
|
case Rate::oil:
|
|
return this->controls_.hasControl(Well::ProducerCMode::ORAT);
|
|
case Rate::gas:
|
|
return this->controls_.hasControl(Well::ProducerCMode::GRAT);
|
|
case Rate::water:
|
|
return this->controls_.hasControl(Well::ProducerCMode::WRAT);
|
|
case Rate::liquid:
|
|
return this->controls_.hasControl(Well::ProducerCMode::LRAT);
|
|
default:
|
|
// Need this to avoid compiler warning : control reaches end of non-void function
|
|
throw std::runtime_error("This should not happen");
|
|
}
|
|
}
|
|
|
|
|
|
std::tuple<double,double,bool,bool,double>
|
|
GasLiftSingleWellGeneric::
|
|
increaseALQtoPositiveOilRate_(double alq,
|
|
double oil_rate,
|
|
double gas_rate,
|
|
bool oil_is_limited,
|
|
bool gas_is_limited,
|
|
std::vector<double>& potentials)
|
|
{
|
|
bool stop_iteration = false;
|
|
double temp_alq = alq;
|
|
while(!stop_iteration) {
|
|
temp_alq += this->increment_;
|
|
if (temp_alq > this->max_alq_) break;
|
|
auto bhp_opt = computeBhpAtThpLimit_(temp_alq);
|
|
if (!bhp_opt) break;
|
|
alq = temp_alq;
|
|
auto bhp_this = getBhpWithLimit_(*bhp_opt);
|
|
computeWellRates_(bhp_this.first, potentials);
|
|
oil_rate = -potentials[this->oil_pos_];
|
|
if (oil_rate > 0) break;
|
|
}
|
|
std::tie(oil_rate, oil_is_limited) = getOilRateWithLimit_(potentials);
|
|
std::tie(gas_rate, gas_is_limited) = getGasRateWithLimit_(potentials);
|
|
return std::make_tuple(oil_rate, gas_rate, oil_is_limited, gas_is_limited, alq);
|
|
}
|
|
|
|
std::tuple<double,double,bool,bool,double>
|
|
GasLiftSingleWellGeneric::
|
|
increaseALQtoMinALQ_(double alq,
|
|
double oil_rate,
|
|
double gas_rate,
|
|
bool oil_is_limited,
|
|
bool gas_is_limited,
|
|
std::vector<double>& potentials)
|
|
{
|
|
auto min_alq = this->min_alq_;
|
|
assert(min_alq >= 0);
|
|
assert(alq < min_alq);
|
|
assert(min_alq < this->max_alq_);
|
|
bool stop_iteration = false;
|
|
double temp_alq = alq;
|
|
while(!stop_iteration) {
|
|
temp_alq += this->increment_;
|
|
if (temp_alq >= min_alq) break;
|
|
auto bhp_opt = computeBhpAtThpLimit_(temp_alq);
|
|
if (!bhp_opt) break;
|
|
alq = temp_alq;
|
|
auto bhp_this = getBhpWithLimit_(*bhp_opt);
|
|
computeWellRates_(bhp_this.first, potentials);
|
|
std::tie(oil_rate, oil_is_limited) = getOilRateWithLimit_(potentials);
|
|
std::tie(gas_rate, gas_is_limited) = getGasRateWithLimit_(potentials);
|
|
if (oil_is_limited || gas_is_limited) break;
|
|
}
|
|
return std::make_tuple(oil_rate, gas_rate, oil_is_limited, gas_is_limited, alq);
|
|
}
|
|
|
|
void
|
|
GasLiftSingleWellGeneric::
|
|
logSuccess_(double alq, const int iteration_idx)
|
|
{
|
|
const std::string message = fmt::format(
|
|
"GLIFT, IT={}, WELL {} : {} ALQ from {} to {}",
|
|
iteration_idx,
|
|
this->well_name_,
|
|
((alq > this->orig_alq_) ? "increased" : "decreased"),
|
|
this->orig_alq_, alq);
|
|
this->deferred_logger_.info(message);
|
|
}
|
|
|
|
std::tuple<double,double,double,double,bool,bool,bool>
|
|
GasLiftSingleWellGeneric::
|
|
maybeAdjustALQbeforeOptimizeLoop_(
|
|
bool increase, double alq, double oil_rate, double gas_rate, double water_rate,
|
|
bool oil_is_limited, bool gas_is_limited,bool water_is_limited,
|
|
std::vector<double> &potentials)
|
|
{
|
|
double orig_alq = alq;
|
|
if (this->debug) {
|
|
const std::string msg = fmt::format("initial ALQ: {}", alq);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
if (!increase && ( oil_is_limited || water_is_limited || gas_is_limited)) {
|
|
// NOTE: Try to decrease ALQ down to a value where the well target is
|
|
// not exceeded.
|
|
// NOTE: This may reduce ALQ below the minimum value set in WLIFTOPT
|
|
// item 5. However, this is OK since the rate target is met and there
|
|
// is no point in using a higher ALQ value then.
|
|
double reduced_oil_rate, reduced_gas_rate, reduced_water_rate, reduced_alq;
|
|
|
|
std::tie(reduced_oil_rate, reduced_gas_rate, reduced_water_rate, oil_is_limited, gas_is_limited, water_is_limited, reduced_alq) =
|
|
reduceALQtoWellTarget_(alq, oil_rate, gas_rate, water_rate,
|
|
oil_is_limited, gas_is_limited, water_is_limited, potentials);
|
|
|
|
// potentially reduce alq if group control is violated
|
|
double reduced2_oil_rate, reduced2_gas_rate, reduced2_water_rate, reduced2_alq;
|
|
std::tie(reduced2_oil_rate, reduced2_gas_rate,
|
|
reduced2_water_rate, reduced2_alq)
|
|
= reduceALQtoGroupTarget(
|
|
alq, oil_rate, gas_rate, water_rate, potentials);
|
|
oil_rate = std::min(reduced_oil_rate, reduced2_oil_rate);
|
|
gas_rate = std::min(reduced_gas_rate, reduced2_gas_rate);
|
|
water_rate = std::min(reduced_water_rate, reduced2_water_rate);
|
|
alq = std::min(reduced_alq, reduced2_alq);
|
|
} else {
|
|
if (increase && oil_rate < 0) {
|
|
// NOTE: Try to increase ALQ up to a value where oil_rate is positive
|
|
std::tie(oil_rate, gas_rate, oil_is_limited, gas_is_limited, alq) =
|
|
increaseALQtoPositiveOilRate_(alq, oil_rate,
|
|
gas_rate, oil_is_limited, gas_is_limited, potentials);
|
|
}
|
|
if (increase && (this->min_alq_> 0) && (alq < this->min_alq_)) {
|
|
// NOTE: Try to increase ALQ up to the minimum limit without checking
|
|
// the economic gradient..
|
|
std::tie(oil_rate, gas_rate, oil_is_limited, gas_is_limited, alq) =
|
|
increaseALQtoMinALQ_(alq, oil_rate, gas_rate,
|
|
oil_is_limited, gas_is_limited, potentials);
|
|
}
|
|
}
|
|
if (this->debug && (orig_alq != alq)) {
|
|
const std::string msg = fmt::format("adjusted ALQ to: {}", alq);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
return std::make_tuple(oil_rate, gas_rate, water_rate, alq, oil_is_limited, gas_is_limited, water_is_limited);
|
|
}
|
|
|
|
std::tuple<double,double,double,double>
|
|
GasLiftSingleWellGeneric::
|
|
reduceALQtoGroupTarget(double alq,
|
|
double oil_rate,
|
|
double gas_rate,
|
|
double water_rate,
|
|
std::vector<double>& potentials) const
|
|
{
|
|
bool stop_this_iteration = true;
|
|
const auto &pairs =
|
|
this->group_info_.getWellGroups(this->well_name_);
|
|
for (const auto &groups : pairs) {
|
|
if (!this->group_state_.has_production_control(groups.first))
|
|
continue;
|
|
const auto& current_control = this->group_state_.production_control(groups.first);
|
|
if(current_control == Group::ProductionCMode::ORAT
|
|
|| current_control == Group::ProductionCMode::LRAT
|
|
|| current_control == Group::ProductionCMode::WRAT
|
|
|| current_control == Group::ProductionCMode::GRAT){
|
|
stop_this_iteration = false;
|
|
displayDebugMessage_("Reducing ALQ to meet groups target before iteration starts.");
|
|
break;
|
|
}
|
|
}
|
|
double temp_alq = alq;
|
|
double oil_rate_orig = oil_rate;
|
|
double gas_rate_orig = gas_rate;
|
|
double water_rate_orig = water_rate;
|
|
while(!stop_this_iteration) {
|
|
temp_alq -= this->increment_;
|
|
if (temp_alq <= 0) break;
|
|
auto bhp_opt = computeBhpAtThpLimit_(temp_alq);
|
|
if (!bhp_opt) break;
|
|
auto bhp_this = getBhpWithLimit_(*bhp_opt);
|
|
computeWellRates_(bhp_this.first, potentials);
|
|
oil_rate = -potentials[this->oil_pos_];
|
|
gas_rate = -potentials[this->gas_pos_];
|
|
water_rate = -potentials[this->water_pos_];
|
|
double delta_oil = oil_rate - oil_rate_orig;
|
|
double delta_gas = gas_rate - gas_rate_orig;
|
|
double delta_water = water_rate - water_rate_orig;
|
|
if (!checkGroupTargetsViolated(delta_oil, delta_gas, delta_water)) {
|
|
break;
|
|
}
|
|
alq = temp_alq;
|
|
}
|
|
return std::make_tuple(oil_rate, gas_rate, water_rate, alq);
|
|
}
|
|
|
|
std::tuple<double,double,double, bool, bool,bool,double>
|
|
GasLiftSingleWellGeneric::
|
|
reduceALQtoWellTarget_(double alq,
|
|
double oil_rate,
|
|
double gas_rate,
|
|
double water_rate,
|
|
bool oil_is_limited,
|
|
bool gas_is_limited,
|
|
bool water_is_limited,
|
|
std::vector<double>& potentials)
|
|
{
|
|
displayDebugMessage_("Reducing ALQ to meet well target before iteration starts..");
|
|
double orig_oil_rate = oil_rate;
|
|
double orig_alq = alq;
|
|
|
|
// check well targets
|
|
bool stop_iteration = false;
|
|
auto limiting_control = Well::ProducerCMode::NONE;
|
|
double temp_alq = alq;
|
|
while(!stop_iteration) {
|
|
temp_alq -= this->increment_;
|
|
if (temp_alq <= 0) break;
|
|
auto bhp_opt = computeBhpAtThpLimit_(temp_alq);
|
|
if (!bhp_opt) break;
|
|
auto bhp_this = getBhpWithLimit_(*bhp_opt);
|
|
computeWellRates_(bhp_this.first, potentials);
|
|
oil_rate = -potentials[this->oil_pos_];
|
|
water_rate = -potentials[this->water_pos_];
|
|
gas_rate = -potentials[this->gas_pos_];
|
|
if (oil_is_limited && this->controls_.hasControl(Well::ProducerCMode::ORAT) && oil_rate < this->controls_.oil_rate) {
|
|
limiting_control = Well::ProducerCMode::ORAT;
|
|
break;
|
|
}
|
|
if (oil_is_limited && this->controls_.hasControl(Well::ProducerCMode::LRAT) && (oil_rate + water_rate) < this->controls_.liquid_rate) {
|
|
limiting_control = Well::ProducerCMode::LRAT;
|
|
break;
|
|
}
|
|
if (gas_is_limited && gas_rate < this->controls_.gas_rate) {
|
|
limiting_control = Well::ProducerCMode::GRAT;
|
|
break;
|
|
}
|
|
if (water_is_limited && water_rate < this->controls_.water_rate) {
|
|
limiting_control = Well::ProducerCMode::WRAT;
|
|
break;
|
|
}
|
|
alq = temp_alq;
|
|
}
|
|
std::tie(oil_rate, oil_is_limited) = getOilRateWithLimit_(potentials);
|
|
std::tie(gas_rate, gas_is_limited) = getGasRateWithLimit_(potentials);
|
|
std::tie(water_rate, water_is_limited) = getWaterRateWithLimit_(potentials);
|
|
|
|
if (this->debug) {
|
|
assert( alq <= orig_alq );
|
|
std::string type;
|
|
double target = 0.0;
|
|
double rate = 0.0;
|
|
if (limiting_control == Well::ProducerCMode::ORAT) {
|
|
rate = oil_rate;
|
|
target = this->controls_.oil_rate;
|
|
type = "ORAT";
|
|
} else if(limiting_control == Well::ProducerCMode::LRAT) {
|
|
rate = oil_rate + water_rate;
|
|
target = this->controls_.liquid_rate;
|
|
type = "LRAT";
|
|
} else if (limiting_control == Well::ProducerCMode::WRAT) {
|
|
rate = water_rate;
|
|
target = this->controls_.water_rate;
|
|
type = "WRAT";
|
|
} else if (limiting_control == Well::ProducerCMode::GRAT) {
|
|
rate = gas_rate;
|
|
target = this->controls_.gas_rate;
|
|
type = "GRAT";
|
|
}
|
|
if (alq < orig_alq) {
|
|
// NOTE: ALQ may drop below zero before we are able to meet the target
|
|
const std::string msg = fmt::format(
|
|
"Reduced (rate, alq) from ({}, {}) to ({}, {}) to meet {} target "
|
|
"at {}. ", orig_oil_rate, orig_alq, rate, alq, type, target);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
else if (alq == orig_alq) {
|
|
// We might not be able to reduce ALQ, for example if ALQ starts out at zero.
|
|
const std::string msg = fmt::format("Not able to reduce ALQ {} further. "
|
|
" {} is {} and target is {}", orig_alq, type, rate, target);
|
|
displayDebugMessage_(msg);
|
|
}
|
|
}
|
|
return std::make_tuple(oil_rate, gas_rate, water_rate,
|
|
oil_is_limited, gas_is_limited, water_is_limited, alq);
|
|
}
|
|
|
|
// INPUT:
|
|
// - increase (boolean) :
|
|
// - true : try increase the lift gas supply,
|
|
// - false : try decrease lift gas supply.
|
|
//
|
|
// OUTPUT:
|
|
//
|
|
// - return value: a new GasLiftWellState or nullptr
|
|
//
|
|
std::unique_ptr<GasLiftWellState>
|
|
GasLiftSingleWellGeneric::
|
|
runOptimizeLoop_(bool increase)
|
|
{
|
|
std::vector<double> potentials(this->num_phases_, 0.0);
|
|
std::unique_ptr<GasLiftWellState> ret_value; // nullptr initially
|
|
if (!computeInitialWellRates_(potentials)) return ret_value;
|
|
bool alq_is_limited = false;
|
|
bool oil_is_limited = false;
|
|
bool gas_is_limited = false;
|
|
bool water_is_limited = false;
|
|
double oil_rate, gas_rate, water_rate;
|
|
std::tie(oil_rate, gas_rate, water_rate, oil_is_limited, gas_is_limited, water_is_limited) =
|
|
getInitialRatesWithLimit_(potentials);
|
|
//if (this->debug) debugShowBhpAlqTable_();
|
|
if (this->debug) debugShowAlqIncreaseDecreaseCounts_();
|
|
if (this->debug) debugShowTargets_();
|
|
bool success = false; // did we succeed to increase alq?
|
|
auto cur_alq = this->orig_alq_;
|
|
double new_oil_rate, new_gas_rate, new_water_rate, new_alq;
|
|
bool new_oil_is_limited, new_gas_is_limited, new_water_is_limited;
|
|
std::tie(new_oil_rate, new_gas_rate, new_water_rate, new_alq,
|
|
new_oil_is_limited, new_gas_is_limited, new_water_is_limited)
|
|
= maybeAdjustALQbeforeOptimizeLoop_(
|
|
increase, cur_alq, oil_rate, gas_rate, water_rate,
|
|
oil_is_limited, gas_is_limited, water_is_limited, potentials);
|
|
double delta_oil = 0.0;
|
|
double delta_gas = 0.0;
|
|
double delta_alq = 0.0;
|
|
double delta_water = 0.0;
|
|
OptimizeState state {*this, increase};
|
|
|
|
if (checkInitialALQmodified_(new_alq, cur_alq)) {
|
|
delta_oil = new_oil_rate - oil_rate;
|
|
delta_gas = new_gas_rate - gas_rate;
|
|
delta_water = new_water_rate - water_rate;
|
|
delta_alq = new_alq - cur_alq;
|
|
if (!(checkGroupTargetsViolated(delta_oil, delta_gas, delta_water)) &&
|
|
!(state.checkGroupALQrateExceeded(delta_alq)))
|
|
{
|
|
oil_rate = new_oil_rate;
|
|
gas_rate = new_gas_rate;
|
|
water_rate = new_water_rate;
|
|
oil_is_limited = new_oil_is_limited;
|
|
gas_is_limited = new_gas_is_limited;
|
|
water_is_limited = new_water_is_limited;
|
|
cur_alq = new_alq;
|
|
success = true;
|
|
}
|
|
else {
|
|
state.stop_iteration = true;
|
|
}
|
|
}
|
|
// we only iterate if well is under thp control
|
|
if (!state.checkThpControl()) {
|
|
state.stop_iteration = true;
|
|
}
|
|
|
|
auto temp_alq = cur_alq;
|
|
if (this->debug) debugShowStartIteration_(temp_alq, increase, oil_rate);
|
|
while (!state.stop_iteration && (++state.it <= this->max_iterations_)) {
|
|
if (!increase && state.checkNegativeOilRate(oil_rate)) break;
|
|
if (state.checkWellRatesViolated(potentials)) break;
|
|
if (checkGroupTargetsViolated(delta_oil, delta_gas, delta_water)) break;
|
|
if (state.checkAlqOutsideLimits(temp_alq, oil_rate)) break;
|
|
std::optional<double> alq_opt;
|
|
std::tie(alq_opt, alq_is_limited)
|
|
= state.addOrSubtractAlqIncrement(temp_alq);
|
|
if (!alq_opt) break;
|
|
delta_alq = *alq_opt - temp_alq;
|
|
if (state.checkGroupALQrateExceeded(delta_alq)) break;
|
|
temp_alq = *alq_opt;
|
|
if (this->debug) state.debugShowIterationInfo(temp_alq);
|
|
if (!state.computeBhpAtThpLimit(temp_alq)) break;
|
|
// NOTE: if BHP is below limit, we set state.stop_iteration = true
|
|
auto bhp = state.getBhpWithLimit();
|
|
computeWellRates_(bhp, potentials);
|
|
std::tie(new_oil_rate, new_oil_is_limited) = getOilRateWithLimit_(potentials);
|
|
std::tie(new_oil_rate, new_oil_is_limited) = getOilRateWithGroupLimit_(new_oil_rate, oil_rate);
|
|
|
|
/* if (this->debug_abort_if_decrease_and_oil_is_limited_) {
|
|
if (oil_is_limited && !increase) {
|
|
// if oil is limited we do not want to decrease
|
|
displayDebugMessage_(
|
|
"decreasing ALQ and oil is limited -> aborting iteration");
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
std::tie(new_gas_rate, new_gas_is_limited) = getGasRateWithLimit_(potentials);
|
|
std::tie(new_gas_rate, new_gas_is_limited) = getGasRateWithGroupLimit_(new_gas_rate, gas_rate);
|
|
|
|
std::tie(new_water_rate, new_water_is_limited) = getWaterRateWithLimit_(potentials);
|
|
std::tie(new_water_rate, new_water_is_limited) = getWaterRateWithGroupLimit_(new_water_rate, water_rate);
|
|
|
|
std::tie(new_oil_rate, new_water_rate, new_oil_is_limited, new_water_is_limited)
|
|
= getLiquidRateWithGroupLimit_(new_oil_rate, oil_rate, new_water_rate, water_rate);
|
|
|
|
|
|
/* if (this->debug_abort_if_increase_and_gas_is_limited_) {
|
|
if (gas_is_limited && increase) {
|
|
// if gas is limited we do not want to increase
|
|
displayDebugMessage_(
|
|
"increasing ALQ and gas is limited -> aborting iteration");
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
auto gradient = state.calcEcoGradient(
|
|
oil_rate, new_oil_rate, gas_rate, new_gas_rate);
|
|
if (this->debug)
|
|
debugCheckNegativeGradient_(
|
|
gradient, cur_alq, temp_alq, oil_rate, new_oil_rate,
|
|
gas_rate, new_gas_rate, increase);
|
|
if (state.checkEcoGradient(gradient)) break;
|
|
cur_alq = temp_alq;
|
|
success = true;
|
|
delta_oil = new_oil_rate - oil_rate;
|
|
delta_gas = new_gas_rate - gas_rate;
|
|
delta_water = new_water_rate - water_rate;
|
|
oil_rate = new_oil_rate;
|
|
gas_rate = new_gas_rate;
|
|
water_rate = new_water_rate;
|
|
oil_is_limited = new_oil_is_limited;
|
|
gas_is_limited = new_gas_is_limited;
|
|
water_is_limited = new_water_is_limited;
|
|
state.updateGroupRates(delta_oil, delta_gas, delta_water, delta_alq);
|
|
}
|
|
if (state.it > this->max_iterations_) {
|
|
warnMaxIterationsExceeded_();
|
|
}
|
|
std::optional<bool> increase_opt;
|
|
if (success) {
|
|
this->well_state_.gliftUpdateAlqIncreaseCount(this->well_name_, increase);
|
|
increase_opt = increase;
|
|
}
|
|
else {
|
|
increase_opt = std::nullopt;
|
|
}
|
|
ret_value = std::make_unique<GasLiftWellState>(oil_rate, oil_is_limited,
|
|
gas_rate, gas_is_limited, cur_alq, alq_is_limited, increase_opt);
|
|
return ret_value;
|
|
}
|
|
|
|
std::unique_ptr<GasLiftWellState>
|
|
GasLiftSingleWellGeneric::
|
|
runOptimize1_()
|
|
{
|
|
std::unique_ptr<GasLiftWellState> state;
|
|
int inc_count = this->well_state_.gliftGetAlqIncreaseCount(this->well_name_);
|
|
int dec_count = this->well_state_.gliftGetAlqDecreaseCount(this->well_name_);
|
|
if (dec_count == 0 && inc_count == 0) {
|
|
state = tryIncreaseLiftGas_();
|
|
if (!state || !(state->alqChanged())) {
|
|
state = tryDecreaseLiftGas_();
|
|
}
|
|
}
|
|
else if (dec_count == 0) {
|
|
assert(inc_count > 0);
|
|
state = tryIncreaseLiftGas_();
|
|
}
|
|
else if (inc_count == 0) {
|
|
assert(dec_count > 0);
|
|
state = tryDecreaseLiftGas_();
|
|
}
|
|
return state;
|
|
}
|
|
|
|
std::unique_ptr<GasLiftWellState>
|
|
GasLiftSingleWellGeneric::
|
|
runOptimize2_()
|
|
{
|
|
std::unique_ptr<GasLiftWellState> state;
|
|
state = tryIncreaseLiftGas_();
|
|
if (!state || !(state->alqChanged())) {
|
|
state = tryDecreaseLiftGas_();
|
|
}
|
|
return state;
|
|
}
|
|
|
|
std::unique_ptr<GasLiftWellState>
|
|
GasLiftSingleWellGeneric::
|
|
tryDecreaseLiftGas_()
|
|
{
|
|
return runOptimizeLoop_(/*increase=*/ false);
|
|
}
|
|
|
|
std::unique_ptr<GasLiftWellState>
|
|
GasLiftSingleWellGeneric::
|
|
tryIncreaseLiftGas_()
|
|
{
|
|
return runOptimizeLoop_(/*increase=*/ true);
|
|
}
|
|
|
|
void
|
|
GasLiftSingleWellGeneric::
|
|
setAlqMinRate_(const GasLiftOpt::Well& well)
|
|
{
|
|
// NOTE: According to WLIFTOPT item 5 :
|
|
// if min_rate() is negative, it means: allocate at least enough lift gas
|
|
// to enable the well to flow
|
|
// NOTE: "to enable the well to flow" : How to interpret this?
|
|
// We choose to interpret it to mean a positive oil rate as returned from
|
|
//
|
|
// computeWellRates_(bhp, cur_potentials);
|
|
//
|
|
// So even if the well is producing gas, if the oil rate is zero
|
|
// we say that the "well is not flowing".
|
|
//
|
|
// Note that if WECON item 2 is set, the well can be shut off
|
|
// before the flow rate reaches zero. Also,
|
|
// if bhp drops below the bhp lower limit, the well might switch to bhp
|
|
// control before the oil rate becomes zero.
|
|
|
|
this->min_alq_ = well.min_rate();
|
|
if (this->min_alq_ > 0) {
|
|
if (this->min_alq_ >= this->max_alq_) {
|
|
// NOTE: We reset the value to a negative value.
|
|
// negative value means: Allocate at least enough lift gas
|
|
// to allow the well to flow.
|
|
// TODO: Consider other options for resetting the value..
|
|
this->min_alq_ = -1;
|
|
displayWarning_("Minimum ALQ value is larger than maximum ALQ value!"
|
|
" Resetting value.");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called when we should use a fixed ALQ value
|
|
void
|
|
GasLiftSingleWellGeneric::
|
|
updateWellStateAlqFixedValue_(const GasLiftOpt::Well& well)
|
|
{
|
|
auto& max_alq_optional = well.max_rate();
|
|
if (max_alq_optional) {
|
|
// According to WLIFTOPT, item 3:
|
|
// If item 2 is NO, then item 3 is regarded as the fixed
|
|
// lift gas injection rate for the well.
|
|
auto new_alq = *max_alq_optional;
|
|
this->well_state_.setALQ(this->well_name_, new_alq);
|
|
}
|
|
// else {
|
|
// // If item 3 is defaulted, the lift gas rate remains
|
|
// // unchanged at its current value.
|
|
//}
|
|
|
|
}
|
|
|
|
// Determine if we should use a fixed ALQ value.
|
|
//
|
|
// From the manual for WLIFTOPT, item 2:
|
|
// Is the well's lift gas injection rate to be calculated by the
|
|
// optimization facility?
|
|
// - YES : The well's lift gas injection rate is calculated by the
|
|
// optimization facility.
|
|
// - NO : The well's lift gas injection rate remains fixed at a
|
|
// value that can be set either in Item 3 of this keyword, or in
|
|
// Item 12 of keyword WCONPROD, or with keyword WELTARG.
|
|
bool
|
|
GasLiftSingleWellGeneric::
|
|
useFixedAlq_(const GasLiftOpt::Well& well)
|
|
{
|
|
auto wliftopt_item2 = well.use_glo();
|
|
if (wliftopt_item2) {
|
|
return false;
|
|
}
|
|
else {
|
|
// auto& max_alq_optional = well.max_rate();
|
|
// if (max_alq_optional) {
|
|
// According to WLIFTOPT, item 3:
|
|
// If item 2 is NO, then item 3 is regarded as the fixed
|
|
// lift gas injection rate for the well.
|
|
// }
|
|
// else {
|
|
// If item 3 is defaulted, the lift gas rate remains
|
|
// unchanged at its current value.
|
|
// }
|
|
return true;
|
|
}
|
|
}
|
|
void
|
|
GasLiftSingleWellGeneric::
|
|
warnMaxIterationsExceeded_()
|
|
{
|
|
const std::string msg = fmt::format(
|
|
"Max iterations ({}) exceeded", this->max_iterations_);
|
|
displayWarning_(msg);
|
|
}
|
|
|
|
/****************************************
|
|
* Methods declared in OptimizeState
|
|
****************************************/
|
|
|
|
std::pair<std::optional<double>, bool>
|
|
GasLiftSingleWellGeneric::OptimizeState::
|
|
addOrSubtractAlqIncrement(double alq)
|
|
{
|
|
auto [alq_opt, limited]
|
|
= this->parent.addOrSubtractAlqIncrement_(alq, this->increase);
|
|
if (!alq_opt) {
|
|
const std::string msg = fmt::format(
|
|
"iteration {}, alq = {} : not able to {} ALQ increment",
|
|
this->it, alq, (this->increase ? "add" : "subtract"));
|
|
}
|
|
return {alq_opt, limited};
|
|
}
|
|
|
|
double
|
|
GasLiftSingleWellGeneric::OptimizeState::
|
|
calcEcoGradient(double oil_rate, double new_oil_rate,
|
|
double gas_rate, double new_gas_rate)
|
|
{
|
|
return this->parent.calcEcoGradient_(oil_rate, new_oil_rate,
|
|
gas_rate, new_gas_rate, this->increase);
|
|
}
|
|
|
|
// NOTE: According to WLIFTOPT item 5 :
|
|
// if min_rate() is negative, it means: allocate at least enough lift gas
|
|
// to enable the well to flow
|
|
// We will interpret this as (see discussion above GasLiftSingleWell()
|
|
// in this file): Allocate at least the amount of lift gas needed to
|
|
// get a positive oil production rate.
|
|
bool
|
|
GasLiftSingleWellGeneric::OptimizeState::
|
|
checkAlqOutsideLimits(double alq, [[maybe_unused]] double oil_rate)
|
|
{
|
|
std::ostringstream ss;
|
|
bool result = false;
|
|
|
|
if (this->increase) {
|
|
if (alq >= this->parent.max_alq_) {
|
|
ss << "ALQ >= " << this->parent.max_alq_ << " (max limit), "
|
|
<< "stopping iteration";
|
|
result = true;
|
|
}
|
|
else { // checking the minimum limit...
|
|
// NOTE: A negative min_alq_ means: allocate at least enough lift gas
|
|
// to enable the well to flow, see WLIFTOPT item 5.
|
|
if (this->parent.min_alq_ < 0) {
|
|
// - if oil rate is negative (i.e. the well is not flowing), continue to
|
|
// increase ALQ (according WLIFTOPT item 5) and try make the well
|
|
// flow.
|
|
// - else if oil rate is already positive, there is no minimum
|
|
// limit for ALQ in this case
|
|
result = false;
|
|
}
|
|
else {
|
|
// NOTE: checking for a lower limit is not necessary
|
|
// when increasing alq. If ALQ was smaller than the minimum when
|
|
// we entered the runOptimizeLoop_() method,
|
|
// increaseALQtoMinALQ_() will ensure that ALQ >= min_alq
|
|
assert(alq >= this->parent.min_alq_ );
|
|
result = false;
|
|
}
|
|
}
|
|
}
|
|
else { // we are decreasing lift gas
|
|
if ( alq == 0 ) {
|
|
ss << "ALQ is zero, cannot decrease further. Stopping iteration.";
|
|
return true;
|
|
}
|
|
else if ( alq < 0 ) {
|
|
ss << "Negative ALQ: " << alq << ". Stopping iteration.";
|
|
return true;
|
|
}
|
|
// NOTE: A negative min_alq_ means: allocate at least enough lift gas
|
|
// to enable the well to flow, see WLIFTOPT item 5.
|
|
if (this->parent.min_alq_ < 0) {
|
|
// We know that the well is flowing (oil_rate > 0) since that was
|
|
// already checked in runOptimizeLoop_() by calling checkNegativeOilRate()
|
|
assert(oil_rate >= 0);
|
|
result = false;
|
|
}
|
|
else {
|
|
if (alq <= this->parent.min_alq_ ) {
|
|
// According to WLIFTOPT item 5:
|
|
// "If a positive value is specified, the well is
|
|
// allocated at least that amount of lift gas,
|
|
// unless the well is unable to flow with that rate
|
|
// of lift gas injection, or unless the well can
|
|
// already meet one of its own rate limits before
|
|
// receiving its minimum lift gas rate."
|
|
//
|
|
// - We already know that the well is flowing, (oil_rate > 0),
|
|
// since that was already checked in runOptimizeLoop_() by calling
|
|
// checkNegativeOilRate().
|
|
// - We also know that the rate limit was not exceeded since that was
|
|
// checked by checkWellRatesViolated()
|
|
assert( oil_rate >= 0);
|
|
ss << "ALQ <= " << this->parent.min_alq_ << " (min limit), "
|
|
<< "stopping iteration";
|
|
result = true;
|
|
}
|
|
else {
|
|
// NOTE: checking for an upper limit should not be necessary
|
|
// when decreasing alq.. so this is just to catch an
|
|
// illegal state at an early point.
|
|
if (alq >= this->parent.max_alq_) {
|
|
warn_( "unexpected: alq above upper limit when trying to "
|
|
"decrease lift gas. aborting iteration.");
|
|
result = true;
|
|
}
|
|
else {
|
|
result = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (this->parent.debug) {
|
|
const std::string msg = ss.str();
|
|
if (!msg.empty())
|
|
this->parent.displayDebugMessage_(msg);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
GasLiftSingleWellGeneric::OptimizeState::
|
|
checkGroupALQrateExceeded(double delta_alq)
|
|
{
|
|
const auto &pairs =
|
|
this->parent.group_info_.getWellGroups(this->parent.well_name_);
|
|
for (const auto &[group_name, efficiency] : pairs) {
|
|
auto max_alq_opt = this->parent.group_info_.maxAlq(group_name);
|
|
if (max_alq_opt) {
|
|
double alq =
|
|
this->parent.group_info_.alqRate(group_name) + efficiency * delta_alq;
|
|
if (alq > *max_alq_opt) {
|
|
if (this->parent.debug) {
|
|
const std::string msg = fmt::format(
|
|
"Group {} : alq {} exceeds max_alq {}. Stopping iteration",
|
|
group_name, alq, *max_alq_opt);
|
|
this->parent.displayDebugMessage_(msg);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool
|
|
GasLiftSingleWellGeneric::OptimizeState::
|
|
checkNegativeOilRate(double oil_rate)
|
|
{
|
|
if (oil_rate < 0) {
|
|
const std::string msg = fmt::format(
|
|
"Negative oil rate {}. Stopping iteration", oil_rate);
|
|
this->parent.displayDebugMessage_(msg);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
GasLiftSingleWellGeneric::OptimizeState::
|
|
checkThpControl()
|
|
{
|
|
const int well_index = this->parent.well_state_.index(this->parent.well_name_).value();
|
|
const Well::ProducerCMode& control_mode = this->parent.well_state_.well(well_index).production_cmode;
|
|
return control_mode == Well::ProducerCMode::THP;
|
|
}
|
|
|
|
//
|
|
// bool checkEcoGradient(double gradient)
|
|
//
|
|
// - Determine if the gradient has reached the limit of the economic gradient.
|
|
//
|
|
// - If we are increasing lift gas, returns true if the gradient is smaller
|
|
// than or equal to the economic gradient,
|
|
//
|
|
// - If we are decreasing lift gas, returns true if the gradient is greater
|
|
// than or equal to the economic gradient. (I.e., we assume too much lift gas
|
|
// is being used and the gradient has become too small. We try to decrease
|
|
// lift gas until the gradient increases and reaches the economic gradient..)
|
|
//
|
|
bool
|
|
GasLiftSingleWellGeneric::OptimizeState::
|
|
checkEcoGradient(double gradient)
|
|
{
|
|
std::ostringstream ss;
|
|
bool result = false;
|
|
|
|
if (this->parent.debug) {
|
|
ss << "checking gradient: " << gradient;
|
|
}
|
|
if (this->increase) {
|
|
if (this->parent.debug) ss << " <= " << this->parent.eco_grad_ << " --> ";
|
|
if (gradient <= this->parent.eco_grad_) {
|
|
if (this->parent.debug) ss << "yes, stopping";
|
|
result = true;
|
|
}
|
|
else {
|
|
if (this->parent.debug) ss << "no, continue";
|
|
}
|
|
}
|
|
else { // decreasing lift gas
|
|
if (this->parent.debug) ss << " >= " << this->parent.eco_grad_ << " --> ";
|
|
if (gradient >= this->parent.eco_grad_) {
|
|
if (this->parent.debug) ss << "yes, stopping";
|
|
result = true;
|
|
}
|
|
else {
|
|
if (this->parent.debug) ss << "no, continue";
|
|
}
|
|
}
|
|
if (this->parent.debug) this->parent.displayDebugMessage_(ss.str());
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
GasLiftSingleWellGeneric::OptimizeState::
|
|
checkRate(double rate, double limit, const std::string& rate_str) const
|
|
{
|
|
if (limit < rate) {
|
|
if (this->parent.debug) {
|
|
const std::string msg = fmt::format(
|
|
"iteration {} : {} rate {} exceeds target {}. Stopping iteration",
|
|
this->it, rate_str, rate, limit);
|
|
this->parent.displayDebugMessage_(msg);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
GasLiftSingleWellGeneric::OptimizeState::
|
|
checkWellRatesViolated(std::vector<double>& potentials)
|
|
{
|
|
auto callback = [*this](double rate, double limit, const std::string& rate_str)
|
|
-> bool
|
|
{ return this->checkRate(rate, limit, rate_str); };
|
|
return this->parent.checkWellRatesViolated_(potentials, callback, this->increase);
|
|
}
|
|
|
|
bool
|
|
GasLiftSingleWellGeneric::OptimizeState::
|
|
computeBhpAtThpLimit(double alq)
|
|
{
|
|
auto bhp_opt = this->parent.computeBhpAtThpLimit_(alq);
|
|
if (bhp_opt) {
|
|
this->bhp = *bhp_opt;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void
|
|
GasLiftSingleWellGeneric::OptimizeState::
|
|
debugShowIterationInfo(double alq)
|
|
{
|
|
const std::string msg = fmt::format("iteration {}, ALQ = {}", this->it, alq);
|
|
this->parent.displayDebugMessage_(msg);
|
|
}
|
|
|
|
|
|
// NOTE: When calculating the gradient, determine what the well would produce if
|
|
// the lift gas injection rate were increased by one increment. The
|
|
// production rates are adjusted if necessary to obey
|
|
// any rate or BHP limits that the well may be subject to. From this
|
|
// information, calculate the well's "weighted incremental
|
|
// gradient"
|
|
//
|
|
// TODO: What does it mean to "adjust the production rates" given a
|
|
// BHP limit?
|
|
//
|
|
double
|
|
GasLiftSingleWellGeneric::OptimizeState::
|
|
getBhpWithLimit()
|
|
{
|
|
auto [new_bhp, limited] = this->parent.getBhpWithLimit_(this->bhp);
|
|
if (limited) {
|
|
// TODO: is it possible that bhp falls below the limit when
|
|
// adding lift gas? I.e. if this->increase == true..
|
|
// TODO: we keep the current alq, but it should probably
|
|
// be adjusted since we changed computed bhp. But how?
|
|
|
|
// Stop iteration, but first check the economic gradient
|
|
// with the bhp_update. If the gradient looks OK (see the
|
|
// main optimize loop) we keep the current ALQ value.
|
|
this->stop_iteration = true;
|
|
}
|
|
return new_bhp;
|
|
}
|
|
|
|
void
|
|
GasLiftSingleWellGeneric::OptimizeState::
|
|
updateGroupRates(double delta_oil, double delta_gas, double delta_water, double delta_alq)
|
|
{
|
|
const auto &pairs =
|
|
this->parent.group_info_.getWellGroups(this->parent.well_name_);
|
|
for (const auto &[group_name, efficiency] : pairs) {
|
|
int idx = this->parent.group_info_.getGroupIdx(group_name);
|
|
this->parent.sync_groups_.insert(idx);
|
|
this->parent.group_info_.update(group_name,
|
|
efficiency * delta_oil, efficiency * delta_gas, efficiency * delta_water, efficiency * delta_alq);
|
|
}
|
|
}
|
|
|
|
|
|
} // namespace Opm
|