mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Implements gas lift optimization for groups.
Extends PR #2824 to include support for GLIFTOPT (item 2, maximum lift gas supply for a group) and group production constraints. The optimization is split into two phases. First the wells are optimized separately (as in PR #2824). In this phase LIFTOPT and WLIFTOPT constraints (e.g. maxmimum lift gas injection for a well, minimum economic gradient) are considered together with well production constraints. Then, in the next phase the wells are optimized in groups. Here, the ALQ distribution from the first phase is used as a starting point. If a group has any production rate constraints, and/or a limit on its total rate of lift gas supply, lift gas is redistributed to the wells that gain the most benefit from it by considering which wells that currently has the largest weighted incremental gradient (i.e. increase in oil rate compared to increase in ALQ).
This commit is contained in:
parent
41063bc735
commit
434640fdf5
@ -420,7 +420,9 @@ namespace Opm {
|
|||||||
const int iterationIdx)
|
const int iterationIdx)
|
||||||
{
|
{
|
||||||
// -------- Mass balance equations --------
|
// -------- Mass balance equations --------
|
||||||
|
//auto stepNum = timer.currentStepNum();
|
||||||
ebosSimulator_.model().newtonMethod().setIterationIndex(iterationIdx);
|
ebosSimulator_.model().newtonMethod().setIterationIndex(iterationIdx);
|
||||||
|
//ebosSimulator_.model().newtonMethod().setCurrentTimeStep(stepNum);
|
||||||
ebosSimulator_.problem().beginIteration();
|
ebosSimulator_.problem().beginIteration();
|
||||||
ebosSimulator_.model().linearizer().linearizeDomain();
|
ebosSimulator_.model().linearizer().linearizeDomain();
|
||||||
ebosSimulator_.problem().endIteration();
|
ebosSimulator_.problem().endIteration();
|
||||||
|
@ -51,10 +51,13 @@
|
|||||||
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GConSale.hpp>
|
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GConSale.hpp>
|
||||||
|
|
||||||
#include <opm/simulators/timestepping/SimulatorReport.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/PerforationData.hpp>
|
||||||
#include <opm/simulators/wells/VFPInjProperties.hpp>
|
#include <opm/simulators/wells/VFPInjProperties.hpp>
|
||||||
#include <opm/simulators/wells/VFPProdProperties.hpp>
|
#include <opm/simulators/wells/VFPProdProperties.hpp>
|
||||||
#include <opm/simulators/flow/countGlobalCells.hpp>
|
|
||||||
#include <opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp>
|
#include <opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp>
|
||||||
#include <opm/simulators/wells/RateConverter.hpp>
|
#include <opm/simulators/wells/RateConverter.hpp>
|
||||||
#include <opm/simulators/wells/WellInterface.hpp>
|
#include <opm/simulators/wells/WellInterface.hpp>
|
||||||
@ -103,6 +106,15 @@ namespace Opm {
|
|||||||
using SparseMatrixAdapter = GetPropType<TypeTag, Properties::SparseMatrixAdapter>;
|
using SparseMatrixAdapter = GetPropType<TypeTag, Properties::SparseMatrixAdapter>;
|
||||||
|
|
||||||
typedef typename Opm::BaseAuxiliaryModule<TypeTag>::NeighborSet NeighborSet;
|
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 numEq = Indices::numEq;
|
||||||
static const int solventSaturationIdx = Indices::solventSaturationIdx;
|
static const int solventSaturationIdx = Indices::solventSaturationIdx;
|
||||||
@ -346,6 +358,8 @@ namespace Opm {
|
|||||||
// Check if well equations is converged.
|
// Check if well equations is converged.
|
||||||
ConvergenceReport getWellConvergence(const std::vector<Scalar>& B_avg, const bool checkGroupConvergence = false) const;
|
ConvergenceReport getWellConvergence(const std::vector<Scalar>& B_avg, const bool checkGroupConvergence = false) const;
|
||||||
|
|
||||||
|
const PhaseUsage& phaseUsage() const { return phase_usage_; }
|
||||||
|
|
||||||
const SimulatorReportSingle& lastReport() const;
|
const SimulatorReportSingle& lastReport() const;
|
||||||
|
|
||||||
void addWellContributions(SparseMatrixAdapter& jacobian) const
|
void addWellContributions(SparseMatrixAdapter& jacobian) const
|
||||||
@ -532,6 +546,14 @@ namespace Opm {
|
|||||||
|
|
||||||
void assembleWellEq(const double dt, Opm::DeferredLogger& deferred_logger);
|
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,
|
// some preparation work, mostly related to group control and RESV,
|
||||||
// at the beginning of each time step (Not report step)
|
// at the beginning of each time step (Not report step)
|
||||||
void prepareTimeStep(Opm::DeferredLogger& deferred_logger);
|
void prepareTimeStep(Opm::DeferredLogger& deferred_logger);
|
||||||
|
@ -306,8 +306,8 @@ namespace Opm {
|
|||||||
Opm::DeferredLogger local_deferredLogger;
|
Opm::DeferredLogger local_deferredLogger;
|
||||||
|
|
||||||
this->resetWellState();
|
this->resetWellState();
|
||||||
this->wellState().disableGliftOptimization();
|
|
||||||
updateAndCommunicateGroupData();
|
updateAndCommunicateGroupData();
|
||||||
|
well_state_.gliftTimeStepInit();
|
||||||
const int reportStepIdx = ebosSimulator_.episodeIndex();
|
const int reportStepIdx = ebosSimulator_.episodeIndex();
|
||||||
const double simulationTime = ebosSimulator_.time();
|
const double simulationTime = ebosSimulator_.time();
|
||||||
std::string exc_msg;
|
std::string exc_msg;
|
||||||
@ -1025,10 +1025,8 @@ namespace Opm {
|
|||||||
updateWellControls(local_deferredLogger, /* check group controls */ false);
|
updateWellControls(local_deferredLogger, /* check group controls */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
gliftDebug("assemble() : running assembleWellEq()..", local_deferredLogger);
|
maybeDoGasLiftOptimize(local_deferredLogger);
|
||||||
this->wellState().enableGliftOptimization();
|
|
||||||
assembleWellEq(dt, local_deferredLogger);
|
assembleWellEq(dt, local_deferredLogger);
|
||||||
this->wellState().disableGliftOptimization();
|
|
||||||
} catch (const std::runtime_error& e) {
|
} catch (const std::runtime_error& e) {
|
||||||
exc_type = ExceptionType::RUNTIME_ERROR;
|
exc_type = ExceptionType::RUNTIME_ERROR;
|
||||||
exc_msg = e.what();
|
exc_msg = e.what();
|
||||||
@ -1047,13 +1045,64 @@ namespace Opm {
|
|||||||
last_report_.assemble_time_well += perfTimer.stop();
|
last_report_.assemble_time_well += perfTimer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maybeDoGasLiftOptimize(Opm::DeferredLogger& deferred_logger)
|
||||||
|
{
|
||||||
|
well_state_.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(
|
||||||
|
well_state_, 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);
|
||||||
|
well_state_.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, well_state_,
|
||||||
|
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->well_state_.getALQ(well->name());
|
||||||
|
const std::string msg = fmt::format("ALQ_REPORT : {} : {}",
|
||||||
|
well->name(), alq);
|
||||||
|
gliftDebug(msg, deferred_logger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename TypeTag>
|
template<typename TypeTag>
|
||||||
void
|
void
|
||||||
BlackoilWellModel<TypeTag>::
|
BlackoilWellModel<TypeTag>::
|
||||||
assembleWellEq(const double dt, Opm::DeferredLogger& deferred_logger)
|
assembleWellEq(const double dt, Opm::DeferredLogger& deferred_logger)
|
||||||
{
|
{
|
||||||
for (auto& well : well_container_) {
|
for (auto& well : well_container_) {
|
||||||
well->maybeDoGasLiftOptimization(this->wellState(), ebosSimulator_, deferred_logger);
|
|
||||||
well->assembleWellEq(ebosSimulator_, dt, this->wellState(), 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
|
1370
opm/simulators/wells/GasLiftSingleWell_impl.hpp
Normal file
1370
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
|
1027
opm/simulators/wells/GasLiftStage2_impl.hpp
Normal file
1027
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::RateConverterType;
|
||||||
using typename Base::SparseMatrixAdapter;
|
using typename Base::SparseMatrixAdapter;
|
||||||
using typename Base::FluidState;
|
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
|
/// the number of reservior equations
|
||||||
using Base::numEq;
|
using Base::numEq;
|
||||||
@ -120,10 +124,13 @@ namespace Opm
|
|||||||
|
|
||||||
virtual void initPrimaryVariablesEvaluation() const override;
|
virtual void initPrimaryVariablesEvaluation() const override;
|
||||||
|
|
||||||
virtual void maybeDoGasLiftOptimization (
|
virtual void gasLiftOptimizationStage1 (
|
||||||
WellState&,
|
WellState&,
|
||||||
const Simulator&,
|
const Simulator&,
|
||||||
DeferredLogger&
|
DeferredLogger&,
|
||||||
|
GLiftProdWells &,
|
||||||
|
GLiftOptWells &,
|
||||||
|
GLiftWellStateMap &
|
||||||
) const override {
|
) const override {
|
||||||
// Not implemented yet
|
// Not implemented yet
|
||||||
}
|
}
|
||||||
|
@ -27,11 +27,13 @@
|
|||||||
#include <opm/simulators/linalg/bda/WellContributions.hpp>
|
#include <opm/simulators/linalg/bda/WellContributions.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <opm/simulators/wells/GasLiftRuntime.hpp>
|
|
||||||
#include <opm/simulators/wells/RateConverter.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/WellInterface.hpp>
|
||||||
#include <opm/simulators/wells/WellProdIndexCalculator.hpp>
|
#include <opm/simulators/wells/WellProdIndexCalculator.hpp>
|
||||||
#include <opm/simulators/wells/ParallelWellInfo.hpp>
|
#include <opm/simulators/wells/ParallelWellInfo.hpp>
|
||||||
|
#include <opm/simulators/wells/GasLiftSingleWell.hpp>
|
||||||
|
|
||||||
#include <opm/models/blackoil/blackoilpolymermodules.hh>
|
#include <opm/models/blackoil/blackoilpolymermodules.hh>
|
||||||
#include <opm/models/blackoil/blackoilsolventmodules.hh>
|
#include <opm/models/blackoil/blackoilsolventmodules.hh>
|
||||||
@ -46,6 +48,7 @@
|
|||||||
#include <dune/common/dynvector.hh>
|
#include <dune/common/dynvector.hh>
|
||||||
#include <dune/common/dynmatrix.hh>
|
#include <dune/common/dynmatrix.hh>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
@ -73,7 +76,10 @@ namespace Opm
|
|||||||
using typename Base::SparseMatrixAdapter;
|
using typename Base::SparseMatrixAdapter;
|
||||||
using typename Base::FluidState;
|
using typename Base::FluidState;
|
||||||
using typename Base::RateVector;
|
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::numEq;
|
||||||
using Base::numPhases;
|
using Base::numPhases;
|
||||||
@ -250,10 +256,13 @@ namespace Opm
|
|||||||
DeferredLogger& deferred_logger
|
DeferredLogger& deferred_logger
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
virtual void maybeDoGasLiftOptimization (
|
virtual void gasLiftOptimizationStage1 (
|
||||||
WellState& well_state,
|
WellState& well_state,
|
||||||
const Simulator& ebosSimulator,
|
const Simulator& ebosSimulator,
|
||||||
DeferredLogger& deferred_logger
|
DeferredLogger& deferred_logger,
|
||||||
|
GLiftProdWells &prod_wells,
|
||||||
|
GLiftOptWells &glift_wells,
|
||||||
|
GLiftWellStateMap &state_map
|
||||||
) const override;
|
) const override;
|
||||||
|
|
||||||
bool checkGliftNewtonIterationIdxOk(
|
bool checkGliftNewtonIterationIdxOk(
|
||||||
@ -399,6 +408,9 @@ namespace Opm
|
|||||||
// Enable GLIFT debug mode. This will enable output of logging messages.
|
// Enable GLIFT debug mode. This will enable output of logging messages.
|
||||||
bool glift_debug = false;
|
bool glift_debug = false;
|
||||||
|
|
||||||
|
// Optimize only wells under THP control
|
||||||
|
bool glift_optimize_only_thp_wells = true;
|
||||||
|
|
||||||
const EvalWell& getBhp() const;
|
const EvalWell& getBhp() const;
|
||||||
|
|
||||||
EvalWell getQs(const int comp_idx) const;
|
EvalWell getQs(const int comp_idx) const;
|
||||||
|
@ -2696,6 +2696,12 @@ namespace Opm
|
|||||||
gliftDebug("Optimization disabled in WellState", deferred_logger);
|
gliftDebug("Optimization disabled in WellState", deferred_logger);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
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 int well_index = index_of_well_;
|
||||||
const Well::ProducerCMode& control_mode
|
const Well::ProducerCMode& control_mode
|
||||||
= well_state.currentProductionControls()[well_index];
|
= well_state.currentProductionControls()[well_index];
|
||||||
@ -2703,12 +2709,18 @@ namespace Opm
|
|||||||
gliftDebug("Not THP control", deferred_logger);
|
gliftDebug("Not THP control", deferred_logger);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!checkGliftNewtonIterationIdxOk(ebos_simulator, deferred_logger)) {
|
if (!checkGliftNewtonIterationIdxOk(ebos_simulator, deferred_logger)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const int report_step_idx = ebos_simulator.episodeIndex();
|
const int report_step_idx = ebos_simulator.episodeIndex();
|
||||||
const Opm::Schedule& schedule = ebos_simulator.vanguard().schedule();
|
const Opm::Schedule& schedule = ebos_simulator.vanguard().schedule();
|
||||||
const GasLiftOpt& glo = schedule.glo(report_step_idx);
|
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();
|
auto increment = glo.gaslift_increment();
|
||||||
// NOTE: According to the manual: LIFTOPT, item 1, :
|
// NOTE: According to the manual: LIFTOPT, item 1, :
|
||||||
// "Increment size for lift gas injection rate. Lift gas is
|
// "Increment size for lift gas injection rate. Lift gas is
|
||||||
@ -2771,7 +2783,7 @@ namespace Opm
|
|||||||
{
|
{
|
||||||
if (this->glift_debug) {
|
if (this->glift_debug) {
|
||||||
const std::string message = fmt::format(
|
const std::string message = fmt::format(
|
||||||
" GLIFT (DEBUG) : Well {} : {}", this->name(), msg);
|
" GLIFT (DEBUG) : SW : Well {} : {}", this->name(), msg);
|
||||||
deferred_logger.info(message);
|
deferred_logger.info(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2819,31 +2831,38 @@ namespace Opm
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename TypeTag>
|
template<typename TypeTag>
|
||||||
void
|
void
|
||||||
StandardWell<TypeTag>::
|
StandardWell<TypeTag>::
|
||||||
maybeDoGasLiftOptimization(
|
gasLiftOptimizationStage1(
|
||||||
WellState& well_state,
|
WellState& well_state,
|
||||||
const Simulator& ebos_simulator,
|
const Simulator& ebos_simulator,
|
||||||
Opm::DeferredLogger& deferred_logger) const
|
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_;
|
const auto& well = well_ecl_;
|
||||||
if (well.isProducer()) {
|
if (well.isProducer()) {
|
||||||
const auto& summary_state = ebos_simulator.vanguard().summaryState();
|
const auto& summary_state = ebos_simulator.vanguard().summaryState();
|
||||||
const Well::ProducerCMode& current_control
|
if ( this->Base::wellHasTHPConstraints(summary_state) ) {
|
||||||
= well_state.currentProductionControls()[this->index_of_well_];
|
|
||||||
if ( this->Base::wellHasTHPConstraints(summary_state)
|
|
||||||
&& current_control != Well::ProducerCMode::BHP ) {
|
|
||||||
if (doGasLiftOptimize(well_state, ebos_simulator, deferred_logger)) {
|
if (doGasLiftOptimize(well_state, ebos_simulator, deferred_logger)) {
|
||||||
const auto& controls = well.productionControls(summary_state);
|
std::unique_ptr<GasLiftSingleWell> glift
|
||||||
GasLiftHandler glift {
|
= std::make_unique<GasLiftSingleWell>(
|
||||||
*this, ebos_simulator, summary_state,
|
*this, ebos_simulator, summary_state,
|
||||||
deferred_logger, well_state, controls };
|
deferred_logger, well_state);
|
||||||
glift.runOptimize();
|
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});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TypeTag>
|
template<typename TypeTag>
|
||||||
|
@ -41,6 +41,15 @@
|
|||||||
#include <opm/simulators/wells/WellGroupHelpers.hpp>
|
#include <opm/simulators/wells/WellGroupHelpers.hpp>
|
||||||
#include <opm/simulators/wells/WellProdIndexCalculator.hpp>
|
#include <opm/simulators/wells/WellProdIndexCalculator.hpp>
|
||||||
#include <opm/simulators/wells/WellStateFullyImplicitBlackoil.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/flow/BlackoilModelParametersEbos.hpp>
|
||||||
|
|
||||||
#include <opm/simulators/timestepping/ConvergenceReport.hpp>
|
#include <opm/simulators/timestepping/ConvergenceReport.hpp>
|
||||||
@ -83,6 +92,11 @@ namespace Opm
|
|||||||
using MaterialLaw = GetPropType<TypeTag, Properties::MaterialLaw>;
|
using MaterialLaw = GetPropType<TypeTag, Properties::MaterialLaw>;
|
||||||
using SparseMatrixAdapter = GetPropType<TypeTag, Properties::SparseMatrixAdapter>;
|
using SparseMatrixAdapter = GetPropType<TypeTag, Properties::SparseMatrixAdapter>;
|
||||||
using RateVector = GetPropType<TypeTag, Properties::RateVector>;
|
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 numEq = Indices::numEq;
|
||||||
static const int numPhases = Indices::numPhases;
|
static const int numPhases = Indices::numPhases;
|
||||||
@ -174,10 +188,13 @@ namespace Opm
|
|||||||
Opm::DeferredLogger& deferred_logger
|
Opm::DeferredLogger& deferred_logger
|
||||||
) = 0;
|
) = 0;
|
||||||
|
|
||||||
virtual void maybeDoGasLiftOptimization (
|
virtual void gasLiftOptimizationStage1 (
|
||||||
WellState& well_state,
|
WellState& well_state,
|
||||||
const Simulator& ebosSimulator,
|
const Simulator& ebosSimulator,
|
||||||
DeferredLogger& deferred_logger
|
DeferredLogger& deferred_logger,
|
||||||
|
GLiftProdWells& prod_wells,
|
||||||
|
GLiftOptWells& glift_wells,
|
||||||
|
GLiftWellStateMap& state_map
|
||||||
) const = 0;
|
) const = 0;
|
||||||
|
|
||||||
void updateWellTestState(const WellState& well_state,
|
void updateWellTestState(const WellState& well_state,
|
||||||
@ -324,6 +341,7 @@ namespace Opm
|
|||||||
WellState& well_state,
|
WellState& well_state,
|
||||||
Opm::DeferredLogger& deferred_logger);
|
Opm::DeferredLogger& deferred_logger);
|
||||||
|
|
||||||
|
const PhaseUsage& phaseUsage() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
@ -435,8 +453,6 @@ namespace Opm
|
|||||||
|
|
||||||
bool changed_to_stopped_this_step_ = false;
|
bool changed_to_stopped_this_step_ = false;
|
||||||
|
|
||||||
const PhaseUsage& phaseUsage() const;
|
|
||||||
|
|
||||||
int flowPhaseToEbosCompIdx( const int phaseIdx ) const;
|
int flowPhaseToEbosCompIdx( const int phaseIdx ) const;
|
||||||
|
|
||||||
int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const;
|
int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const;
|
||||||
|
@ -1283,10 +1283,66 @@ namespace Opm
|
|||||||
this->current_alq_[name] = value;
|
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 {
|
bool gliftOptimizationEnabled() const {
|
||||||
return do_glift_optimization_;
|
return do_glift_optimization_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gliftTimeStepInit() {
|
||||||
|
this->alq_increase_count_.clear();
|
||||||
|
this->alq_decrease_count_.clear();
|
||||||
|
disableGliftOptimization();
|
||||||
|
}
|
||||||
|
|
||||||
void disableGliftOptimization() {
|
void disableGliftOptimization() {
|
||||||
do_glift_optimization_ = false;
|
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> group_grat_target_from_sales;
|
||||||
std::map<std::string, double> current_alq_;
|
std::map<std::string, double> current_alq_;
|
||||||
std::map<std::string, double> default_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_;
|
bool do_glift_optimization_;
|
||||||
|
|
||||||
std::vector<double> perfRateSolvent_;
|
std::vector<double> perfRateSolvent_;
|
||||||
|
Loading…
Reference in New Issue
Block a user