Merge pull request #3736 from blattms/backport-of-pr-3712

Better error handling for problems in conditions of ACTIONX
This commit is contained in:
Markus Blatt 2023-10-30 08:44:33 +01:00 committed by GitHub
commit 08ac19f242
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 117 additions and 28 deletions

View File

@ -314,6 +314,14 @@ class KeywordLocation;
*/
const static std::string ACTIONX_ILLEGAL_KEYWORD;
/*
Error flag marking parser errors ic ACTIONX conditions
*/
const static std::string ACTIONX_CONDITION_ERROR;
/*
Error flag marking that an ACTIONX has no condition
*/
const static std::string ACTIONX_NO_CONDITION;
/*
The RPTSCH, RPTSOL and RPTSCHED keywords have two alternative forms,

View File

@ -75,7 +75,10 @@ class ActionX {
public:
ActionX();
ActionX(const std::string& name, size_t max_run, double max_wait, std::time_t start_time);
ActionX(const DeckKeyword& kw, const Actdims& actimds, std::time_t start_time);
ActionX(const std::string& name, size_t max_run, double max_wait,
std::time_t start_time,
const std::vector<Condition>&& conditions,
const std::vector<std::string>&& tokens);
ActionX(const DeckRecord& record, std::time_t start_time);
explicit ActionX(const RestartIO::RstAction& rst_action);
@ -131,6 +134,15 @@ private:
std::vector<Condition> m_conditions;
};
/// \brief Parse ActionX keyword.
/// \param kw The keyword representation of ActionX
/// \param actdims Dimensions for ActionX as specified in the deck.
/// \param start_time The first time that the ActionX should be evaluated
/// \return A tuple of the ActionX created and a vector that contains for each error experienced
/// during parsing the string indicating the type of error and the error string itself.
std::tuple<ActionX, std::vector<std::pair<std::string, std::string>>>
parseActionX(const DeckKeyword& kw, const Actdims& actimds, std::time_t start_time);
}
}
#endif /* WELL_HPP_ */

View File

@ -114,6 +114,8 @@ namespace Opm {
this->addKey(SUMMARY_REGION_TOO_LARGE, InputErrorAction::WARN);
addKey(ACTIONX_ILLEGAL_KEYWORD, InputErrorAction::THROW_EXCEPTION);
addKey(ACTIONX_CONDITION_ERROR, InputErrorAction::THROW_EXCEPTION);
addKey(ACTIONX_NO_CONDITION, InputErrorAction::WARN);
addKey(RPT_MIXED_STYLE, InputErrorAction::WARN);
addKey(RPT_UNKNOWN_MNEMONIC, InputErrorAction::WARN);
@ -369,6 +371,8 @@ namespace Opm {
const std::string ParseContext::SCHEDULE_INVALID_NAME = "SCHEDULE_INVALID_NAME";
const std::string ParseContext::ACTIONX_ILLEGAL_KEYWORD = "ACTIONX_ILLEGAL_KEYWORD";
const std::string ParseContext::ACTIONX_CONDITION_ERROR = "ACTIONX_CONDITION_ERROR";
const std::string ParseContext::ACTIONX_NO_CONDITION = "ACTIONX_NO_CONDITION";
const std::string ParseContext::SIMULATOR_KEYWORD_NOT_SUPPORTED = "SIMULATOR_KEYWORD_NOT_SUPPORTED";
const std::string ParseContext::SIMULATOR_KEYWORD_NOT_SUPPORTED_CRITICAL = "SIMULATOR_KEYWORD_NOT_SUPPORTED_CRITICAL";

View File

@ -22,6 +22,8 @@
#include <cstdlib>
#include <stdexcept>
#include <fmt/format.h>
#include <opm/input/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include "ActionParser.hpp"
@ -128,7 +130,9 @@ ParseNode Parser::current() const {
Action::ASTNode Parser::parse_left() {
auto current = this->current();
if (current.type != TokenType::ecl_expr)
return TokenType::error;
throw std::invalid_argument(fmt::format("Expected expression as left hand side "
"of comparison, but got {} instead.",
current.value));
std::string func = current.value;
FuncType func_type = get_func(current.value);
@ -270,15 +274,17 @@ Action::ASTNode Parser::parse(const std::vector<std::string>& tokens) {
return ASTNode( start_node.type );
auto tree = parser.parse_or();
if (tree.type == TokenType::error)
throw std::invalid_argument("Failed to parse ACTIONX condition.");
auto current = parser.current();
if (current.type != TokenType::end) {
size_t index = parser.current_pos;
throw std::invalid_argument("Extra unhandled data starting with token[" + std::to_string(index) + "] = " + current.value);
throw std::invalid_argument("Extra unhandled data starting with token[" + std::to_string(index) + "] = " + current.value+
" in ACTIONX condition.");
}
if (tree.type == TokenType::error)
throw std::invalid_argument("Failed to parse");
return tree;
}
}

View File

@ -30,6 +30,7 @@
#include <opm/input/eclipse/Schedule/Action/Actdims.hpp>
#include <opm/input/eclipse/Schedule/Action/State.hpp>
#include <opm/input/eclipse/Schedule/Well/WellMatcher.hpp>
#include <opm/input/eclipse/Parser/ParseContext.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/W.hpp>
#include <opm/io/eclipse/rst/action.hpp>
#include <opm/common/utility/OpmInputError.hpp>
@ -73,6 +74,50 @@ bool ActionX::valid_keyword(const std::string& keyword) {
return (actionx_allowed_list.find(keyword) != actionx_allowed_list.end());
}
std::tuple<ActionX, std::vector<std::pair<std::string, std::string>>>
parseActionX(const DeckKeyword& kw, const Actdims& actdims,
std::time_t start_time)
{
std::vector<std::pair<std::string, std::string>> condition_errors;
std::vector<std::string> tokens;
std::vector<Condition> conditions;
auto record = kw.getRecord(0);
const std::string name = record.getItem("NAME").getTrimmedString(0);
for (size_t record_index = 1; record_index < kw.size(); record_index++) {
const auto& cond_tokens = RawString::strings( kw.getRecord(record_index)
.getItem("CONDITION").getData<RawString>() );
for (const auto& token : cond_tokens)
tokens.push_back(dequote(token, kw.location()));
conditions.emplace_back(cond_tokens, kw.location());
}
if (conditions.empty())
condition_errors.push_back({ParseContext::ACTIONX_NO_CONDITION,
fmt::format("Action {} is missing a condition.", name)});
if (conditions.size() > actdims.max_conditions())
condition_errors.push_back({ ParseContext::ACTIONX_CONDITION_ERROR,
fmt::format("Action {} has too many conditions - adjust item "
"4 of ACTDIMS to at least {}",
name, conditions.size())});
try
{
return { ActionX(name,
record.getItem("NUM").get<int>(0),
record.getItem("MIN_WAIT").getSIDouble(0),
start_time, std::move(conditions), std::move(tokens)),
condition_errors};
}
catch(const std::invalid_argument& e)
{
condition_errors.push_back({ ParseContext::ACTIONX_CONDITION_ERROR,
fmt::format("condition of action {} has the following error: {}", name, e.what())});
return {ActionX(kw.getRecord(0), start_time), condition_errors};
}
}
ActionX::ActionX() :
m_start_time(0)
@ -119,24 +164,13 @@ ActionX::ActionX(const DeckRecord& record, std::time_t start_time) :
ActionX::ActionX(const DeckKeyword& kw, const Actdims& actdims, std::time_t start_time) :
ActionX(kw.getRecord(0), start_time)
{
std::vector<std::string> tokens;
for (size_t record_index = 1; record_index < kw.size(); record_index++) {
const auto& record = kw.getRecord(record_index);
const auto& cond_tokens = RawString::strings( record.getItem("CONDITION").getData<RawString>() );
for (const auto& token : cond_tokens)
tokens.push_back(dequote(token, kw.location()));
this->m_conditions.emplace_back(cond_tokens, kw.location());
}
if (this->m_conditions.size() > actdims.max_conditions())
throw OpmInputError(fmt::format("Action {} has too many conditions - adjust item 4 of ACTDIMS to at least {}", this->name(), this->m_conditions.size()), kw.location());
this->condition = Action::AST(tokens);
}
ActionX::ActionX(const std::string& name, size_t max_run, double min_wait,
std::time_t start_time,
const std::vector<Condition>&& conditions,
const std::vector<std::string>&& tokens)
: m_name(name), m_max_run(max_run), m_min_wait(min_wait),
m_start_time(start_time), condition(tokens), m_conditions(conditions)
{}
ActionX ActionX::serializationTestObject()

View File

@ -665,9 +665,15 @@ void Schedule::iterateScheduleSection(std::size_t load_start, std::size_t load_e
logger.location(location);
if (keyword.is<ParserKeywords::ACTIONX>()) {
Action::ActionX action(keyword,
this->m_static.m_runspec.actdims(),
std::chrono::system_clock::to_time_t(this->snapshots[report_step].start_time()));
auto [action, condition_errors] =
Action::parseActionX(keyword,
this->m_static.m_runspec.actdims(),
std::chrono::system_clock::to_time_t(this->snapshots[report_step].start_time()));
for(const auto& [ marker, msg]: condition_errors) {
parseContext.handleError(marker, msg, keyword.location(), errors);
}
while (true) {
keyword_index++;
if (keyword_index == block.size())

View File

@ -87,8 +87,27 @@ ACTIONX
const auto deck = Parser{}.parseString( action_kw );
const auto& kw = deck["ACTIONX"].back();
Action::ActionX action2(kw, {}, 0);
const auto& [action2, condition_errors2 ] =
Action::parseActionX(kw, {}, 0);
BOOST_CHECK_EQUAL(action2.name(), "ACTION");
BOOST_CHECK_EQUAL(condition_errors2.size(), 0U);
// left hand side has to be an expression.
// Check whether we add an error to condition_errors
// if that is not the case
const auto action_kw_num_first = std::string{ R"(
ACTIONX
'ACTION' /
0.75 < WWCT OPX /
/
)"};
const auto deck1 = Parser{}.parseString( action_kw_num_first);
const auto& [action3, condition_errors3] =
Action::parseActionX(deck1["ACTIONX"].back(), {}, 0);
BOOST_CHECK_EQUAL(condition_errors3.size(), 1U);
}