From 40737227713932e6db67bce980baf64b7fb43d66 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Sat, 10 Aug 2019 16:18:29 +0200 Subject: [PATCH 1/2] Basic support for parsing verbatim keywords with code --- CMakeLists_files.cmake | 1 + opm/parser/eclipse/Parser/Parser.hpp | 4 + opm/parser/eclipse/Parser/ParserEnums.hpp | 3 +- opm/parser/eclipse/Parser/ParserItem.hpp | 8 +- opm/parser/eclipse/Parser/ParserKeyword.hpp | 5 + .../EclipseState/Schedule/Schedule.cpp | 6 +- src/opm/parser/eclipse/Parser/Parser.cpp | 149 +++++++++--- src/opm/parser/eclipse/Parser/ParserEnums.cpp | 5 + src/opm/parser/eclipse/Parser/ParserItem.cpp | 2 + .../parser/eclipse/Parser/ParserKeyword.cpp | 125 ++++++---- .../parser/eclipse/Parser/raw/RawRecord.cpp | 22 +- .../parser/eclipse/Parser/raw/RawRecord.hpp | 1 + src/opm/parser/eclipse/RawDeck/RawKeyword.cpp | 218 ++++++++++++++++++ .../eclipse/share/keywords/900_OPM/P/PYACTION | 1 + .../eclipse/share/keywords/keyword_list.cmake | 3 +- tests/parser/PYACTION.cpp | 79 +++++++ 16 files changed, 548 insertions(+), 84 deletions(-) create mode 100644 src/opm/parser/eclipse/RawDeck/RawKeyword.cpp create mode 100644 src/opm/parser/eclipse/share/keywords/900_OPM/P/PYACTION create mode 100644 tests/parser/PYACTION.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 54be7c025..b4b6b3b01 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -254,6 +254,7 @@ if(ENABLE_ECL_INPUT) tests/parser/ParseContextTests.cpp tests/parser/ParseContext_EXIT1.cpp tests/parser/ParseDATAWithDefault.cpp + tests/parser/PYACTION.cpp tests/parser/PORVTests.cpp tests/parser/RawKeywordTests.cpp tests/parser/ResinsightTest.cpp diff --git a/opm/parser/eclipse/Parser/Parser.hpp b/opm/parser/eclipse/Parser/Parser.hpp index 2d61bf7ee..cadd0ead1 100644 --- a/opm/parser/eclipse/Parser/Parser.hpp +++ b/opm/parser/eclipse/Parser/Parser.hpp @@ -123,6 +123,8 @@ namespace Opm { const ParseContext& context, ErrorGuard& errors); + const std::vector> codeKeywords() const; + private: // associative map of the parser internal name and the corresponding ParserKeyword object std::vector< std::unique_ptr< const ParserKeyword > > keyword_storage; @@ -132,6 +134,8 @@ namespace Opm { // ParserKeyword object for keywords which match a regular expression std::map< string_view, const ParserKeyword* > m_wildCardKeywords; + std::vector> code_keywords; + bool hasWildCardKeyword(const std::string& keyword) const; const ParserKeyword* matchingKeyword(const string_view& keyword) const; diff --git a/opm/parser/eclipse/Parser/ParserEnums.hpp b/opm/parser/eclipse/Parser/ParserEnums.hpp index a3f9d30fb..1db0fb8bd 100644 --- a/opm/parser/eclipse/Parser/ParserEnums.hpp +++ b/opm/parser/eclipse/Parser/ParserEnums.hpp @@ -28,7 +28,8 @@ namespace Opm { SLASH_TERMINATED = 0, FIXED = 1, OTHER_KEYWORD_IN_DECK = 2, - UNKNOWN=3 + UNKNOWN = 3, + FIXED_CODE = 4 }; diff --git a/opm/parser/eclipse/Parser/ParserItem.hpp b/opm/parser/eclipse/Parser/ParserItem.hpp index 4145e11e9..c19577f3c 100644 --- a/opm/parser/eclipse/Parser/ParserItem.hpp +++ b/opm/parser/eclipse/Parser/ParserItem.hpp @@ -46,10 +46,8 @@ namespace Opm { RAW_STRING and UDA. DataType: This the C++ type of items generated when parsing the deck, - currently the available datatypes are int, double and std::string. - The mapping from input type to data type is many-to-one, and - currently both STRING and RAW_STRING map to std::string and both - DOUBLE and UDA map to double. + currently the available datatypes are int, double, std::string and + the user defined type UDAValue. Splitting the type treatment in two layers in this way enables properties/transformations to be added to the data before they are @@ -65,7 +63,7 @@ namespace Opm { static item_size size_from_string( const std::string& ); static std::string string_from_size( item_size ); - enum class itype {UNKNOWN, DOUBLE, INT, STRING, RAW_STRING, UDA}; + enum class itype {UNKNOWN, DOUBLE, INT, STRING, RAW_STRING, UDA, CODE}; static itype from_string(const std::string& string_value); static std::string to_string(itype input_type); std::string type_literal() const; diff --git a/opm/parser/eclipse/Parser/ParserKeyword.hpp b/opm/parser/eclipse/Parser/ParserKeyword.hpp index 834111b96..dd2a41b28 100644 --- a/opm/parser/eclipse/Parser/ParserKeyword.hpp +++ b/opm/parser/eclipse/Parser/ParserKeyword.hpp @@ -113,6 +113,8 @@ namespace Opm { bool hasMultipleDeckNames() const; void clearDeckNames(); void addDeckName( const std::string& deckName ); + void setCodeEnd(const std::string& end); + const std::string& codeEnd() const; DeckNameSet::const_iterator deckNamesBegin() const; DeckNameSet::const_iterator deckNamesEnd() const; @@ -127,6 +129,7 @@ namespace Opm { const KeywordSize& getKeywordSize() const; bool isDataKeyword() const; bool rawStringKeyword() const; + bool isCodeKeyword() const; std::string createDeclaration(const std::string& indent) const; std::string createDecl() const; @@ -149,11 +152,13 @@ namespace Opm { bool m_isTableCollection; std::string m_Description; bool raw_string_keyword = false; + std::string code_end; static bool validNameStart(const string_view& name); void initDeckNames( const Json::JsonObject& jsonConfig ); void initSectionNames( const Json::JsonObject& jsonConfig ); void initMatchRegex( const Json::JsonObject& jsonObject ); + void initCode( const Json::JsonObject& jsonConfig ); void initData( const Json::JsonObject& jsonConfig ); void initSize( const Json::JsonObject& jsonConfig ); void initSizeKeyword(const Json::JsonObject& sizeObject); diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index 0ed623750..7a04ac160 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -1508,14 +1508,14 @@ namespace { double guide_rate = 0; GroupProduction::GuideRateDef guide_rate_def = GroupProduction::GuideRateDef::NO_GUIDE_RATE; if (group_name != "FIELD") { - if (record.getItem("GUIDE_RATE_DEF").hasValue(0)) { + if (record.getItem("GUIDE_RATE_DEF").hasValue(0)) { std::string guide_rate_str = record.getItem("GUIDE_RATE_DEF").getTrimmedString(0); guide_rate_def = GroupProduction::GetGuideRateFromString( guide_rate_str ); if ((guide_rate_str == "INJ" || guide_rate_str == "POTN" || guide_rate_str == "FORM")) { std::string msg = "The supplied guide_rate value will be ignored"; - parseContext.handleError(ParseContext::SCHEDULE_IGNORED_GUIDE_RATE, msg, errors); - } + parseContext.handleError(ParseContext::SCHEDULE_IGNORED_GUIDE_RATE, msg, errors); + } else { guide_rate = record.getItem("GUIDE_RATE").get(0); if (guide_rate == 0) diff --git a/src/opm/parser/eclipse/Parser/Parser.cpp b/src/opm/parser/eclipse/Parser/Parser.cpp index d053c2478..6b159f905 100644 --- a/src/opm/parser/eclipse/Parser/Parser.cpp +++ b/src/opm/parser/eclipse/Parser/Parser.cpp @@ -196,24 +196,84 @@ inline bool getline( string_view& input, string_view& line ) { * everything after (terminating) slashes. Manually copying into the string for * performance. */ -inline std::string clean( const std::string& str ) { +inline std::string fast_clean( const std::string& str ) { std::string dst; dst.resize( str.size() ); string_view input( str ), line; auto dsti = dst.begin(); - while( getline( input, line ) ) { - line = trim( strip_comments(line)); + while( true ) { - std::copy( line.begin(), line.end(), dsti ); - dsti += std::distance( line.begin(), line.end() ); - *dsti++ = '\n'; + if ( getline( input, line ) ) { + line = trim( strip_comments(line)); + + std::copy( line.begin(), line.end(), dsti ); + dsti += std::distance( line.begin(), line.end() ); + *dsti++ = '\n'; + } else + break; } dst.resize( std::distance( dst.begin(), dsti ) ); return dst; } +inline std::string clean( const std::vector>& code_keywords, const std::string& str ) { + auto count = std::count_if(code_keywords.begin(), code_keywords.end(), [&str](const std::pair& code_pair) + { + return str.find(code_pair.first) != std::string::npos; + }); + + if (count == 0) + return fast_clean(str); + else { + std::string dst; + dst.resize( str.size() ); + + string_view input( str ), line; + auto dsti = dst.begin(); + while( true ) { + for (const auto& code_pair : code_keywords) { + const auto& keyword = code_pair.first; + + if (input.starts_with(keyword)) { + std::string end_string = code_pair.second; + auto end_pos = input.find(end_string); + if (end_pos == std::string::npos) { + std::copy(input.begin(), input.end(), dsti); + dsti += std::distance( input.begin(), input.end() ); + input = string_view(input.end(), input.end()); + break; + } else { + end_pos += end_string.size(); + std::copy(input.begin(), input.begin() + end_pos, dsti); + dsti += end_pos; + *dsti++ = '\n'; + input = string_view(input.begin() + end_pos + 1, input.end()); + break; + } + } + } + + if ( getline( input, line ) ) { + line = trim( strip_comments(line)); + + std::copy( line.begin(), line.end(), dsti ); + dsti += std::distance( line.begin(), line.end() ); + *dsti++ = '\n'; + } else + break; + } + + dst.resize( std::distance( dst.begin(), dsti ) ); + return dst; + } +} + + + + + inline std::string make_deck_name(const string_view& str) { auto first_sep = std::find_if( str.begin(), str.end(), RawConsts::is_separator() ); return uppercase( str.substr(0, first_sep - str.begin()) ); @@ -238,7 +298,6 @@ inline bool isTerminatedRecordString(const string_view& line) { } - struct file { file( boost::filesystem::path p, const std::string& in ) : input( in ), path( p ) @@ -266,8 +325,8 @@ void InputStack::push( std::string&& input, boost::filesystem::path p ) { class ParserState { public: - ParserState( const ParseContext&, ErrorGuard& ); - ParserState( const ParseContext&, ErrorGuard&, boost::filesystem::path ); + ParserState( const std::vector>&, const ParseContext&, ErrorGuard& ); + ParserState( const std::vector>&, const ParseContext&, ErrorGuard&, boost::filesystem::path ); void loadString( const std::string& ); void loadFile( const boost::filesystem::path& ); @@ -286,11 +345,11 @@ class ParserState { void closeFile(); private: + const std::vector> code_keywords; InputStack input_stack; std::map< std::string, std::string > pathMap; boost::filesystem::path rootPath; - public: ParserKeywordSizeEnum lastSizeType = SLASH_TERMINATED; std::string lastKeyWord; @@ -301,7 +360,6 @@ class ParserState { bool unknown_keyword = false; }; - const boost::filesystem::path& ParserState::current_path() const { return this->input_stack.top().path; } @@ -329,6 +387,7 @@ string_view ParserState::getline() { } + void ParserState::ungetline(const string_view& line) { auto& file_view = this->input_stack.top().input; if (line.end() + 1 != file_view.begin()) @@ -345,14 +404,19 @@ void ParserState::closeFile() { this->input_stack.pop(); } -ParserState::ParserState(const ParseContext& __parseContext, ErrorGuard& errors_arg) : +ParserState::ParserState(const std::vector>& code_keywords_arg, + const ParseContext& __parseContext, + ErrorGuard& errors_arg) : + code_keywords(code_keywords_arg), parseContext( __parseContext ), errors( errors_arg ) {} -ParserState::ParserState( const ParseContext& context, +ParserState::ParserState( const std::vector>& code_keywords_arg, + const ParseContext& context, ErrorGuard& errors_arg, boost::filesystem::path p ) : + code_keywords(code_keywords_arg), rootPath( boost::filesystem::canonical( p ).parent_path() ), parseContext( context ), errors( errors_arg ) @@ -361,7 +425,7 @@ ParserState::ParserState( const ParseContext& context, } void ParserState::loadString(const std::string& input) { - this->input_stack.push( str::clean( input + "\n" ) ); + this->input_stack.push( str::clean( this->code_keywords, input + "\n" ) ); } void ParserState::loadFile(const boost::filesystem::path& inputFile) { @@ -406,7 +470,7 @@ void ParserState::loadFile(const boost::filesystem::path& inputFile) { throw std::runtime_error( "Error when reading input file '" + inputFileCanonical.string() + "'" ); - this->input_stack.push( str::clean( buffer ), inputFileCanonical ); + this->input_stack.push( str::clean( this->code_keywords, buffer ), inputFileCanonical ); } /* @@ -505,14 +569,18 @@ RawKeyword * newRawKeyword(const ParserKeyword& parserKeyword, const std::string rawSizeType); } - if( parserKeyword.hasFixedSize() ) + if( parserKeyword.hasFixedSize() ) { + auto size_type = Raw::FIXED; + if (parserKeyword.isCodeKeyword()) + size_type = Raw::CODE; + return new RawKeyword( keywordString, parserState.current_path().string(), parserState.line(), raw_string_keyword, - Raw::FIXED, + size_type, parserKeyword.getFixedSize()); - + } const auto& keyword_size = parserKeyword.getKeywordSize(); const auto& deck = parserState.deck; @@ -625,11 +693,26 @@ std::unique_ptr tryParseKeyword( ParserState& parserState, const Par } } else { /* We are looking at some random gibberish?! */ - if (!parserState.unknown_keyword) { + if (!parserState.unknown_keyword) parserState.handleRandomText( line ); - } } } else { + if (rawKeyword->getSizeType() == Raw::CODE) { + const auto& parserKeyword = parser.getParserKeywordFromDeckName(rawKeyword->getKeywordName()); + auto end_pos = line.find(parserKeyword.codeEnd()); + if (end_pos != std::string::npos) { + string_view line_content = { line.begin(), line.begin() + end_pos}; + record_buffer = str::update_record_buffer( record_buffer, line_content ); + + RawRecord record(record_buffer, true); + rawKeyword->addRecord(record); + return rawKeyword; + } else + record_buffer = str::update_record_buffer( record_buffer.begin(), line ); + + continue; + } + if (rawKeyword->getSizeType() == Raw::UNKNOWN) { /* When we are spinning through a keyword of size type UNKNOWN it @@ -655,7 +738,6 @@ std::unique_ptr tryParseKeyword( ParserState& parserState, const Par line = str::del_after_slash(line, rawKeyword->rawStringKeyword()); record_buffer = str::update_record_buffer(record_buffer, line); - if (is_title) { if (record_buffer.empty()) { RawRecord record("opm/flow simulation"); @@ -681,17 +763,26 @@ std::unique_ptr tryParseKeyword( ParserState& parserState, const Par record_buffer = str::emptystr; } - } - } if (rawKeyword) { if (rawKeyword->getSizeType() == Raw::UNKNOWN) rawKeyword->terminateKeyword(); - if (!rawKeyword->isFinished()) + if (!rawKeyword->isFinished()) { + /* + It is not necessary to explicitly terminate code keywords, in that + case they will load all the content until EOF is reached. + */ + if (rawKeyword->getSizeType() == Raw::CODE) { + RawRecord record(string_view{ record_buffer.begin(), record_buffer.end() + 1}, true); + rawKeyword->addRecord(record); + return rawKeyword; + } + throw std::invalid_argument("Keyword " + rawKeyword->getKeywordName() + " is not properly terminated"); + } } return rawKeyword; @@ -842,7 +933,7 @@ bool parseState( ParserState& parserState, const Parser& parser ) { } Deck Parser::parseFile(const std::string &dataFileName, const ParseContext& parseContext, ErrorGuard& errors) const { - ParserState parserState( parseContext, errors, dataFileName ); + ParserState parserState( this->codeKeywords(), parseContext, errors, dataFileName ); parseState( parserState, *this ); applyUnitsToDeck( parserState.deck ); @@ -858,7 +949,7 @@ bool parseState( ParserState& parserState, const Parser& parser ) { Deck Parser::parseString(const std::string &data, const ParseContext& parseContext, ErrorGuard& errors) const { - ParserState parserState( parseContext, errors ); + ParserState parserState( this->codeKeywords(), parseContext, errors ); parserState.loadString( data ); parseState( parserState, *this ); @@ -932,6 +1023,8 @@ void Parser::addParserKeyword( std::unique_ptr< const ParserKeyword >&& parserKe m_wildCardKeywords[ name ] = ptr; } + if (ptr->isCodeKeyword()) + this->code_keywords.emplace_back( ptr->getName(), ptr->codeEnd() ); } @@ -1015,6 +1108,10 @@ std::vector Parser::getAllDeckNames () const { } } + const std::vector> Parser::codeKeywords() const { + return this->code_keywords; + } + void Parser::applyUnitsToDeck(Deck& deck) const { /* diff --git a/src/opm/parser/eclipse/Parser/ParserEnums.cpp b/src/opm/parser/eclipse/Parser/ParserEnums.cpp index e5f059851..3b3895fc0 100644 --- a/src/opm/parser/eclipse/Parser/ParserEnums.cpp +++ b/src/opm/parser/eclipse/Parser/ParserEnums.cpp @@ -39,6 +39,9 @@ namespace Opm { case UNKNOWN: return "UNKNOWN"; break; + case FIXED_CODE: + return "FIXED_CODE"; + break; default: throw std::invalid_argument("Implementation error - should NOT be here"); } @@ -55,6 +58,8 @@ namespace Opm { return OTHER_KEYWORD_IN_DECK; else if (stringValue == "UNKNOWN") return UNKNOWN; + else if (stringValue == "FIXED_CODE") + return FIXED_CODE; else throw std::invalid_argument("String: " + stringValue + " can not be converted to enum value"); } diff --git a/src/opm/parser/eclipse/Parser/ParserItem.cpp b/src/opm/parser/eclipse/Parser/ParserItem.cpp index e1cad5011..0f8862b14 100644 --- a/src/opm/parser/eclipse/Parser/ParserItem.cpp +++ b/src/opm/parser/eclipse/Parser/ParserItem.cpp @@ -220,6 +220,8 @@ void ParserItem::setInputType(ParserItem::itype input_type_arg) { else if (input_type == itype::UDA) this->setDataType( UDAValue(0) ); + else if (input_type == itype::CODE) + this->setDataType( std::string() ); else throw std::invalid_argument("BUG: input type not recognized in setInputType()"); } diff --git a/src/opm/parser/eclipse/Parser/ParserKeyword.cpp b/src/opm/parser/eclipse/Parser/ParserKeyword.cpp index 431ca0f27..969679324 100644 --- a/src/opm/parser/eclipse/Parser/ParserKeyword.cpp +++ b/src/opm/parser/eclipse/Parser/ParserKeyword.cpp @@ -39,6 +39,8 @@ namespace Opm { void ParserKeyword::setSizeType( ParserKeywordSizeEnum sizeType ) { m_keywordSizeType = sizeType; + if (sizeType == FIXED_CODE) + this->m_fixedSize = 1; } void ParserKeyword::setFixedSize( size_t keywordSize) { @@ -102,6 +104,15 @@ namespace Opm { m_Description = description; } + + void ParserKeyword::setCodeEnd(const std::string& end) { + this->code_end = end; + } + + const std::string& ParserKeyword::codeEnd() const { + return this->code_end; + } + void ParserKeyword::initSize(const Json::JsonObject& jsonConfig) { // The number of record has been set explicitly with the size: keyword if (jsonConfig.has_item("size")) { @@ -113,30 +124,35 @@ namespace Opm { } else initSizeKeyword(sizeObject); - } else { - if (jsonConfig.has_item("num_tables")) { - Json::JsonObject numTablesObject = jsonConfig.get_item("num_tables"); - - if (!numTablesObject.is_object()) - throw std::invalid_argument("The num_tables key must point to a {} object"); - - initSizeKeyword(numTablesObject); - m_isTableCollection = true; - } else { - if (jsonConfig.has_item("items") || jsonConfig.has_item("records")) - // The number of records is undetermined - the keyword will be '/' terminated. - m_keywordSizeType = SLASH_TERMINATED; - else { - m_keywordSizeType = FIXED; - if (jsonConfig.has_item("data")) - m_fixedSize = 1; - else - m_fixedSize = 0; - } - } + return; } + + if (jsonConfig.has_item("num_tables")) { + Json::JsonObject numTablesObject = jsonConfig.get_item("num_tables"); + + if (!numTablesObject.is_object()) + throw std::invalid_argument( + "The num_tables key must point to a {} object"); + + initSizeKeyword(numTablesObject); + m_isTableCollection = true; + + return; + } + + if (jsonConfig.has_item("items") || jsonConfig.has_item("records")) { + // The number of records is undetermined - the keyword will be '/' + // terminated. + m_keywordSizeType = SLASH_TERMINATED; + return; + } else { + m_keywordSizeType = FIXED; + m_fixedSize = 0; + } + } + ParserKeyword::ParserKeyword(const Json::JsonObject& jsonConfig) { if (jsonConfig.has_item("name")) { @@ -179,9 +195,11 @@ namespace Opm { if (jsonConfig.has_item("data")) initData(jsonConfig); - if (jsonConfig.has_item("description")) { + if (jsonConfig.has_item("code")) + this->initCode(jsonConfig); + + if (jsonConfig.has_item("description")) m_Description = jsonConfig.get_string("description"); - } } @@ -331,6 +349,25 @@ void set_dimensions( ParserItem& item, } + void ParserKeyword::initCode(const Json::JsonObject& jsonConfig) { + this->m_fixedSize = 1U; + this->m_keywordSizeType = FIXED_CODE; + + const Json::JsonObject codeConfig = jsonConfig.get_item("code"); + if (!codeConfig.has_item("end")) + throw std::invalid_argument("The end: is missing from the code block"); + this->setCodeEnd(codeConfig.get_string("end")); + + const std::string itemName("code"); + auto input_type = ParserItem::itype::RAW_STRING; + ParserItem item( itemName, input_type); + ParserRecord record; + item.setSizeType( ParserItem::item_size::ALL ); + + record.addItem(item); + this->addRecord(record); + } + void ParserKeyword::initData(const Json::JsonObject& jsonConfig) { this->m_fixedSize = 1U; @@ -503,7 +540,7 @@ void set_dimensions( ParserItem& item, } bool ParserKeyword::hasFixedSize() const { - return m_keywordSizeType == FIXED; + return (this->m_keywordSizeType == FIXED || this->m_keywordSizeType == FIXED_CODE); } enum ParserKeywordSizeEnum ParserKeyword::getSizeType() const { @@ -524,6 +561,9 @@ void set_dimensions( ParserItem& item, return this->m_records.front().isDataRecord(); } + bool ParserKeyword::isCodeKeyword() const { + return (this->m_keywordSizeType == FIXED_CODE); + } bool ParserKeyword::hasMatchRegex() const { return !m_matchRegexString.empty(); @@ -588,26 +628,25 @@ void set_dimensions( ParserItem& item, const std::string lhs = "keyword"; const std::string indent = " "; - ss << className() << "::" << className() << "( ) : ParserKeyword(\"" << m_name << "\") {" << '\n'; + ss << className() << "::" << className() << "( ) : ParserKeyword(\"" << m_name << "\")" << '\n' << "{" << '\n'; { const std::string sizeString(ParserKeywordSizeEnum2String(m_keywordSizeType)); ss << indent; switch (m_keywordSizeType) { - case SLASH_TERMINATED: - ss << "setSizeType(" << sizeString << ");" << '\n'; - break; - case UNKNOWN: - ss << "setSizeType(" << sizeString << ");" << '\n'; - break; - case FIXED: - ss << "setFixedSize( (size_t) " << m_fixedSize << ");" << '\n'; - break; - case OTHER_KEYWORD_IN_DECK: - ss << "setSizeType(" << sizeString << ");" << '\n'; - ss << indent << "initSizeKeyword(\"" << keyword_size.keyword << "\",\"" << keyword_size.item << "\"," << keyword_size.shift << ");" << '\n'; - if (m_isTableCollection) - ss << "setTableCollection( true );" << '\n'; - break; + case SLASH_TERMINATED: + case FIXED_CODE: + case UNKNOWN: + ss << "setSizeType(" << sizeString << ");" << '\n'; + break; + case FIXED: + ss << "setFixedSize( (size_t) " << m_fixedSize << ");" << '\n'; + break; + case OTHER_KEYWORD_IN_DECK: + ss << "setSizeType(" << sizeString << ");" << '\n'; + ss << indent << "initSizeKeyword(\"" << keyword_size.keyword << "\",\"" << keyword_size.item << "\"," << keyword_size.shift << ");" << '\n'; + if (m_isTableCollection) + ss << indent << "setTableCollection( true );" << '\n'; + break; } } @@ -632,6 +671,9 @@ void set_dimensions( ParserItem& item, if (hasMatchRegex()) ss << indent << "setMatchRegex(\"" << m_matchRegexString << "\");" << '\n'; + if (this->m_keywordSizeType == FIXED_CODE) + ss << indent << "setCodeEnd(\"" << this->code_end << "\");" << '\n'; + { if (m_records.size() > 0 ) { for( const auto& record : *this ) { @@ -690,12 +732,15 @@ void set_dimensions( ParserItem& item, return false; if( m_name != rhs.m_name + || this->code_end != rhs.code_end || m_matchRegexString != rhs.m_matchRegexString || m_keywordSizeType != rhs.m_keywordSizeType + || isCodeKeyword() != rhs.isCodeKeyword() || isDataKeyword() != rhs.isDataKeyword() || m_isTableCollection != rhs.m_isTableCollection ) return false; + switch( m_keywordSizeType ) { case FIXED: if( m_fixedSize != rhs.m_fixedSize ) diff --git a/src/opm/parser/eclipse/Parser/raw/RawRecord.cpp b/src/opm/parser/eclipse/Parser/raw/RawRecord.cpp index 5b8af1e03..9aaecd4d4 100644 --- a/src/opm/parser/eclipse/Parser/raw/RawRecord.cpp +++ b/src/opm/parser/eclipse/Parser/raw/RawRecord.cpp @@ -77,18 +77,24 @@ inline bool even_quotes( const T& str ) { } - RawRecord::RawRecord(const string_view& singleRecordString) : - m_sanitizedRecordString( singleRecordString ), - m_recordItems( splitSingleRecordString( m_sanitizedRecordString ) ) + RawRecord::RawRecord(const string_view& singleRecordString, bool text) : + m_sanitizedRecordString( singleRecordString ) { - if( !even_quotes( singleRecordString ) ) - throw std::invalid_argument( - "Input string is not a complete record string, " - "offending string: '" + singleRecordString + "'" - ); + if (text) + this->m_recordItems.push_back(this->m_sanitizedRecordString); + else { + this->m_recordItems = splitSingleRecordString( m_sanitizedRecordString ); + + if( !even_quotes( singleRecordString ) ) + throw std::invalid_argument("Input string is not a complete record string, " + "offending string: '" + singleRecordString + "'"); + } } + RawRecord::RawRecord(const string_view& singleRecordString) : + RawRecord(singleRecordString, false) + {} void RawRecord::prepend( size_t count, string_view tok ) { this->m_recordItems.insert( this->m_recordItems.begin(), count, tok ); diff --git a/src/opm/parser/eclipse/Parser/raw/RawRecord.hpp b/src/opm/parser/eclipse/Parser/raw/RawRecord.hpp index a2a6b6fe5..4c510090f 100644 --- a/src/opm/parser/eclipse/Parser/raw/RawRecord.hpp +++ b/src/opm/parser/eclipse/Parser/raw/RawRecord.hpp @@ -35,6 +35,7 @@ namespace Opm { class RawRecord { public: + RawRecord( const string_view&, bool text); explicit RawRecord( const string_view&); inline string_view pop_front(); diff --git a/src/opm/parser/eclipse/RawDeck/RawKeyword.cpp b/src/opm/parser/eclipse/RawDeck/RawKeyword.cpp new file mode 100644 index 000000000..1d884cd4d --- /dev/null +++ b/src/opm/parser/eclipse/RawDeck/RawKeyword.cpp @@ -0,0 +1,218 @@ +/* + Copyright 2013 Statoil 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 +#include + +namespace Opm { + + static const std::string emptystr = ""; + + RawKeyword::RawKeyword(const string_view& name, Raw::KeywordSizeEnum sizeType , const std::string& filename, size_t lineNR, bool slash_terminated) : + slash_terminated_records(slash_terminated), + m_partialRecordString( emptystr ) + { + if (sizeType == Raw::SLASH_TERMINATED || sizeType == Raw::UNKNOWN) { + commonInit(name.string(),filename,lineNR); + m_sizeType = sizeType; + } else + throw std::invalid_argument("Error - invalid sizetype on input"); + } + + RawKeyword::RawKeyword(const string_view& name , const std::string& filename, size_t lineNR , size_t inputSize, bool slash_terminated, bool isTableCollection ) : + slash_terminated_records(slash_terminated) + { + commonInit(name.string(),filename,lineNR); + if (isTableCollection) { + m_sizeType = Raw::TABLE_COLLECTION; + m_numTables = inputSize; + } else { + m_sizeType = Raw::FIXED; + m_fixedSize = inputSize; + if (m_fixedSize == 0) + m_isFinished = true; + else + m_isFinished = false; + } + } + + + void RawKeyword::commonInit(const std::string& name , const std::string& filename, size_t lineNR) { + setKeywordName( name ); + m_filename = filename; + m_lineNR = lineNR; + + this->m_is_title = name == "TITLE"; + } + + + const std::string& RawKeyword::getKeywordName() const { + return m_name; + } + + size_t RawKeyword::size() const { + return m_records.size(); + } + + static inline bool isTerminator( const string_view& line ) { + return line.size() == 1 && line.back() == RawConsts::slash; + } + + /// Important method, being repeatedly called. When a record is terminated, + /// it is added to the list of records, and a new record is started. + + void RawKeyword::addRawRecordString(const string_view& partialRecordString) { + if( m_partialRecordString == emptystr ) + m_partialRecordString = partialRecordString; + else + m_partialRecordString = { m_partialRecordString.begin(), partialRecordString.end() }; + + + if( isTerminator( m_partialRecordString ) ) { + if (m_sizeType == Raw::TABLE_COLLECTION) { + m_currentNumTables += 1; + if (m_currentNumTables == m_numTables) { + m_isFinished = true; + m_partialRecordString = emptystr; + return; + } + } else if( m_sizeType == Raw::SLASH_TERMINATED) { + m_isFinished = true; + m_partialRecordString = emptystr; + return; + } + } + + if( m_isFinished ) return; + + if( this->is_title() ) { + + string_view recstr = m_partialRecordString == "" + ? "untitled" + : m_partialRecordString; + + m_records.emplace_back( recstr ); + m_partialRecordString = emptystr; + m_isFinished = true; + return; + } + + if( RawRecord::isTerminatedRecordString( partialRecordString ) ) { + this->m_partialRecordString = string_view{ this->m_partialRecordString.begin(), this->m_partialRecordString.end() - 1 }; + this->terminateRecord(false); + } + } + + + void RawKeyword::terminateRecord(bool text) { + this->m_records.emplace_back( this->m_partialRecordString, text ); + m_partialRecordString = emptystr; + + if( m_sizeType == Raw::FIXED && m_records.size() == m_fixedSize ) + m_isFinished = true; + }; + + const RawRecord& RawKeyword::getFirstRecord() const { + return *m_records.begin(); + } + + bool RawKeyword::isKeywordPrefix(const string_view& line, std::string& keyword ) { + // make the keyword string ALL_UPPERCASE because Eclipse seems + // to be case-insensitive (although this is one of its + // undocumented features...) + keyword = uppercase( ParserKeyword::getDeckName( line ).string() ); + + return isValidKeyword( keyword ); + } + + bool RawKeyword::isValidKeyword(const std::string& keywordCandidate) { + return ParserKeyword::validDeckName(keywordCandidate); + } + + void RawKeyword::setKeywordName(const std::string& name) { + m_name = boost::algorithm::trim_right_copy(name); + if (!isValidKeyword(m_name)) { + throw std::invalid_argument("Not a valid keyword:" + name); + } else if (m_name.size() > Opm::RawConsts::maxKeywordLength) { + throw std::invalid_argument("Too long keyword:" + name); + } else if (boost::algorithm::trim_left_copy(m_name) != m_name) { + throw std::invalid_argument("Illegal whitespace start of keyword:" + name); + } + } + + bool RawKeyword::isPartialRecordStringEmpty() const { + return m_partialRecordString.size() == 0; + } + + + void RawKeyword::finalizeUnknownSize() { + if (m_sizeType == Raw::UNKNOWN) + m_isFinished = true; + else + throw std::invalid_argument("Fatal error finalizing keyword:" + m_name + " Only RawKeywords with UNKNOWN size can be explicitly finalized."); + } + + + bool RawKeyword::isFinished() const { + return m_isFinished; + } + + const std::string& RawKeyword::getFilename() const { + return m_filename; + } + + size_t RawKeyword::getLineNR() const { + return m_lineNR; + } + + RawKeyword::const_iterator RawKeyword::begin() const { + return this->m_records.begin(); + } + + RawKeyword::const_iterator RawKeyword::end() const { + return this->m_records.end(); + } + + RawKeyword::iterator RawKeyword::begin() { + return this->m_records.begin(); + } + + RawKeyword::iterator RawKeyword::end() { + return this->m_records.end(); + } + + bool RawKeyword::is_title() const { + return this->m_is_title; + } + + Raw::KeywordSizeEnum RawKeyword::getSizeType() const { + return m_sizeType; + } + + bool RawKeyword::slashTerminatedRecords() const { + return this->slash_terminated_records; + } + +} + diff --git a/src/opm/parser/eclipse/share/keywords/900_OPM/P/PYACTION b/src/opm/parser/eclipse/share/keywords/900_OPM/P/PYACTION new file mode 100644 index 000000000..678daad66 --- /dev/null +++ b/src/opm/parser/eclipse/share/keywords/900_OPM/P/PYACTION @@ -0,0 +1 @@ +{"name" : "PYACTION", "sections" : ["SCHEDULE"], "code" : {"end" : "<<<"}} diff --git a/src/opm/parser/eclipse/share/keywords/keyword_list.cmake b/src/opm/parser/eclipse/share/keywords/keyword_list.cmake index 47cd47f58..a11c52b11 100644 --- a/src/opm/parser/eclipse/share/keywords/keyword_list.cmake +++ b/src/opm/parser/eclipse/share/keywords/keyword_list.cmake @@ -566,8 +566,9 @@ set( keywords 900_OPM/O/OILDENT 900_OPM/P/PINTDIMS 900_OPM/P/PLYVMH - 900_OPM/P/POLYMW 900_OPM/P/PLYMWINJ + 900_OPM/P/POLYMW + 900_OPM/P/PYACTION 900_OPM/R/RHO 900_OPM/S/SKPRPOLY 900_OPM/S/SKPRWAT diff --git a/tests/parser/PYACTION.cpp b/tests/parser/PYACTION.cpp new file mode 100644 index 000000000..50c259a59 --- /dev/null +++ b/tests/parser/PYACTION.cpp @@ -0,0 +1,79 @@ +/* + Copyright 2019 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 "config.h" + +#define BOOST_TEST_MODULE PY_ACTION_TESTER +#include +#include +#include + +using namespace Opm; + +BOOST_AUTO_TEST_CASE(ParsePYACTION) { + const std::string input_code = R"(from math import sin +import random +print("sin(0) = {}".format(sin(0))) +#--- +if random.random() > 0.25: + print("Large outcome") +else: + print("Small result") +A = 100 +B = A / 10 +C = B * 20 +)"; + + const std::string deck_string1 = R"( +SCHEDULE + +PYACTION Her comes an ignored comment +)" + input_code + "<<<"; + + const std::string deck_string2 = R"( +SCHEDULE + +PYACTION +)" + input_code; + + + const std::string deck_string3 = R"( +SCHEDULE + +PYACTION -- Comment +)" + input_code + "<<<" + "\nGRID"; + + + Parser parser; + { + auto deck = parser.parseString(deck_string1); + const auto& parsed_code = deck.getKeyword("PYACTION").getRecord(0).getItem("code").get(0); + BOOST_CHECK_EQUAL(parsed_code, input_code); + } + { + auto deck = parser.parseString(deck_string2); + const auto& parsed_code = deck.getKeyword("PYACTION").getRecord(0).getItem("code").get(0); + BOOST_CHECK_EQUAL(parsed_code, input_code); + } + { + auto deck = parser.parseString(deck_string3); + const auto& parsed_code = deck.getKeyword("PYACTION").getRecord(0).getItem("code").get(0); + BOOST_CHECK_EQUAL(parsed_code, input_code); + BOOST_CHECK( deck.hasKeyword("GRID")); + } +} From 807fa9ecdb0697bdb8674e1a0e86ba463f1db8b0 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Mon, 19 Aug 2019 16:39:17 +0200 Subject: [PATCH 2/2] Add keyword PYINPUT --- src/opm/parser/eclipse/share/keywords/900_OPM/P/PYINPUT | 3 +++ src/opm/parser/eclipse/share/keywords/keyword_list.cmake | 1 + 2 files changed, 4 insertions(+) create mode 100644 src/opm/parser/eclipse/share/keywords/900_OPM/P/PYINPUT diff --git a/src/opm/parser/eclipse/share/keywords/900_OPM/P/PYINPUT b/src/opm/parser/eclipse/share/keywords/900_OPM/P/PYINPUT new file mode 100644 index 000000000..76f2559cc --- /dev/null +++ b/src/opm/parser/eclipse/share/keywords/900_OPM/P/PYINPUT @@ -0,0 +1,3 @@ +{"name" : "PYACTION", + "sections" : ["RUNSPEC", "GRID", "EDIT", "PROPS", "REGIONS", "SOLUTION", "SUMMARY", "SCHEDULE"], + "code" : {"end" : "<<<"}} diff --git a/src/opm/parser/eclipse/share/keywords/keyword_list.cmake b/src/opm/parser/eclipse/share/keywords/keyword_list.cmake index a11c52b11..d9b5a6f11 100644 --- a/src/opm/parser/eclipse/share/keywords/keyword_list.cmake +++ b/src/opm/parser/eclipse/share/keywords/keyword_list.cmake @@ -569,6 +569,7 @@ set( keywords 900_OPM/P/PLYMWINJ 900_OPM/P/POLYMW 900_OPM/P/PYACTION + 900_OPM/P/PYINPUT 900_OPM/R/RHO 900_OPM/S/SKPRPOLY 900_OPM/S/SKPRWAT