From 87bbc46bbbb36489affb881447498effe795a5d7 Mon Sep 17 00:00:00 2001 From: Steinar Foss Date: Wed, 11 Dec 2019 14:36:18 +0100 Subject: [PATCH] can parse keywords of type double-record. ... ParserKeyword: bool double_records set. added keywords CECONT. ParserKeywords: double_records set by keyword generator. Parsertests: added test for parsing double_records. ParserKeywords.parse: if double_records returns empty. double_records keywords set to SLASH_TERMINATED. ... ParserKeyword: for double records size_type = DOUBLE_SLASH_TERMINATED. double_records: RawKeyword is Raw::DOUBLE_SLASH_TERMINATED. RawKeyword uses m_isTempFinished. Parser: can parse files with double-record keywords. Parser.cpp/RawRecord.cpp: removed comments. DeckKeyword has param m_isDoubleRecordKeyword. test ParseDoubleRecords: double records to have linear structure. DeckKeyword: Can create records of type double_record. double-records parser test transferred to ParserTests.cpp. ... ... --- opm/parser/eclipse/Deck/DeckKeyword.hpp | 3 + opm/parser/eclipse/Parser/ParserEnums.hpp | 3 +- opm/parser/eclipse/Parser/ParserKeyword.hpp | 3 + src/opm/parser/eclipse/Deck/DeckKeyword.cpp | 8 ++ src/opm/parser/eclipse/Parser/Parser.cpp | 13 ++- src/opm/parser/eclipse/Parser/ParserEnums.cpp | 3 + .../parser/eclipse/Parser/ParserKeyword.cpp | 61 ++++++++-- .../parser/eclipse/Parser/raw/RawEnums.hpp | 1 + .../parser/eclipse/Parser/raw/RawKeyword.cpp | 12 +- .../parser/eclipse/Parser/raw/RawKeyword.hpp | 1 + .../share/keywords/000_Eclipse100/C/CECONT | 22 ++++ .../eclipse/share/keywords/keyword_list.cmake | 1 + tests/parser/ParserTests.cpp | 106 ++++++++++++++++++ 13 files changed, 224 insertions(+), 13 deletions(-) create mode 100644 src/opm/parser/eclipse/share/keywords/000_Eclipse100/C/CECONT diff --git a/opm/parser/eclipse/Deck/DeckKeyword.hpp b/opm/parser/eclipse/Deck/DeckKeyword.hpp index 93a8067ec..1c870b1ef 100644 --- a/opm/parser/eclipse/Deck/DeckKeyword.hpp +++ b/opm/parser/eclipse/Deck/DeckKeyword.hpp @@ -58,7 +58,9 @@ namespace Opm { DeckRecord& getRecord(size_t index); const DeckRecord& getDataRecord() const; void setDataKeyword(bool isDataKeyword = true); + void setDoubleRecordKeyword(bool isDoubleRecordKeyword = true); bool isDataKeyword() const; + bool isDoubleRecordKeyword() const; const std::vector& getIntData() const; const std::vector& getRawDoubleData() const; @@ -93,6 +95,7 @@ namespace Opm { std::vector< DeckRecord > m_recordList; bool m_isDataKeyword; bool m_slashTerminated; + bool m_isDoubleRecordKeyword = false; }; } diff --git a/opm/parser/eclipse/Parser/ParserEnums.hpp b/opm/parser/eclipse/Parser/ParserEnums.hpp index 1db0fb8bd..d51af0ea4 100644 --- a/opm/parser/eclipse/Parser/ParserEnums.hpp +++ b/opm/parser/eclipse/Parser/ParserEnums.hpp @@ -29,7 +29,8 @@ namespace Opm { FIXED = 1, OTHER_KEYWORD_IN_DECK = 2, UNKNOWN = 3, - FIXED_CODE = 4 + FIXED_CODE = 4, + DOUBLE_SLASH_TERMINATED = 5 }; diff --git a/opm/parser/eclipse/Parser/ParserKeyword.hpp b/opm/parser/eclipse/Parser/ParserKeyword.hpp index 3946d396e..a7ff8bb6e 100644 --- a/opm/parser/eclipse/Parser/ParserKeyword.hpp +++ b/opm/parser/eclipse/Parser/ParserKeyword.hpp @@ -131,7 +131,9 @@ namespace Opm { bool rawStringKeyword() const; bool isCodeKeyword() const; bool isAlternatingKeyword() const; + bool isDoubleRecordKeyword() const; void setAlternatingKeyword(bool alternating); + void setDoubleRecordsKeyword(bool double_rec); std::string createDeclaration(const std::string& indent) const; std::string createDecl() const; @@ -154,6 +156,7 @@ namespace Opm { std::string m_Description; bool raw_string_keyword = false; bool alternating_keyword = false; + bool double_records = false; std::string code_end; static bool validNameStart(const string_view& name); diff --git a/src/opm/parser/eclipse/Deck/DeckKeyword.cpp b/src/opm/parser/eclipse/Deck/DeckKeyword.cpp index dc6f13749..e98b48b74 100644 --- a/src/opm/parser/eclipse/Deck/DeckKeyword.cpp +++ b/src/opm/parser/eclipse/Deck/DeckKeyword.cpp @@ -189,10 +189,18 @@ namespace Opm { m_isDataKeyword = isDataKeyword_; } + void DeckKeyword::setDoubleRecordKeyword(bool isDoubleRecordKeyword) { + m_isDoubleRecordKeyword = isDoubleRecordKeyword; + } + bool DeckKeyword::isDataKeyword() const { return m_isDataKeyword; } + bool DeckKeyword::isDoubleRecordKeyword() const { + return m_isDoubleRecordKeyword; + } + const std::string& DeckKeyword::name() const { return m_keywordName; } diff --git a/src/opm/parser/eclipse/Parser/Parser.cpp b/src/opm/parser/eclipse/Parser/Parser.cpp index 350e27b9f..18b78e43a 100644 --- a/src/opm/parser/eclipse/Parser/Parser.cpp +++ b/src/opm/parser/eclipse/Parser/Parser.cpp @@ -560,11 +560,16 @@ 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) { bool raw_string_keyword = parserKeyword.rawStringKeyword(); - if( parserKeyword.getSizeType() == SLASH_TERMINATED || parserKeyword.getSizeType() == UNKNOWN) { + if( parserKeyword.getSizeType() == SLASH_TERMINATED || parserKeyword.getSizeType() == UNKNOWN || parserKeyword.getSizeType() == DOUBLE_SLASH_TERMINATED) { - const auto rawSizeType = parserKeyword.getSizeType() == SLASH_TERMINATED - ? Raw::SLASH_TERMINATED - : Raw::UNKNOWN; + const auto size_type = parserKeyword.getSizeType(); + Raw::KeywordSizeEnum rawSizeType; + + switch(size_type) { + case SLASH_TERMINATED: rawSizeType = Raw::SLASH_TERMINATED; break; + case UNKNOWN: rawSizeType = Raw::UNKNOWN; break; + case DOUBLE_SLASH_TERMINATED: rawSizeType = Raw::DOUBLE_SLASH_TERMINATED; break; + } return new RawKeyword( keywordString, parserState.current_path().string(), diff --git a/src/opm/parser/eclipse/Parser/ParserEnums.cpp b/src/opm/parser/eclipse/Parser/ParserEnums.cpp index 3b3895fc0..1d0ce03ea 100644 --- a/src/opm/parser/eclipse/Parser/ParserEnums.cpp +++ b/src/opm/parser/eclipse/Parser/ParserEnums.cpp @@ -42,6 +42,9 @@ namespace Opm { case FIXED_CODE: return "FIXED_CODE"; break; + case DOUBLE_SLASH_TERMINATED: + return "DOUBLE_SLASH_TERMINATED"; + break; default: throw std::invalid_argument("Implementation error - should NOT be here"); } diff --git a/src/opm/parser/eclipse/Parser/ParserKeyword.cpp b/src/opm/parser/eclipse/Parser/ParserKeyword.cpp index 42c4f8b2b..a7caeb3be 100644 --- a/src/opm/parser/eclipse/Parser/ParserKeyword.cpp +++ b/src/opm/parser/eclipse/Parser/ParserKeyword.cpp @@ -140,6 +140,11 @@ namespace Opm { return; } + if (jsonConfig.has_item("records_set")) { + m_keywordSizeType = DOUBLE_SLASH_TERMINATED; + return; + } + if (jsonConfig.has_item("items") || jsonConfig.has_item("records")) { // The number of records is undetermined - the keyword will be '/' // terminated. @@ -184,7 +189,9 @@ namespace Opm { initSectionNames(jsonConfig); initMatchRegex(jsonConfig); - if (jsonConfig.has_item("items") && (jsonConfig.has_item("records") || jsonConfig.has_item("alternating_records"))) + if (jsonConfig.has_item("items") && (jsonConfig.has_item("records") || + jsonConfig.has_item("alternating_records") || + jsonConfig.has_item("records_set") )) throw std::invalid_argument("Fatal error in " + getName() + " configuration. Can NOT have both records: and items:"); if (jsonConfig.has_item("items")) { @@ -205,6 +212,12 @@ namespace Opm { parseRecords( recordsConfig ); } + if (jsonConfig.has_item("records_set")) { + double_records = true; + const Json::JsonObject recordsConfig = jsonConfig.get_item("records_set"); + parseRecords( recordsConfig ); + } + if (jsonConfig.has_item("data")) initData(jsonConfig); @@ -523,19 +536,40 @@ void set_dimensions( ParserItem& item, UnitSystem& active_unitsystem, UnitSystem& default_unitsystem, const std::string& filename) const { + if( !rawKeyword.isFinished() ) throw std::invalid_argument("Tried to create a deck keyword from an incomplete raw keyword " + rawKeyword.getKeywordName()); DeckKeyword keyword( rawKeyword.location(), rawKeyword.getKeywordName() ); keyword.setDataKeyword( isDataKeyword() ); - size_t record_nr = 0; - for( auto& rawRecord : rawKeyword ) { - if( m_records.size() == 0 && rawRecord.size() > 0 ) - throw std::invalid_argument("Missing item information " + rawKeyword.getKeywordName()); + if (double_records) + keyword.setDoubleRecordKeyword(); - keyword.addRecord( this->getRecord( record_nr ).parse( parseContext, errors, rawRecord, active_unitsystem, default_unitsystem, rawKeyword.getKeywordName(), filename ) ); - record_nr++; + if (double_records) { + /* Note: this merely dumps all records sequentially into m_recordList. + In order to actually use double-record keywords, DeckKeyword needs to have a + 2-dimensional DeckRecord structure, e.g. + std::vector< std::vector> m_recordList; */ + size_t record_nr = 0; + for (auto& rawRecord : rawKeyword) { + if (rawRecord.size() == 0) + record_nr = 0; + else { + keyword.addRecord( this->getRecord( record_nr ).parse( parseContext, errors, rawRecord, active_unitsystem, default_unitsystem, rawKeyword.getKeywordName(), filename ) ); + record_nr++; + } + } + } + else { + size_t record_nr = 0; + for( auto& rawRecord : rawKeyword ) { + if( m_records.size() == 0 && rawRecord.size() > 0 ) + throw std::invalid_argument("Missing item information " + rawKeyword.getKeywordName()); + + keyword.addRecord( this->getRecord( record_nr ).parse( parseContext, errors, rawRecord, active_unitsystem, default_unitsystem, rawKeyword.getKeywordName(), filename ) ); + record_nr++; + } } if (this->hasFixedSize( )) @@ -588,10 +622,18 @@ void set_dimensions( ParserItem& item, return alternating_keyword; } + bool ParserKeyword::isDoubleRecordKeyword() const { + return double_records; + } + void ParserKeyword::setAlternatingKeyword(bool alternating) { alternating_keyword = alternating; } + void ParserKeyword::setDoubleRecordsKeyword(bool double_rec) { + double_records = double_rec; + } + bool ParserKeyword::hasMatchRegex() const { return !m_matchRegexString.empty(); } @@ -662,6 +704,7 @@ void set_dimensions( ParserItem& item, switch (m_keywordSizeType) { case SLASH_TERMINATED: case FIXED_CODE: + case DOUBLE_SLASH_TERMINATED: case UNKNOWN: ss << "setSizeType(" << sizeString << ");" << '\n'; break; @@ -698,6 +741,10 @@ void set_dimensions( ParserItem& item, if (alternating_keyword) ss << indent << "setAlternatingKeyword(true);" << '\n'; + // set DoubleRecords + if (double_records) + ss << indent << "setDoubleRecordsKeyword(true);" << '\n'; + // set the deck name match regex if (hasMatchRegex()) ss << indent << "setMatchRegex(\"" << m_matchRegexString << "\");" << '\n'; diff --git a/src/opm/parser/eclipse/Parser/raw/RawEnums.hpp b/src/opm/parser/eclipse/Parser/raw/RawEnums.hpp index 2630a4d8a..591865ea8 100644 --- a/src/opm/parser/eclipse/Parser/raw/RawEnums.hpp +++ b/src/opm/parser/eclipse/Parser/raw/RawEnums.hpp @@ -30,6 +30,7 @@ namespace Opm { UNKNOWN = 3, TABLE_COLLECTION = 4, CODE = 5, + DOUBLE_SLASH_TERMINATED = 6 }; } } diff --git a/src/opm/parser/eclipse/Parser/raw/RawKeyword.cpp b/src/opm/parser/eclipse/Parser/raw/RawKeyword.cpp index 20767b0dd..0844ea0e2 100644 --- a/src/opm/parser/eclipse/Parser/raw/RawKeyword.cpp +++ b/src/opm/parser/eclipse/Parser/raw/RawKeyword.cpp @@ -96,6 +96,13 @@ namespace { if (this->m_sizeType == Raw::SLASH_TERMINATED) this->m_isFinished = true; + if (this->m_sizeType == Raw::DOUBLE_SLASH_TERMINATED) { + if (m_isTempFinished) + this->m_isFinished = true; + else + this->m_isTempFinished = true; + } + if (m_sizeType == Raw::TABLE_COLLECTION) { m_currentNumTables += 1; if (m_currentNumTables == m_numTables) @@ -110,12 +117,15 @@ namespace { bool RawKeyword::addRecord(RawRecord record) { + + if (record.size() > 0) + m_isTempFinished = false; + this->m_records.push_back(std::move(record)); if (m_records.size() == this->m_fixedSize) { if( this->m_sizeType == Raw::FIXED || this->m_sizeType == Raw::CODE) this->m_isFinished = true; } - return this->m_isFinished; } diff --git a/src/opm/parser/eclipse/Parser/raw/RawKeyword.hpp b/src/opm/parser/eclipse/Parser/raw/RawKeyword.hpp index 50086cf7f..5ba58fc26 100644 --- a/src/opm/parser/eclipse/Parser/raw/RawKeyword.hpp +++ b/src/opm/parser/eclipse/Parser/raw/RawKeyword.hpp @@ -71,6 +71,7 @@ namespace Opm { size_t m_fixedSize = 0; size_t m_numTables = 0; size_t m_currentNumTables = 0; + bool m_isTempFinished = false; bool m_isFinished = false; std::vector< RawRecord > m_records; diff --git a/src/opm/parser/eclipse/share/keywords/000_Eclipse100/C/CECONT b/src/opm/parser/eclipse/share/keywords/000_Eclipse100/C/CECONT new file mode 100644 index 000000000..111577bb1 --- /dev/null +++ b/src/opm/parser/eclipse/share/keywords/000_Eclipse100/C/CECONT @@ -0,0 +1,22 @@ +{ + "name" : "CECONT", "sections" : ["SCHEDULE"], "records_set" : [ + [ + {"name" : "WELL", "value_type" : "STRING"}, + {"name" : "I", "value_type" : "INT", "default" : 0}, + {"name" : "J", "value_type" : "INT", "default" : 0}, + {"name" : "K_UPPER", "value_type" : "INT", "default" : 0}, + {"name" : "K_LOWER", "value_type" : "INT", "default" : 0}, + {"name" : "PROCEDURE", "value_type" : "STRING", "default" : "CON"}, + {"name" : "CHECK_STOPPED_WELLS", "value_type" : "STRING", "default" : "NO"} + ], + [ + {"name" : "TRACER", "value_type" : "STRING"}, + {"name" : "MAX_TOTAL_TRACER_RATE", "value_type" : "DOUBLE", "default" : 1e200}, + {"name" : "MAX_TOTAL_TRACER_CONC", "value_type" : "DOUBLE", "default" : 1e200}, + {"name" : "MAX_FREE_TRACER_RATE", "value_type" : "DOUBLE", "default" : 1e200}, + {"name" : "MAX_FREE_TRACER_CONC", "value_type" : "DOUBLE", "default" : 1e200}, + {"name" : "MAX_SOL_TRACER_RATE", "value_type" : "DOUBLE", "default" : 1e200}, + {"name" : "MAX_SOL_TRACER_CONC", "value_type" : "DOUBLE", "default" : 1e200} + ] + ] +} diff --git a/src/opm/parser/eclipse/share/keywords/keyword_list.cmake b/src/opm/parser/eclipse/share/keywords/keyword_list.cmake index 2f0eeb903..b7b49e135 100644 --- a/src/opm/parser/eclipse/share/keywords/keyword_list.cmake +++ b/src/opm/parser/eclipse/share/keywords/keyword_list.cmake @@ -79,6 +79,7 @@ set( keywords 000_Eclipse100/C/CART 000_Eclipse100/C/CBMOPTS 000_Eclipse100/C/CECON + 000_Eclipse100/C/CECONT 000_Eclipse100/C/COAL 000_Eclipse100/C/COALADS 000_Eclipse100/C/COALNUM diff --git a/tests/parser/ParserTests.cpp b/tests/parser/ParserTests.cpp index 2a2b461ac..f5f07c550 100644 --- a/tests/parser/ParserTests.cpp +++ b/tests/parser/ParserTests.cpp @@ -1774,6 +1774,38 @@ BOOST_AUTO_TEST_CASE(GetAlternatingKeywordFromParser) { BOOST_CHECK(kw.getRecord(13) == record1); } +BOOST_AUTO_TEST_CASE(ConstructFromJson_withDoubleRecords) { + const std::string json_string = R"( + {"name" : "CECONT", "sections" : ["PROPS"] , "records_set" : [[ + {"name" : "WELL", "value_type" : "STRING"}, + {"name" : "I", "value_type" : "INT"}, + {"name" : "J", "value_type" : "INT"}, + {"name" : "K", "value_type" : "INT"}], [ + {"name" : "TRACER" , "value_type" : "STRING"}, + {"name" : "rate", "value_type" : "DOUBLE"}]]} + )"; + Json::JsonObject jsonObject( json_string ); + ParserKeyword kw( jsonObject ); + BOOST_CHECK( kw.isDoubleRecordKeyword() ); + BOOST_CHECK_EQUAL(kw.getRecord(0).size(), 4); + BOOST_CHECK_EQUAL(kw.getRecord(1).size(), 2); +} + +BOOST_AUTO_TEST_CASE(GetDoubleRecordKeywordFromParser) { + Parser parser; + BOOST_CHECK(parser.hasKeyword("CECONT")); + ParserKeyword kw = parser.getKeyword("CECONT"); + BOOST_CHECK(kw.isDoubleRecordKeyword()); + BOOST_CHECK(kw.getSizeType() == DOUBLE_SLASH_TERMINATED); + auto record0 = kw.getRecord(0); + auto record1 = kw.getRecord(1); + BOOST_CHECK_EQUAL(record0.get(0).name(), "WELL"); + BOOST_CHECK_EQUAL(record1.get(0).name(), "TRACER"); + BOOST_CHECK(kw.getRecord(11) == record1); + BOOST_CHECK(kw.getRecord(13) == record1); +} + + BOOST_AUTO_TEST_CASE(Create1Arg) { ParserKeyword kw("GRID"); @@ -2136,4 +2168,78 @@ DENSITY } } +BOOST_AUTO_TEST_CASE(ParseDoubleRecords) { + const auto deck_string = std::string { R"( +RUNSPEC +FIELD +TABDIMS + 1* 2 +/ + +PROPS + +DENSITY + 50.91 62.4 / + 51.90 64.2 / + +SCHEDULE + +CECONT +PROD1 4* CON NO / +TR1 1000.0 0.5 / +TR2 2* 1* 0.8 2* / +TR3 1500.0 0.1 / +/ +PROD2 5 6 3 3 CON+ NO / +TR3 100.0 0.05 / +/ +PROD3 6* / +TR4 200 5* / +PLY 1* 0.7 / +/ +/ + +GCONSUMP +PLAT-A 20 50 / +PLAT-B 15 / +/ + +)" }; + Parser parser; + auto deck = parser.parseString(deck_string); + BOOST_CHECK( deck.hasKeyword("CECONT") ); + BOOST_CHECK( deck.hasKeyword("GCONSUMP") ); + + auto kw_density = deck.getKeyword("DENSITY"); + + auto kw = deck.getKeyword("CECONT"); + BOOST_CHECK( kw.isDoubleRecordKeyword() ); + + auto record00 = kw.getRecord(0); + BOOST_CHECK_EQUAL(record00.getItem(0).name(), "WELL"); + BOOST_CHECK_EQUAL(record00.getItem(0).get(0), "PROD1"); + BOOST_CHECK(record00.getItem(1).getType() == type_tag::integer); + BOOST_CHECK_EQUAL(record00.getItem(1).get(0), 0); + + auto record01 = kw.getRecord(1); + BOOST_CHECK_EQUAL(record01.getItem(0).name(), "TRACER"); + BOOST_CHECK_EQUAL(record01.getItem(0).get(0), "TR1"); + BOOST_CHECK(record01.getItem(1).getType() == type_tag::fdouble); + BOOST_CHECK_EQUAL(record01.getItem(1).get(0), 1000); + + auto record04 = kw.getRecord(4); + BOOST_CHECK_EQUAL(record04.getItem(0).name(), "WELL"); + BOOST_CHECK_EQUAL(record04.getItem(0).get(0), "PROD2"); + BOOST_CHECK(record04.getItem(1).getType() == type_tag::integer); + BOOST_CHECK_EQUAL(record04.getItem(1).get(0), 5); + + auto record08 = kw.getRecord(8); + BOOST_CHECK_EQUAL(record08.getItem(0).name(), "TRACER"); + BOOST_CHECK_EQUAL(record08.getItem(0).get(0), "PLY"); + BOOST_CHECK(record08.getItem(2).getType() == type_tag::fdouble); + BOOST_CHECK_EQUAL(record08.getItem(2).get(0), 0.7); + + +} +