diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake
index c8d100823..e650bb71c 100644
--- a/CMakeLists_files.cmake
+++ b/CMakeLists_files.cmake
@@ -74,6 +74,7 @@ if(ENABLE_ECL_INPUT)
src/opm/parser/eclipse/EclipseState/IOConfig/IOConfig.cpp
src/opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.cpp
src/opm/parser/eclipse/EclipseState/Runspec.cpp
+ src/opm/parser/eclipse/EclipseState/Schedule/ActionAST.cpp
src/opm/parser/eclipse/EclipseState/Schedule/ActionContext.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Actions.cpp
src/opm/parser/eclipse/EclipseState/Schedule/ActionX.cpp
@@ -440,6 +441,7 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/EclipseState/Aquancon.hpp
opm/parser/eclipse/EclipseState/AquiferCT.hpp
opm/parser/eclipse/EclipseState/Aquifetp.hpp
+ opm/parser/eclipse/EclipseState/Schedule/ActionAST.hpp
opm/parser/eclipse/EclipseState/Schedule/ActionContext.hpp
opm/parser/eclipse/EclipseState/Schedule/Actions.hpp
opm/parser/eclipse/EclipseState/Schedule/ActionX.hpp
diff --git a/opm/parser/eclipse/EclipseState/Schedule/ActionAST.hpp b/opm/parser/eclipse/EclipseState/Schedule/ActionAST.hpp
new file mode 100644
index 000000000..642986f45
--- /dev/null
+++ b/opm/parser/eclipse/EclipseState/Schedule/ActionAST.hpp
@@ -0,0 +1,141 @@
+/*
+ Copyright 2018 Equinor ASA.
+
+ This file is part of the Open Porous Media project (OPM).
+
+ OPM is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OPM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OPM. If not, see .
+*/
+
+
+#ifndef ActionAST_HPP
+#define ActionAST_HPP
+
+#include
+#include
+
+
+namespace Opm {
+
+class ActionContext;
+
+enum TokenType {
+ number, // 0
+ ecl_expr, // 1
+ open_paren, // 2
+ close_paren, // 3
+ op_gt, // 4
+ op_ge, // 5
+ op_lt, // 6
+ op_le, // 7
+ op_eq, // 8
+ op_ne, // 9
+ op_and, // 10
+ op_or, // 11
+ end, // 12
+ error // 13
+};
+
+struct ParseNode {
+ ParseNode(TokenType type, const std::string& value) :
+ type(type),
+ value(value)
+ {}
+
+
+ ParseNode(TokenType type) : ParseNode(type, "")
+ {}
+
+
+ TokenType type;
+ std::string value;
+};
+
+
+class ASTNode {
+public:
+
+ASTNode() :
+ type(TokenType::error)
+{}
+
+
+ASTNode(TokenType type):
+ type(type)
+{}
+
+
+ASTNode(double value) :
+ type(TokenType::number),
+ number(value)
+{}
+
+
+ASTNode(TokenType type, const std::string& func, const std::vector& arg_list):
+ type(type),
+ func(func),
+ arg_list(arg_list)
+{}
+
+ bool eval(const ActionContext& context) const;
+ double value(const ActionContext& context) const;
+ TokenType type;
+ void add_child(const ASTNode& child);
+ size_t size() const;
+
+private:
+ std::string func;
+ std::vector arg_list;
+ double number;
+
+ /*
+ To have a memmber std::vector inside the ASTNode class is
+ supposedly borderline undefined behaviour; it compiles without warnings
+ and works. Good for enough for me.
+ */
+ std::vector children;
+};
+
+
+
+class ActionParser {
+public:
+ ActionParser(const std::vector& tokens);
+ TokenType get_type(const std::string& arg) const;
+ ParseNode current() const;
+ ParseNode next();
+ size_t pos() const;
+ void print() const;
+private:
+ const std::vector& tokens;
+ ssize_t current_pos = -1;
+};
+
+
+class ActionAST{
+public:
+ ActionAST() = default;
+ explicit ActionAST(const std::vector& tokens);
+ ASTNode parse_right(ActionParser& parser);
+ ASTNode parse_left(ActionParser& parser);
+ ASTNode parse_op(ActionParser& parser);
+ ASTNode parse_cmp(ActionParser& parser);
+ ASTNode parse_or(ActionParser& parser);
+ ASTNode parse_and(ActionParser& parser);
+
+ bool eval(const ActionContext& context) const;
+private:
+ ASTNode tree;
+};
+}
+#endif
diff --git a/opm/parser/eclipse/EclipseState/Schedule/ActionContext.hpp b/opm/parser/eclipse/EclipseState/Schedule/ActionContext.hpp
index bb507704a..d82fdf741 100644
--- a/opm/parser/eclipse/EclipseState/Schedule/ActionContext.hpp
+++ b/opm/parser/eclipse/EclipseState/Schedule/ActionContext.hpp
@@ -33,9 +33,14 @@ namespace Opm {
class ActionContext {
public:
+ ActionContext();
+
double get(const std::string& func, const std::string& arg) const;
void add(const std::string& func, const std::string& arg, double value);
+ double get(const std::string& func) const;
+ void add(const std::string& func, double value);
+
private:
std::map values;
};
diff --git a/opm/parser/eclipse/EclipseState/Schedule/ActionX.hpp b/opm/parser/eclipse/EclipseState/Schedule/ActionX.hpp
index 1f3b62f2c..d1f8eea92 100644
--- a/opm/parser/eclipse/EclipseState/Schedule/ActionX.hpp
+++ b/opm/parser/eclipse/EclipseState/Schedule/ActionX.hpp
@@ -26,6 +26,7 @@
#include
#include
+#include
namespace Opm {
/*
@@ -78,6 +79,7 @@ private:
std::time_t m_start_time;
std::vector keywords;
+ ActionAST ast;
size_t run_count = 0;
std::time_t last_run = 0;
};
diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/ActionAST.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/ActionAST.cpp
new file mode 100644
index 000000000..a9ec63bcb
--- /dev/null
+++ b/src/opm/parser/eclipse/EclipseState/Schedule/ActionAST.cpp
@@ -0,0 +1,337 @@
+/*
+ Copyright 2018 Equinor ASA.
+
+ This file is part of the Open Porous Media project (OPM).
+
+ OPM is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OPM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OPM. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+namespace Opm {
+
+ActionParser::ActionParser(const std::vector& tokens) :
+ tokens(tokens)
+{}
+
+
+TokenType ActionParser::get_type(const std::string& arg) const {
+ std::string lower_arg = arg;
+ std::for_each(lower_arg.begin(),
+ lower_arg.end(),
+ [](char& c) {
+ c = std::tolower(static_cast(c));
+ });
+
+ if (lower_arg == "and")
+ return TokenType::op_and;
+
+ if (lower_arg == "or")
+ return TokenType::op_or;
+
+ if (lower_arg == "(")
+ return TokenType::open_paren;
+
+ if (lower_arg == ")")
+ return TokenType::close_paren;
+
+ if (lower_arg == ">" || lower_arg == ".gt.")
+ return TokenType::op_gt;
+
+ if (lower_arg == ">=" || lower_arg == ".ge.")
+ return TokenType::op_ge;
+
+ if (lower_arg == "<=" || lower_arg == ".le.")
+ return TokenType::op_le;
+
+ if (lower_arg == "<" || lower_arg == ".lt.")
+ return TokenType::op_lt;
+
+ if (lower_arg == "<=" || lower_arg == ".le.")
+ return TokenType::op_le;
+
+ if (lower_arg == "=" || lower_arg == ".eq.")
+ return TokenType::op_eq;
+
+ if (lower_arg == "!=" || lower_arg == ".ne.")
+ return TokenType::op_ne;
+
+ {
+ char * end_ptr;
+ strtod(lower_arg.c_str(), &end_ptr);
+ if (std::strlen(end_ptr) == 0)
+ return TokenType::number;
+ }
+
+ return TokenType::ecl_expr;
+}
+
+ParseNode ActionParser::next() {
+ this->current_pos++;
+ if (static_cast(this->current_pos) == this->tokens.size())
+ return TokenType::end;
+
+ std::string arg = this->tokens[this->current_pos];
+ return ParseNode(get_type(arg), arg);
+}
+
+
+ParseNode ActionParser::current() const {
+ if (static_cast(this->current_pos) == this->tokens.size())
+ return TokenType::end;
+
+ std::string arg = this->tokens[this->current_pos];
+ return ParseNode(get_type(arg), arg);
+}
+
+
+size_t ActionParser::pos() const {
+ return this->current_pos;
+}
+
+/*****************************************************************/
+
+void ASTNode::add_child(const ASTNode& child) {
+ this->children.push_back(child);
+}
+
+double 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;
+
+ if (this->arg_list.size() == 0)
+ return 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);
+ }
+}
+
+
+bool ASTNode::eval(const ActionContext& context) const {
+ if (this->children.size() == 0)
+ throw std::invalid_argument("bool eval should not reach leafnodes");
+
+ if (this->type == TokenType::op_or || this->type == TokenType::op_and) {
+ bool value = (this->type == TokenType::op_and);
+ for (const auto& child : this->children) {
+ if (this->type == TokenType::op_or)
+ value = value || child.eval(context);
+ else
+ value = value && child.eval(context);
+ }
+ 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");
+ }
+}
+
+
+size_t ASTNode::size() const {
+ return this->children.size();
+}
+
+
+/*****************************************************************/
+
+ASTNode ActionAST::parse_left(ActionParser& parser) {
+ auto current = parser.current();
+ if (current.type != TokenType::ecl_expr)
+ return TokenType::error;
+
+ std::string func = current.value;
+ std::vector arg_list;
+ current = parser.next();
+ while (current.type == TokenType::ecl_expr || current.type == TokenType::number) {
+ arg_list.push_back(current.value);
+ current = parser.next();
+ }
+
+ return ASTNode(TokenType::ecl_expr, func, arg_list);
+}
+
+ASTNode ActionAST::parse_op(ActionParser& parser) {
+ auto current = parser.current();
+ if (current.type == TokenType::op_gt ||
+ current.type == TokenType::op_ge ||
+ current.type == TokenType::op_lt ||
+ current.type == TokenType::op_le ||
+ current.type == TokenType::op_eq ||
+ current.type == TokenType::op_ne) {
+ parser.next();
+ return current.type;
+ }
+ return TokenType::error;
+}
+
+
+ASTNode ActionAST::parse_right(ActionParser& parser) {
+ auto current = parser.current();
+ if (current.type == TokenType::number) {
+ parser.next();
+ return ASTNode( strtod(current.value.c_str(), nullptr) );
+ }
+
+ current = parser.current();
+ if (current.type != TokenType::ecl_expr)
+ return TokenType::error;
+
+ std::string func = current.value;
+ std::vector arg_list;
+ current = parser.next();
+ while (current.type == TokenType::ecl_expr || current.type == TokenType::number) {
+ arg_list.push_back(current.value);
+ current = parser.next();
+ }
+ return ASTNode(TokenType::ecl_expr, func, arg_list);
+}
+
+
+
+ASTNode ActionAST::parse_cmp(ActionParser& parser) {
+ auto current = parser.current();
+
+ if (current.type == TokenType::open_paren) {
+ parser.next();
+ auto inner_expr = this->parse_or(parser);
+
+ current = parser.current();
+ if (current.type != TokenType::close_paren)
+ return TokenType::error;
+
+ parser.next();
+ return inner_expr;
+ } else {
+ auto left_node = parse_left(parser);
+ if (left_node.type == TokenType::error)
+ return TokenType::error;
+
+ auto op_node = parse_op(parser);
+ if (op_node.type == TokenType::error)
+ return TokenType::error;
+
+ auto right_node = parse_right(parser);
+ if (right_node.type == TokenType::error)
+ return TokenType::error;
+
+ op_node.add_child(left_node);
+ op_node.add_child(right_node);
+ return op_node;
+ }
+}
+
+ASTNode ActionAST::parse_and(ActionParser& parser) {
+ auto left = this->parse_cmp(parser);
+ if (left.type == TokenType::error)
+ return TokenType::error;
+
+ auto current = parser.current();
+ if (current.type == TokenType::op_and) {
+ ASTNode and_node(TokenType::op_and);
+ and_node.add_child(left);
+
+ while (parser.current().type == TokenType::op_and) {
+ parser.next();
+ auto next_cmp = this->parse_cmp(parser);
+ if (next_cmp.type == TokenType::error)
+ return TokenType::error;
+
+ and_node.add_child(next_cmp);
+ }
+ return and_node;
+ }
+
+ return left;
+}
+
+
+ASTNode ActionAST::parse_or(ActionParser& parser) {
+ auto left = this->parse_and(parser);
+ if (left.type == TokenType::error)
+ return TokenType::error;
+
+ auto current = parser.current();
+ if (current.type == TokenType::op_or) {
+ ASTNode or_node(TokenType::op_or);
+ or_node.add_child(left);
+
+ while (parser.current().type == TokenType::op_or) {
+ parser.next();
+ auto next_cmp = this->parse_or(parser);
+ if (next_cmp.type == TokenType::error)
+ return TokenType::error;
+
+ or_node.add_child(next_cmp);
+ }
+ return or_node;
+ }
+
+ return left;
+}
+
+ActionAST::ActionAST(const std::vector& tokens) {
+ ActionParser parser(tokens);
+ auto current = parser.next();
+ this->tree = this->parse_or(parser);
+ current = parser.current();
+ if (current.type != TokenType::end) {
+ size_t index = parser.pos();
+ throw std::invalid_argument("Extra unhandled data starting with token[" + std::to_string(index) + "] = " + current.value);
+ }
+
+ if (this->tree.type == TokenType::error)
+ throw std::invalid_argument("Failed to parse");
+}
+
+bool ActionAST::eval(const ActionContext& context) const {
+ return this->tree.eval(context);
+}
+
+}
diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/ActionContext.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/ActionContext.cpp
index d13bcd9e0..25a69b953 100644
--- a/src/opm/parser/eclipse/EclipseState/Schedule/ActionContext.cpp
+++ b/src/opm/parser/eclipse/EclipseState/Schedule/ActionContext.cpp
@@ -18,15 +18,28 @@
*/
#include
+#include
namespace Opm {
+ ActionContext::ActionContext() {
+ for (const auto& pair : TimeMap::eclipseMonthIndices())
+ this->add(pair.first, pair.second);
+ }
+
void ActionContext::add(const std::string& func, const std::string& arg, double value) {
this->values[func + ":" + arg] = value;
}
+ void ActionContext::add(const std::string& func, double value) {
+ this->values[func] = value;
+ }
double ActionContext::get(const std::string& func, const std::string& arg) const {
return this->values.at( func + ":" + arg );
}
+
+ double ActionContext::get(const std::string& func) const {
+ return this->values.at( func );
+ }
}
diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/ActionX.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/ActionX.cpp
index 070455461..561a8d3b7 100644
--- a/src/opm/parser/eclipse/EclipseState/Schedule/ActionX.cpp
+++ b/src/opm/parser/eclipse/EclipseState/Schedule/ActionX.cpp
@@ -45,7 +45,15 @@ ActionX::ActionX(const DeckRecord& record, std::time_t start_time) :
ActionX::ActionX(const DeckKeyword& kw, std::time_t start_time) :
ActionX(kw.getRecord(0), start_time)
-{}
+{
+ std::vector tokens;
+ for (size_t record_index = 1; record_index < kw.size(); record_index++) {
+ const auto& record = kw.getRecord(record_index);
+ for (const auto& token : record.getItem("CONDITION").getData())
+ tokens.push_back(token);
+ }
+ this->ast = ActionAST(tokens);
+}
void ActionX::addKeyword(const DeckKeyword& kw) {
diff --git a/tests/parser/ACTIONX.cpp b/tests/parser/ACTIONX.cpp
index 94560990f..44c06ff25 100644
--- a/tests/parser/ACTIONX.cpp
+++ b/tests/parser/ACTIONX.cpp
@@ -30,6 +30,7 @@
#include
#include
+#include
#include
#include
#include
@@ -198,3 +199,251 @@ BOOST_AUTO_TEST_CASE(TestContext) {
context.add("FUNC", "ARG", 100);
BOOST_CHECK_EQUAL(context.get("FUNC", "ARG"), 100);
}
+
+
+
+Opm::Schedule make_action(const std::string& action_string) {
+ std::string start = std::string{ R"(
+SCHEDULE
+)"};
+ std::string end = std::string{ R"(
+ENDACTIO
+
+TSTEP
+ 10 /
+)"};
+
+ std::string deck_string = start + action_string + end;
+ Opm::Parser parser;
+ auto deck = parser.parseString(deck_string, Opm::ParseContext());
+ EclipseGrid grid1(10,10,10);
+ TableManager table ( deck );
+ Eclipse3DProperties eclipseProperties ( deck , table, grid1);
+ Runspec runspec(deck);
+
+ return Schedule(deck, grid1, eclipseProperties, runspec, ParseContext());
+}
+
+
+BOOST_AUTO_TEST_CASE(TestActionAST_BASIC) {
+ // Missing comparator
+ BOOST_REQUIRE_THROW( ActionAST( {"WWCT", "OPX", "0.75"} ), std::invalid_argument);
+
+ // Left hand side must be function expression
+ BOOST_REQUIRE_THROW( ActionAST({"0.75", "<", "1.0"}), std::invalid_argument);
+
+ //Extra data
+ BOOST_REQUIRE_THROW(ActionAST({"0.75", "<", "1.0", "EXTRA"}), std::invalid_argument);
+
+ ActionAST ast1({"WWCT", "OPX", ">", "0.75"});
+ ActionAST ast2({"WWCT", "OPX", "=", "WWCT", "OPX"});
+ ActionAST ast3({"WWCT", "OPY", ">", "0.75"});
+ ActionContext context;
+
+ context.add("WWCT", "OPX", 100);
+ BOOST_CHECK(ast1.eval(context));
+
+ context.add("WWCT", "OPX", -100);
+ BOOST_CHECK(!ast1.eval(context));
+
+ BOOST_CHECK(ast2.eval(context));
+ BOOST_REQUIRE_THROW(ast3.eval(context), std::out_of_range);
+}
+
+BOOST_AUTO_TEST_CASE(TestActionAST_OR_AND) {
+ ActionAST ast_or({"WWCT", "OPX", ">", "0.75", "OR", "WWCT", "OPY", ">", "0.75"});
+ ActionAST ast_and({"WWCT", "OPX", ">", "0.75", "AND", "WWCT", "OPY", ">", "0.75"});
+ ActionAST par({"WWCT", "OPX", ">", "0.75", "AND", "(", "WWCT", "OPY", ">", "0.75", "OR", "WWCT", "OPZ", ">", "0.75", ")"});
+ ActionContext context;
+
+ 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));
+
+
+ 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));
+
+
+ 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));
+
+ 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_AUTO_TEST_CASE(DATE) {
+ ActionAST ast({"MNTH", ">=", "JUN"});
+ ActionContext context;
+ context.add("MNTH", 6);
+ BOOST_CHECK( ast.eval(context) );
+
+ context.add("MNTH", 8);
+ BOOST_CHECK( ast.eval(context) );
+
+ context.add("MNTH", 5);
+ BOOST_CHECK( !ast.eval(context) );
+}
+
+
+BOOST_AUTO_TEST_CASE(MANUAL1) {
+ ActionAST ast({"GGPR", "FIELD", ">", "50000", "AND", "WGOR", "PR", ">" ,"GGOR", "FIELD"});
+ ActionContext context;
+
+ context.add("GGPR", "FIELD", 60000 );
+ context.add("WGOR", "PR" , 300 );
+ context.add("GGOR", "FIELD", 200);
+ BOOST_CHECK( ast.eval(context) );
+
+ context.add("GGPR", "FIELD", 0 );
+ context.add("WGOR", "PR" , 300 );
+ context.add("GGOR", "FIELD", 200);
+ BOOST_CHECK( !ast.eval(context) );
+
+ context.add("GGPR", "FIELD", 60000 );
+ context.add("WGOR", "PR" , 100 );
+ context.add("GGOR", "FIELD", 200);
+ BOOST_CHECK( !ast.eval(context) );
+}
+
+BOOST_AUTO_TEST_CASE(MANUAL2) {
+ ActionAST ast({"GWCT", "LIST1", ">", "0.70", "AND", "(", "GWPR", "LIST1", ">", "GWPR", "LIST2", "OR", "GWPR", "LIST1", ">", "GWPR", "LIST3", ")"});
+ ActionContext context;
+
+ 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));
+
+ 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));
+
+ 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));
+
+ 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));
+
+ 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_AUTO_TEST_CASE(MANUAL3) {
+ ActionAST ast({"MNTH", ".GE.", "MAR", "AND", "MNTH", ".LE.", "OCT", "AND", "GMWL", "HIGH", ".GE.", "4"});
+ ActionContext context;
+
+ context.add("MNTH", 4);
+ context.add("GMWL", "HIGH", 4);
+ BOOST_CHECK( ast.eval(context));
+
+ context.add("MNTH", 3);
+ context.add("GMWL", "HIGH", 4);
+ BOOST_CHECK( ast.eval(context));
+
+ context.add("MNTH", 11);
+ context.add("GMWL", "HIGH", 4);
+ BOOST_CHECK( !ast.eval(context));
+
+ context.add("MNTH", 3);
+ context.add("GMWL", "HIGH", 3);
+ BOOST_CHECK( !ast.eval(context));
+}
+
+
+BOOST_AUTO_TEST_CASE(MANUAL4) {
+ ActionAST ast({"GWCT", "FIELD", ">", "0.8", "AND", "DAY", ">", "1", "AND", "MNTH", ">", "JUN", "AND", "YEAR", ">=", "2021"});
+ ActionContext context;
+
+
+ context.add("MNTH", 7);
+ context.add("DAY", 2);
+ context.add("YEAR", 2030);
+ context.add("GWCT", "FIELD", 1.0);
+ BOOST_CHECK( ast.eval(context) );
+
+ context.add("MNTH", 7);
+ context.add("DAY", 2);
+ context.add("YEAR", 2019);
+ context.add("GWCT", "FIELD", 1.0);
+ BOOST_CHECK( !ast.eval(context) );
+}
+
+
+
+BOOST_AUTO_TEST_CASE(MANUAL5) {
+ ActionAST ast({"WCG2", "PROD1", ">", "WCG5", "PROD2", "AND", "GCG3", "G1", ">", "GCG7", "G2", "OR", "FCG1", ">", "FCG7"});
+ ActionContext context;
+
+ context.add("WCG2", "PROD1", 100);
+ context.add("WCG5", "PROD2", 50);
+ context.add("GCG3", "G1", 200);
+ context.add("GCG7", "G2", 100);
+ context.add("FCG1", 100);
+ context.add("FCG7", 50);
+ BOOST_CHECK(ast.eval(context));
+
+ context.add("WCG2", "PROD1", 100);
+ context.add("WCG5", "PROD2", 50);
+ context.add("GCG3", "G1", 200);
+ context.add("GCG7", "G2", 100);
+ context.add("FCG1", 100);
+ context.add("FCG7", 150);
+ BOOST_CHECK(ast.eval(context));
+
+ context.add("WCG2", "PROD1", 100);
+ context.add("WCG5", "PROD2", 50);
+ context.add("GCG3", "G1", 20);
+ context.add("GCG7", "G2", 100);
+ context.add("FCG1", 100);
+ context.add("FCG7", 150);
+ BOOST_CHECK(!ast.eval(context));
+
+ context.add("WCG2", "PROD1", 100);
+ context.add("WCG5", "PROD2", 50);
+ context.add("GCG3", "G1", 20);
+ context.add("GCG7", "G2", 100);
+ context.add("FCG1", 200);
+ context.add("FCG7", 150);
+ BOOST_CHECK(ast.eval(context));
+}
+
+
+
+BOOST_AUTO_TEST_CASE(LGR) {
+ ActionAST ast({"LWCC" , "OPX", "LOCAL", "1", "2", "3", ">", "100"});
+ ActionContext context;
+
+ context.add("LWCC", "OPX:LOCAL:1:2:3", 200);
+ BOOST_CHECK(ast.eval(context));
+
+ context.add("LWCC", "OPX:LOCAL:1:2:3", 20);
+ BOOST_CHECK(!ast.eval(context));
+}