Merge pull request #1778 from joakim-hove/gas-lift-opt
Internalize keywords for Gas Lift Optimization
This commit is contained in:
commit
310bd82b8b
@ -98,6 +98,7 @@ if(ENABLE_ECL_INPUT)
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/Action/Condition.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/ArrayDimChecker.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/Events.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/Group/Group.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/Group/GuideRate.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/Group/GuideRateConfig.cpp
|
||||
@ -668,6 +669,7 @@ if(ENABLE_ECL_INPUT)
|
||||
opm/parser/eclipse/EclipseState/Schedule/Action/ASTNode.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/Action/PyAction.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/ArrayDimChecker.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/Network/Branch.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/Network/ExtNetwork.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/Network/Node.hpp
|
||||
|
251
opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.hpp
Normal file
251
opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.hpp
Normal file
@ -0,0 +1,251 @@
|
||||
/*
|
||||
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 GAS_LIFT_OPT_HPP
|
||||
#define GAS_LIFT_OPT_HPP
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
class GasLiftOpt {
|
||||
public:
|
||||
|
||||
class Group {
|
||||
public:
|
||||
Group() = default;
|
||||
|
||||
Group(const std::string& name) :
|
||||
m_name(name)
|
||||
{}
|
||||
|
||||
const std::optional<double>& max_lift_gas() const {
|
||||
return this->m_max_lift_gas;
|
||||
}
|
||||
|
||||
void max_lift_gas(double value) {
|
||||
if (value >= 0)
|
||||
this->m_max_lift_gas = value;
|
||||
}
|
||||
|
||||
const std::optional<double>& max_total_gas() const {
|
||||
return this->m_max_total_gas;
|
||||
}
|
||||
|
||||
void max_total_gas(double value) {
|
||||
if (value >= 0)
|
||||
this->m_max_total_gas = value;
|
||||
}
|
||||
|
||||
const std::string& name() const {
|
||||
return this->m_name;
|
||||
}
|
||||
|
||||
template<class Serializer>
|
||||
void serializeOp(Serializer& serializer)
|
||||
{
|
||||
serializer(m_name);
|
||||
serializer(m_max_lift_gas);
|
||||
serializer(m_max_total_gas);
|
||||
}
|
||||
|
||||
|
||||
static Group serializeObject() {
|
||||
Group group;
|
||||
group.m_name = "GR";
|
||||
group.m_max_lift_gas = 100;
|
||||
group.m_max_total_gas = 200;
|
||||
return group;
|
||||
}
|
||||
|
||||
|
||||
bool operator==(const Group& other) const {
|
||||
return this->m_name == other.m_name &&
|
||||
this->m_max_lift_gas == other.m_max_lift_gas &&
|
||||
this->m_max_total_gas == other.m_max_total_gas;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::optional<double> m_max_lift_gas;
|
||||
std::optional<double> m_max_total_gas;
|
||||
};
|
||||
|
||||
|
||||
class Well {
|
||||
public:
|
||||
Well() = default;
|
||||
Well(const std::string& name, bool use_glo) :
|
||||
m_name(name),
|
||||
m_use_glo(use_glo)
|
||||
{}
|
||||
|
||||
const std::string& name() const {
|
||||
return this->m_name;
|
||||
}
|
||||
|
||||
bool use_glo() const {
|
||||
return this->m_use_glo;
|
||||
}
|
||||
|
||||
void max_rate(double value) {
|
||||
this->m_max_rate = value;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
The semantics of the max_rate is quite complicated:
|
||||
|
||||
1. If the std::optional<double> has a value that value should be
|
||||
used as the maximum rate and all is fine.
|
||||
|
||||
2. If the std::optional<double> does not a have well we must check
|
||||
the value of Well::use_glo():
|
||||
|
||||
False: The maximum gas lift should have been set with WCONPROD /
|
||||
WELTARG - this code does not provide a value in that case.
|
||||
|
||||
True: If the well should be controlled with gas lift optimization
|
||||
the value to use should be the largest ALQ value in the wells
|
||||
VFP table.
|
||||
*/
|
||||
const std::optional<double>& max_rate() const {
|
||||
return this->m_max_rate;
|
||||
}
|
||||
|
||||
void weight_factor(double value) {
|
||||
if (this->m_use_glo)
|
||||
this->m_weight = value;
|
||||
}
|
||||
|
||||
double weight_factor() const {
|
||||
return this->m_weight;
|
||||
}
|
||||
|
||||
void inc_weight_factor(double value) {
|
||||
if (this->m_use_glo)
|
||||
this->m_inc_weight = value;
|
||||
}
|
||||
|
||||
double inc_weight_factor() const {
|
||||
return this->m_inc_weight;
|
||||
}
|
||||
|
||||
void min_rate(double value) {
|
||||
if (this->m_use_glo)
|
||||
this->m_min_rate = value;
|
||||
}
|
||||
|
||||
double min_rate() const {
|
||||
return this->m_min_rate;
|
||||
}
|
||||
|
||||
void alloc_extra_gas(bool value) {
|
||||
if (this->m_use_glo)
|
||||
this->m_alloc_extra_gas = value;
|
||||
}
|
||||
|
||||
bool alloc_extra_gas() const {
|
||||
return this->m_alloc_extra_gas;
|
||||
}
|
||||
|
||||
template<class Serializer>
|
||||
void serializeOp(Serializer& serializer)
|
||||
{
|
||||
serializer(m_name);
|
||||
serializer(m_use_glo);
|
||||
serializer(m_max_rate);
|
||||
serializer(m_min_rate);
|
||||
serializer(m_weight);
|
||||
serializer(m_inc_weight);
|
||||
serializer(m_alloc_extra_gas);
|
||||
}
|
||||
|
||||
static Well serializeObject() {
|
||||
Well well;
|
||||
well.m_name = "WELL";
|
||||
well.m_max_rate = 2000;
|
||||
well.m_min_rate = 56;
|
||||
well.m_use_glo = true;
|
||||
well.m_weight = 1.25;
|
||||
well.m_inc_weight = 0.25;
|
||||
well.m_alloc_extra_gas = false;
|
||||
return well;
|
||||
}
|
||||
|
||||
bool operator==(const Well& other) const {
|
||||
return this->m_name == other.m_name &&
|
||||
this->m_max_rate == other.m_max_rate &&
|
||||
this->m_min_rate == other.m_min_rate &&
|
||||
this->m_use_glo == other.m_use_glo &&
|
||||
this->m_weight == other.m_weight &&
|
||||
this->m_inc_weight == other.m_inc_weight &&
|
||||
this->m_alloc_extra_gas == other.m_alloc_extra_gas;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::optional<double> m_max_rate;
|
||||
double m_min_rate = 0;
|
||||
bool m_use_glo;
|
||||
double m_weight = 1;
|
||||
double m_inc_weight = 0;
|
||||
bool m_alloc_extra_gas = false;
|
||||
};
|
||||
|
||||
GasLiftOpt() = default;
|
||||
|
||||
const Group& group(const std::string& gname) const;
|
||||
const Well& well(const std::string& wname) const;
|
||||
|
||||
void gaslift_increment(double gaslift_increment);
|
||||
void min_eco_gradient(double min_eco_gradient);
|
||||
void min_wait(double min_wait);
|
||||
void all_newton(double all_newton);
|
||||
void add_group(const Group& group);
|
||||
void add_well(const Well& well);
|
||||
bool active() const;
|
||||
|
||||
static GasLiftOpt serializeObject();
|
||||
bool operator==(const GasLiftOpt& other) const;
|
||||
|
||||
template<class Serializer>
|
||||
void serializeOp(Serializer& serializer)
|
||||
{
|
||||
serializer(m_increment);
|
||||
serializer(m_min_eco_gradient);
|
||||
serializer(m_min_wait);
|
||||
serializer(m_all_newton);
|
||||
serializer.map(m_groups);
|
||||
serializer.map(m_wells);
|
||||
}
|
||||
private:
|
||||
double m_increment = 0;
|
||||
double m_min_eco_gradient;
|
||||
double m_min_wait;
|
||||
bool m_all_newton = true;
|
||||
|
||||
std::map<std::string, GasLiftOpt::Group> m_groups;
|
||||
std::map<std::string, GasLiftOpt::Well> m_wells;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/DynamicState.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/DynamicVector.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Events.hpp>
|
||||
@ -266,6 +267,7 @@ namespace Opm
|
||||
|
||||
|
||||
const Network::ExtNetwork& network(std::size_t report_step) const;
|
||||
const GasLiftOpt& glo(std::size_t report_step) const;
|
||||
|
||||
bool operator==(const Schedule& data) const;
|
||||
std::shared_ptr<const Python> python() const;
|
||||
@ -310,6 +312,7 @@ namespace Opm
|
||||
global_whistctl_mode.template serializeOp<Serializer, false>(serializer);
|
||||
m_actions.serializeOp(serializer);
|
||||
m_network.serializeOp(serializer);
|
||||
m_glo.serializeOp(serializer);
|
||||
rft_config.serializeOp(serializer);
|
||||
m_nupcol.template serializeOp<Serializer, false>(serializer);
|
||||
restart_config.serializeOp(serializer);
|
||||
@ -346,6 +349,7 @@ namespace Opm
|
||||
DynamicState<Well::ProducerCMode> global_whistctl_mode;
|
||||
DynamicState<std::shared_ptr<Action::Actions>> m_actions;
|
||||
DynamicState<std::shared_ptr<Network::ExtNetwork>> m_network;
|
||||
DynamicState<std::shared_ptr<GasLiftOpt>> m_glo;
|
||||
RFTConfig rft_config;
|
||||
DynamicState<int> m_nupcol;
|
||||
RestartConfig restart_config;
|
||||
@ -429,7 +433,9 @@ namespace Opm
|
||||
|
||||
void handleBRANPROP( const DeckKeyword& keyword, size_t currentStep);
|
||||
void handleNODEPROP( const DeckKeyword& keyword, size_t currentStep);
|
||||
|
||||
void handleLIFTOPT(const DeckKeyword& keyword, std::size_t currentStep);
|
||||
void handleGLIFTOPT(const DeckKeyword& keyword, std::size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors);
|
||||
void handleWLIFTOPT(const DeckKeyword& keyword, std::size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors);
|
||||
void handleTUNING( const DeckKeyword& keyword, size_t currentStep);
|
||||
void handlePYACTION( std::shared_ptr<const Python> python, const std::string& input_path, const DeckKeyword& keyword, size_t currentStep);
|
||||
void handleNUPCOL( const DeckKeyword& keyword, size_t currentStep);
|
||||
|
100
src/opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.cpp
Normal file
100
src/opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
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 <stdexcept>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.hpp>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
bool GasLiftOpt::active() const {
|
||||
return (this->m_increment > 0);
|
||||
}
|
||||
|
||||
void GasLiftOpt::gaslift_increment(double gaslift_increment) {
|
||||
this->m_increment = gaslift_increment;
|
||||
}
|
||||
|
||||
void GasLiftOpt::min_eco_gradient(double min_eco_gradient) {
|
||||
this->m_min_eco_gradient = min_eco_gradient;
|
||||
}
|
||||
|
||||
void GasLiftOpt::min_wait(double min_wait) {
|
||||
this->m_min_wait = min_wait;
|
||||
}
|
||||
|
||||
void GasLiftOpt::all_newton(double all_newton) {
|
||||
this->m_all_newton = all_newton;
|
||||
}
|
||||
|
||||
const GasLiftOpt::Group& GasLiftOpt::group(const std::string& gname) const {
|
||||
const auto iter = this->m_groups.find(gname);
|
||||
if (iter == this->m_groups.end())
|
||||
throw std::out_of_range("No such group: " + gname + " configured for gas lift optimization");
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
|
||||
void GasLiftOpt::add_group(const Group& group) {
|
||||
auto iter = this->m_groups.find(group.name());
|
||||
if (iter == this->m_groups.end())
|
||||
this->m_groups.insert( std::make_pair(group.name(), group) );
|
||||
else
|
||||
iter->second = group;
|
||||
}
|
||||
|
||||
|
||||
void GasLiftOpt::add_well(const Well& well) {
|
||||
auto iter = this->m_wells.find(well.name());
|
||||
if (iter == this->m_wells.end())
|
||||
this->m_wells.insert( std::make_pair(well.name(), well) );
|
||||
else
|
||||
iter->second = well;
|
||||
}
|
||||
|
||||
|
||||
const GasLiftOpt::Well& GasLiftOpt::well(const std::string& wname) const {
|
||||
const auto iter = this->m_wells.find(wname);
|
||||
if (iter == this->m_wells.end())
|
||||
throw std::out_of_range("No such well: " + wname + " configured for gas lift optimization");
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
GasLiftOpt GasLiftOpt::serializeObject() {
|
||||
GasLiftOpt glo;
|
||||
glo.m_increment = 0;
|
||||
glo.m_min_eco_gradient = 100;
|
||||
glo.m_min_wait = 1;
|
||||
glo.m_all_newton = true;
|
||||
return glo;
|
||||
}
|
||||
|
||||
bool GasLiftOpt::operator==(const GasLiftOpt& other) const {
|
||||
return this->m_increment == other.m_increment &&
|
||||
this->m_min_eco_gradient == other.m_min_eco_gradient &&
|
||||
this->m_min_wait == other.m_min_wait &&
|
||||
this->m_all_newton == other.m_all_newton &&
|
||||
this->m_groups == other.m_groups &&
|
||||
this->m_wells == other.m_wells;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -149,6 +149,7 @@ std::pair<std::time_t, std::size_t> restart_info(const RestartIO::RstState * rst
|
||||
global_whistctl_mode(this->m_timeMap, Well::ProducerCMode::CMODE_UNDEFINED),
|
||||
m_actions(this->m_timeMap, std::make_shared<Action::Actions>()),
|
||||
m_network(this->m_timeMap, std::make_shared<Network::ExtNetwork>()),
|
||||
m_glo(this->m_timeMap, std::make_shared<GasLiftOpt>()),
|
||||
rft_config(this->m_timeMap),
|
||||
m_nupcol(this->m_timeMap, ParserKeywords::NUPCOL::NUM_ITER::defaultValue),
|
||||
restart_config(m_timeMap, deck, parseContext, errors),
|
||||
@ -263,6 +264,7 @@ Schedule::Schedule(const Deck& deck, const EclipseState& es, const ParseContext&
|
||||
result.wlist_manager = {{std::make_shared<WListManager>(WListManager::serializeObject())}, 1};
|
||||
result.udq_config = {{std::make_shared<UDQConfig>(UDQConfig::serializeObject())}, 1};
|
||||
result.m_network = {{std::make_shared<Network::ExtNetwork>(Network::ExtNetwork::serializeObject())}, 1};
|
||||
result.m_glo = {{std::make_shared<GasLiftOpt>(GasLiftOpt::serializeObject())}, 1};
|
||||
result.udq_active = {{std::make_shared<UDQActive>(UDQActive::serializeObject())}, 1};
|
||||
result.guide_rate_config = {{std::make_shared<GuideRateConfig>(GuideRateConfig::serializeObject())}, 1};
|
||||
result.gconsale = {{std::make_shared<GConSale>(GConSale::serializeObject())}, 1};
|
||||
@ -492,6 +494,15 @@ Schedule::Schedule(const Deck& deck, const EclipseState& es, const ParseContext&
|
||||
else if (keyword.name() == "BRANPROP")
|
||||
handleBRANPROP(keyword, currentStep);
|
||||
|
||||
else if (keyword.name() == "LIFTOPT")
|
||||
handleLIFTOPT(keyword, currentStep);
|
||||
|
||||
else if (keyword.name() == "GLIFTOPT")
|
||||
handleGLIFTOPT(keyword, currentStep, parseContext, errors);
|
||||
|
||||
else if (keyword.name() == "WLIFTOPT")
|
||||
handleWLIFTOPT(keyword, currentStep, parseContext, errors);
|
||||
|
||||
else if (keyword.name() == "PYACTION")
|
||||
handlePYACTION(python, input_path, keyword, currentStep);
|
||||
|
||||
@ -3031,6 +3042,7 @@ void Schedule::invalidNamePattern( const std::string& namePattern, std::size_t
|
||||
compareMap(this->vfpprod_tables, data.vfpprod_tables) &&
|
||||
compareMap(this->vfpinj_tables, data.vfpinj_tables) &&
|
||||
compareDynState(this->m_network, data.m_network) &&
|
||||
compareDynState(this->m_glo, data.m_glo) &&
|
||||
compareDynState(this->wtest_config, data.wtest_config) &&
|
||||
compareDynState(this->wlist_manager, data.wlist_manager) &&
|
||||
compareDynState(this->udq_config, data.udq_config) &&
|
||||
@ -3175,6 +3187,93 @@ void Schedule::handleNODEPROP(const DeckKeyword& keyword, std::size_t report_ste
|
||||
this->updateNetwork(ext_network, report_step);
|
||||
}
|
||||
|
||||
const GasLiftOpt& Schedule::glo(std::size_t report_step) const {
|
||||
return *this->m_glo[report_step];
|
||||
}
|
||||
|
||||
void Schedule::handleLIFTOPT(const DeckKeyword& keyword, std::size_t report_step) {
|
||||
using LO = ParserKeywords::LIFTOPT;
|
||||
auto glo = std::make_shared<GasLiftOpt>( this->glo(report_step) );
|
||||
const auto& record = keyword.getRecord(0);
|
||||
double gaslift_increment = record.getItem<LO::INCREMENT_SIZE>().getSIDouble(0);
|
||||
double min_eco_gradient = record.getItem<LO::MIN_ECONOMIC_GRADIENT>().getSIDouble(0);
|
||||
double min_wait = record.getItem<LO::MIN_INTERVAL_BETWEEN_GAS_LIFT_OPTIMIZATIONS>().getSIDouble(0);
|
||||
bool all_newton = DeckItem::to_bool( record.getItem<LO::OPTIMISE_GAS_LIFT>().get<std::string>(0) );
|
||||
|
||||
glo->gaslift_increment(gaslift_increment);
|
||||
glo->min_eco_gradient(min_eco_gradient);
|
||||
glo->min_wait(min_wait);
|
||||
glo->all_newton(all_newton);
|
||||
|
||||
this->m_glo.update(report_step, std::move(glo));
|
||||
}
|
||||
|
||||
|
||||
void Schedule::handleGLIFTOPT(const DeckKeyword& keyword, std::size_t report_step, const ParseContext& parseContext, ErrorGuard& errors) {
|
||||
using GLO = ParserKeywords::GLIFTOPT;
|
||||
auto glo = std::make_shared<GasLiftOpt>( this->glo(report_step) );
|
||||
for (const auto& record : keyword) {
|
||||
const std::string& groupNamePattern = record.getItem<GLO::GROUP_NAME>().getTrimmedString(0);
|
||||
const auto group_names = this->groupNames(groupNamePattern);
|
||||
if (group_names.empty())
|
||||
invalidNamePattern(groupNamePattern, report_step, parseContext, errors, keyword);
|
||||
|
||||
const auto& max_total_item = record.getItem<GLO::MAX_TOTAL_GAS_RATE>();
|
||||
const auto& max_gas_item = record.getItem<GLO::MAX_LIFT_GAS_SUPPLY>();
|
||||
double max_lift_gas_value = -1;
|
||||
double max_total_gas_value = -1;
|
||||
|
||||
if (max_gas_item.hasValue(0))
|
||||
max_lift_gas_value = max_gas_item.getSIDouble(0);
|
||||
|
||||
if (max_total_item.hasValue(0))
|
||||
max_total_gas_value = max_total_item.getSIDouble(0);
|
||||
|
||||
for (const auto& gname : group_names) {
|
||||
auto group = GasLiftOpt::Group(gname);
|
||||
group.max_lift_gas(max_lift_gas_value);
|
||||
group.max_total_gas(max_total_gas_value);
|
||||
|
||||
glo->add_group(group);
|
||||
}
|
||||
}
|
||||
this->m_glo.update(report_step, std::move(glo));
|
||||
}
|
||||
|
||||
void Schedule::handleWLIFTOPT(const DeckKeyword& keyword, std::size_t report_step, const ParseContext& parseContext, ErrorGuard& errors) {
|
||||
using WLO = ParserKeywords::WLIFTOPT;
|
||||
auto glo = std::make_shared<GasLiftOpt>( this->glo(report_step) );
|
||||
|
||||
for (const auto& record : keyword) {
|
||||
const std::string& wellNamePattern = record.getItem<WLO::WELL>().getTrimmedString(0);
|
||||
bool use_glo = DeckItem::to_bool( record.getItem<WLO::USE_OPTIMIZER>().get<std::string>(0));
|
||||
bool alloc_extra_gas = DeckItem::to_bool( record.getItem<WLO::ALLOCATE_EXTRA_LIFT_GAS>().get<std::string>(0));
|
||||
double weight_factor = record.getItem<WLO::WEIGHT_FACTOR>().get<double>(0);
|
||||
double inc_weight_factor = record.getItem<WLO::DELTA_GAS_RATE_WEIGHT_FACTOR>().get<double>(0);
|
||||
double min_rate = record.getItem<WLO::MIN_LIFT_GAS_RATE>().getSIDouble(0);
|
||||
const auto& max_rate_item = record.getItem<WLO::MAX_LIFT_GAS_RATE>();
|
||||
|
||||
const auto well_names = this->wellNames(wellNamePattern);
|
||||
if (well_names.empty())
|
||||
invalidNamePattern(wellNamePattern, report_step, parseContext, errors, keyword);
|
||||
|
||||
for (const auto& wname : well_names) {
|
||||
auto well = GasLiftOpt::Well(wname, use_glo);
|
||||
|
||||
if (max_rate_item.hasValue(0))
|
||||
well.max_rate( max_rate_item.getSIDouble(0) );
|
||||
|
||||
well.weight_factor(weight_factor);
|
||||
well.inc_weight_factor(inc_weight_factor);
|
||||
well.min_rate(min_rate);
|
||||
well.alloc_extra_gas(alloc_extra_gas);
|
||||
|
||||
glo->add_well(well);
|
||||
}
|
||||
}
|
||||
|
||||
this->m_glo.update(report_step, std::move(glo));
|
||||
}
|
||||
|
||||
void Schedule::handleBRANPROP(const DeckKeyword& keyword, std::size_t report_step) {
|
||||
using BP = ParserKeywords::BRANPROP;
|
||||
|
@ -11,11 +11,13 @@
|
||||
{
|
||||
"name": "MAX_LIFT_GAS_SUPPLY",
|
||||
"value_type": "DOUBLE",
|
||||
"dimension" : "GasSurfaceVolume/Time",
|
||||
"default": -1e+20
|
||||
},
|
||||
{
|
||||
"name": "MAX_TOTAL_GAS_RATE",
|
||||
"value_type": "DOUBLE",
|
||||
"dimension" : "GasSurfaceVolume/Time",
|
||||
"default": -1e+20
|
||||
}
|
||||
]
|
||||
|
@ -7,15 +7,18 @@
|
||||
"items": [
|
||||
{
|
||||
"name": "INCREMENT_SIZE",
|
||||
"value_type": "DOUBLE"
|
||||
"value_type": "DOUBLE",
|
||||
"dimension" : "GasSurfaceVolume/Time"
|
||||
},
|
||||
{
|
||||
"name": "MIN_ECONOMIC_GRADIENT",
|
||||
"value_type": "DOUBLE"
|
||||
"value_type": "DOUBLE",
|
||||
"dimension" : "LiquidSurfaceVolume/GasSurfaceVolume"
|
||||
},
|
||||
{
|
||||
"name": "MIN_INTERVAL_BETWEEN_GAS_LIFT_OPTIMIZATIONS",
|
||||
"value_type": "DOUBLE",
|
||||
"dimension" : "Time",
|
||||
"default": 0
|
||||
},
|
||||
{
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/OilVaporizationProperties.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
@ -3604,3 +3605,85 @@ BOOST_AUTO_TEST_CASE(SKIPREST_VFP) {
|
||||
const auto& tables = sched.getVFPProdTables(3);
|
||||
BOOST_CHECK( !tables.empty() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(GASLIFT_OPT) {
|
||||
GasLiftOpt glo;
|
||||
BOOST_CHECK(!glo.active());
|
||||
BOOST_CHECK_THROW(glo.group("NO_SUCH_GROUP"), std::out_of_range);
|
||||
BOOST_CHECK_THROW(glo.well("NO_SUCH_WELL"), std::out_of_range);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(GASLIFT_OPT_DECK) {
|
||||
const auto input = R"(-- Turns on gas lift optimization
|
||||
SCHEDULE
|
||||
|
||||
GRUPTREE
|
||||
'PROD' 'FIELD' /
|
||||
|
||||
'M5S' 'PLAT-A' /
|
||||
'M5N' 'PLAT-A' /
|
||||
|
||||
'C1' 'M5N' /
|
||||
'F1' 'M5N' /
|
||||
'B1' 'M5S' /
|
||||
'G1' 'M5S' /
|
||||
/
|
||||
|
||||
LIFTOPT
|
||||
12500 5E-3 0.0 YES /
|
||||
|
||||
|
||||
-- Group lift gas limits for gas lift optimization
|
||||
GLIFTOPT
|
||||
'PLAT-A' 200000 / --
|
||||
/
|
||||
|
||||
WELSPECS
|
||||
--WELL GROUP IHEEL JHEEL DREF PHASE DRAD INFEQ SIINS XFLOW PRTAB DENS
|
||||
'B-1H' 'B1' 11 3 1* OIL 1* 1* SHUT 1* 1* 1* /
|
||||
'B-2H' 'B1' 4 7 1* OIL 1* 1* SHUT 1* 1* 1* /
|
||||
'B-3H' 'B1' 11 12 1* OIL 1* 1* SHUT 1* 1* 1* /
|
||||
'C-1H' 'C1' 13 20 1* OIL 1* 1* SHUT 1* 1* 1* /
|
||||
'C-2H' 'C1' 12 27 1* OIL 1* 1* SHUT 1* 1* 1* /
|
||||
/
|
||||
|
||||
-- well savailable for gass lift
|
||||
-- minimum gas lift rate, enough to keep well flowing
|
||||
WLIFTOPT
|
||||
'B-1H' YES 150000 1.01 -1.0 /
|
||||
'B-2H' YES 150000 1.01 -1.0 /
|
||||
'B-3H' YES 150000 1.01 -1.0 /
|
||||
'C-1H' YES 150000 1.01 -1.0 1.0 YES/
|
||||
'C-2H' NO 150000 1.01 -1.0 /
|
||||
/
|
||||
)";
|
||||
Opm::UnitSystem unitSystem = UnitSystem( UnitSystem::UnitType::UNIT_TYPE_METRIC );
|
||||
double siFactorG = unitSystem.parse("GasSurfaceVolume/Time").getSIScaling();
|
||||
const auto sched = make_schedule(input);
|
||||
const auto& glo = sched.glo(0);
|
||||
const auto& plat_group = glo.group("PLAT-A");
|
||||
BOOST_CHECK_EQUAL( *plat_group.max_lift_gas(), siFactorG * 200000);
|
||||
BOOST_CHECK(!plat_group.max_total_gas().has_value());
|
||||
|
||||
|
||||
const auto& w1 = glo.well("B-1H");
|
||||
BOOST_CHECK(w1.use_glo());
|
||||
BOOST_CHECK_EQUAL(*w1.max_rate(), 150000 * siFactorG);
|
||||
BOOST_CHECK_EQUAL(w1.weight_factor(), 1.01);
|
||||
|
||||
const auto& w2 = glo.well("C-2H");
|
||||
BOOST_CHECK_EQUAL(w2.weight_factor(), 1.00);
|
||||
BOOST_CHECK_EQUAL(w2.min_rate(), 0.00);
|
||||
BOOST_CHECK_EQUAL(w2.inc_weight_factor(), 0.00);
|
||||
BOOST_CHECK(!w2.alloc_extra_gas());
|
||||
|
||||
const auto& w3 = glo.well("C-1H");
|
||||
BOOST_CHECK_EQUAL(w3.min_rate(), -1.00 * siFactorG);
|
||||
BOOST_CHECK_EQUAL(w3.inc_weight_factor(), 1.00);
|
||||
BOOST_CHECK(w3.alloc_extra_gas());
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user