UDQ: add UDT support

This commit is contained in:
Arne Morten Kvarving
2023-10-24 08:21:00 +02:00
parent aaa8af923c
commit db0f99de77
8 changed files with 254 additions and 23 deletions

View File

@@ -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;
};

View File

@@ -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,
};

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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())
{

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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));
}