Implement basic WTMULT behavior

This commit is contained in:
Joakim Hove 2021-10-19 11:59:49 +02:00
parent a3d37aad1e
commit 5e3e20c552
6 changed files with 188 additions and 1 deletions

View File

@ -652,6 +652,7 @@ namespace Opm
void handleWSOLVENT (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleWTEMP (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleWTEST (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleWTMULT (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleWTRACER (const HandlerContext&, const ParseContext&, ErrorGuard&);
};
}

View File

@ -296,6 +296,7 @@ public:
InjectionControls controls(const UnitSystem& unit_system, const SummaryState& st, double udq_default) const;
bool updateUDQActive(const UDQConfig& udq_config, UDQActive& active) const;
void update_uda(const UDQConfig& udq_config, UDQActive& udq_active, UDAControl control, const UDAValue& value);
void handleWTMULT(Well::WELTARGCMode cmode, double factor);
template<class Serializer>
void serializeOp(Serializer& serializer)
@ -426,6 +427,7 @@ public:
void setBHPLimit(const double limit);
int productionControls() const { return this->m_productionControls; }
void handleWTMULT(Well::WELTARGCMode cmode, double factor);
template<class Serializer>
void serializeOp(Serializer& serializer)

View File

@ -2009,6 +2009,82 @@ namespace {
}
/*
The WTMULT keyword can optionally use UDA values in three different ways:
1. The target can be UDA - instead of the standard strings "ORAT", "GRAT",
"WRAT", ..., the keyword can be configured with a UDA which is evaluated to
an integer and then mapped to one of the common controls.
2. The scaling factor itself can be a UDA.
3. The target we aim to scale might already be specified as a UDA.
The current implementation does not support UDA usage in any part of WTMULT
codepath.
*/
void Schedule::handleWTMULT(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) {
for (const auto& record : handlerContext.keyword) {
const auto& wellNamePattern = record.getItem<ParserKeywords::WTMULT::WELL>().getTrimmedString(0);
const auto& control = record.getItem<ParserKeywords::WTMULT::CONTROL>().get<std::string>(0);
const auto& factor = record.getItem<ParserKeywords::WTMULT::FACTOR>().get<UDAValue>(0);
const auto& num = record.getItem<ParserKeywords::WTMULT::NUM>().get<int>(0);
if (factor.is<std::string>()) {
std::string reason = fmt::format("Use of UDA value: {} is not supported as multiplier", factor.get<std::string>());
throw OpmInputError(reason, handlerContext.keyword.location());
}
if (this->snapshots.back().udq().has_keyword(control)) {
std::string reason = fmt::format("Use of UDA value: {} is not supported for control target", control);
throw OpmInputError(reason, handlerContext.keyword.location());
}
if (num != 1) {
std::string reason = fmt::format("Only NUM=1 is supported in WTMULT keyword");
throw OpmInputError(reason, handlerContext.keyword.location());
}
const auto cmode = Well::WELTARGCModeFromString(control);
if (cmode == Well::WELTARGCMode::GUID)
throw std::logic_error("Multiplying guide rate is not implemented");
const auto well_names = this->wellNames(wellNamePattern, handlerContext.currentStep, handlerContext.matching_wells);
if (well_names.empty())
invalidNamePattern(wellNamePattern, handlerContext.currentStep, parseContext, errors, handlerContext.keyword);
for (const auto& well_name : well_names) {
auto well = this->snapshots.back().wells.get(well_name);
if (well.isInjector()) {
bool update_well = true;
auto properties = std::make_shared<Well::WellInjectionProperties>(well.getInjectionProperties());
properties->handleWTMULT( cmode, factor.get<double>());
well.updateInjection(properties);
if (update_well) {
this->snapshots.back().events().addEvent(ScheduleEvents::INJECTION_UPDATE);
this->snapshots.back().wellgroup_events().addEvent(well_name, ScheduleEvents::INJECTION_UPDATE);
this->snapshots.back().wells.update(std::move(well));
}
} else {
bool update_well = true;
auto properties = std::make_shared<Well::WellProductionProperties>(well.getProductionProperties());
properties->handleWTMULT( cmode, factor.get<double>());
well.updateProduction(properties);
if (update_well) {
this->snapshots.back().events().addEvent(ScheduleEvents::PRODUCTION_UPDATE);
this->snapshots.back().wellgroup_events().addEvent(well_name,
ScheduleEvents::PRODUCTION_UPDATE);
this->snapshots.back().wells.update(std::move(well));
}
}
}
}
}
bool Schedule::handleNormalKeyword(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) {
using handler_function = void (Schedule::*)(const HandlerContext&, const ParseContext&, ErrorGuard&);
static const std::unordered_map<std::string,handler_function> handler_functions = {
@ -2100,6 +2176,7 @@ namespace {
{ "WSOLVENT", &Schedule::handleWSOLVENT },
{ "WTEMP" , &Schedule::handleWTEMP },
{ "WTEST" , &Schedule::handleWTEST },
{ "WTMULT" , &Schedule::handleWTMULT },
{ "WTRACER" , &Schedule::handleWTRACER },
};

View File

@ -339,5 +339,39 @@ namespace Opm {
}
}
void Well::WellInjectionProperties::handleWTMULT(Well::WELTARGCMode cmode, double factor) {
if (cmode == Well::WELTARGCMode::BHP)
this->BHPTarget *= factor;
else if (cmode == WELTARGCMode::ORAT) {
if (this->injectorType == InjectorType::OIL)
this->surfaceInjectionRate *= factor;
else
std::invalid_argument("Well type must be OIL to scale the oil rate");
}
else if (cmode == WELTARGCMode::WRAT) {
if (this->injectorType == InjectorType::WATER)
this->surfaceInjectionRate *= factor;
else
std::invalid_argument("Well type must be WATER to scale the water rate");
}
else if (cmode == WELTARGCMode::GRAT) {
if(this->injectorType == InjectorType::GAS)
this->surfaceInjectionRate *= factor;
else
std::invalid_argument("Well type must be GAS to scale the gas rate");
}
else if (cmode == WELTARGCMode::THP)
this->THPTarget *= factor;
else if (cmode == WELTARGCMode::RESV)
this->reservoirInjectionRate*= factor;
else throw std::invalid_argument("Invalid keyword (MODE) supplied");
}
}

View File

@ -17,6 +17,7 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fmt/format.h>
#include <iostream>
#include <string>
#include <vector>
@ -270,6 +271,38 @@ void Well::WellProductionProperties::handleWCONHIST(const std::optional<VFPProdT
}
void Well::WellProductionProperties::handleWTMULT(Well::WELTARGCMode cmode, double factor) {
switch (cmode) {
case Well::WELTARGCMode::ORAT:
this->OilRate *= factor;
break;
case Well::WELTARGCMode::GRAT:
this->GasRate *= factor;
break;
case Well::WELTARGCMode::WRAT:
this->WaterRate *= factor;
break;
case Well::WELTARGCMode::LRAT:
this->LiquidRate *= factor;
break;
case Well::WELTARGCMode::RESV:
this->ResVRate *= factor;
break;
case Well::WELTARGCMode::BHP:
this->BHPTarget *= factor;
break;
case Well::WELTARGCMode::THP:
this->THPTarget *= factor;
break;
case Well::WELTARGCMode::LIFT:
this->ALQValue *= factor;
break;
default:
throw std::logic_error("Unhandled WTMULT control");
}
}
bool Well::WellProductionProperties::operator==(const Well::WellProductionProperties& other) const {
return OilRate == other.OilRate

View File

@ -856,15 +856,24 @@ DATES -- 1
/
WELSPECS
'OP_1' 'OP' 9 9 1* 'OIL' 1* 1* 1* 1* 1* 1* 1* /
'I1' 'I' 5 5 2522.5 'WATER' /
/
COMPDAT
'OP_1' 9 9 1 1 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 /
'OP_1' 9 9 2 2 'OPEN' 1* 46.825 0.311 4332.346 1* 1* 'X' 22.123 /
'OP_1' 9 9 3 9 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 /
'OP_1' 9 9 3 9 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 /
'I1' 8 8 1 1 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 /
'I1' 8 8 2 2 'OPEN' 1* 46.825 0.311 4332.346 1* 1* 'X' 22.123 /
'I1' 8 8 3 9 'OPEN' 1* 32.948 0.311 3047.839 1* 1* 'X' 22.100 /
/
WCONPROD
'OP_1' 'OPEN' 'ORAT' 0.000 0.000 0.000 5* /
/
WCONINJE
'I1' 'WATER' 'OPEN' 'RATE' 200 1* 450.0 /
/
DATES -- 2
20 JAN 2010 /
/
@ -878,6 +887,20 @@ WELTARG
OP_1 THP 2000 /
OP_1 GUID 2300.14 /
/
DATES
1 FEB 2010 /
/
WTMULT
OP_1 ORAT 2 /
OP_1 GRAT 3 /
OP_1 WRAT 4 /
I1 WRAT 2 /
I1 BHP 3 /
I1 THP 4 /
/
)";
const auto& schedule = make_schedule(input);
@ -910,8 +933,25 @@ WELTARG
BOOST_CHECK (wpp_2.hasProductionControl( Opm::Well::ProducerCMode::ORAT) );
BOOST_CHECK (wpp_2.hasProductionControl( Opm::Well::ProducerCMode::RESV) );
const auto& well_3 = schedule.getWell("OP_1", 3);
const auto wpp_3 = well_3.getProductionProperties();
const auto prod_controls3 = wpp_3.controls(st, 0);
BOOST_CHECK_EQUAL(prod_controls3.oil_rate, 2 * 1300 * siFactorL);
BOOST_CHECK_EQUAL(prod_controls3.water_rate, 4 * 1400 * siFactorL);
BOOST_CHECK_EQUAL(prod_controls3.gas_rate, 3 * 1500.52 * siFactorG);
const auto& inj_controls2 = schedule.getWell("I1", 2).getInjectionProperties().controls(unitSystem, st, 0);
const auto& inj_controls3 = schedule.getWell("I1", 3).getInjectionProperties().controls(unitSystem, st, 0);
BOOST_CHECK_EQUAL(inj_controls2.surface_rate * 2, inj_controls3.surface_rate);
BOOST_CHECK_EQUAL(inj_controls2.bhp_limit * 3, inj_controls3.bhp_limit);
BOOST_CHECK_EQUAL(inj_controls2.thp_limit * 4, inj_controls3.thp_limit);
}
BOOST_AUTO_TEST_CASE(createDeckWithWeltArg_UDA) {
std::string input = R"(
START -- 0