mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Merge pull request #3097 from hakonhagland/glift_groups
Implements gas lift optimization for groups.
This commit is contained in:
@@ -51,10 +51,13 @@
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GConSale.hpp>
|
||||
|
||||
#include <opm/simulators/timestepping/SimulatorReport.hpp>
|
||||
#include <opm/simulators/flow/countGlobalCells.hpp>
|
||||
#include <opm/simulators/wells/GasLiftSingleWell.hpp>
|
||||
#include <opm/simulators/wells/GasLiftStage2.hpp>
|
||||
#include <opm/simulators/wells/GasLiftWellState.hpp>
|
||||
#include <opm/simulators/wells/PerforationData.hpp>
|
||||
#include <opm/simulators/wells/VFPInjProperties.hpp>
|
||||
#include <opm/simulators/wells/VFPProdProperties.hpp>
|
||||
#include <opm/simulators/flow/countGlobalCells.hpp>
|
||||
#include <opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp>
|
||||
#include <opm/simulators/wells/RateConverter.hpp>
|
||||
#include <opm/simulators/wells/WellInterface.hpp>
|
||||
@@ -103,6 +106,15 @@ namespace Opm {
|
||||
using SparseMatrixAdapter = GetPropType<TypeTag, Properties::SparseMatrixAdapter>;
|
||||
|
||||
typedef typename Opm::BaseAuxiliaryModule<TypeTag>::NeighborSet NeighborSet;
|
||||
using GasLiftSingleWell = Opm::GasLiftSingleWell<TypeTag>;
|
||||
using GasLiftStage2 = Opm::GasLiftStage2<TypeTag>;
|
||||
using GLiftWellState = Opm::GasLiftWellState<TypeTag>;
|
||||
using GLiftWellStateMap =
|
||||
std::map<std::string,std::unique_ptr<GLiftWellState>>;
|
||||
using GLiftOptWells =
|
||||
std::map<std::string,std::unique_ptr<GasLiftSingleWell>>;
|
||||
using GLiftProdWells =
|
||||
std::map<std::string,const WellInterface<TypeTag> *>;
|
||||
|
||||
static const int numEq = Indices::numEq;
|
||||
static const int solventSaturationIdx = Indices::solventSaturationIdx;
|
||||
@@ -346,6 +358,8 @@ namespace Opm {
|
||||
// Check if well equations is converged.
|
||||
ConvergenceReport getWellConvergence(const std::vector<Scalar>& B_avg, const bool checkGroupConvergence = false) const;
|
||||
|
||||
const PhaseUsage& phaseUsage() const { return phase_usage_; }
|
||||
|
||||
const SimulatorReportSingle& lastReport() const;
|
||||
|
||||
void addWellContributions(SparseMatrixAdapter& jacobian) const
|
||||
@@ -532,6 +546,14 @@ namespace Opm {
|
||||
|
||||
void assembleWellEq(const double dt, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
void maybeDoGasLiftOptimize(Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
void gliftDebugShowALQ(Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
void gasLiftOptimizationStage2(Opm::DeferredLogger& deferred_logger,
|
||||
GLiftProdWells &prod_wells, GLiftOptWells &glift_wells,
|
||||
GLiftWellStateMap &map);
|
||||
|
||||
// some preparation work, mostly related to group control and RESV,
|
||||
// at the beginning of each time step (Not report step)
|
||||
void prepareTimeStep(Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
@@ -306,8 +306,8 @@ namespace Opm {
|
||||
Opm::DeferredLogger local_deferredLogger;
|
||||
|
||||
this->resetWellState();
|
||||
this->wellState().disableGliftOptimization();
|
||||
updateAndCommunicateGroupData();
|
||||
this->wellState().gliftTimeStepInit();
|
||||
const int reportStepIdx = ebosSimulator_.episodeIndex();
|
||||
const double simulationTime = ebosSimulator_.time();
|
||||
std::string exc_msg;
|
||||
@@ -1025,10 +1025,8 @@ namespace Opm {
|
||||
updateWellControls(local_deferredLogger, /* check group controls */ false);
|
||||
}
|
||||
|
||||
gliftDebug("assemble() : running assembleWellEq()..", local_deferredLogger);
|
||||
this->wellState().enableGliftOptimization();
|
||||
maybeDoGasLiftOptimize(local_deferredLogger);
|
||||
assembleWellEq(dt, local_deferredLogger);
|
||||
this->wellState().disableGliftOptimization();
|
||||
} catch (const std::runtime_error& e) {
|
||||
exc_type = ExceptionType::RUNTIME_ERROR;
|
||||
exc_msg = e.what();
|
||||
@@ -1047,13 +1045,67 @@ namespace Opm {
|
||||
last_report_.assemble_time_well += perfTimer.stop();
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
maybeDoGasLiftOptimize(Opm::DeferredLogger& deferred_logger)
|
||||
{
|
||||
this->wellState().enableGliftOptimization();
|
||||
GLiftOptWells glift_wells;
|
||||
GLiftProdWells prod_wells;
|
||||
GLiftWellStateMap state_map;
|
||||
// Stage1: Optimize single wells not checking any group limits
|
||||
for (auto& well : well_container_) {
|
||||
well->gasLiftOptimizationStage1(
|
||||
this->wellState(), ebosSimulator_, deferred_logger,
|
||||
prod_wells, glift_wells, state_map);
|
||||
}
|
||||
gasLiftOptimizationStage2(deferred_logger, prod_wells, glift_wells, state_map);
|
||||
if (this->glift_debug) gliftDebugShowALQ(deferred_logger);
|
||||
this->wellState().disableGliftOptimization();
|
||||
}
|
||||
|
||||
// If a group has any production rate constraints, and/or a limit
|
||||
// on its total rate of lift gas supply, allocate lift gas
|
||||
// preferentially to the wells that gain the most benefit from
|
||||
// it. Lift gas increments are allocated in turn to the well that
|
||||
// currently has the largest weighted incremental gradient. The
|
||||
// procedure takes account of any limits on the group production
|
||||
// rate or lift gas supply applied to any level of group.
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
gasLiftOptimizationStage2(Opm::DeferredLogger& deferred_logger,
|
||||
GLiftProdWells &prod_wells, GLiftOptWells &glift_wells,
|
||||
GLiftWellStateMap &glift_well_state_map)
|
||||
{
|
||||
|
||||
GasLiftStage2 glift {*this, ebosSimulator_, deferred_logger, this->wellState(),
|
||||
prod_wells, glift_wells, glift_well_state_map};
|
||||
glift.runOptimize();
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
gliftDebugShowALQ(Opm::DeferredLogger& deferred_logger)
|
||||
{
|
||||
for (auto& well : this->well_container_) {
|
||||
if (well->isProducer()) {
|
||||
auto alq = this->wellState().getALQ(well->name());
|
||||
const std::string msg = fmt::format("ALQ_REPORT : {} : {}",
|
||||
well->name(), alq);
|
||||
gliftDebug(msg, deferred_logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
assembleWellEq(const double dt, Opm::DeferredLogger& deferred_logger)
|
||||
{
|
||||
for (auto& well : well_container_) {
|
||||
well->maybeDoGasLiftOptimization(this->wellState(), ebosSimulator_, deferred_logger);
|
||||
well->assembleWellEq(ebosSimulator_, dt, this->wellState(), deferred_logger);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_GASLIFT_RUNTIME_HEADER_INCLUDED
|
||||
#define OPM_GASLIFT_RUNTIME_HEADER_INCLUDED
|
||||
|
||||
#include <opm/models/utils/propertysystem.hh>
|
||||
#include <opm/models/utils/parametersystem.hh>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/output/data/Wells.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
|
||||
#include <opm/simulators/utils/DeferredLogger.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.hpp>
|
||||
// NOTE: StandardWell.hpp includes ourself (GasLiftRuntime.hpp), so we need
|
||||
// to forward declare StandardWell for it to be defined in this file.
|
||||
namespace Opm {
|
||||
template<typename TypeTag> class StandardWell;
|
||||
}
|
||||
#include <opm/simulators/wells/StandardWell.hpp>
|
||||
|
||||
#include <opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
template<class TypeTag>
|
||||
class GasLiftRuntime {
|
||||
using Simulator = GetPropType<TypeTag, Properties::Simulator>;
|
||||
using WellState = WellStateFullyImplicitBlackoil;
|
||||
using StdWell = Opm::StandardWell<TypeTag>;
|
||||
// TODO: same definition with WellInterface, and
|
||||
// WellStateFullyImplicitBlackoil eventually they should go
|
||||
// to a common header file.
|
||||
static const int Water = BlackoilPhases::Aqua;
|
||||
static const int Oil = BlackoilPhases::Liquid;
|
||||
static const int Gas = BlackoilPhases::Vapour;
|
||||
struct OptimizeState;
|
||||
public:
|
||||
GasLiftRuntime(
|
||||
const StdWell &std_well,
|
||||
const Simulator &ebos_simulator,
|
||||
const SummaryState &summary_state,
|
||||
DeferredLogger &deferred_logger,
|
||||
WellState &well_state,
|
||||
const Well::ProductionControls &controls
|
||||
);
|
||||
void runOptimize();
|
||||
private:
|
||||
void computeInitialWellRates_();
|
||||
void computeWellRates_(double bhp, std::vector<double> &potentials);
|
||||
void debugShowIterationInfo_(OptimizeState &state, double alq);
|
||||
void debugShowStartIteration_(double alq, bool increase);
|
||||
void displayDebugMessage_(const std::string &msg);
|
||||
void displayWarning_();
|
||||
void displayWarning_(std::string warning);
|
||||
bool getGasRateWithLimit_(double& new_rate, const std::vector<double> &potentials);
|
||||
bool getOilRateWithLimit_(double& new_rate, const std::vector<double> &potentials);
|
||||
void logSuccess_();
|
||||
bool runOptimizeLoop_(bool increase);
|
||||
void setAlqMaxRate_(const GasLiftOpt::Well &well);
|
||||
void setAlqMinRate_(const GasLiftOpt::Well &well);
|
||||
bool tryIncreaseLiftGas_();
|
||||
bool tryDecreaseLiftGas_();
|
||||
void updateWellStateAlqFixedValue_(const GasLiftOpt::Well &well);
|
||||
bool useFixedAlq_(const GasLiftOpt::Well &well);
|
||||
void warnMaxIterationsExceeded_();
|
||||
|
||||
const Well::ProductionControls &controls_;
|
||||
DeferredLogger &deferred_logger_;
|
||||
const Simulator &ebos_simulator_;
|
||||
std::vector<double> potentials_;
|
||||
const StdWell &std_well_;
|
||||
const SummaryState &summary_state_;
|
||||
WellState &well_state_;
|
||||
std::string well_name_;
|
||||
bool debug; // extra debug output
|
||||
|
||||
double alpha_w_;
|
||||
double alpha_g_;
|
||||
double eco_grad_;
|
||||
int gas_pos_;
|
||||
bool has_run_init_ = false;
|
||||
double increment_;
|
||||
double max_alq_;
|
||||
int max_iterations_;
|
||||
double min_alq_;
|
||||
double new_alq_;
|
||||
int oil_pos_;
|
||||
bool optimize_;
|
||||
double orig_alq_;
|
||||
int water_pos_;
|
||||
|
||||
struct OptimizeState {
|
||||
OptimizeState( GasLiftRuntime &parent_, bool increase_ ) :
|
||||
parent(parent_),
|
||||
increase(increase_),
|
||||
it(0),
|
||||
stop_iteration(false),
|
||||
bhp(-1)
|
||||
{}
|
||||
|
||||
GasLiftRuntime &parent;
|
||||
bool increase;
|
||||
int it;
|
||||
bool stop_iteration;
|
||||
double bhp;
|
||||
|
||||
double addOrSubtractAlqIncrement(double alq);
|
||||
double calcGradient(double oil_rate, double new_oil_rate,
|
||||
double gas_rate, double new_gas_rate);
|
||||
bool checkAlqOutsideLimits(double alq, double oil_rate);
|
||||
bool checkEcoGradient(double gradient);
|
||||
bool checkOilRateExceedsTarget(double oil_rate);
|
||||
bool checkRate(double rate, double limit, const std::string rate_str);
|
||||
bool checkWellRatesViolated(std::vector<double> &potentials);
|
||||
bool computeBhpAtThpLimit(double alq);
|
||||
double getBhpWithLimit();
|
||||
void warn_(std::string msg) {parent.displayWarning_(msg);}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#include "GasLiftRuntime_impl.hpp"
|
||||
|
||||
#endif // OPM_GASLIFT_RUNTIME_HEADER_INCLUDED
|
||||
@@ -1,816 +0,0 @@
|
||||
/*
|
||||
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 <opm/simulators/wells/StandardWell.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
|
||||
#include <opm/simulators/utils/DeferredLogger.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>
|
||||
#include <opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
template<typename TypeTag>
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
GasLiftRuntime(
|
||||
const StdWell &std_well,
|
||||
const Simulator &ebos_simulator,
|
||||
const SummaryState &summary_state,
|
||||
DeferredLogger &deferred_logger,
|
||||
WellState &well_state,
|
||||
const Well::ProductionControls &controls
|
||||
) :
|
||||
controls_{controls},
|
||||
deferred_logger_{deferred_logger},
|
||||
ebos_simulator_{ebos_simulator},
|
||||
potentials_(well_state.numPhases(),0.0),
|
||||
std_well_{std_well},
|
||||
summary_state_{summary_state},
|
||||
well_state_{well_state},
|
||||
debug{false} // extra debugging output
|
||||
{
|
||||
int well_index = this->std_well_.indexOfWell();
|
||||
const Well::ProducerCMode& control_mode
|
||||
= well_state_.currentProductionControls()[well_index];
|
||||
if (control_mode != Well::ProducerCMode::THP)
|
||||
throw std::logic_error("Bug in flow - invalid control mode detected\n");
|
||||
const Opm::Schedule& schedule = this->ebos_simulator_.vanguard().schedule();
|
||||
const int report_step_idx = this->ebos_simulator_.episodeIndex();
|
||||
auto ecl_well = this->std_well_.wellEcl();
|
||||
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();
|
||||
auto& gl_well = glo.well(this->well_name_);
|
||||
|
||||
if(useFixedAlq_(gl_well)) {
|
||||
updateWellStateAlqFixedValue_(gl_well);
|
||||
this->optimize_ = false; // lift gas supply is fixed
|
||||
}
|
||||
else {
|
||||
setAlqMaxRate_(gl_well);
|
||||
this->optimize_ = true;
|
||||
}
|
||||
computeInitialWellRates_();
|
||||
|
||||
if(this->optimize_) {
|
||||
setAlqMinRate_(gl_well);
|
||||
// NOTE: According to item 4 in WLIFTOPT, this value does not
|
||||
// have to be positive.
|
||||
// TODO: Does it make sense to have a negative value?
|
||||
this->alpha_w_ = gl_well.weight_factor();
|
||||
if (this->alpha_w_ <= 0 ) {
|
||||
displayWarning_("Nonpositive value for alpha_w ignored");
|
||||
this->alpha_w_ = 1.0;
|
||||
}
|
||||
|
||||
// NOTE: According to item 6 in WLIFTOPT:
|
||||
// "If this value is greater than zero, the incremental gas rate will influence
|
||||
// the calculation of the incremental gradient and may be used
|
||||
// to discourage the allocation of lift gas to wells which produce more gas."
|
||||
// TODO: Does this mean that we should ignore this value if it
|
||||
// is negative?
|
||||
this->alpha_g_ = gl_well.inc_weight_factor();
|
||||
|
||||
const auto& pu = std_well_.phaseUsage();
|
||||
this->oil_pos_ = pu.phase_pos[Oil];
|
||||
this->gas_pos_ = pu.phase_pos[Gas];
|
||||
this->water_pos_ = pu.phase_pos[Water];
|
||||
this->new_alq_ = this->orig_alq_;
|
||||
// TODO: adhoc value.. Should we keep max_iterations_ as a safety measure
|
||||
// or does it not make sense to have it?
|
||||
this->max_iterations_ = 1000;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************
|
||||
* Methods in alphabetical order
|
||||
****************************************/
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
computeInitialWellRates_()
|
||||
{
|
||||
// get the alq value used for this well for the previous time step, or
|
||||
// if gas lift optimization has not been applied to this well yet, the
|
||||
// default value is used.
|
||||
this->orig_alq_ = this->well_state_.getALQ(this->well_name_);
|
||||
// NOTE: compute initial rates with current ALQ
|
||||
this->std_well_.computeWellRatesWithThpAlqProd(
|
||||
this->ebos_simulator_, this->summary_state_, this->deferred_logger_,
|
||||
this->potentials_, this->orig_alq_);
|
||||
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
computeWellRates_(double bhp, std::vector<double> &potentials)
|
||||
{
|
||||
this->std_well_.computeWellRatesWithBhp(
|
||||
this->ebos_simulator_, bhp, potentials, this->deferred_logger_);
|
||||
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
debugShowIterationInfo_(OptimizeState &state, double alq)
|
||||
{
|
||||
const std::string msg = fmt::format("iteration {}, ALQ = {}", state.it, alq);
|
||||
this->displayDebugMessage_(msg);
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
debugShowStartIteration_(double alq, bool increase)
|
||||
{
|
||||
const std::string msg =
|
||||
fmt::format("starting {} iteration, ALQ = {}, oilrate = {}",
|
||||
(increase ? "increase" : "decrease"),
|
||||
alq,
|
||||
-this->potentials_[this->oil_pos_]);
|
||||
this->displayDebugMessage_(msg);
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
displayDebugMessage_(const std::string &msg)
|
||||
{
|
||||
|
||||
const std::string message = fmt::format(
|
||||
" GLIFT (DEBUG) : Well {} : {}", this->well_name_, msg);
|
||||
this->deferred_logger_.info(message);
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
displayWarning_(std::string msg)
|
||||
{
|
||||
const std::string message = fmt::format(
|
||||
"GAS LIFT OPTIMIZATION, WELL {} : {}", this->well_name_, msg);
|
||||
this->deferred_logger_.warning("WARNING", message);
|
||||
}
|
||||
|
||||
// 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.
|
||||
template<typename TypeTag>
|
||||
bool
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
getGasRateWithLimit_(double& new_rate, const std::vector<double> &potentials)
|
||||
{
|
||||
new_rate = -potentials[this->gas_pos_];
|
||||
bool limit = false;
|
||||
if (this->controls_.hasControl(Well::ProducerCMode::GRAT)) {
|
||||
auto target = this->controls_.gas_rate;
|
||||
if (new_rate > target) {
|
||||
new_rate = target;
|
||||
limit = true;
|
||||
}
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
|
||||
|
||||
// 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..
|
||||
template<typename TypeTag>
|
||||
bool
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
getOilRateWithLimit_(double& new_rate, const std::vector<double> &potentials)
|
||||
{
|
||||
new_rate = -potentials[this->oil_pos_];
|
||||
bool limit = false;
|
||||
if (this->controls_.hasControl(Well::ProducerCMode::ORAT)) {
|
||||
auto target = this->controls_.oil_rate;
|
||||
if (new_rate > target) {
|
||||
new_rate = target;
|
||||
limit = true;
|
||||
}
|
||||
}
|
||||
if (this->controls_.hasControl(Well::ProducerCMode::LRAT)) {
|
||||
auto target = this->controls_.liquid_rate;
|
||||
auto oil_rate = -potentials[this->oil_pos_];
|
||||
auto water_rate = -potentials[this->water_pos_];
|
||||
auto liq_rate = oil_rate + water_rate;
|
||||
if (liq_rate > target) {
|
||||
new_rate = oil_rate * (target/liq_rate);
|
||||
limit = true;
|
||||
}
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
logSuccess_()
|
||||
{
|
||||
|
||||
const std::string message = fmt::format(
|
||||
"GLIFT, WELL {} {} ALQ from {} to {}",
|
||||
this->well_name_,
|
||||
((this->new_alq_ > this->orig_alq_) ? "increased" : "decreased"),
|
||||
this->orig_alq_,
|
||||
this->new_alq_);
|
||||
this->deferred_logger_.info(message);
|
||||
}
|
||||
|
||||
/* - At this point we know that this is a production well, and that its current
|
||||
* control mode is THP.
|
||||
*
|
||||
* - We would like to check if it is possible to
|
||||
* 1) increase the oil production by adding lift gas injection to the
|
||||
* well, or if that is not possible, if we 2) should reduce the amount
|
||||
* of lift gas injected due to a too small gain in oil production
|
||||
* (with the current lift gas injection rate)
|
||||
* - For 1) above, we should not add lift gas if it would cause an oil
|
||||
* rate target to be exceeded, and for 2) we should not reduce the
|
||||
* amount of liftgas injected below the minimum lift gas injection
|
||||
* rate.
|
||||
*
|
||||
* NOTE: If reducing or adding lift-gas further would cause
|
||||
* one of the well targets like ORAT, WRAT, GRAT, LRAT, CRAT, RESV, BHP,
|
||||
* to become violated we should stop the lift gas optimization
|
||||
* loop.. and then updateWellControls() will later (hopefully) switch the well's
|
||||
* control mode from THP to the mode of the violated target.
|
||||
*
|
||||
* - Lift gas is added if it is economical viable depending on
|
||||
* the ratio of oil gained compared to the amount of liftgas added.
|
||||
*
|
||||
* - Lift gas supply may be limited.
|
||||
*
|
||||
* - The current value of liftgas for the well is stored in the WellState object.
|
||||
*
|
||||
* - It is assumed that the oil production rate is concave function F
|
||||
* of the amount of lift gas, such that it increases initially due to the
|
||||
* reduced density of the mixture in the tubing. However, as the
|
||||
* lift gas supply is increased further, friction pressure losses in the
|
||||
* tubing become more important, and the production rate peaks and
|
||||
* then starts to decrease.
|
||||
* Since lift gas injection has a price, e.g. compression costs can
|
||||
* be expressed as a cost per unit rate of lift gas injection,
|
||||
* it must be balanced against the value of the extra amount of
|
||||
* oil produced. Thus there is a "minimum economic gradient" of oil
|
||||
* production rate versus lift gas injection rate, at which the
|
||||
* value of the extra amount of oil produced by a small increase in
|
||||
* the lift gas injection rate is equal to the cost of supplying the
|
||||
* extra amount of lift gas. The optimum lift gas injection rate is then somewhat
|
||||
* lower than the peak value.
|
||||
*
|
||||
* Based on this assumption, we know that if the gradient (derivative) of F is
|
||||
* positive, but greater than the economic gradient (assuming the
|
||||
* economic gradient is positive), we should add
|
||||
* lift gas. On the other hand, if the gradient of F is negative or
|
||||
* if it is positive but smaller than the economic gradient, the amount
|
||||
* of lift gas injected should be decreased.
|
||||
*
|
||||
*/
|
||||
template<typename TypeTag>
|
||||
void
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
runOptimize()
|
||||
{
|
||||
if (this->optimize_) {
|
||||
if (!tryIncreaseLiftGas_()) {
|
||||
if (!tryDecreaseLiftGas_()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
logSuccess_();
|
||||
this->well_state_.setALQ(this->well_name_, this->new_alq_);
|
||||
}
|
||||
// NOTE: In addition to the new ALQ value, we also implicitly
|
||||
// return this->potentials_
|
||||
}
|
||||
|
||||
// INPUT:
|
||||
// - increase (boolean) :
|
||||
// - true : try increase the lift gas supply,
|
||||
// - false : try decrease lift gas supply.
|
||||
//
|
||||
// OUTPUT:
|
||||
// - return value: success (true/false)
|
||||
// - potentials_ : updated well potentials if success
|
||||
// - new_alq_ : updated alq if success
|
||||
template<typename TypeTag>
|
||||
bool
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
runOptimizeLoop_(bool increase)
|
||||
{
|
||||
auto cur_potentials = this->potentials_; // make copy, since we may fail..
|
||||
auto oil_rate = -cur_potentials[this->oil_pos_];
|
||||
auto gas_rate = -cur_potentials[this->gas_pos_];
|
||||
bool success = false; // did we succeed to increase alq?
|
||||
auto cur_alq = this->orig_alq_;
|
||||
if (cur_alq == 0 && !increase) // we don't decrease alq below zero
|
||||
return false;
|
||||
|
||||
auto alq = cur_alq;
|
||||
OptimizeState state {*this, increase};
|
||||
if (this->debug) debugShowStartIteration_(alq, increase);
|
||||
while (!state.stop_iteration && (++state.it <= this->max_iterations_)) {
|
||||
if (state.checkWellRatesViolated(cur_potentials)) break;
|
||||
if (state.checkAlqOutsideLimits(alq, oil_rate)) break;
|
||||
alq = state.addOrSubtractAlqIncrement(alq);
|
||||
if(this->debug) debugShowIterationInfo_(state, alq);
|
||||
if (!state.computeBhpAtThpLimit(alq)) break;
|
||||
// NOTE: if BHP is below limit, we set state.stop_iteration = true
|
||||
auto bhp = state.getBhpWithLimit();
|
||||
computeWellRates_(bhp, cur_potentials);
|
||||
double new_oil_rate = 0.0;
|
||||
bool oil_is_limited = getOilRateWithLimit_(new_oil_rate, cur_potentials);
|
||||
if (oil_is_limited && !increase) //if oil is limited we do not want to decrease
|
||||
break;
|
||||
double new_gas_rate = 0.0;
|
||||
bool gas_is_limited = getGasRateWithLimit_(new_gas_rate, cur_potentials);
|
||||
if (gas_is_limited && increase) // if gas is limited we do not want to increase
|
||||
break;
|
||||
auto gradient = state.calcGradient(
|
||||
oil_rate, new_oil_rate, gas_rate, new_gas_rate);
|
||||
if (state.checkEcoGradient(gradient)) {
|
||||
if (state.it == 1) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
state.stop_iteration = true;
|
||||
}
|
||||
}
|
||||
cur_alq = alq;
|
||||
success = true;
|
||||
oil_rate = new_oil_rate;
|
||||
gas_rate = new_gas_rate;
|
||||
}
|
||||
if (state.it > this->max_iterations_) {
|
||||
warnMaxIterationsExceeded_();
|
||||
}
|
||||
if (success) {
|
||||
this->potentials_ = cur_potentials;
|
||||
this->new_alq_ = cur_alq;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
setAlqMaxRate_(const GasLiftOpt::Well &well)
|
||||
{
|
||||
auto& max_alq_optional = well.max_rate();
|
||||
if (max_alq_optional) {
|
||||
// NOTE: To prevent extrapolation of the VFP tables, any value
|
||||
// entered here must not exceed the largest ALQ value in the well's VFP table.
|
||||
this->max_alq_ = *max_alq_optional;
|
||||
}
|
||||
else { // i.e. WLIFTOPT, item 3 has been defaulted
|
||||
// According to the Eclipse manual for WLIFTOPT, item 3:
|
||||
// The default value should be set to the largest ALQ
|
||||
// value in the well's VFP table
|
||||
const auto& table = std_well_.vfp_properties_->getProd()->getTable(
|
||||
this->controls_.vfp_table_number);
|
||||
const auto& alq_values = table.getALQAxis();
|
||||
// Assume the alq_values are sorted in ascending order, so
|
||||
// the last item should be the largest value:
|
||||
this->max_alq_ = alq_values.back();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
bool
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
tryDecreaseLiftGas_()
|
||||
{
|
||||
return runOptimizeLoop_(/*increase=*/ false);
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
bool
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
tryIncreaseLiftGas_()
|
||||
{
|
||||
return runOptimizeLoop_(/*increase=*/ true);
|
||||
}
|
||||
|
||||
|
||||
// Called when we should use a fixed ALQ value
|
||||
template<typename TypeTag>
|
||||
void
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
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.
|
||||
template<typename TypeTag>
|
||||
bool
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
Opm::GasLiftRuntime<TypeTag>::
|
||||
warnMaxIterationsExceeded_()
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "Max iterations (" << this->max_iterations_ << ") exceeded in "
|
||||
<< "gas lift optimization for well " << this->well_name_;
|
||||
deferred_logger_.warning("MAX_ITERATIONS_EXCEEDED", ss.str());
|
||||
}
|
||||
|
||||
/****************************************
|
||||
* Methods declared in OptimizeState
|
||||
****************************************/
|
||||
|
||||
template<typename TypeTag>
|
||||
double
|
||||
Opm::GasLiftRuntime<TypeTag>::OptimizeState::
|
||||
addOrSubtractAlqIncrement(double alq)
|
||||
{
|
||||
if (this->increase) {
|
||||
alq += this->parent.increment_;
|
||||
// NOTE: if max_alq_ was defaulted in WCONPROD, item 3, it has
|
||||
// already been set to the largest value in the VFP table in
|
||||
// the contructor of GasLiftRuntime
|
||||
if (alq > this->parent.max_alq_) alq = this->parent.max_alq_;
|
||||
}
|
||||
else {
|
||||
alq -= this->parent.increment_;
|
||||
if (this->parent.min_alq_ > 0) {
|
||||
if (alq < this->parent.min_alq_) alq = this->parent.min_alq_;
|
||||
}
|
||||
}
|
||||
return alq;
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
double
|
||||
Opm::GasLiftRuntime<TypeTag>::OptimizeState::
|
||||
calcGradient(double oil_rate, double new_oil_rate, double gas_rate, double new_gas_rate)
|
||||
{
|
||||
auto dqo = new_oil_rate - oil_rate;
|
||||
auto dqg = new_gas_rate - gas_rate;
|
||||
// TODO: Should we do any error checks on the calculation of the
|
||||
// gradient?
|
||||
// NOTE: The eclipse techincal description (chapter 25) says:
|
||||
// "The gas rate term in the denominator is subject to the
|
||||
// constraint alpha_g_ * dqg >= 0.0"
|
||||
auto gradient = (this->parent.alpha_w_ * dqo) /
|
||||
(this->parent.increment_ + this->parent.alpha_g_*dqg);
|
||||
return gradient;
|
||||
}
|
||||
|
||||
// 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 GasLiftRuntime()
|
||||
// in this file): Allocate at least the amount of lift gas needed to
|
||||
// get a positive oil production rate.
|
||||
template<typename TypeTag>
|
||||
bool
|
||||
Opm::GasLiftRuntime<TypeTag>::OptimizeState::
|
||||
checkAlqOutsideLimits(double alq, 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 {
|
||||
// NOTE: A negative min_alq_ means: allocate at least enough lift gas
|
||||
// to enable the well to flow, see WCONPROD item 5.
|
||||
if (this->parent.min_alq_ < 0) {
|
||||
result = false;
|
||||
}
|
||||
else {
|
||||
// NOTE: checking for a lower limit should not be necessary
|
||||
// when increasing alq.. so this is just to catch an
|
||||
// illegal state at an early point.
|
||||
if (alq < this->parent.min_alq_ ) {
|
||||
warn_("unexpected: alq below lower limit when trying to "
|
||||
"increase lift gas. aborting iteration.");
|
||||
result = true;
|
||||
}
|
||||
else {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // we are decreasing lift gas
|
||||
// NOTE: A negative min_alq_ means: allocate at least enough lift gas
|
||||
// to enable the well to flow, see WCONPROD item 5.
|
||||
if (this->parent.min_alq_ < 0) {
|
||||
// If the oil rate is already zero or negative (non-flowing well)
|
||||
// we assume we will not be able to increase it by decreasing the lift gas
|
||||
if ( oil_rate <= 0 ) {
|
||||
ss << "Oil rate ( " << oil_rate << " ) <= 0 when decreasing lift gas. "
|
||||
<< "We will not be able to make this well flowing by decreasing "
|
||||
<< "lift gas, stopping iteration.";
|
||||
result = true;
|
||||
}
|
||||
else {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (alq <= this->parent.min_alq_ ) {
|
||||
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) this->parent.displayDebugMessage_(ss.str());
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
bool
|
||||
Opm::GasLiftRuntime<TypeTag>::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 << "true";
|
||||
result = true;
|
||||
}
|
||||
else {
|
||||
if (this->parent.debug) ss << "false";
|
||||
}
|
||||
}
|
||||
else { // decreasing lift gas
|
||||
if (this->parent.debug) ss << " >= " << this->parent.eco_grad_ << " --> ";
|
||||
if (gradient >= this->parent.eco_grad_) {
|
||||
if (this->parent.debug) ss << "true";
|
||||
result = true;
|
||||
}
|
||||
else {
|
||||
if (this->parent.debug) ss << "false";
|
||||
}
|
||||
}
|
||||
if (this->parent.debug) this->parent.displayDebugMessage_(ss.str());
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
bool
|
||||
Opm::GasLiftRuntime<TypeTag>::OptimizeState::
|
||||
checkRate(double rate, double limit, const std::string rate_str)
|
||||
{
|
||||
|
||||
if (limit < rate ) {
|
||||
if (this->parent.debug) {
|
||||
const std::string msg = fmt::format(
|
||||
"iteration {} : rate {} exceeds target rate {}. Stopping iteration",
|
||||
this->it, rate_str, rate, limit);
|
||||
this->parent.displayDebugMessage_(msg);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
bool
|
||||
Opm::GasLiftRuntime<TypeTag>::OptimizeState::
|
||||
checkWellRatesViolated(std::vector<double> &potentials)
|
||||
{
|
||||
if (this->parent.controls_.hasControl(Well::ProducerCMode::ORAT)) {
|
||||
auto oil_rate = -potentials[this->parent.oil_pos_];
|
||||
if (this->checkRate(oil_rate, this->parent.controls_.oil_rate, "oil"))
|
||||
return true;
|
||||
}
|
||||
if (this->parent.controls_.hasControl(Well::ProducerCMode::WRAT)) {
|
||||
auto water_rate = -potentials[this->parent.water_pos_];
|
||||
if (this->checkRate(water_rate, this->parent.controls_.water_rate, "water"))
|
||||
return true;
|
||||
}
|
||||
if (this->parent.controls_.hasControl(Well::ProducerCMode::GRAT)) {
|
||||
auto gas_rate = -potentials[this->parent.gas_pos_];
|
||||
if (this->checkRate(gas_rate, this->parent.controls_.gas_rate, "gas"))
|
||||
return true;
|
||||
}
|
||||
if (this->parent.controls_.hasControl(Well::ProducerCMode::LRAT)) {
|
||||
auto oil_rate = -potentials[this->parent.oil_pos_];
|
||||
auto water_rate = -potentials[this->parent.water_pos_];
|
||||
auto liq_rate = oil_rate + water_rate;
|
||||
if (this->checkRate(liq_rate, this->parent.controls_.liquid_rate, "liquid"))
|
||||
return true;
|
||||
}
|
||||
// TODO: Also check RESV, see checkIndividualContraints() in
|
||||
// WellInterface_impl.hpp
|
||||
// TODO: Check group contraints?
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename TypeTag>
|
||||
bool
|
||||
Opm::GasLiftRuntime<TypeTag>::OptimizeState::
|
||||
computeBhpAtThpLimit(double alq)
|
||||
{
|
||||
auto bhp_at_thp_limit = this->parent.std_well_.computeBhpAtThpLimitProdWithAlq(
|
||||
this->parent.ebos_simulator_,
|
||||
this->parent.summary_state_,
|
||||
this->parent.deferred_logger_,
|
||||
alq);
|
||||
if (!bhp_at_thp_limit) {
|
||||
const std::string msg = fmt::format(
|
||||
"Failed in getting converged bhp potential for well {}",
|
||||
this->parent.well_name_);
|
||||
this->parent.deferred_logger_.warning(
|
||||
"FAILURE_GETTING_CONVERGED_POTENTIAL", msg);
|
||||
return false;
|
||||
}
|
||||
this->bhp = *bhp_at_thp_limit;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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?
|
||||
//
|
||||
template<typename TypeTag>
|
||||
double
|
||||
Opm::GasLiftRuntime<TypeTag>::OptimizeState::
|
||||
getBhpWithLimit()
|
||||
{
|
||||
auto bhp_update = this->bhp;
|
||||
if (this->parent.controls_.hasControl(Well::ProducerCMode::BHP)) {
|
||||
auto limit = this->parent.controls_.bhp_limit;
|
||||
// TODO: is it possible that bhp falls below the limit when
|
||||
// adding lift gas? I.e. if this->increase == true..
|
||||
if (this->bhp < limit) {
|
||||
// TODO: we keep the current alq, but it should probably
|
||||
// be adjusted since we changed computed bhp. But how?
|
||||
bhp_update = limit;
|
||||
// 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 bhp_update;
|
||||
}
|
||||
235
opm/simulators/wells/GasLiftSingleWell.hpp
Normal file
235
opm/simulators/wells/GasLiftSingleWell.hpp
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_GASLIFT_SINGLE_WELL_HEADER_INCLUDED
|
||||
#define OPM_GASLIFT_SINGLE_WELL_HEADER_INCLUDED
|
||||
|
||||
#include <opm/models/utils/propertysystem.hh>
|
||||
#include <opm/models/utils/parametersystem.hh>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/output/data/Wells.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
|
||||
#include <opm/simulators/utils/DeferredLogger.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.hpp>
|
||||
// NOTE: StandardWell.hpp includes ourself (GasLiftSingleWell.hpp), so we need
|
||||
// to forward declare StandardWell for it to be defined in this file.
|
||||
namespace Opm {
|
||||
template<typename TypeTag> class StandardWell;
|
||||
}
|
||||
#include <opm/simulators/wells/StandardWell.hpp>
|
||||
|
||||
#include <opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/simulators/wells/GasLiftWellState.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
template<class TypeTag>
|
||||
class GasLiftSingleWell
|
||||
{
|
||||
using Simulator = GetPropType<TypeTag, Properties::Simulator>;
|
||||
using WellState = WellStateFullyImplicitBlackoil;
|
||||
using StdWell = Opm::StandardWell<TypeTag>;
|
||||
using GLiftWellState = Opm::GasLiftWellState<TypeTag>;
|
||||
// TODO: same definition with WellInterface, and
|
||||
// WellStateFullyImplicitBlackoil eventually they should go
|
||||
// to a common header file.
|
||||
static const int Water = BlackoilPhases::Aqua;
|
||||
static const int Oil = BlackoilPhases::Liquid;
|
||||
static const int Gas = BlackoilPhases::Vapour;
|
||||
static constexpr double ALQ_EPSILON = 1e-8;
|
||||
struct OptimizeState;
|
||||
class Stage2State;
|
||||
public:
|
||||
GasLiftSingleWell(
|
||||
const StdWell &std_well,
|
||||
const Simulator &ebos_simulator,
|
||||
const SummaryState &summary_state,
|
||||
DeferredLogger &deferred_logger,
|
||||
WellState &well_state
|
||||
);
|
||||
struct GradInfo;
|
||||
std::optional<GradInfo> calcIncOrDecGradient(
|
||||
double oil_rate, double gas_rate, double alq, bool increase) const;
|
||||
std::pair<double, double> getStage2Rates();
|
||||
const WellInterface<TypeTag> &getStdWell() const { return std_well_; }
|
||||
bool hasStage2Rates();
|
||||
std::unique_ptr<GLiftWellState> runOptimize();
|
||||
const std::string& name() const {return well_name_; }
|
||||
void updateStage2State(const GradInfo &gi, bool increase);
|
||||
|
||||
struct GradInfo
|
||||
{
|
||||
GradInfo() { }
|
||||
|
||||
GradInfo(double grad_, double new_oil_rate_, bool oil_is_limited_,
|
||||
double new_gas_rate_, bool gas_is_limited_,
|
||||
double alq_, bool alq_is_limited_) :
|
||||
grad{grad_},
|
||||
new_oil_rate{new_oil_rate_},
|
||||
oil_is_limited{oil_is_limited_},
|
||||
new_gas_rate{new_gas_rate_},
|
||||
gas_is_limited{gas_is_limited_},
|
||||
alq{alq_},
|
||||
alq_is_limited{alq_is_limited_} {}
|
||||
double grad;
|
||||
double new_oil_rate;
|
||||
bool oil_is_limited;
|
||||
double new_gas_rate;
|
||||
bool gas_is_limited;
|
||||
double alq;
|
||||
bool alq_is_limited;
|
||||
};
|
||||
|
||||
private:
|
||||
std::pair<std::optional<double>, bool> addOrSubtractAlqIncrement_(
|
||||
double alq, bool increase) const;
|
||||
double calcEcoGradient_(double oil_rate, double new_oil_rate,
|
||||
double gas_rate, double new_gas_rate, bool increase) const;
|
||||
bool checkALQequal_(double alq1, double alq2) const;
|
||||
bool checkInitialALQmodified_(double alq, double initial_alq) const;
|
||||
bool checkWellRatesViolated_(
|
||||
std::vector<double> &potentials,
|
||||
const std::function<bool(double, double, const std::string &)> &callback,
|
||||
bool increase
|
||||
);
|
||||
std::optional<double> computeBhpAtThpLimit_(double alq) const;
|
||||
bool computeInitialWellRates_(std::vector<double> &potentials);
|
||||
void computeWellRates_(
|
||||
double bhp, std::vector<double> &potentials, bool debug_output=true) const;
|
||||
void 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;
|
||||
void debugShowAlqIncreaseDecreaseCounts_();
|
||||
void debugShowBhpAlqTable_();
|
||||
void debugShowStartIteration_(double alq, bool increase, double oil_rate);
|
||||
void debugShowTargets_();
|
||||
void displayDebugMessage_(const std::string &msg) const;
|
||||
void displayWarning_(std::string warning);
|
||||
std::pair<double, bool> getBhpWithLimit_(double bhp) const;
|
||||
std::pair<double, bool> getGasRateWithLimit_(
|
||||
const std::vector<double> &potentials) const;
|
||||
std::tuple<double,double,bool,bool> getInitialRatesWithLimit_(
|
||||
const std::vector<double> &potentials);
|
||||
std::pair<double, bool> getOilRateWithLimit_(
|
||||
const std::vector<double> &potentials) const;
|
||||
std::tuple<double,double,bool,bool,double>
|
||||
increaseALQtoPositiveOilRate_(double alq, double oil_rate, double gas_rate,
|
||||
bool oil_is_limited, bool gas_is_limited, std::vector<double> &potentials);
|
||||
std::tuple<double,double,bool,bool,double>
|
||||
increaseALQtoMinALQ_(double alq, double oil_rate, double gas_rate,
|
||||
bool oil_is_limited, bool gas_is_limited, std::vector<double> &potentials);
|
||||
void logSuccess_(double alq);
|
||||
std::tuple<double,double,bool,bool,double>
|
||||
reduceALQtoOilTarget_(double alq, double oil_rate, double gas_rate,
|
||||
bool oil_is_limited, bool gas_is_limited, std::vector<double> &potentials);
|
||||
|
||||
std::unique_ptr<GLiftWellState> runOptimize1_();
|
||||
std::unique_ptr<GLiftWellState> runOptimize2_();
|
||||
std::unique_ptr<GLiftWellState> runOptimizeLoop_(bool increase);
|
||||
void setAlqMaxRate_(const GasLiftOpt::Well &well);
|
||||
void setAlqMinRate_(const GasLiftOpt::Well &well);
|
||||
std::unique_ptr<GLiftWellState> tryIncreaseLiftGas_();
|
||||
std::unique_ptr<GLiftWellState> tryDecreaseLiftGas_();
|
||||
void updateWellStateAlqFixedValue_(const GasLiftOpt::Well &well);
|
||||
bool useFixedAlq_(const GasLiftOpt::Well &well);
|
||||
void warnMaxIterationsExceeded_();
|
||||
|
||||
DeferredLogger &deferred_logger_;
|
||||
const Simulator &ebos_simulator_;
|
||||
const StdWell &std_well_;
|
||||
const SummaryState &summary_state_;
|
||||
WellState &well_state_;
|
||||
std::string well_name_;
|
||||
const Well &ecl_well_;
|
||||
const Well::ProductionControls controls_;
|
||||
int num_phases_;
|
||||
bool debug; // extra debug output
|
||||
bool debug_limit_increase_decrease_;
|
||||
bool debug_abort_if_decrease_and_oil_is_limited_ = false;
|
||||
bool debug_abort_if_increase_and_gas_is_limited_ = false;
|
||||
|
||||
double alpha_w_;
|
||||
double alpha_g_;
|
||||
double eco_grad_;
|
||||
int gas_pos_;
|
||||
bool has_run_init_ = false;
|
||||
double increment_;
|
||||
double max_alq_;
|
||||
int max_iterations_;
|
||||
double min_alq_;
|
||||
int oil_pos_;
|
||||
bool optimize_;
|
||||
double orig_alq_;
|
||||
int water_pos_;
|
||||
|
||||
struct OptimizeState
|
||||
{
|
||||
OptimizeState( GasLiftSingleWell &parent_, bool increase_ ) :
|
||||
parent{parent_},
|
||||
increase{increase_},
|
||||
it{0},
|
||||
stop_iteration{false},
|
||||
bhp{-1}
|
||||
{}
|
||||
|
||||
GasLiftSingleWell &parent;
|
||||
bool increase;
|
||||
int it;
|
||||
bool stop_iteration;
|
||||
double bhp;
|
||||
|
||||
std::pair<std::optional<double>,bool> addOrSubtractAlqIncrement(double alq);
|
||||
double calcEcoGradient(double oil_rate, double new_oil_rate,
|
||||
double gas_rate, double new_gas_rate);
|
||||
bool checkAlqOutsideLimits(double alq, double oil_rate);
|
||||
bool checkEcoGradient(double gradient);
|
||||
bool checkNegativeOilRate(double oil_rate);
|
||||
bool checkOilRateExceedsTarget(double oil_rate);
|
||||
bool checkRate(double rate, double limit, const std::string &rate_str) const;
|
||||
bool checkWellRatesViolated(std::vector<double> &potentials);
|
||||
bool computeBhpAtThpLimit(double alq);
|
||||
void debugShowIterationInfo(double alq);
|
||||
double getBhpWithLimit();
|
||||
void warn_(std::string msg) {parent.displayWarning_(msg);}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#include "GasLiftSingleWell_impl.hpp"
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
|
||||
#endif // OPM_GASLIFT_SINGLE_WELL_HEADER_INCLUDED
|
||||
1383
opm/simulators/wells/GasLiftSingleWell_impl.hpp
Normal file
1383
opm/simulators/wells/GasLiftSingleWell_impl.hpp
Normal file
File diff suppressed because it is too large
Load Diff
225
opm/simulators/wells/GasLiftStage2.hpp
Normal file
225
opm/simulators/wells/GasLiftStage2.hpp
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_GASLIFT_STAGE2_HEADER_INCLUDED
|
||||
#define OPM_GASLIFT_STAGE2_HEADER_INCLUDED
|
||||
|
||||
#include <ebos/eclproblem.hh>
|
||||
#include <opm/models/utils/propertysystem.hh>
|
||||
#include <opm/models/utils/parametersystem.hh>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/output/data/Wells.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.hpp>
|
||||
#include <opm/simulators/wells/StandardWell.hpp>
|
||||
#include <opm/simulators/wells/GasLiftSingleWell.hpp>
|
||||
#include <opm/simulators/wells/GasLiftWellState.hpp>
|
||||
#include <opm/simulators/utils/DeferredLogger.hpp>
|
||||
#include <opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp>
|
||||
// NOTE: BlackoilWellModel.hpp includes ourself (GasLiftStage2.hpp), so we need
|
||||
// to forward declare BlackoilWellModel for it to be defined in this file.
|
||||
namespace Opm {
|
||||
template<typename TypeTag> class BlackoilWellModel;
|
||||
}
|
||||
#include <opm/simulators/wells/BlackoilWellModel.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
template<class TypeTag>
|
||||
class GasLiftStage2 {
|
||||
using Simulator = GetPropType<TypeTag, Properties::Simulator>;
|
||||
using WellState = WellStateFullyImplicitBlackoil;
|
||||
using BlackoilWellModel = Opm::BlackoilWellModel<TypeTag>;
|
||||
using GasLiftSingleWell = Opm::GasLiftSingleWell<TypeTag>;
|
||||
using GLiftWellState = Opm::GasLiftWellState<TypeTag>;
|
||||
using GLiftOptWells = typename BlackoilWellModel::GLiftOptWells;
|
||||
using GLiftProdWells = typename BlackoilWellModel::GLiftProdWells;
|
||||
using GLiftWellStateMap = typename BlackoilWellModel::GLiftWellStateMap;
|
||||
using GradPair = std::pair<std::string, double>;
|
||||
using GradPairItr = std::vector<GradPair>::iterator;
|
||||
using GradInfo = typename GasLiftSingleWell::GradInfo;
|
||||
using GradMap = std::map<std::string, GradInfo>;
|
||||
static const int Water = BlackoilPhases::Aqua;
|
||||
static const int Oil = BlackoilPhases::Liquid;
|
||||
static const int Gas = BlackoilPhases::Vapour;
|
||||
public:
|
||||
GasLiftStage2(
|
||||
const BlackoilWellModel &well_model,
|
||||
const Simulator &ebos_simulator,
|
||||
DeferredLogger &deferred_logger,
|
||||
WellState &well_state,
|
||||
GLiftProdWells &prod_wells,
|
||||
GLiftOptWells &glift_wells,
|
||||
GLiftWellStateMap &state_map
|
||||
);
|
||||
void runOptimize();
|
||||
private:
|
||||
void addOrRemoveALQincrement_(
|
||||
GradMap &grad_map, const std::string well_name, bool add);
|
||||
std::optional<GradInfo> calcIncOrDecGrad_(
|
||||
const std::string name, const GasLiftSingleWell &gs_well, bool increase);
|
||||
bool checkRateAlreadyLimited_(GLiftWellState &state, bool increase);
|
||||
GradInfo deleteDecGradItem_(const std::string &name);
|
||||
GradInfo deleteIncGradItem_(const std::string &name);
|
||||
GradInfo deleteGrad_(const std::string &name, bool increase);
|
||||
void displayDebugMessage_(const std::string &msg);
|
||||
void displayDebugMessage2B_(const std::string &msg);
|
||||
void displayDebugMessage_(const std::string &msg, const std::string &group_name);
|
||||
void displayWarning_(const std::string &msg, const std::string &group_name);
|
||||
void displayWarning_(const std::string &msg);
|
||||
std::tuple<double, double, double> getCurrentGroupRates_(
|
||||
const Opm::Group &group);
|
||||
std::tuple<double, double, double> getCurrentGroupRatesRecursive_(
|
||||
const Opm::Group &group);
|
||||
std::tuple<double, double, double> getCurrentWellRates_(
|
||||
const std::string &well_name, const std::string &group_name);
|
||||
std::vector<GasLiftSingleWell *> getGroupGliftWells_(
|
||||
const Opm::Group &group);
|
||||
void getGroupGliftWellsRecursive_(
|
||||
const Opm::Group &group, std::vector<GasLiftSingleWell *> &wells);
|
||||
std::pair<double, double> getStdWellRates_(const WellInterface<TypeTag> &well);
|
||||
void optimizeGroup_(const Opm::Group &group);
|
||||
void optimizeGroupsRecursive_(const Opm::Group &group);
|
||||
void recalculateGradientAndUpdateData_(
|
||||
GradPairItr &grad_itr, bool increase,
|
||||
std::vector<GradPair> &grads, std::vector<GradPair> &other_grads);
|
||||
void redistributeALQ_(
|
||||
std::vector<GasLiftSingleWell *> &wells, const Opm::Group &group,
|
||||
std::vector<GradPair> &inc_grads, std::vector<GradPair> &dec_grads);
|
||||
void removeSurplusALQ_(
|
||||
const Opm::Group &group,
|
||||
std::vector<GradPair> &inc_grads, std::vector<GradPair> &dec_grads);
|
||||
void saveGrad_(GradMap &map, const std::string &name, GradInfo &grad);
|
||||
void saveDecGrad_(const std::string &name, GradInfo &grad);
|
||||
void saveIncGrad_(const std::string &name, GradInfo &grad);
|
||||
void sortGradients_(std::vector<GradPair> &grads);
|
||||
std::optional<GradInfo> updateGrad_(
|
||||
const std::string &name, GradInfo &grad, bool increase);
|
||||
void updateGradVector_(
|
||||
const std::string &name, std::vector<GradPair> &grads, double grad);
|
||||
|
||||
DeferredLogger &deferred_logger_;
|
||||
const Simulator &ebos_simulator_;
|
||||
const BlackoilWellModel &well_model_;
|
||||
WellState &well_state_;
|
||||
GLiftProdWells &prod_wells_;
|
||||
GLiftOptWells &stage1_wells_;
|
||||
GLiftWellStateMap &well_state_map_;
|
||||
|
||||
int report_step_idx_;
|
||||
const SummaryState &summary_state_;
|
||||
const Schedule &schedule_;
|
||||
const PhaseUsage &phase_usage_;
|
||||
const GasLiftOpt& glo_;
|
||||
GradMap inc_grads_;
|
||||
GradMap dec_grads_;
|
||||
bool debug_;
|
||||
int max_iterations_ = 1000;
|
||||
//int time_step_idx_;
|
||||
int nonlinear_iteration_idx_;
|
||||
|
||||
struct OptimizeState {
|
||||
OptimizeState( GasLiftStage2 &parent_, const Opm::Group &group_ ) :
|
||||
parent{parent_},
|
||||
group{group_},
|
||||
it{0}
|
||||
{}
|
||||
GasLiftStage2 &parent;
|
||||
const Opm::Group &group;
|
||||
int it;
|
||||
|
||||
using GradInfo = typename GasLiftStage2::GradInfo;
|
||||
using GradPair = typename GasLiftStage2::GradPair;
|
||||
using GradPairItr = typename GasLiftStage2::GradPairItr;
|
||||
using GradMap = typename GasLiftStage2::GradMap;
|
||||
void calculateEcoGradients(std::vector<GasLiftSingleWell *> &wells,
|
||||
std::vector<GradPair> &inc_grads, std::vector<GradPair> &dec_grads);
|
||||
bool checkAtLeastTwoWells(std::vector<GasLiftSingleWell *> &wells);
|
||||
void debugShowIterationInfo();
|
||||
std::pair<std::optional<GradPairItr>,std::optional<GradPairItr>>
|
||||
getEcoGradients(
|
||||
std::vector<GradPair> &inc_grads, std::vector<GradPair> &dec_grads);
|
||||
void recalculateGradients(
|
||||
std::vector<GradPair> &inc_grads, std::vector<GradPair> &dec_grads,
|
||||
GradPairItr &min_dec_grad_itr, GradPairItr &max_inc_grad_itr);
|
||||
void redistributeALQ( GradPairItr &min_dec_grad, GradPairItr &max_inc_grad);
|
||||
|
||||
private:
|
||||
void displayDebugMessage_(const std::string &msg);
|
||||
void displayWarning_(const std::string &msg);
|
||||
|
||||
};
|
||||
|
||||
struct SurplusState {
|
||||
SurplusState( GasLiftStage2 &parent_, const Opm::Group &group_,
|
||||
double oil_rate_, double gas_rate_, double alq_, double min_eco_grad_,
|
||||
double oil_target_, double gas_target_,
|
||||
std::optional<double> max_glift_) :
|
||||
parent{parent_},
|
||||
group{group_},
|
||||
oil_rate{oil_rate_},
|
||||
gas_rate{gas_rate_},
|
||||
alq{alq_},
|
||||
min_eco_grad{min_eco_grad_},
|
||||
oil_target{oil_target_},
|
||||
gas_target{gas_target_},
|
||||
max_glift{max_glift_},
|
||||
it{0}
|
||||
{}
|
||||
GasLiftStage2 &parent;
|
||||
const Opm::Group &group;
|
||||
double oil_rate;
|
||||
double gas_rate;
|
||||
double alq;
|
||||
const double min_eco_grad;
|
||||
const double oil_target;
|
||||
const double gas_target;
|
||||
std::optional<double> max_glift;
|
||||
int it;
|
||||
|
||||
void addOrRemoveALQincrement(
|
||||
GradMap &grad_map, const std::string well_name, bool add);
|
||||
bool checkALQlimit();
|
||||
bool checkEcoGradient(const std::string &well_name, double eco_grad);
|
||||
bool checkGasTarget();
|
||||
bool checkOilTarget();
|
||||
void updateRates(const std::string &name, const GradInfo &gi);
|
||||
private:
|
||||
};
|
||||
};
|
||||
|
||||
#include "GasLiftStage2_impl.hpp"
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_GASLIFT_STAGE2_HEADER_INCLUDED
|
||||
1026
opm/simulators/wells/GasLiftStage2_impl.hpp
Normal file
1026
opm/simulators/wells/GasLiftStage2_impl.hpp
Normal file
File diff suppressed because it is too large
Load Diff
82
opm/simulators/wells/GasLiftWellState.hpp
Normal file
82
opm/simulators/wells/GasLiftWellState.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_GASLIFT_WELL_STATE_HEADER_INCLUDED
|
||||
#define OPM_GASLIFT_WELL_STATE_HEADER_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
template<class TypeTag>
|
||||
class GasLiftWellState
|
||||
{
|
||||
public:
|
||||
//GasLiftWellState() { }
|
||||
GasLiftWellState(double oil_rate, bool oil_is_limited,
|
||||
double gas_rate, bool gas_is_limited,
|
||||
double alq, bool alq_is_limited, std::optional<bool> increase) :
|
||||
oil_rate_{oil_rate},
|
||||
oil_is_limited_{oil_is_limited},
|
||||
gas_rate_{gas_rate},
|
||||
gas_is_limited_{gas_is_limited},
|
||||
alq_{alq},
|
||||
alq_is_limited_{alq_is_limited},
|
||||
increase_{increase}
|
||||
{}
|
||||
double alq() const { return alq_; }
|
||||
bool alqChanged() { return increase_.has_value(); }
|
||||
bool alqIsLimited() const { return alq_is_limited_; }
|
||||
bool gasIsLimited() const { return gas_is_limited_; }
|
||||
double gasRate() const { return gas_rate_; }
|
||||
std::pair<double, double> getRates() { return {oil_rate_, gas_rate_}; }
|
||||
std::optional<bool> increase() const { return increase_; }
|
||||
bool oilIsLimited() const { return oil_is_limited_; }
|
||||
double oilRate() const { return oil_rate_; }
|
||||
void update(double oil_rate, bool oil_is_limited,
|
||||
double gas_rate, bool gas_is_limited,
|
||||
double alq, bool alq_is_limited,
|
||||
bool increase)
|
||||
{
|
||||
oil_rate_ = oil_rate;
|
||||
oil_is_limited_ = oil_is_limited;
|
||||
gas_rate_ = gas_rate;
|
||||
gas_is_limited_ = gas_is_limited;
|
||||
alq_ = alq;
|
||||
alq_is_limited_ = alq_is_limited;
|
||||
increase_ = increase;
|
||||
}
|
||||
private:
|
||||
double oil_rate_;
|
||||
bool oil_is_limited_;
|
||||
double gas_rate_;
|
||||
bool gas_is_limited_;
|
||||
double alq_;
|
||||
bool alq_is_limited_;
|
||||
std::optional<bool> increase_;
|
||||
};
|
||||
|
||||
#include "GasLiftWellState_impl.hpp"
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_GASLIFT_WELL_STATE_HEADER_INCLUDED
|
||||
18
opm/simulators/wells/GasLiftWellState_impl.hpp
Normal file
18
opm/simulators/wells/GasLiftWellState_impl.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
@@ -45,6 +45,10 @@ namespace Opm
|
||||
using typename Base::RateConverterType;
|
||||
using typename Base::SparseMatrixAdapter;
|
||||
using typename Base::FluidState;
|
||||
using typename Base::GasLiftSingleWell;
|
||||
using typename Base::GLiftProdWells;
|
||||
using typename Base::GLiftOptWells;
|
||||
using typename Base::GLiftWellStateMap;
|
||||
|
||||
/// the number of reservior equations
|
||||
using Base::numEq;
|
||||
@@ -120,10 +124,13 @@ namespace Opm
|
||||
|
||||
virtual void initPrimaryVariablesEvaluation() const override;
|
||||
|
||||
virtual void maybeDoGasLiftOptimization (
|
||||
virtual void gasLiftOptimizationStage1 (
|
||||
WellState&,
|
||||
const Simulator&,
|
||||
DeferredLogger&
|
||||
DeferredLogger&,
|
||||
GLiftProdWells &,
|
||||
GLiftOptWells &,
|
||||
GLiftWellStateMap &
|
||||
) const override {
|
||||
// Not implemented yet
|
||||
}
|
||||
|
||||
@@ -27,11 +27,13 @@
|
||||
#include <opm/simulators/linalg/bda/WellContributions.hpp>
|
||||
#endif
|
||||
|
||||
#include <opm/simulators/wells/GasLiftRuntime.hpp>
|
||||
#include <opm/simulators/wells/RateConverter.hpp>
|
||||
#include <opm/simulators/wells/VFPInjProperties.hpp>
|
||||
#include <opm/simulators/wells/VFPProdProperties.hpp>
|
||||
#include <opm/simulators/wells/WellInterface.hpp>
|
||||
#include <opm/simulators/wells/WellProdIndexCalculator.hpp>
|
||||
#include <opm/simulators/wells/ParallelWellInfo.hpp>
|
||||
#include <opm/simulators/wells/GasLiftSingleWell.hpp>
|
||||
|
||||
#include <opm/models/blackoil/blackoilpolymermodules.hh>
|
||||
#include <opm/models/blackoil/blackoilsolventmodules.hh>
|
||||
@@ -46,6 +48,7 @@
|
||||
#include <dune/common/dynvector.hh>
|
||||
#include <dune/common/dynmatrix.hh>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -73,7 +76,10 @@ namespace Opm
|
||||
using typename Base::SparseMatrixAdapter;
|
||||
using typename Base::FluidState;
|
||||
using typename Base::RateVector;
|
||||
using GasLiftHandler = Opm::GasLiftRuntime<TypeTag>;
|
||||
using typename Base::GasLiftSingleWell;
|
||||
using typename Base::GLiftOptWells;
|
||||
using typename Base::GLiftProdWells;
|
||||
using typename Base::GLiftWellStateMap;
|
||||
|
||||
using Base::numEq;
|
||||
using Base::numPhases;
|
||||
@@ -250,10 +256,13 @@ namespace Opm
|
||||
DeferredLogger& deferred_logger
|
||||
) const;
|
||||
|
||||
virtual void maybeDoGasLiftOptimization (
|
||||
virtual void gasLiftOptimizationStage1 (
|
||||
WellState& well_state,
|
||||
const Simulator& ebosSimulator,
|
||||
DeferredLogger& deferred_logger
|
||||
DeferredLogger& deferred_logger,
|
||||
GLiftProdWells &prod_wells,
|
||||
GLiftOptWells &glift_wells,
|
||||
GLiftWellStateMap &state_map
|
||||
) const override;
|
||||
|
||||
bool checkGliftNewtonIterationIdxOk(
|
||||
@@ -399,6 +408,9 @@ namespace Opm
|
||||
// Enable GLIFT debug mode. This will enable output of logging messages.
|
||||
bool glift_debug = false;
|
||||
|
||||
// Optimize only wells under THP control
|
||||
bool glift_optimize_only_thp_wells = true;
|
||||
|
||||
const EvalWell& getBhp() const;
|
||||
|
||||
EvalWell getQs(const int comp_idx) const;
|
||||
|
||||
@@ -275,7 +275,7 @@ namespace Opm
|
||||
return primary_variables_evaluation_[SFrac];
|
||||
}
|
||||
}
|
||||
else if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
|
||||
else if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
|
||||
if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx) && compIdx == Indices::canonicalToActiveComponentIndex(FluidSystem::gasCompIdx)) {
|
||||
return primary_variables_evaluation_[GFrac];
|
||||
}
|
||||
@@ -300,7 +300,7 @@ namespace Opm
|
||||
|
||||
well_fraction -= primary_variables_evaluation_[GFrac];
|
||||
}
|
||||
|
||||
|
||||
return well_fraction;
|
||||
}
|
||||
|
||||
@@ -1115,7 +1115,7 @@ namespace Opm
|
||||
|
||||
if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
|
||||
F[pu.phase_pos[Oil]] = 1.0;
|
||||
|
||||
|
||||
if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
|
||||
F[pu.phase_pos[Water]] = primary_variables_[WFrac];
|
||||
F[pu.phase_pos[Oil]] -= F[pu.phase_pos[Water]];
|
||||
@@ -1213,7 +1213,7 @@ namespace Opm
|
||||
std::vector<double> F(number_of_phases_, 0.0);
|
||||
double F_solvent = 0.0;
|
||||
if ( FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) ) {
|
||||
const int oil_pos = pu.phase_pos[Oil];
|
||||
const int oil_pos = pu.phase_pos[Oil];
|
||||
F[oil_pos] = 1.0;
|
||||
|
||||
if ( FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx) ) {
|
||||
@@ -1234,7 +1234,7 @@ namespace Opm
|
||||
}
|
||||
}
|
||||
else if ( FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx) ) {
|
||||
const int water_pos = pu.phase_pos[Water];
|
||||
const int water_pos = pu.phase_pos[Water];
|
||||
F[water_pos] = 1.0;
|
||||
|
||||
if ( FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx) ) {
|
||||
@@ -1244,7 +1244,7 @@ namespace Opm
|
||||
}
|
||||
}
|
||||
else if ( FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx) ) {
|
||||
const int gas_pos = pu.phase_pos[Gas];
|
||||
const int gas_pos = pu.phase_pos[Gas];
|
||||
F[gas_pos] = 1.0;
|
||||
}
|
||||
|
||||
@@ -2692,23 +2692,42 @@ namespace Opm
|
||||
{
|
||||
|
||||
gliftDebug("checking if GLIFT should be done..", deferred_logger);
|
||||
std::size_t num_procs = ebos_simulator.gridView().comm().size();
|
||||
if (num_procs > 1u) {
|
||||
const std::string msg = fmt::format(" GLIFT: skipping optimization. "
|
||||
"Parallel run not supported yet: num procs = {}", num_procs);
|
||||
deferred_logger.warning(msg);
|
||||
return false;
|
||||
}
|
||||
if (!well_state.gliftOptimizationEnabled()) {
|
||||
gliftDebug("Optimization disabled in WellState", deferred_logger);
|
||||
return false;
|
||||
}
|
||||
const int well_index = index_of_well_;
|
||||
const Well::ProducerCMode& control_mode
|
||||
= well_state.currentProductionControls()[well_index];
|
||||
if (control_mode != Well::ProducerCMode::THP ) {
|
||||
gliftDebug("Not THP control", deferred_logger);
|
||||
if (well_state.gliftCheckAlqOscillation(name())) {
|
||||
gliftDebug("further optimization skipped due to oscillation in ALQ",
|
||||
deferred_logger);
|
||||
return false;
|
||||
}
|
||||
if (this->glift_optimize_only_thp_wells) {
|
||||
const int well_index = index_of_well_;
|
||||
const Well::ProducerCMode& control_mode
|
||||
= well_state.currentProductionControls()[well_index];
|
||||
if (control_mode != Well::ProducerCMode::THP ) {
|
||||
gliftDebug("Not THP control", deferred_logger);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!checkGliftNewtonIterationIdxOk(ebos_simulator, deferred_logger)) {
|
||||
return false;
|
||||
}
|
||||
const int report_step_idx = ebos_simulator.episodeIndex();
|
||||
const Opm::Schedule& schedule = ebos_simulator.vanguard().schedule();
|
||||
const GasLiftOpt& glo = schedule.glo(report_step_idx);
|
||||
if (!glo.has_well(name())) {
|
||||
gliftDebug("Gas Lift not activated: WLIFTOPT is probably missing",
|
||||
deferred_logger);
|
||||
return false;
|
||||
}
|
||||
auto increment = glo.gaslift_increment();
|
||||
// NOTE: According to the manual: LIFTOPT, item 1, :
|
||||
// "Increment size for lift gas injection rate. Lift gas is
|
||||
@@ -2771,7 +2790,7 @@ namespace Opm
|
||||
{
|
||||
if (this->glift_debug) {
|
||||
const std::string message = fmt::format(
|
||||
" GLIFT (DEBUG) : Well {} : {}", this->name(), msg);
|
||||
" GLIFT (DEBUG) : SW : Well {} : {}", this->name(), msg);
|
||||
deferred_logger.info(message);
|
||||
}
|
||||
}
|
||||
@@ -2819,30 +2838,37 @@ namespace Opm
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
StandardWell<TypeTag>::
|
||||
maybeDoGasLiftOptimization(
|
||||
WellState& well_state,
|
||||
const Simulator& ebos_simulator,
|
||||
Opm::DeferredLogger& deferred_logger) const
|
||||
gasLiftOptimizationStage1(
|
||||
WellState& well_state,
|
||||
const Simulator& ebos_simulator,
|
||||
Opm::DeferredLogger& deferred_logger,
|
||||
GLiftProdWells &prod_wells,
|
||||
GLiftOptWells &glift_wells,
|
||||
GLiftWellStateMap &glift_state_map
|
||||
//std::map<std::string, WellInterface *> &prod_wells
|
||||
) const
|
||||
{
|
||||
const auto& well = well_ecl_;
|
||||
if (well.isProducer()) {
|
||||
const auto& summary_state = ebos_simulator.vanguard().summaryState();
|
||||
const Well::ProducerCMode& current_control
|
||||
= well_state.currentProductionControls()[this->index_of_well_];
|
||||
if ( this->Base::wellHasTHPConstraints(summary_state)
|
||||
&& current_control != Well::ProducerCMode::BHP ) {
|
||||
if ( this->Base::wellHasTHPConstraints(summary_state) ) {
|
||||
if (doGasLiftOptimize(well_state, ebos_simulator, deferred_logger)) {
|
||||
const auto& controls = well.productionControls(summary_state);
|
||||
GasLiftHandler glift {
|
||||
*this, ebos_simulator, summary_state,
|
||||
deferred_logger, well_state, controls };
|
||||
glift.runOptimize();
|
||||
std::unique_ptr<GasLiftSingleWell> glift
|
||||
= std::make_unique<GasLiftSingleWell>(
|
||||
*this, ebos_simulator, summary_state,
|
||||
deferred_logger, well_state);
|
||||
auto state = glift->runOptimize();
|
||||
if (state) {
|
||||
glift_state_map.insert({this->name(), std::move(state)});
|
||||
glift_wells.insert({this->name(), std::move(glift)});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
prod_wells.insert({this->name(), this});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,15 @@
|
||||
#include <opm/simulators/wells/WellGroupHelpers.hpp>
|
||||
#include <opm/simulators/wells/WellProdIndexCalculator.hpp>
|
||||
#include <opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp>
|
||||
// NOTE: GasLiftSingleWell.hpp includes StandardWell.hpp which includes ourself
|
||||
// (WellInterface.hpp), so we need to forward declare GasLiftSingleWell
|
||||
// for it to be defined in this file. Similar for BlackoilWellModel
|
||||
namespace Opm {
|
||||
template<typename TypeTag> class GasLiftSingleWell;
|
||||
template<typename TypeTag> class BlackoilWellModel;
|
||||
}
|
||||
#include <opm/simulators/wells/GasLiftSingleWell.hpp>
|
||||
#include <opm/simulators/wells/BlackoilWellModel.hpp>
|
||||
#include <opm/simulators/flow/BlackoilModelParametersEbos.hpp>
|
||||
|
||||
#include <opm/simulators/timestepping/ConvergenceReport.hpp>
|
||||
@@ -83,6 +92,11 @@ namespace Opm
|
||||
using MaterialLaw = GetPropType<TypeTag, Properties::MaterialLaw>;
|
||||
using SparseMatrixAdapter = GetPropType<TypeTag, Properties::SparseMatrixAdapter>;
|
||||
using RateVector = GetPropType<TypeTag, Properties::RateVector>;
|
||||
using GasLiftSingleWell = Opm::GasLiftSingleWell<TypeTag>;
|
||||
using GLiftOptWells = typename Opm::BlackoilWellModel<TypeTag>::GLiftOptWells;
|
||||
using GLiftProdWells = typename Opm::BlackoilWellModel<TypeTag>::GLiftProdWells;
|
||||
using GLiftWellStateMap =
|
||||
typename Opm::BlackoilWellModel<TypeTag>::GLiftWellStateMap;
|
||||
|
||||
static const int numEq = Indices::numEq;
|
||||
static const int numPhases = Indices::numPhases;
|
||||
@@ -174,10 +188,13 @@ namespace Opm
|
||||
Opm::DeferredLogger& deferred_logger
|
||||
) = 0;
|
||||
|
||||
virtual void maybeDoGasLiftOptimization (
|
||||
virtual void gasLiftOptimizationStage1 (
|
||||
WellState& well_state,
|
||||
const Simulator& ebosSimulator,
|
||||
DeferredLogger& deferred_logger
|
||||
DeferredLogger& deferred_logger,
|
||||
GLiftProdWells& prod_wells,
|
||||
GLiftOptWells& glift_wells,
|
||||
GLiftWellStateMap& state_map
|
||||
) const = 0;
|
||||
|
||||
void updateWellTestState(const WellState& well_state,
|
||||
@@ -324,6 +341,7 @@ namespace Opm
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
const PhaseUsage& phaseUsage() const;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -435,8 +453,6 @@ namespace Opm
|
||||
|
||||
bool changed_to_stopped_this_step_ = false;
|
||||
|
||||
const PhaseUsage& phaseUsage() const;
|
||||
|
||||
int flowPhaseToEbosCompIdx( const int phaseIdx ) const;
|
||||
|
||||
int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const;
|
||||
|
||||
@@ -1283,10 +1283,66 @@ namespace Opm
|
||||
this->current_alq_[name] = value;
|
||||
}
|
||||
|
||||
bool gliftCheckAlqOscillation(const std::string &name) const {
|
||||
if ((this->alq_increase_count_.count(name) == 1) &&
|
||||
(this->alq_decrease_count_.count(name) == 1))
|
||||
{
|
||||
if ((this->alq_increase_count_.at(name) >= 1) &&
|
||||
(this->alq_decrease_count_.at(name) >= 1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int gliftGetAlqDecreaseCount(const std::string &name) {
|
||||
if (this->alq_decrease_count_.count(name) == 0) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return this->alq_decrease_count_[name];
|
||||
}
|
||||
}
|
||||
|
||||
int gliftGetAlqIncreaseCount(const std::string &name) {
|
||||
if (this->alq_increase_count_.count(name) == 0) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return this->alq_increase_count_[name];
|
||||
}
|
||||
}
|
||||
|
||||
void gliftUpdateAlqIncreaseCount(const std::string &name, bool increase) {
|
||||
if (increase) {
|
||||
if (this->alq_increase_count_.count(name) == 0) {
|
||||
this->alq_increase_count_[name] = 1;
|
||||
}
|
||||
else {
|
||||
this->alq_increase_count_[name]++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this->alq_decrease_count_.count(name) == 0) {
|
||||
this->alq_decrease_count_[name] = 1;
|
||||
}
|
||||
else {
|
||||
this->alq_decrease_count_[name]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool gliftOptimizationEnabled() const {
|
||||
return do_glift_optimization_;
|
||||
}
|
||||
|
||||
void gliftTimeStepInit() {
|
||||
this->alq_increase_count_.clear();
|
||||
this->alq_decrease_count_.clear();
|
||||
disableGliftOptimization();
|
||||
}
|
||||
|
||||
void disableGliftOptimization() {
|
||||
do_glift_optimization_ = false;
|
||||
}
|
||||
@@ -1325,6 +1381,8 @@ namespace Opm
|
||||
std::map<std::string, double> group_grat_target_from_sales;
|
||||
std::map<std::string, double> current_alq_;
|
||||
std::map<std::string, double> default_alq_;
|
||||
std::map<std::string, int> alq_increase_count_;
|
||||
std::map<std::string, int> alq_decrease_count_;
|
||||
bool do_glift_optimization_;
|
||||
|
||||
std::vector<double> perfRateSolvent_;
|
||||
|
||||
Reference in New Issue
Block a user