diff --git a/opm/parser/eclipse/Parser/ParseContext.hpp b/opm/parser/eclipse/Parser/ParseContext.hpp index 09ceb5da0..13be801c8 100644 --- a/opm/parser/eclipse/Parser/ParseContext.hpp +++ b/opm/parser/eclipse/Parser/ParseContext.hpp @@ -198,6 +198,14 @@ class KeywordLocation; */ const static std::string PARSE_MISSING_INCLUDE; + /* + For certain keywords, other, specific keywords are either + required or prohibited. When such keywords are found in an + invalid combination (missing required or present prohibited + keyword), this error situation occurs. + */ + const static std::string PARSE_INVALID_KEYWORD_COMBINATION; + /// Dynamic number of wells exceeds maximum declared in /// RUNSPEC keyword WELLDIMS (item 1). const static std::string RUNSPEC_NUMWELLS_TOO_LARGE; diff --git a/opm/parser/eclipse/Parser/ParserKeyword.hpp b/opm/parser/eclipse/Parser/ParserKeyword.hpp index f2c381cf5..3b2eb607d 100644 --- a/opm/parser/eclipse/Parser/ParserKeyword.hpp +++ b/opm/parser/eclipse/Parser/ParserKeyword.hpp @@ -171,6 +171,8 @@ namespace Opm { void initMatchRegex( const Json::JsonObject& jsonObject ); void initCode( const Json::JsonObject& jsonConfig ); void initData( const Json::JsonObject& jsonConfig ); + void initProhibitedKeywords(const Json::JsonObject& keywordList); + void initRequiredKeywords(const Json::JsonObject& keywordList); void initSize( const Json::JsonObject& jsonConfig ); void initSizeKeyword(const Json::JsonObject& sizeObject); void commonInit(const std::string& name, ParserKeywordSizeEnum sizeType); diff --git a/src/opm/parser/eclipse/Parser/ParseContext.cpp b/src/opm/parser/eclipse/Parser/ParseContext.cpp index 1d47467e4..91f40d4f9 100644 --- a/src/opm/parser/eclipse/Parser/ParseContext.cpp +++ b/src/opm/parser/eclipse/Parser/ParseContext.cpp @@ -81,6 +81,7 @@ namespace Opm { addKey(PARSE_MISSING_INCLUDE, InputError::EXIT1); addKey(PARSE_LONG_KEYWORD, InputError::WARN); addKey(PARSE_WGNAME_SPACE, InputError::THROW_EXCEPTION); + addKey(PARSE_INVALID_KEYWORD_COMBINATION, InputError::THROW_EXCEPTION); addKey(UNIT_SYSTEM_MISMATCH, InputError::THROW_EXCEPTION); @@ -325,6 +326,7 @@ namespace Opm { const std::string ParseContext::PARSE_MISSING_INCLUDE = "PARSE_MISSING_INCLUDE"; const std::string ParseContext::PARSE_LONG_KEYWORD = "PARSE_LONG_KEYWORD"; const std::string ParseContext::PARSE_WGNAME_SPACE = "PARSE_WGNAME_SPACE"; + const std::string ParseContext::PARSE_INVALID_KEYWORD_COMBINATION = "PARSE_INVALID_KEYWORD_COMBINATION"; const std::string ParseContext::UNIT_SYSTEM_MISMATCH = "UNIT_SYSTEM_MISMATCH"; diff --git a/src/opm/parser/eclipse/Parser/Parser.cpp b/src/opm/parser/eclipse/Parser/Parser.cpp index 233291b47..8739e6ee6 100644 --- a/src/opm/parser/eclipse/Parser/Parser.cpp +++ b/src/opm/parser/eclipse/Parser/Parser.cpp @@ -581,6 +581,32 @@ void ParserState::addPathAlias( const std::string& alias, const std::string& pat RawKeyword * newRawKeyword(const ParserKeyword& parserKeyword, const std::string& keywordString, ParserState& parserState, const Parser& parser) { + for (const auto& keyword : parserKeyword.prohibitedKeywords()) { + if (parserState.deck.hasKeyword(keyword)) { + parserState + .parseContext + .handleError( + ParseContext::PARSE_INVALID_KEYWORD_COMBINATION, + fmt::format("Incompatible keyword combination: {} declared when {} is already present.", keywordString, keyword), + KeywordLocation { keywordString, parserState.current_path(), parserState.line() } , + parserState.errors + ); + } + } + + for (const auto& keyword : parserKeyword.requiredKeywords()) { + if (!parserState.deck.hasKeyword(keyword)) { + parserState + .parseContext + .handleError( + ParseContext::PARSE_INVALID_KEYWORD_COMBINATION, + fmt::format("Incompatible keyword combination: {} declared, but {} is missing.", keywordString, keyword), + KeywordLocation { keywordString, parserState.current_path(), parserState.line() } , + parserState.errors + ); + } + } + bool raw_string_keyword = parserKeyword.rawStringKeyword(); if( parserKeyword.getSizeType() == SLASH_TERMINATED || parserKeyword.getSizeType() == UNKNOWN || parserKeyword.getSizeType() == DOUBLE_SLASH_TERMINATED) { diff --git a/src/opm/parser/eclipse/Parser/ParserKeyword.cpp b/src/opm/parser/eclipse/Parser/ParserKeyword.cpp index 3e621ee08..5e4303fc0 100644 --- a/src/opm/parser/eclipse/Parser/ParserKeyword.cpp +++ b/src/opm/parser/eclipse/Parser/ParserKeyword.cpp @@ -189,6 +189,14 @@ namespace Opm { initSectionNames(jsonConfig); initMatchRegex(jsonConfig); + if (jsonConfig.has_item("prohibits")) { + initProhibitedKeywords(jsonConfig.get_item("prohibits")); + } + + if (jsonConfig.has_item("requires")) { + initRequiredKeywords(jsonConfig.get_item("requires")); + } + if (jsonConfig.has_item("items") && (jsonConfig.has_item("records") || jsonConfig.has_item("alternating_records") || jsonConfig.has_item("records_set") )) @@ -227,7 +235,33 @@ namespace Opm { } + void ParserKeyword::initProhibitedKeywords(const Json::JsonObject& keywordList) { + if (!keywordList.is_array()) + throw std::invalid_argument("The 'prohibits' JSON item of keyword "+m_name+" needs to be a list"); + for (std::size_t keywordIndex { 0 } ; keywordIndex != keywordList.size() ; ++keywordIndex) { + const auto keywordObject { keywordList.get_array_item(keywordIndex) } ; + + if (!keywordObject.is_string()) + throw std::invalid_argument("The sub-items of 'prohibits' of keyword "+m_name+" need to be strings"); + + m_prohibits.emplace_back(keywordObject.as_string()); + } + } + + void ParserKeyword::initRequiredKeywords(const Json::JsonObject& keywordList) { + if (!keywordList.is_array()) + throw std::invalid_argument("The 'requires' JSON item of keyword "+m_name+" needs to be a list"); + + for (std::size_t keywordIndex { 0 } ; keywordIndex != keywordList.size() ; ++keywordIndex) { + const auto keywordObject { keywordList.get_array_item(keywordIndex) } ; + + if (!keywordObject.is_string()) + throw std::invalid_argument("The sub-items of 'requires' of keyword "+m_name+" need to be strings"); + + m_requires.emplace_back(keywordObject.as_string()); + } + } void ParserKeyword::initSizeKeyword(const std::string& sizeKeyword, const std::string& sizeItem, int size_shift) { keyword_size = KeywordSize(sizeKeyword, sizeItem, size_shift); @@ -742,6 +776,23 @@ void set_dimensions( ParserItem& item, ss << indent << "addValidSectionName(\"" << *sectionNameIt << "\");" << '\n'; } + // set required and prohibited keywords + if (!m_prohibits.empty()) { + ss << indent << "setProhibitedKeywords({" << '\n'; + for (const auto& keyword : m_prohibits) { + ss << indent << indent << '"' << keyword << '"' << "," << '\n'; + } + ss << indent << "});" << '\n'; + } + + if (!m_requires.empty()) { + ss << indent << "setRequiredKeywords({" << '\n'; + for (const auto& keyword : m_requires) { + ss << indent << indent << '"' << keyword << '"' << "," << '\n'; + } + ss << indent << "});" << '\n'; + } + // add the deck names ss << indent << "clearDeckNames();\n"; for (auto deckNameIt = m_deckNames.begin(); diff --git a/tests/parser/ParseContextTests.cpp b/tests/parser/ParseContextTests.cpp index 2a0aef92b..28fcd8c5d 100644 --- a/tests/parser/ParseContextTests.cpp +++ b/tests/parser/ParseContextTests.cpp @@ -514,6 +514,38 @@ BOOST_AUTO_TEST_CASE(test_1arg_constructor) { } } +BOOST_AUTO_TEST_CASE( test_invalid_keyword_combination_required ) { + const std::string deckString = R"( +AQUCT + 1 2000.0 1.5 100 .3 3.0e-5 330 10 360.0 1 2 / +)"; + + ParseContext parseContext; + Parser parser; + ErrorGuard errors; + + BOOST_CHECK_THROW(parser.parseString(deckString, parseContext, errors), OpmInputError); +} + +BOOST_AUTO_TEST_CASE( test_invalid_keyword_combination_prohibited ) { + const std::string deckString = R"( +EQLDIMS +/ + +TEMPVD + 0.5 0 / + +RTEMPVD + 0.5 0 / +)"; + + ParseContext parseContext; + Parser parser; + ErrorGuard errors; + + BOOST_CHECK_THROW(parser.parseString(deckString, parseContext, errors), OpmInputError); +} + BOOST_AUTO_TEST_CASE( test_invalid_wtemplate_config ) { const std::string defDeckString = R"( START -- 0