From 42f1278db39dbe728bc2ebce515dbea584d75400 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Tue, 29 Sep 2020 22:01:24 +0200 Subject: [PATCH] Changes in restart TimeMap and Schedule::iterateSection This commit changes two aspects of restarts where well and group information is assembled from the restart file: o We require the restart date to be present in the Schedule file as a DATES keyword. o SKIPREST behavior is implemented unconditionally for restarts with restart files, even if the SKIPREST keyword is not present. --- .../eclipse/EclipseState/Schedule/TimeMap.hpp | 5 +- .../EclipseState/Schedule/Schedule.cpp | 92 +++++++++++++------ .../eclipse/EclipseState/Schedule/TimeMap.cpp | 90 ++++++++---------- tests/SPE1CASE1.DATA | 9 +- tests/parser/TimeMapTest.cpp | 26 +----- 5 files changed, 113 insertions(+), 109 deletions(-) diff --git a/opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp b/opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp index c4a8469c9..1aa8eda14 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp @@ -51,12 +51,12 @@ namespace Opm { double getTotalTime() const; double seconds(size_t timeStep) const; std::size_t restart_offset() const; + std::time_t restart_time() const; std::time_t operator[] (size_t index) const; /// Return the date and time where a given time step starts. std::time_t getStartTime(size_t tStepIdx) const; std::time_t getEndTime() const; - bool skiprest() const; /// Return the period of time in seconds which passed between the start of the simulation and a given point in time. double getTimePassedUntil(size_t tLevelIdx) const; /// Return the length of a given time step in seconds. @@ -83,7 +83,6 @@ namespace Opm { serializer(m_timeList); serializer.vector(m_first_timestep_years); serializer.vector(m_first_timestep_months); - serializer(m_skiprest); serializer(m_restart_offset); } @@ -117,8 +116,8 @@ namespace Opm { std::vector m_timeList; std::vector m_first_timestep_years; // A list of the first timestep of every year std::vector m_first_timestep_months; // A list of the first timestep of every month - bool m_skiprest = false; std::size_t m_restart_offset = 0; + std::time_t m_restart_time; }; std::ostream& operator<<(std::ostream& stream, const TimeMap& tm); diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index c62191514..10f65c68b 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -311,15 +311,60 @@ namespace { all. */ std::unordered_set skiprest_whitelist = {"VFPPROD", "VFPINJ", "RPTSCHED", "RPTRST", "TUNING", "MESSAGES"}; + std::size_t currentStep = 0; + const auto restart_offset = this->m_timeMap.restart_offset(); - std::size_t currentStep; - if (this->m_timeMap.skiprest()) - currentStep = 0; - else - currentStep = this->m_timeMap.restart_offset(); + /* + The behavior of variable restart_skip is more lenient than the + SKIPREST keyword. If this is a restarted[1] run the loop iterating + over keywords will skip the all keywords[2] until DATES keyword with + the restart date is encountered - irrespective of whether the SKIPREST + keyword is present in the deck or not. + [1]: opm/flow can restart in a mode where all the keywords from the + historical part of the Schedule section is internalized, and only + the solution fields are read from the restart file. In this case + we will have TimeMap::restart_offset() == 0. + + [2]: With the exception of the keywords in the skiprest_whitelist; + these keywords will be assigned to report step 0. + */ + + auto restart_skip = restart_offset > 0; while (true) { + if (keywordIdx == section.size()) + break; + const auto& keyword = section.getKeyword(keywordIdx); + if (keyword.name() == "DATES") { + checkIfAllConnectionsIsShut(currentStep); + for (const auto& record : keyword) { + if (restart_skip) { + auto deck_time = TimeMap::timeFromEclipse(record); + if (deck_time == this->m_timeMap.restart_time()) { + restart_skip = false; + currentStep = this->m_timeMap.restart_offset(); + } + } else + currentStep += 1; + } + keywordIdx++; + continue; + } + + if (keyword.name() == "TSTEP") { + checkIfAllConnectionsIsShut(currentStep); + currentStep += keyword.getRecord(0).getItem(0).data_size(); + keywordIdx++; + continue; + } + + if (restart_skip && skiprest_whitelist.count(keyword.name()) == 0) { + OpmLog::info("Skipping keyword: " + keyword.name() + " while loading SCHEDULE section"); + keywordIdx++; + continue; + } + if (keyword.name() == "ACTIONX") { Action::ActionX action(keyword, this->m_timeMap.getStartTime(currentStep)); while (true) { @@ -334,39 +379,32 @@ namespace { if (Action::ActionX::valid_keyword(action_keyword.name())) action.addKeyword(action_keyword); else { - std::string msg = "The keyword {0} is not supported in a ACTIONX block. file: {1} line: {2}"; std::string msg_fmt = "The keyword {keyword} is not supported in the ACTIONX block\n" "In {file} line {line}."; - parseContext.handleError( ParseContext::ACTIONX_ILLEGAL_KEYWORD, msg, action_keyword.location(), errors); + parseContext.handleError( ParseContext::ACTIONX_ILLEGAL_KEYWORD, msg_fmt, action_keyword.location(), errors); } } this->addACTIONX(action, currentStep); + keywordIdx++; + continue; } - else if (keyword.name() == "DATES") { - checkIfAllConnectionsIsShut(currentStep); - currentStep += keyword.size(); - } - - else if (keyword.name() == "TSTEP") { - checkIfAllConnectionsIsShut(currentStep); - currentStep += keyword.getRecord(0).getItem(0).data_size(); - } - - else { - if (currentStep >= this->m_timeMap.restart_offset() || skiprest_whitelist.count(keyword.name())) - this->handleKeyword(python, input_path, currentStep, section, keywordIdx, keyword, parseContext, errors, grid, fp, rftProperties); - else - OpmLog::info("Skipping keyword: " + keyword.name() + " while loading SCHEDULE section"); - } - + this->handleKeyword(python, + input_path, + currentStep, + section, + keywordIdx, + keyword, + parseContext, + errors, + grid, + fp, + rftProperties); keywordIdx++; - if (keywordIdx == section.size()) - break; } - checkIfAllConnectionsIsShut(currentStep); + for (auto rftPair = rftProperties.begin(); rftPair != rftProperties.end(); ++rftPair) { const DeckKeyword& keyword = *rftPair->first; std::size_t timeStep = rftPair->second; diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/TimeMap.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/TimeMap.cpp index 9dce15899..5c4d3c728 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/TimeMap.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/TimeMap.cpp @@ -85,41 +85,31 @@ namespace { this->m_restart_offset += 1; } - TimeMap::TimeMap( const Deck& deck, const std::pair& restart) { - bool skiprest = deck.hasKeyword(); - { - std::time_t start_time; - if (deck.hasKeyword("START")) { - // Use the 'START' keyword to find out the start date (if the - // keyword was specified) - const auto& keyword = deck.getKeyword("START"); - start_time = timeFromEclipse(keyword.getRecord(0)); - } else { - // The default start date is not specified in the Eclipse - // reference manual. We hence just assume it is same as for - // the START keyword for Eclipse R100, i.e., January 1st, - // 1983... - start_time = mkdate(1983, 1, 1); - } - this->init_start(start_time); - } - auto restart_time = restart.first; + TimeMap::TimeMap( const Deck& deck, const std::pair& restart) { + std::time_t start_time; + if (deck.hasKeyword("START")) { + // Use the 'START' keyword to find out the start date (if the + // keyword was specified) + const auto& keyword = deck.getKeyword("START"); + start_time = timeFromEclipse(keyword.getRecord(0)); + } else { + // The default start date is not specified in the Eclipse + // reference manual. We hence just assume it is same as for + // the START keyword for Eclipse R100, i.e., January 1st, + // 1983... + start_time = mkdate(1983, 1, 1); + } + this->init_start(start_time); + + this->m_restart_time = restart.first; this->m_restart_offset = restart.second; - bool skip = false; for (std::size_t it = 1; it < this->m_restart_offset; it++) this->m_timeList.push_back(invalid_time); - if (this->m_restart_offset > 0) { - if (skiprest) - skip = true; - else { - this->m_timeList.push_back(restart_time); - skip = false; - } - } - + bool skip = (this->m_restart_offset > 0); + bool restart_found = false; for( const auto& keyword : SCHEDULESection(deck)) { // We're only interested in "TSTEP" and "DATES" keywords, // so we ignore everything else here... @@ -130,11 +120,13 @@ namespace { for (size_t recordIndex = 0; recordIndex < keyword.size(); recordIndex++) { const auto &record = keyword.getRecord(recordIndex); const std::time_t nextTime = TimeMap::timeFromEclipse(record); - if (nextTime == restart_time) + if (nextTime == this->m_restart_time) { skip = false; + restart_found = true; + } if (!skip) - addTime(nextTime); + this->addTime(nextTime); } continue; @@ -143,36 +135,25 @@ namespace { if (skip) continue; - addFromTSTEPKeyword(keyword); + this->addFromTSTEPKeyword(keyword); } /* - There is a coupling between the presence of the SKIPREST keyword and - the restart argument: The restart argument indicates whether this is - deck should be parsed as restarted deck. If m_restart_offset == 0 we do - not interpret this as restart situation and the presence of SKIPREST - is ignored. In the opposite case we verify - post loading - that we - have actually located the restart date - otherwise "something is - broken". + It is a hard requirement that the restart date is found as a DATES + keyword, although it is technically possible to create a valid + restarted case using TSTEP we do not accept that. */ - if (this->m_restart_offset != 0) { - if (skiprest) { - const auto iter = std::find(this->m_timeList.begin(), this->m_timeList.end(), restart_time); - if (iter == this->m_timeList.end()) { - TimeStampUTC ts(restart_time); - throw std::invalid_argument("Could not find restart date " + std::to_string(ts.year()) + "-" + std::to_string(ts.month()) + "-" + std::to_string(ts.day())); - } - this->m_skiprest = true; - } + if (this->m_restart_offset != 0 && !restart_found) { + TimeStampUTC ts(this->m_restart_time); + throw std::invalid_argument("Could not find restart date " + std::to_string(ts.year()) + "-" + std::to_string(ts.month()) + "-" + std::to_string(ts.day())); } } TimeMap TimeMap::serializeObject() { TimeMap result({123}); - result.m_skiprest = true; result.m_restart_offset = 4; - + result.m_restart_time = 19867234; return result; } @@ -229,7 +210,7 @@ namespace { } void TimeMap::addTStep(int64_t step) { - addTime(forward(m_timeList.back(), step)); + this->addTime(forward(m_timeList.back(), step)); } size_t TimeMap::size() const { @@ -279,7 +260,7 @@ namespace { for (size_t itemIndex = 0; itemIndex < item.data_size(); itemIndex++) { const int64_t seconds = static_cast(item.getSIDouble(itemIndex)); - addTStep(seconds); + this->addTStep(seconds); } } } @@ -444,10 +425,11 @@ namespace { return this->m_restart_offset; } - bool TimeMap::skiprest() const { - return this->m_skiprest; + std::time_t TimeMap::restart_time() const { + return this->m_restart_time; } + std::ostream& operator<<(std::ostream& stream, const TimeMap& tm) { std::stringstream ss; ss << "{"; diff --git a/tests/SPE1CASE1.DATA b/tests/SPE1CASE1.DATA index e408a82da..13e0c5753 100644 --- a/tests/SPE1CASE1.DATA +++ b/tests/SPE1CASE1.DATA @@ -449,7 +449,14 @@ TSTEP 28 31 30 31 30 31 31 30 31 30 31 31 28 31 30 31 30 31 31 30 31 30 31 31 28 31 30 31 30 31 31 30 31 30 31 -31 28 31 30 31 30 31 31 30 31 30 31 +31 / + +DATES + 28 'FEB' 2019 / +/ + +TSTEP + 31 30 31 30 31 31 30 31 30 31 31 28 31 30 31 30 31 31 30 31 30 31 31 28 31 30 31 30 31 31 30 31 30 31 31 28 31 30 31 30 31 31 30 31 30 31 diff --git a/tests/parser/TimeMapTest.cpp b/tests/parser/TimeMapTest.cpp index a800d0085..e9dbd8d0d 100644 --- a/tests/parser/TimeMapTest.cpp +++ b/tests/parser/TimeMapTest.cpp @@ -223,7 +223,6 @@ BOOST_AUTO_TEST_CASE(TimeStepsCorrect) { BOOST_CHECK_EQUAL(tmap.getTimeStepLength(8), 6*24*60*60); BOOST_CHECK_EQUAL(tmap.getTimeStepLength(9), 7*24*60*60); - BOOST_CHECK(!tmap.skiprest()); } @@ -663,7 +662,7 @@ SCHEDULE --/ DATES - 1 JUL 2005 / + 1 JAN 2005 / / DATES @@ -675,25 +674,10 @@ DATES / )"; - std::string deck_string3 = R"( -START - 1 JAN 2000 / - -RESTART - 'CASE' 5 / - -SCHEDULE - --- This test does not have SKIPREST - -TSTEP - 1 1 1 / -)"; Opm::Parser parser; const auto deck1 = parser.parseString(deck_string1); const auto deck2 = parser.parseString(deck_string2); - const auto deck3 = parser.parseString(deck_string3); // The date 2005-01-02 is not present as a DATES in the deck; invalid input. auto invalid_restart = std::make_pair(Opm::asTimeT(Opm::TimeStampUTC(2005, 1, 2)), 5); @@ -706,15 +690,9 @@ TSTEP auto start = tm1[0]; BOOST_CHECK_EQUAL(start , Opm::asTimeT(Opm::TimeStampUTC(2000,1,1))); BOOST_CHECK_EQUAL(tm1[5] , Opm::asTimeT(Opm::TimeStampUTC(2005,1,1))); - BOOST_CHECK(tm1.skiprest()); Opm::TimeMap tm2(deck2, valid_restart); BOOST_CHECK_EQUAL(tm2[5], Opm::asTimeT(Opm::TimeStampUTC(2005,1,1))); - BOOST_CHECK_EQUAL(tm2[6], Opm::asTimeT(Opm::TimeStampUTC(2005,7,1))); + BOOST_CHECK_EQUAL(tm2[6], Opm::asTimeT(Opm::TimeStampUTC(2006,1,1))); - Opm::TimeMap tm3(deck3, valid_restart); - BOOST_CHECK_EQUAL(tm3[5], Opm::asTimeT(Opm::TimeStampUTC(2005,1,1))); - BOOST_CHECK_EQUAL(tm3[6], Opm::asTimeT(Opm::TimeStampUTC(2005,1,2))); - BOOST_CHECK_EQUAL(tm3[7], Opm::asTimeT(Opm::TimeStampUTC(2005,1,3))); - BOOST_CHECK_EQUAL(tm3[8], Opm::asTimeT(Opm::TimeStampUTC(2005,1,4))); }