From 15c473e3bb72b3bf9b3a603b45e230f91cd3908a Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Wed, 2 Jan 2019 11:42:50 +0100 Subject: [PATCH] Implement support for well capture when evaluating ACTIONX --- .../EclipseState/Schedule/ActionAST.hpp | 6 +- .../EclipseState/Schedule/ActionAST.cpp | 72 ++++++------ tests/parser/ACTIONX.cpp | 104 +++++++++++------- 3 files changed, 102 insertions(+), 80 deletions(-) diff --git a/opm/parser/eclipse/EclipseState/Schedule/ActionAST.hpp b/opm/parser/eclipse/EclipseState/Schedule/ActionAST.hpp index 8f359ca69..4fa7e7b7c 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/ActionAST.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/ActionAST.hpp @@ -107,8 +107,8 @@ ASTNode(TokenType type_arg, const std::string& func_arg, const std::vector& matching_wells) const; + ActionValue value(const ActionContext& context) const; TokenType type; void add_child(const ASTNode& child); size_t size() const; @@ -153,7 +153,7 @@ public: ASTNode parse_or(ActionParser& parser); ASTNode parse_and(ActionParser& parser); - bool eval(const ActionContext& context) const; + bool eval(const ActionContext& context, std::vector& matching_wells) const; private: ASTNode tree; }; diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/ActionAST.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/ActionAST.cpp index 95c96965a..b047d51c9 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/ActionAST.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/ActionAST.cpp @@ -167,6 +167,18 @@ bool ActionValue::eval_cmp_wells(TokenType op, double rhs, std::vectorwell_values) { const std::string& well = pair.first; const double value = pair.second; + /* + It is less than clear how the matching_wells should be treated when + multiple conditons are nested; in the current implementation a + matching well is quite simply added to the list. This has two + consequences which are not-obviously-correct: + + 1. The wells might appear multiple times. + + 2. If a well matches in *one* conditon, and not in another - it will + be in the matching_wells list. + */ + if (eval_cmp_scalar(value, op, rhs)) { matching_wells.push_back(well); ret_value = true; @@ -202,25 +214,32 @@ void ASTNode::add_child(const ASTNode& child) { this->children.push_back(child); } -double ASTNode::value(const ActionContext& context) const { +ActionValue ASTNode::value(const ActionContext& context) const { if (this->children.size() != 0) throw std::invalid_argument("value() method should only reach leafnodes"); if (this->type == TokenType::number) - return this->number; + return ActionValue(this->number); if (this->arg_list.size() == 0) - return context.get(this->func); + return ActionValue(context.get(this->func)); else { - std::string arg_key = this->arg_list[0]; - for (size_t index = 1; index < this->arg_list.size(); index++) - arg_key += ":" + this->arg_list[index]; - return context.get(this->func, arg_key); + if (this->arg_list[0] == "*") { + ActionValue well_values; + for (const auto& well : context.wells(this->func)) + well_values.add_well(well, context.get(this->func, well)); + return well_values; + } else { + std::string arg_key = this->arg_list[0]; + for (size_t index = 1; index < this->arg_list.size(); index++) + arg_key += ":" + this->arg_list[index]; + return ActionValue(context.get(this->func, arg_key)); + } } } -bool ASTNode::eval(const ActionContext& context) const { +bool ASTNode::eval(const ActionContext& context, std::vector& matching_wells) const { if (this->children.size() == 0) throw std::invalid_argument("bool eval should not reach leafnodes"); @@ -228,39 +247,16 @@ bool ASTNode::eval(const ActionContext& context) const { bool value = (this->type == TokenType::op_and); for (const auto& child : this->children) { if (this->type == TokenType::op_or) - value = value || child.eval(context); + value = value || child.eval(context, matching_wells); else - value = value && child.eval(context); + value = value && child.eval(context, matching_wells); } return value; } - double v1 = this->children[0].value(context); - double v2 = this->children[1].value(context); - - switch (this->type) { - - case TokenType::op_eq: - return v1 == v2; - - case TokenType::op_ge: - return v1 >= v2; - - case TokenType::op_le: - return v1 <= v2; - - case TokenType::op_ne: - return v1 != v2; - - case TokenType::op_gt: - return v1 > v2; - - case TokenType::op_lt: - return v1 < v2; - - default: - throw std::invalid_argument("Incorrect operator type - expected comparison"); - } + auto v1 = this->children[0].value(context); + auto v2 = this->children[1].value(context); + return v1.eval_cmp(this->type, v2, matching_wells); } @@ -420,8 +416,8 @@ ActionAST::ActionAST(const std::vector& tokens) { throw std::invalid_argument("Failed to parse"); } -bool ActionAST::eval(const ActionContext& context) const { - return this->tree.eval(context); +bool ActionAST::eval(const ActionContext& context, std::vector& matching_wells) const { + return this->tree.eval(context, matching_wells); } } diff --git a/tests/parser/ACTIONX.cpp b/tests/parser/ACTIONX.cpp index fdbee51fd..14c8f29ad 100644 --- a/tests/parser/ACTIONX.cpp +++ b/tests/parser/ACTIONX.cpp @@ -252,15 +252,16 @@ BOOST_AUTO_TEST_CASE(TestActionAST_BASIC) { ActionAST ast3({"WWCT", "OPY", ">", "0.75"}); SummaryState st; ActionContext context(st); + std::vector matching_wells; context.add("WWCT", "OPX", 100); - BOOST_CHECK(ast1.eval(context)); + BOOST_CHECK(ast1.eval(context, matching_wells)); context.add("WWCT", "OPX", -100); - BOOST_CHECK(!ast1.eval(context)); + BOOST_CHECK(!ast1.eval(context, matching_wells)); - BOOST_CHECK(ast2.eval(context)); - BOOST_REQUIRE_THROW(ast3.eval(context), std::out_of_range); + BOOST_CHECK(ast2.eval(context, matching_wells)); + BOOST_REQUIRE_THROW(ast3.eval(context, matching_wells), std::out_of_range); } BOOST_AUTO_TEST_CASE(TestActionAST_OR_AND) { @@ -269,50 +270,53 @@ BOOST_AUTO_TEST_CASE(TestActionAST_OR_AND) { ActionAST par({"WWCT", "OPX", ">", "0.75", "AND", "(", "WWCT", "OPY", ">", "0.75", "OR", "WWCT", "OPZ", ">", "0.75", ")"}); SummaryState st; ActionContext context(st); + std::vector matching_wells; context.add("WWCT", "OPX", 100); context.add("WWCT", "OPY", -100); context.add("WWCT", "OPZ", 100); - BOOST_CHECK( ast_or.eval(context) ); - BOOST_CHECK( !ast_and.eval(context) ); - BOOST_CHECK( par.eval(context)); + BOOST_CHECK( ast_or.eval(context, matching_wells) ); + BOOST_CHECK( !ast_and.eval(context, matching_wells) ); + BOOST_CHECK( par.eval(context, matching_wells)); context.add("WWCT", "OPX", -100); context.add("WWCT", "OPY", 100); context.add("WWCT", "OPZ", 100); - BOOST_CHECK( ast_or.eval(context) ); - BOOST_CHECK( !ast_and.eval(context) ); - BOOST_CHECK( !par.eval(context)); + BOOST_CHECK( ast_or.eval(context, matching_wells) ); + BOOST_CHECK( !ast_and.eval(context, matching_wells) ); + BOOST_CHECK( !par.eval(context, matching_wells)); context.add("WWCT", "OPX", 100); context.add("WWCT", "OPY", 100); context.add("WWCT", "OPZ", -100); - BOOST_CHECK( ast_or.eval(context) ); - BOOST_CHECK( ast_and.eval(context) ); - BOOST_CHECK( par.eval(context)); + BOOST_CHECK( ast_or.eval(context, matching_wells) ); + BOOST_CHECK( ast_and.eval(context, matching_wells) ); + BOOST_CHECK( par.eval(context, matching_wells)); context.add("WWCT", "OPX", -100); context.add("WWCT", "OPY", -100); context.add("WWCT", "OPZ", -100); - BOOST_CHECK( !ast_or.eval(context) ); - BOOST_CHECK( !ast_and.eval(context) ); - BOOST_CHECK( !par.eval(context)); + BOOST_CHECK( !ast_or.eval(context, matching_wells) ); + BOOST_CHECK( !ast_and.eval(context, matching_wells) ); + BOOST_CHECK( !par.eval(context, matching_wells)); } BOOST_AUTO_TEST_CASE(DATE) { ActionAST ast({"MNTH", ">=", "JUN"}); SummaryState st; ActionContext context(st); + std::vector matching_wells; + context.add("MNTH", 6); - BOOST_CHECK( ast.eval(context) ); + BOOST_CHECK( ast.eval(context, matching_wells) ); context.add("MNTH", 8); - BOOST_CHECK( ast.eval(context) ); + BOOST_CHECK( ast.eval(context, matching_wells) ); context.add("MNTH", 5); - BOOST_CHECK( !ast.eval(context) ); + BOOST_CHECK( !ast.eval(context, matching_wells) ); } @@ -320,79 +324,82 @@ BOOST_AUTO_TEST_CASE(MANUAL1) { ActionAST ast({"GGPR", "FIELD", ">", "50000", "AND", "WGOR", "PR", ">" ,"GGOR", "FIELD"}); SummaryState st; ActionContext context(st); + std::vector matching_wells; context.add("GGPR", "FIELD", 60000 ); context.add("WGOR", "PR" , 300 ); context.add("GGOR", "FIELD", 200); - BOOST_CHECK( ast.eval(context) ); + BOOST_CHECK( ast.eval(context, matching_wells) ); context.add("GGPR", "FIELD", 0 ); context.add("WGOR", "PR" , 300 ); context.add("GGOR", "FIELD", 200); - BOOST_CHECK( !ast.eval(context) ); + BOOST_CHECK( !ast.eval(context, matching_wells) ); context.add("GGPR", "FIELD", 60000 ); context.add("WGOR", "PR" , 100 ); context.add("GGOR", "FIELD", 200); - BOOST_CHECK( !ast.eval(context) ); + BOOST_CHECK( !ast.eval(context, matching_wells) ); } BOOST_AUTO_TEST_CASE(MANUAL2) { ActionAST ast({"GWCT", "LIST1", ">", "0.70", "AND", "(", "GWPR", "LIST1", ">", "GWPR", "LIST2", "OR", "GWPR", "LIST1", ">", "GWPR", "LIST3", ")"}); SummaryState st; ActionContext context(st); + std::vector matching_wells; context.add("GWCT", "LIST1", 1.0); context.add("GWPR", "LIST1", 1 ); context.add("GWPR", "LIST2", 2 ); context.add("GWPR", "LIST3", 3 ); - BOOST_CHECK( !ast.eval(context)); + BOOST_CHECK( !ast.eval(context, matching_wells)); context.add("GWCT", "LIST1", 1.0); context.add("GWPR", "LIST1", 1 ); context.add("GWPR", "LIST2", 2 ); context.add("GWPR", "LIST3", 0 ); - BOOST_CHECK( ast.eval(context)); + BOOST_CHECK( ast.eval(context, matching_wells)); context.add("GWCT", "LIST1", 1.0); context.add("GWPR", "LIST1", 1 ); context.add("GWPR", "LIST2", 0 ); context.add("GWPR", "LIST3", 3 ); - BOOST_CHECK( ast.eval(context)); + BOOST_CHECK( ast.eval(context, matching_wells)); context.add("GWCT", "LIST1", 1.0); context.add("GWPR", "LIST1", 1 ); context.add("GWPR", "LIST2", 0 ); context.add("GWPR", "LIST3", 0 ); - BOOST_CHECK( ast.eval(context)); + BOOST_CHECK( ast.eval(context, matching_wells)); context.add("GWCT", "LIST1", 0.0); context.add("GWPR", "LIST1", 1 ); context.add("GWPR", "LIST2", 0 ); context.add("GWPR", "LIST3", 3 ); - BOOST_CHECK( !ast.eval(context)); + BOOST_CHECK( !ast.eval(context, matching_wells)); } BOOST_AUTO_TEST_CASE(MANUAL3) { ActionAST ast({"MNTH", ".GE.", "MAR", "AND", "MNTH", ".LE.", "OCT", "AND", "GMWL", "HIGH", ".GE.", "4"}); SummaryState st; ActionContext context(st); + std::vector matching_wells; context.add("MNTH", 4); context.add("GMWL", "HIGH", 4); - BOOST_CHECK( ast.eval(context)); + BOOST_CHECK( ast.eval(context, matching_wells)); context.add("MNTH", 3); context.add("GMWL", "HIGH", 4); - BOOST_CHECK( ast.eval(context)); + BOOST_CHECK( ast.eval(context, matching_wells)); context.add("MNTH", 11); context.add("GMWL", "HIGH", 4); - BOOST_CHECK( !ast.eval(context)); + BOOST_CHECK( !ast.eval(context, matching_wells)); context.add("MNTH", 3); context.add("GMWL", "HIGH", 3); - BOOST_CHECK( !ast.eval(context)); + BOOST_CHECK( !ast.eval(context, matching_wells)); } @@ -400,19 +407,20 @@ BOOST_AUTO_TEST_CASE(MANUAL4) { ActionAST ast({"GWCT", "FIELD", ">", "0.8", "AND", "DAY", ">", "1", "AND", "MNTH", ">", "JUN", "AND", "YEAR", ">=", "2021"}); SummaryState st; ActionContext context(st); + std::vector matching_wells; context.add("MNTH", 7); context.add("DAY", 2); context.add("YEAR", 2030); context.add("GWCT", "FIELD", 1.0); - BOOST_CHECK( ast.eval(context) ); + BOOST_CHECK( ast.eval(context, matching_wells) ); context.add("MNTH", 7); context.add("DAY", 2); context.add("YEAR", 2019); context.add("GWCT", "FIELD", 1.0); - BOOST_CHECK( !ast.eval(context) ); + BOOST_CHECK( !ast.eval(context, matching_wells) ); } @@ -421,6 +429,7 @@ BOOST_AUTO_TEST_CASE(MANUAL5) { ActionAST ast({"WCG2", "PROD1", ">", "WCG5", "PROD2", "AND", "GCG3", "G1", ">", "GCG7", "G2", "OR", "FCG1", ">", "FCG7"}); SummaryState st; ActionContext context(st); + std::vector matching_wells; context.add("WCG2", "PROD1", 100); context.add("WCG5", "PROD2", 50); @@ -428,7 +437,7 @@ BOOST_AUTO_TEST_CASE(MANUAL5) { context.add("GCG7", "G2", 100); context.add("FCG1", 100); context.add("FCG7", 50); - BOOST_CHECK(ast.eval(context)); + BOOST_CHECK(ast.eval(context, matching_wells)); context.add("WCG2", "PROD1", 100); context.add("WCG5", "PROD2", 50); @@ -436,7 +445,7 @@ BOOST_AUTO_TEST_CASE(MANUAL5) { context.add("GCG7", "G2", 100); context.add("FCG1", 100); context.add("FCG7", 150); - BOOST_CHECK(ast.eval(context)); + BOOST_CHECK(ast.eval(context, matching_wells)); context.add("WCG2", "PROD1", 100); context.add("WCG5", "PROD2", 50); @@ -444,7 +453,7 @@ BOOST_AUTO_TEST_CASE(MANUAL5) { context.add("GCG7", "G2", 100); context.add("FCG1", 100); context.add("FCG7", 150); - BOOST_CHECK(!ast.eval(context)); + BOOST_CHECK(!ast.eval(context, matching_wells)); context.add("WCG2", "PROD1", 100); context.add("WCG5", "PROD2", 50); @@ -452,7 +461,7 @@ BOOST_AUTO_TEST_CASE(MANUAL5) { context.add("GCG7", "G2", 100); context.add("FCG1", 200); context.add("FCG7", 150); - BOOST_CHECK(ast.eval(context)); + BOOST_CHECK(ast.eval(context, matching_wells)); } @@ -461,12 +470,13 @@ BOOST_AUTO_TEST_CASE(LGR) { ActionAST ast({"LWCC" , "OPX", "LOCAL", "1", "2", "3", ">", "100"}); SummaryState st; ActionContext context(st); + std::vector matching_wells; context.add("LWCC", "OPX:LOCAL:1:2:3", 200); - BOOST_CHECK(ast.eval(context)); + BOOST_CHECK(ast.eval(context, matching_wells)); context.add("LWCC", "OPX:LOCAL:1:2:3", 20); - BOOST_CHECK(!ast.eval(context)); + BOOST_CHECK(!ast.eval(context, matching_wells)); } @@ -513,3 +523,19 @@ BOOST_AUTO_TEST_CASE(ActionValueTest) { } + +BOOST_AUTO_TEST_CASE(TestMatchingWells) { + ActionAST ast({"WOPR", "*", ">", "1.0"}); + SummaryState st; + std::vector matching_wells; + + st.add_well_var("OPX", "WOPR", 0); + st.add_well_var("OPY", "WOPR", 0.50); + st.add_well_var("OPZ", "WOPR", 2.0); + + ActionContext context(st); + BOOST_CHECK( ast.eval(context, matching_wells) ); + + BOOST_CHECK_EQUAL( matching_wells.size(), 1); + BOOST_CHECK_EQUAL( matching_wells[0], "OPZ" ); +}