UDQ: add UDT support
This commit is contained in:
@@ -106,6 +106,20 @@ private:
|
||||
UDQSet eval_number(const UDQVarType target_type,
|
||||
const UDQContext& context) const;
|
||||
|
||||
UDQSet eval_table_lookup(const UDQVarType target_type,
|
||||
const std::string& string_value,
|
||||
const UDQContext& context) const;
|
||||
|
||||
UDQSet eval_table_lookup_field(const std::string& string_value,
|
||||
const UDQContext& context) const;
|
||||
UDQSet eval_table_lookup_group(const std::string& string_value,
|
||||
const UDQContext& context) const;
|
||||
UDQSet eval_table_lookup_segment(const std::string& string_value,
|
||||
const UDQContext& context) const;
|
||||
UDQSet eval_table_lookup_well(const std::string& string_value,
|
||||
const UDQContext& context) const;
|
||||
|
||||
|
||||
void func_tokens(std::set<UDQTokenType>& tokens) const;
|
||||
};
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ enum class UDQVarType
|
||||
BLOCK_VAR = 7,
|
||||
WELL_VAR = 8,
|
||||
GROUP_VAR = 9,
|
||||
TABLE_LOOKUP = 10,
|
||||
};
|
||||
|
||||
enum class UDQTokenType
|
||||
@@ -124,6 +125,8 @@ enum class UDQTokenType
|
||||
scalar_func_prod = 46,
|
||||
//
|
||||
table_lookup = 47,
|
||||
table_lookup_start = 48,
|
||||
table_lookup_end = 49,
|
||||
//
|
||||
end = 100,
|
||||
};
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <opm/input/eclipse/Schedule/UDQ/UDQFunction.hpp>
|
||||
#include <opm/input/eclipse/Schedule/UDQ/UDQFunctionTable.hpp>
|
||||
#include <opm/input/eclipse/Schedule/UDQ/UDQSet.hpp>
|
||||
#include <opm/input/eclipse/Schedule/UDQ/UDT.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
@@ -322,6 +323,11 @@ UDQASTNode::eval_expression(const UDQContext& context) const
|
||||
return UDQSet::scalar(string_value, context.get(string_value));
|
||||
}
|
||||
|
||||
if (data_type == UDQVarType::TABLE_LOOKUP) {
|
||||
const auto param_type = UDQ::targetType(this->selector[0]);
|
||||
return this->eval_table_lookup(param_type, string_value, context);
|
||||
}
|
||||
|
||||
if (const auto scalar = context.get(string_value); scalar.has_value()) {
|
||||
return UDQSet::scalar(string_value, scalar.value());
|
||||
}
|
||||
@@ -512,6 +518,92 @@ UDQASTNode::eval_number(const UDQVarType target_type,
|
||||
}
|
||||
}
|
||||
|
||||
UDQSet
|
||||
UDQASTNode::eval_table_lookup(const UDQVarType target_type,
|
||||
const std::string& string_value,
|
||||
const UDQContext& context) const
|
||||
{
|
||||
switch (target_type) {
|
||||
case UDQVarType::FIELD_VAR:
|
||||
return eval_table_lookup_field(string_value, context);
|
||||
case UDQVarType::GROUP_VAR:
|
||||
return eval_table_lookup_group(string_value, context);
|
||||
case UDQVarType::SEGMENT_VAR:
|
||||
return eval_table_lookup_segment(string_value, context);
|
||||
case UDQVarType::WELL_VAR:
|
||||
return eval_table_lookup_well(string_value, context);
|
||||
default:
|
||||
throw std::invalid_argument {
|
||||
"Unsupported target_type: " + std::to_string(static_cast<int>(target_type))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
UDQSet
|
||||
UDQASTNode::eval_table_lookup_field(const std::string& string_value,
|
||||
const UDQContext& context) const
|
||||
{
|
||||
const UDT& udt = context.get_udt(string_value);
|
||||
const auto xvar = context.get(this->selector[0]);
|
||||
double val = 0.0;
|
||||
if (xvar.has_value()) {
|
||||
val = udt(*xvar);
|
||||
}
|
||||
|
||||
return UDQSet::scalar("dummy", val);
|
||||
}
|
||||
|
||||
UDQSet
|
||||
UDQASTNode::eval_table_lookup_group(const std::string& string_value,
|
||||
const UDQContext& context) const
|
||||
{
|
||||
const UDT& udt = context.get_udt(string_value);
|
||||
UDQSet result = UDQSet::groups("dummy", context.groups());
|
||||
for (const auto& group : context.groups()) {
|
||||
const auto xvar = context.get_group_var(group, this->selector[0]);
|
||||
if (xvar.has_value()) {
|
||||
result.assign(group, udt(*xvar));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
UDQSet
|
||||
UDQASTNode::eval_table_lookup_segment(const std::string& string_value,
|
||||
const UDQContext& context) const
|
||||
{
|
||||
const UDT& udt = context.get_udt(string_value);
|
||||
const auto all_msw_segments = UDQSet::getSegmentItems(context.segments());
|
||||
UDQSet result = UDQSet::segments("dummy", all_msw_segments);
|
||||
for (const auto& ms_well : all_msw_segments) {
|
||||
for (const auto& segment : ms_well.numbers) {
|
||||
const auto xvar = context.get_segment_var(ms_well.well, this->selector[0], segment);
|
||||
if (xvar.has_value()) {
|
||||
result.assign(ms_well.well, segment, udt(*xvar));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
UDQSet
|
||||
UDQASTNode::eval_table_lookup_well(const std::string& string_value,
|
||||
const UDQContext& context) const
|
||||
{
|
||||
const UDT& udt = context.get_udt(string_value);
|
||||
UDQSet result = UDQSet::wells("dummy", context.wells());
|
||||
for (const auto& well : context.wells()) {
|
||||
const auto xvar = context.get_well_var(well, this->selector[0]);
|
||||
if (xvar.has_value()) {
|
||||
result.assign(well, udt(*xvar));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void UDQASTNode::func_tokens(std::set<UDQTokenType>& tokens) const
|
||||
{
|
||||
tokens.insert(this->type);
|
||||
|
||||
@@ -187,8 +187,28 @@ make_udq_tokens(const std::vector<std::string>& string_tokens)
|
||||
}
|
||||
|
||||
tokens.emplace_back(string_token, selector);
|
||||
}
|
||||
else {
|
||||
} else if (token_type == Opm::UDQTokenType::table_lookup) {
|
||||
std::vector<std::string> selector;
|
||||
while (token_index < string_tokens.size()) {
|
||||
auto next_token_type = Opm::UDQ::tokenType(string_tokens[token_index]);
|
||||
if (next_token_type == Opm::UDQTokenType::table_lookup_end) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (next_token_type != Opm::UDQTokenType::table_lookup_start) {
|
||||
const auto& select_token = string_tokens[token_index];
|
||||
if (Opm::RawConsts::is_quote()(select_token[0])) {
|
||||
selector.push_back(select_token.substr(1, select_token.size() - 2));
|
||||
}
|
||||
else {
|
||||
selector.push_back(select_token);
|
||||
}
|
||||
}
|
||||
++token_index;
|
||||
}
|
||||
++token_index;
|
||||
tokens.emplace_back(string_token, selector);
|
||||
} else {
|
||||
tokens.emplace_back(string_token, token_type);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ namespace {
|
||||
|| (t == Opm::UDQVarType::NONE)
|
||||
|| (t == Opm::UDQVarType::SCALAR)
|
||||
|| (t == Opm::UDQVarType::FIELD_VAR)
|
||||
|| (t == Opm::UDQVarType::TABLE_LOOKUP)
|
||||
;
|
||||
}
|
||||
|
||||
@@ -234,6 +235,9 @@ namespace Opm { namespace UDQ {
|
||||
|
||||
UDQVarType targetType(const std::string& keyword)
|
||||
{
|
||||
if (keyword.substr(0,3) == "TU_") {
|
||||
return UDQVarType::TABLE_LOOKUP;
|
||||
}
|
||||
const char first_char = keyword[0];
|
||||
switch (first_char) {
|
||||
case 'C': return UDQVarType::CONNECTION_VAR;
|
||||
@@ -409,10 +413,18 @@ UDQTokenType tokenType(const std::string& token)
|
||||
return UDQTokenType::open_paren;
|
||||
}
|
||||
|
||||
if (token == "[") {
|
||||
return UDQTokenType::table_lookup_start;
|
||||
}
|
||||
|
||||
if (token == ")") {
|
||||
return UDQTokenType::close_paren;
|
||||
}
|
||||
|
||||
if (token == "]") {
|
||||
return UDQTokenType::table_lookup_end;
|
||||
}
|
||||
|
||||
if (const auto number = try_parse_double(token);
|
||||
number.has_value())
|
||||
{
|
||||
|
||||
@@ -64,6 +64,12 @@ namespace {
|
||||
return lhs == Opm::UDQVarType::WELL_VAR;
|
||||
}
|
||||
|
||||
if (rhs == Opm::UDQVarType::TABLE_LOOKUP)
|
||||
return lhs == Opm::UDQVarType::WELL_VAR ||
|
||||
lhs == Opm::UDQVarType::FIELD_VAR ||
|
||||
lhs == Opm::UDQVarType::SEGMENT_VAR ||
|
||||
lhs == Opm::UDQVarType::GROUP_VAR;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -74,11 +80,6 @@ namespace Opm {
|
||||
UDQTokenType UDQParser::get_type(const std::string& arg) const
|
||||
{
|
||||
auto func_type = UDQ::funcType(arg);
|
||||
if (func_type == UDQTokenType::table_lookup) {
|
||||
throw std::invalid_argument {
|
||||
"Table lookup function TU*[] is not supported in UDQ"
|
||||
};
|
||||
}
|
||||
|
||||
if (func_type != UDQTokenType::error) {
|
||||
return func_type;
|
||||
@@ -91,6 +92,13 @@ UDQTokenType UDQParser::get_type(const std::string& arg) const
|
||||
if (arg == ")") {
|
||||
return UDQTokenType::close_paren;
|
||||
}
|
||||
if (arg == "[") {
|
||||
return UDQTokenType::table_lookup_start;
|
||||
}
|
||||
|
||||
if (arg == "]") {
|
||||
return UDQTokenType::table_lookup_end;
|
||||
}
|
||||
|
||||
{
|
||||
char* end_ptr;
|
||||
|
||||
@@ -2325,16 +2325,16 @@ G2 1* 3.0 2* 0.9 RESV /
|
||||
/
|
||||
)"};
|
||||
|
||||
Parser parser;
|
||||
auto deck = parser.parseString(deck_string);
|
||||
BOOST_CHECK( deck.hasKeyword("GCUTBACK") );
|
||||
auto kw = deck["GCUTBACK"].back();
|
||||
BOOST_CHECK_EQUAL( kw.size(), 2 );
|
||||
auto record = kw.getRecord(1);
|
||||
BOOST_CHECK_EQUAL( record.getItem(5).get<double>(0), 0.9 );
|
||||
const auto& tracerkm = deck.get<ParserKeywords::TRACERKM>().back();
|
||||
BOOST_CHECK_EQUAL(tracerkm.size(), 5);
|
||||
BOOST_CHECK_EQUAL(deck.count<ParserKeywords::SAVE>(), 2);
|
||||
Parser parser;
|
||||
auto deck = parser.parseString(deck_string);
|
||||
BOOST_CHECK( deck.hasKeyword("GCUTBACK") );
|
||||
auto kw = deck["GCUTBACK"].back();
|
||||
BOOST_CHECK_EQUAL( kw.size(), 2 );
|
||||
auto record = kw.getRecord(1);
|
||||
BOOST_CHECK_EQUAL( record.getItem(5).get<double>(0), 0.9 );
|
||||
const auto& tracerkm = deck.get<ParserKeywords::TRACERKM>().back();
|
||||
BOOST_CHECK_EQUAL(tracerkm.size(), 5);
|
||||
BOOST_CHECK_EQUAL(deck.count<ParserKeywords::SAVE>(), 2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -337,12 +337,6 @@ BOOST_AUTO_TEST_CASE(MIX_SCALAR) {
|
||||
BOOST_CHECK_EQUAL( res_add["P1"].get() , 2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_TABLE_EXCEPTION) {
|
||||
UDQParams udqp;
|
||||
KeywordLocation location;
|
||||
BOOST_CHECK_THROW(UDQDefine(udqp, "WU",0, location, {"TUPRICE[WOPR]"}), std::invalid_argument);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQFieldSetTest) {
|
||||
std::vector<std::string> wells = {"P1", "P2", "P3", "P4"};
|
||||
KeywordLocation location;
|
||||
@@ -2932,3 +2926,91 @@ BOOST_AUTO_TEST_CASE(UDQ_Update_SummaryState)
|
||||
BOOST_CHECK_CLOSE(st.get_group_var("BB", "GUNDA_ST"), -123.4 , 1.0e-8);
|
||||
BOOST_CHECK_CLOSE(st.get_group_var("G1", "GUNDA_ST"), 652.44, 1.0e-8);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_WITH_UDT_FIELD)
|
||||
{
|
||||
std::string valid = R"(
|
||||
SCHEDULE
|
||||
|
||||
UDT
|
||||
'TU_FBHP' 1 /
|
||||
'LC' 100.0 500.0 / -- FOPR values
|
||||
100.0 180.0 / -- FBHP values
|
||||
/
|
||||
/
|
||||
|
||||
UDQ
|
||||
ASSIGN FU_FOPR 110.0 /
|
||||
ASSIGN FU_WBHP 0 /
|
||||
DEFINE FU_WBHP0 FU_WBHP /
|
||||
DEFINE FU_WBHP TU_FBHP[FU_FOPR] UMIN FU_WBHP0 /
|
||||
/
|
||||
|
||||
)";
|
||||
|
||||
auto schedule = make_schedule(valid);
|
||||
UDQState udq_state(0);
|
||||
SummaryState st(TimeService::now());
|
||||
UDQFunctionTable udqft;
|
||||
WellMatcher wm(NameOrder({"W1", "W2", "W3"}));
|
||||
const auto& udq = schedule.getUDQConfig(0);
|
||||
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
|
||||
UDQContext context(udqft, wm, udq.tables(), segmentMatcherFactory, st, udq_state);
|
||||
|
||||
const auto& ass = udq.assign("FU_WBHP");
|
||||
context.update_assign("FU_WBHP", ass.eval());
|
||||
const auto& ass2 = udq.assign("FU_FOPR");
|
||||
context.update_assign("FU_FOPR", ass2.eval());
|
||||
udq.define("FU_WBHP0");
|
||||
const auto& def = udq.define("FU_WBHP");
|
||||
auto res = def.eval(context);
|
||||
|
||||
BOOST_CHECK_EQUAL(res.size(), 1U);
|
||||
BOOST_CHECK_EQUAL(res[0].get(), 100.0 + (180.0 - 100.0) * (110.0 - 100.0) / (500.0 - 100.0));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(UDQ_WITH_UDT_WELL)
|
||||
{
|
||||
std::string valid = R"(
|
||||
SCHEDULE
|
||||
|
||||
|
||||
WELSPECS
|
||||
'PROD1' 'TEST' 5 1 1* 'OIL' 2* 'STOP' 4* /
|
||||
'PROD2' 'TEST' 1 1 1* 'OIL' 2* 'STOP' 4* /
|
||||
/
|
||||
|
||||
UDT
|
||||
'TU_FBHP' 1 /
|
||||
'LC' 100.0 500.0 / -- FOPR values
|
||||
100.0 180.0 / -- FBHP values
|
||||
/
|
||||
/
|
||||
|
||||
UDQ
|
||||
ASSIGN WU_WBHP 0 /
|
||||
DEFINE WU_WBHP0 FU_WBHP /
|
||||
DEFINE WU_WBHP TU_FBHP[WOPR] UMIN WU_WBHP0 /
|
||||
/
|
||||
|
||||
)";
|
||||
|
||||
auto schedule = make_schedule(valid);
|
||||
UDQState udq_state(0);
|
||||
SummaryState st(TimeService::now());
|
||||
UDQFunctionTable udqft;
|
||||
WellMatcher wm(NameOrder({"PROD1", "PROD2"}));
|
||||
const auto& udq = schedule.getUDQConfig(0);
|
||||
|
||||
st.update_well_var("PROD1", "WOPR", 120.0);
|
||||
st.update_well_var("PROD2", "WOPR", 450.0);
|
||||
|
||||
auto segmentMatcherFactory = []() { return std::make_unique<SegmentMatcher>(ScheduleState {}); };
|
||||
udq.eval(0, schedule, wm, segmentMatcherFactory, st, udq_state);
|
||||
|
||||
const double wu_wbhp1 = st.get_well_var("PROD1", "WU_WBHP");
|
||||
const double wu_wbhp2 = st.get_well_var("PROD2", "WU_WBHP");
|
||||
|
||||
BOOST_CHECK_EQUAL(wu_wbhp1, 100.0 + (180.0 - 100.0) * (120.0 - 100.0) / (500.0 - 100.0));
|
||||
BOOST_CHECK_EQUAL(wu_wbhp2, 100.0 + (180.0 - 100.0) * (450.0 - 100.0) / (500.0 - 100.0));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user