Merge pull request #3747 from akva2/fbhpdef

implement support for FBHPDEF
This commit is contained in:
Bård Skaflestad 2023-11-07 14:54:11 +01:00 committed by GitHub
commit 980ac2599a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 286 additions and 27 deletions

View File

@ -367,6 +367,7 @@ namespace Opm
this->template pack_unpack<GasLiftOpt>(serializer);
this->template pack_unpack<RFTConfig>(serializer);
this->template pack_unpack<RSTConfig>(serializer);
this->template pack_unpack<ScheduleState::BHPDefaults>(serializer);
this->template pack_unpack_map<int, VFPProdTable>(serializer);
this->template pack_unpack_map<int, VFPInjTable>(serializer);
@ -712,6 +713,7 @@ namespace Opm
void handleDRVDT (HandlerContext&);
void handleDRVDTR (HandlerContext&);
void handleEXIT (HandlerContext&);
void handleFBHPDEF (HandlerContext&);
void handleGCONINJE (HandlerContext&);
void handleGCONPROD (HandlerContext&);
void handleGCONSALE (HandlerContext&);

View File

@ -279,7 +279,28 @@ namespace Opm {
std::unordered_map<K, std::shared_ptr<T>> m_data;
};
struct BHPDefaults {
std::optional<double> prod_target;
std::optional<double> inj_limit;
static BHPDefaults serializationTestObject()
{
return BHPDefaults{1.0, 2.0};
}
bool operator==(const BHPDefaults& rhs) const
{
return this->prod_target == rhs.prod_target
&& this->inj_limit == rhs.inj_limit;
}
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(prod_target);
serializer(inj_limit);
}
};
ScheduleState() = default;
explicit ScheduleState(const time_point& start_time);
@ -382,6 +403,8 @@ namespace Opm {
ptr_member<RFTConfig> rft_config;
ptr_member<RSTConfig> rst_config;
ptr_member<BHPDefaults> bhp_defaults;
template <typename T>
ptr_member<T>& get() {
return const_cast<ptr_member<T>&>(std::as_const(*this).template get<T>());
@ -428,6 +451,8 @@ namespace Opm {
return this->rft_config;
else if constexpr ( std::is_same_v<T, RSTConfig> )
return this->rst_config;
else if constexpr ( std::is_same_v<T, BHPDefaults> )
return this->bhp_defaults;
else
static_assert(always_false1::value, "Template type <T> not supported in get()");
}

View File

@ -168,8 +168,29 @@ public:
static WellInjectionProperties serializationTestObject();
void handleWELTARG(WELTARGCMode cmode, const UDAValue& new_arg, double SIFactorP);
void handleWCONINJE(const DeckRecord& record, bool availableForGroupControl, const std::string& well_name);
void handleWCONINJH(const DeckRecord& record, const bool is_producer, const std::string& well_name, const KeywordLocation& loc);
//! \brief Handle a WCONINJE keyword.
//! \param record The deck record to use
//! \param bhp_def The default BHP target in input units
//! \param availableForGroupControl True if available for group control
//! \param well_name Name of well
void handleWCONINJE(const DeckRecord& record,
const double bhp_def,
bool availableForGroupControl,
const std::string& well_name);
//! \brief Handle a WCONINJH keyword.
//! \param record The deck record to use
//! \param bhp_def The default BHP limit in SI units
//! \param is_producer True if well is a producer
//! \param well_name Name of well
//! \param loc Location of keyword for logging purpuses
void handleWCONINJH(const DeckRecord& record,
const double bhp_def,
const bool is_producer,
const std::string& well_name,
const KeywordLocation& loc);
bool hasInjectionControl(InjectorCMode controlModeArg) const {
if (injectionControls & static_cast<int>(controlModeArg))
return true;
@ -240,6 +261,7 @@ public:
// BHP and THP limit
double bhp_hist_limit = 0.0;
double thp_hist_limit = 0.0;
bool bhp_hist_limit_defaulted = true; // Tracks whether value was defaulted or not
// historical BHP and THP under historical mode
double BHPH = 0.0;
@ -273,8 +295,28 @@ public:
// this is used to check whether the specified control mode is an effective history matching production mode
static bool effectiveHistoryProductionControl(ProducerCMode cmode);
void handleWCONPROD( const std::optional<VFPProdTable::ALQ_TYPE>& alq_type, const UnitSystem& unit_system, const std::string& well, const DeckRecord& record);
void handleWCONHIST( const std::optional<VFPProdTable::ALQ_TYPE>& alq_type, const UnitSystem& unit_system, const DeckRecord& record);
//! \brief Handle WCONPROD keyword.
//! \param alq_type ALQ type
//! \param bhp_def Default BHP target in SI units
//! \param unit_system Unit system to use
//! \param well Well name
//! \param record Deck record to use
void handleWCONPROD(const std::optional<VFPProdTable::ALQ_TYPE>& alq_type,
const double bhp_def,
const UnitSystem& unit_system,
const std::string& well,
const DeckRecord& record);
//! \brief Handle WCONHIST keyword.
//! \param alq_type ALQ type
//! \param bhp_def Default BHP limit in SI units
//! \param unit_system Unit system to use
//! \param record Deck record to use
void handleWCONHIST(const std::optional<VFPProdTable::ALQ_TYPE>& alq_type,
const double bhp_def,
const UnitSystem& unit_system,
const DeckRecord& record);
void handleWELTARG( WELTARGCMode cmode, const UDAValue& new_arg, double SiFactorP);
void resetDefaultBHPLimit();
void clearControls();

View File

@ -86,6 +86,7 @@
#include <opm/input/eclipse/Parser/ParserKeywords/B.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/C.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/D.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/F.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/G.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/L.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/N.hpp>
@ -465,6 +466,20 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
this->applyEXIT(handlerContext.keyword, handlerContext.currentStep);
}
void Schedule::handleFBHPDEF(HandlerContext& handlerContext)
{
using FBHP = ParserKeywords::FBHPDEF;
const auto& record = handlerContext.keyword.getRecord(0);
ScheduleState::BHPDefaults bhp_defaults;
const auto& prod_limit = record.getItem<FBHP::TARGET_BHP>();
const auto& inj_limit = record.getItem<FBHP::LIMIT_BHP>();
if (!(prod_limit.defaultApplied(0) && inj_limit.defaultApplied(0))) {
bhp_defaults.prod_target = prod_limit.getSIDouble(0);
bhp_defaults.inj_limit = inj_limit.getSIDouble(0);
}
this->snapshots.back().bhp_defaults.update(std::move(bhp_defaults));
}
void Schedule::handleGCONINJE(HandlerContext& handlerContext) {
using GI = ParserKeywords::GCONINJE;
auto current_step = handlerContext.currentStep;
@ -1300,10 +1315,22 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
throw OpmInputError(reason, handlerContext.keyword.location());
}
}
properties->handleWCONHIST(alq_type, this->m_static.m_unit_system, record);
double default_bhp;
if (this->snapshots.back().bhp_defaults.get().prod_target) {
default_bhp = *this->snapshots.back().bhp_defaults.get().prod_target;
} else {
default_bhp = UnitSystem::newMETRIC().to_si(UnitSystem::measure::pressure,
ParserKeywords::FBHPDEF::TARGET_BHP::defaultValue);
}
properties->handleWCONHIST(alq_type,
default_bhp,
this->m_static.m_unit_system, record);
if (switching_from_injector) {
properties->resetDefaultBHPLimit();
if (properties->bhp_hist_limit_defaulted) {
properties->setBHPLimit(default_bhp);
}
auto inj_props = std::make_shared<Well::WellInjectionProperties>(well2.getInjectionProperties());
inj_props->resetBHPLimit();
@ -1375,10 +1402,23 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
throw OpmInputError(reason, handlerContext.keyword.location());
}
}
properties->handleWCONPROD(alq_type, this->m_static.m_unit_system, well_name, record);
double default_bhp_target;
if (this->snapshots.back().bhp_defaults.get().prod_target) {
default_bhp_target = *this->snapshots.back().bhp_defaults.get().prod_target;
} else {
default_bhp_target = UnitSystem::newMETRIC().to_si(UnitSystem::measure::pressure,
ParserKeywords::WCONPROD::BHP::defaultValue.get<double>());
}
properties->handleWCONPROD(alq_type, default_bhp_target,
this->m_static.m_unit_system,
well_name, record);
if (switching_from_injector) {
properties->resetDefaultBHPLimit();
if (properties->bhp_hist_limit_defaulted) {
properties->setBHPLimit(default_bhp_target);
}
update_well = true;
this->snapshots.back().wellgroup_events().addEvent( well2.name(), ScheduleEvents::WELL_SWITCHED_INJECTOR_PRODUCER);
}
@ -1428,7 +1468,21 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
auto injection = std::make_shared<Well::WellInjectionProperties>(well2.getInjectionProperties());
auto previousInjectorType = injection->injectorType;
injection->handleWCONINJE(record, well2.isAvailableForGroupControl(), well_name);
double default_bhp_limit;
if (this->snapshots.back().bhp_defaults.get().inj_limit) {
default_bhp_limit = this->m_static.m_unit_system.from_si(UnitSystem::measure::pressure,
*this->snapshots.back().bhp_defaults.get().inj_limit);
} else {
default_bhp_limit = UnitSystem::newMETRIC().to_si(UnitSystem::measure::pressure,
ParserKeywords::WCONINJE::BHP::defaultValue.get<double>());
default_bhp_limit = this->m_static.m_unit_system.from_si(UnitSystem::measure::pressure,
default_bhp_limit);
}
injection->handleWCONINJE(record, default_bhp_limit,
well2.isAvailableForGroupControl(), well_name);
const bool switching_from_producer = well2.isProducer();
if (well2.updateInjection(injection))
update_well = true;
@ -1497,9 +1551,20 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno)
auto well2 = this->snapshots.back().wells.get( well_name );
auto injection = std::make_shared<Well::WellInjectionProperties>(well2.getInjectionProperties());
auto previousInjectorType = injection->injectorType;
injection->handleWCONINJH(record, well2.isProducer(), well_name, handlerContext.keyword.location());
const bool switching_from_producer = well2.isProducer();
double default_bhp_limit;
if (this->snapshots.back().bhp_defaults.get().inj_limit) {
default_bhp_limit = *this->snapshots.back().bhp_defaults.get().inj_limit;
} else {
default_bhp_limit = UnitSystem::newMETRIC().to_si(UnitSystem::measure::pressure,
6891.2);
}
injection->handleWCONINJH(record, default_bhp_limit,
well2.isProducer(), well_name,
handlerContext.keyword.location());
const bool switching_from_producer = well2.isProducer();
if (well2.updateInjection(injection))
update_well = true;
@ -2789,6 +2854,7 @@ Well{0} entered with 'FIELD' parent group:
{ "DRVDTR" , &Schedule::handleDRVDTR },
{ "ENDBOX" , &Schedule::handleGEOKeyword},
{ "EXIT", &Schedule::handleEXIT },
{ "FBHPDEF", &Schedule::handleFBHPDEF },
{ "GCONINJE", &Schedule::handleGCONINJE },
{ "GCONPROD", &Schedule::handleGCONPROD },
{ "GCONSALE", &Schedule::handleGCONSALE },

View File

@ -2427,6 +2427,7 @@ void Schedule::create_first(const time_point& start_time, const std::optional<ti
sched_state.network_balance.update(Network::Balance{ runspec.networkDimensions().active() });
sched_state.update_sumthin(this->m_static.sumthin);
sched_state.rptonly(this->m_static.rptonly);
sched_state.bhp_defaults.update( ScheduleState::BHPDefaults() );
//sched_state.update_date( start_time );
this->addGroup("FIELD", 0);
}

View File

@ -19,6 +19,8 @@
#include <opm/input/eclipse/Schedule/ScheduleState.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/F.hpp>
#include <opm/input/eclipse/Schedule/Action/Actions.hpp>
#include <opm/input/eclipse/Schedule/GasLiftOpt.hpp>
#include <opm/input/eclipse/Schedule/Group/GConSale.hpp>
@ -307,6 +309,7 @@ bool ScheduleState::operator==(const ScheduleState& other) const {
this->guide_rate.get() == other.guide_rate.get() &&
this->rft_config.get() == other.rft_config.get() &&
this->udq.get() == other.udq.get() &&
this->bhp_defaults.get() == other.bhp_defaults.get() &&
this->wells == other.wells &&
this->groups == other.groups &&
this->vfpprod == other.vfpprod &&
@ -338,6 +341,7 @@ ScheduleState ScheduleState::serializationTestObject() {
ts.m_sumthin = 12.345;
ts.m_rptonly = true;
ts.bhp_defaults.update( BHPDefaults::serializationTestObject() );
ts.pavg.update( PAvg::serializationTestObject() );
ts.wtest_config.update( WellTestConfig::serializationTestObject() );
ts.gconsump.update( GConSump::serializationTestObject() );

View File

@ -427,8 +427,11 @@ Well::Well(const RestartIO::RstWell& rst_well,
if (! i->predictionMode) {
if (i->controlMode == Well::InjectorCMode::BHP)
i->bhp_hist_limit = rst_well.hist_bhp_target;
else
else {
// defaults (ie FBHPDEF) are not stored in the restart file.
// we thus use an empirically obtained default value.
i->resetDefaultHistoricalBHPLimit();
}
}
else if (this->isAvailableForGroupControl())
i->addInjectionControl(Well::InjectorCMode::GRUP);

View File

@ -85,7 +85,11 @@ namespace Opm {
return result;
}
void Well::WellInjectionProperties::handleWCONINJE(const DeckRecord& record, bool availableForGroupControl, const std::string& well_name) {
void Well::WellInjectionProperties::handleWCONINJE(const DeckRecord& record,
const double bhp_def,
bool availableForGroupControl,
const std::string& well_name)
{
this->injectorType = InjectorTypeFromString( record.getItem("TYPE").getTrimmedString(0) );
this->predictionMode = true;
@ -118,7 +122,11 @@ namespace Opm {
current behavoir agrees with the behavior of Eclipse when BHPLimit is not
specified while employed during group control.
*/
this->BHPTarget = record.getItem("BHP").get<UDAValue>(0);
if (record.getItem("BHP").defaultApplied(0)) {
this->BHPTarget.update(bhp_def);
} else {
this->BHPTarget = record.getItem("BHP").get<UDAValue>(0);
}
this->addInjectionControl(InjectorCMode::BHP);
if (availableForGroupControl)
@ -182,6 +190,7 @@ namespace Opm {
void
Well::WellInjectionProperties::handleWCONINJH(const DeckRecord& record,
const double bhp_def,
const bool is_producer,
const std::string& well_name,
const KeywordLocation& loc)
@ -232,7 +241,7 @@ namespace Opm {
if (switching_from_prediction ||
switching_from_BHP_control ||
switching_from_producer) {
this->resetDefaultHistoricalBHPLimit();
this->bhp_hist_limit = bhp_def;
}
// otherwise, we keep its previous BHP limit
}

View File

@ -160,13 +160,22 @@ namespace Opm {
void Well::WellProductionProperties::handleWCONPROD(const std::optional<VFPProdTable::ALQ_TYPE>& alq_type, const UnitSystem& unit_system_arg, const std::string& /* well */, const DeckRecord& record)
void Well::WellProductionProperties::handleWCONPROD(const std::optional<VFPProdTable::ALQ_TYPE>& alq_type,
const double bhp_def,
const UnitSystem& unit_system_arg,
const std::string& /* well */,
const DeckRecord& record)
{
this->predictionMode = true;
this->init_vfp(alq_type, unit_system_arg, record);
this->init_rates(record);
this->BHPTarget = record.getItem("BHP").get<UDAValue>(0);
if (record.getItem("BHP").defaultApplied(0)) {
this->BHPTarget.update(unit_system_arg.from_si(UnitSystem::measure::pressure,
bhp_def));
} else {
this->BHPTarget = record.getItem("BHP").get<UDAValue>(0);
}
this->THPTarget = record.getItem("THP").get<UDAValue>(0);
this->LiquidRate = record.getItem("LRAT").get<UDAValue>(0);
this->ResVRate = record.getItem("RESV").get<UDAValue>(0);
@ -209,7 +218,10 @@ namespace Opm {
originate from the WCONHIST keyword. Predictions are handled with the
default constructor and the handleWCONPROD() method.
*/
void Well::WellProductionProperties::handleWCONHIST(const std::optional<VFPProdTable::ALQ_TYPE>& alq_type, const UnitSystem& unit_system_arg, const DeckRecord& record)
void Well::WellProductionProperties::handleWCONHIST(const std::optional<VFPProdTable::ALQ_TYPE>& alq_type,
const double bhp_def,
const UnitSystem& unit_system_arg,
const DeckRecord& record)
{
this->init_rates(record);
this->init_vfp(alq_type, unit_system_arg, record);
@ -220,11 +232,9 @@ void Well::WellProductionProperties::handleWCONHIST(const std::optional<VFPProdT
// or switching from injector to producer
// or switching from BHP control to RATE control (under history matching mode)
// we use the defaulted BHP limit, otherwise, we use the previous BHP limit
if (this->predictionMode)
this->resetDefaultBHPLimit();
if (this->controlMode == ProducerCMode::BHP)
this->resetDefaultBHPLimit();
if (this->predictionMode || this->controlMode == ProducerCMode::BHP) {
this->setBHPLimit(bhp_def);
}
this->init_history(record);
}
@ -258,6 +268,7 @@ void Well::WellProductionProperties::handleWCONHIST(const std::optional<VFPProdT
else
this->bhp_hist_limit = new_arg.get<double>() * SiFactorP;
this->addProductionControl( ProducerCMode::BHP );
this->bhp_hist_limit_defaulted = false;
}
else if (cmode == WELTARGCMode::THP){
this->THPTarget.update_value( new_arg );

View File

@ -8,12 +8,14 @@
{
"name": "TARGET_BHP",
"value_type": "DOUBLE",
"dimension": "Length"
"dimension": "Pressure",
"default": 1.01325
},
{
"name": "LIMIT_BHP",
"value_type": "DOUBLE",
"dimension": "Pressure"
"dimension": "Pressure",
"default": 6895
}
]
}

View File

@ -47,6 +47,8 @@
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/common/utility/TimeService.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/F.hpp>
using namespace Opm;
namespace {
@ -589,7 +591,9 @@ namespace {
auto deck = parser.parseString(input);
const auto& record = deck["WCONHIST"].back().getRecord(0);
Opm::Well::WellProductionProperties hist(unit_system, "W");
hist.handleWCONHIST(alq_type, unit_system, record);
hist.handleWCONHIST(alq_type,
Opm::ParserKeywords::FBHPDEF::TARGET_BHP::defaultValue * unit::barsa,
unit_system, record);
return hist;
@ -642,7 +646,9 @@ namespace {
const auto& kwd = deck["WCONPROD"].back();
const auto& record = kwd.getRecord(0);
Opm::Well::WellProductionProperties pred(unit_system, "W");
pred.handleWCONPROD(alq_type, unit_system, "WELL", record);
pred.handleWCONPROD(alq_type,
Opm::ParserKeywords::FBHPDEF::TARGET_BHP::defaultValue * unit::barsa,
unit_system, "WELL", record);
return pred;
}
@ -1870,3 +1876,91 @@ END
}
}
}
BOOST_AUTO_TEST_CASE(FBHPDEF_Basic)
{
const auto deck = Parser{}.parseString(R"(RUNSPEC
DIMENS
10 10 3 /
GRID
DXV
10*100.0 /
DYV
10*100.0 /
DZV
3*5.0 /
DEPTHZ
121*2000 /
PERMX
300*100.0 /
COPY
PERMX PERMY /
PERMX PERMZ /
/
MULTIPLY
PERMZ 0.1 /
/
PORO
300*0.3 /
SCHEDULE
WELSPECS
'P' 'G' 10 10 1* 'OIL' /
'I' 'G' 1 1 1* 'GAS' /
'I2' 'W' 1 1 1* 'WATER' /
'I3' 'W' 1 1 1* 'WATER' /
/
COMPDAT
'P' 10 10 1 3 'OPEN' /
'I' 1 1 1 1 'OPEN' /
'I2' 1 1 1 1 'OPEN' /
'I3' 1 1 1 1 'OPEN' /
/
WCONINJH
I3 WATER OPEN 116281 1* 0 /
/
FBHPDEF
5.0 20.0 /
WCONPROD
'P' 'OPEN' 'LRAT' 1* 1* 1* 1234.567 1* 1* /
/
WCONINJH
I2 WATER OPEN 116281 1* 0 /
/
FBHPDEF
2.0 30.0 /
WCONINJE
'I' 'GAS' 'OPEN' 'RATE' 20.0E3 /
/
TSTEP
30.0 /
WELSPECS
'P' 'G1' /
/
TSTEP
30.0 /
END
)");
const auto es = EclipseState { deck };
const auto sched = Schedule { deck, es };
const auto& wellP = sched.getWell("P", 0);
BOOST_CHECK_EQUAL(wellP.getProductionProperties().BHPTarget.get<double>(), 5.0);
const auto& wellI = sched.getWell("I", 0);
BOOST_CHECK_CLOSE(wellI.getInjectionProperties().BHPTarget.get<double>(), 30.0, 1e-12);
const auto& wellI2 = sched.getWell("I2", 0);
BOOST_CHECK_EQUAL(wellI2.getInjectionProperties().bhp_hist_limit, 20.0 * unit::barsa);
const auto& wellI3 = sched.getWell("I3", 0);
BOOST_CHECK_CLOSE(wellI3.getInjectionProperties().bhp_hist_limit, 6891.2 * unit::barsa, 1e-12);
}