From 70353f496d61ed55583b6bcc2a999304cf189f12 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Wed, 21 Oct 2020 17:22:18 +0200 Subject: [PATCH] Allow restart date to be at a TSTEP --- .../eclipse/EclipseState/Schedule/TimeMap.hpp | 7 +- .../eclipse/EclipseState/Schedule/TimeMap.cpp | 73 ++++++----- tests/parser/TimeMapTest.cpp | 122 +++++++++++++++++- 3 files changed, 165 insertions(+), 37 deletions(-) diff --git a/opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp b/opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp index 1aa8eda14..071ea57ec 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp @@ -36,6 +36,8 @@ namespace Opm { class Deck; class DeckKeyword; class DeckRecord; + class KeywordLocation; + class TimeMapContext; class TimeMap { public: @@ -108,9 +110,8 @@ namespace Opm { bool isTimestepInFreqSequence (size_t timestep, size_t start_timestep, size_t frequency, bool years) const; size_t closest(const std::vector & vec, size_t value) const; - void addTStep(int64_t step); - void addTime(std::time_t newTime); - void addFromTSTEPKeyword( const DeckKeyword& TSTEPKeyword ); + void addTime(std::time_t newTime, TimeMapContext& context, const KeywordLocation& location); + void addFromTSTEPKeyword( const DeckKeyword& TSTEPKeyword, TimeMapContext& context); void init_start(std::time_t start_time); std::vector m_timeList; diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/TimeMap.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/TimeMap.cpp index abe3f7251..3fa691b9a 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/TimeMap.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/TimeMap.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -44,6 +45,8 @@ constexpr const std::time_t invalid_time = -1; namespace Opm { + + namespace { const std::map month_indices = {{"JAN", 1}, {"FEB", 2}, @@ -62,8 +65,21 @@ namespace { {"DEC", 12}, {"DES", 12}}; + } +struct TimeMapContext { + bool rst_skip; + std::time_t last_time; + + TimeMapContext(bool skip, std::time_t t) : + rst_skip(skip), + last_time(t) + {} +}; + + + void TimeMap::init_start(std::time_t start_time) { auto timestamp = TimeStampUTC{start_time}; @@ -77,6 +93,7 @@ namespace { if (time_points.empty()) throw std::invalid_argument("Can not initialize with empty list of time points"); + TimeMapContext context(false, time_points[0]); this->init_start(time_points[0]); for (std::size_t ti = 1; ti < time_points.size(); ti++) { if (time_points[ti] == invalid_time) { @@ -84,7 +101,7 @@ namespace { this->m_restart_offset += 1; } else - this->addTime( time_points[ti] ); + this->addTime( time_points[ti], context, {} ); } if (this->m_restart_offset > 0) this->m_restart_offset += 1; @@ -113,8 +130,7 @@ namespace { for (std::size_t it = 1; it < this->m_restart_offset; it++) this->m_timeList.push_back(invalid_time); - bool skip = (this->m_restart_offset > 0); - bool restart_found = false; + TimeMapContext context(this->m_restart_offset > 0, start_time); for( const auto& keyword : SCHEDULESection(deck)) { // We're only interested in "TSTEP" and "DATES" keywords, // so we ignore everything else here... @@ -134,32 +150,11 @@ namespace { std::throw_with_nested(opm_error); } - if (nextTime == this->m_restart_time) { - skip = false; - restart_found = true; - } - - if (!skip) - this->addTime(nextTime); + this->addTime(nextTime, context, keyword.location()); } - continue; } - - if (skip) - continue; - - this->addFromTSTEPKeyword(keyword); - } - - /* - 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 && !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())); + this->addFromTSTEPKeyword(keyword, context); } } @@ -199,7 +194,22 @@ namespace { this->m_timeList.front()); } - void TimeMap::addTime(std::time_t newTime) { + void TimeMap::addTime(std::time_t newTime, TimeMapContext& context, const KeywordLocation& location) { + context.last_time = newTime; + if (context.rst_skip) { + if (newTime < this->m_restart_time) + return; + + if (newTime == this->m_restart_time) + context.rst_skip = false; + + if (newTime > this->m_restart_time) { + TimeStampUTC ts(this->m_restart_time); + auto reason = fmt::format("Have scanned past restart data: {:4d}-{:02d}-{:02d}", ts.year(), ts.month(), ts.day()); + throw OpmInputError(reason, location); + } + } + const std::time_t lastTime = m_timeList.back(); const size_t step = m_timeList.size(); if (newTime > lastTime) { @@ -223,10 +233,6 @@ namespace { throw std::invalid_argument("Times added must be in strictly increasing order."); } - void TimeMap::addTStep(int64_t step) { - this->addTime(forward(m_timeList.back(), step)); - } - size_t TimeMap::size() const { return m_timeList.size(); } @@ -266,7 +272,7 @@ namespace { } - void TimeMap::addFromTSTEPKeyword(const DeckKeyword &TSTEPKeyword) { + void TimeMap::addFromTSTEPKeyword(const DeckKeyword &TSTEPKeyword, TimeMapContext& context) { if (TSTEPKeyword.name() != "TSTEP") throw std::invalid_argument("Method requires TSTEP keyword input."); { @@ -274,7 +280,8 @@ namespace { for (size_t itemIndex = 0; itemIndex < item.data_size(); itemIndex++) { const int64_t seconds = static_cast(item.getSIDouble(itemIndex)); - this->addTStep(seconds); + std::time_t next_time = TimeMap::forward(context.last_time, seconds); + this->addTime(next_time, context, TSTEPKeyword.location()); } } } diff --git a/tests/parser/TimeMapTest.cpp b/tests/parser/TimeMapTest.cpp index e9dbd8d0d..dd5f31d94 100644 --- a/tests/parser/TimeMapTest.cpp +++ b/tests/parser/TimeMapTest.cpp @@ -683,7 +683,7 @@ DATES auto invalid_restart = std::make_pair(Opm::asTimeT(Opm::TimeStampUTC(2005, 1, 2)), 5); auto valid_restart = std::make_pair(Opm::asTimeT(Opm::TimeStampUTC(2005, 1, 1)), 5); - BOOST_CHECK_THROW( Opm::TimeMap(deck1, invalid_restart) , std::invalid_argument); + BOOST_CHECK_THROW( Opm::TimeMap(deck1, invalid_restart) , std::exception); Opm::TimeMap tm1(deck1, valid_restart); BOOST_CHECK_THROW( tm1[1], std::invalid_argument ); BOOST_CHECK_THROW( tm1[4], std::invalid_argument ); @@ -696,3 +696,123 @@ DATES BOOST_CHECK_EQUAL(tm2[6], Opm::asTimeT(Opm::TimeStampUTC(2006,1,1))); } + +BOOST_AUTO_TEST_CASE(RESTART2) { + std::string deck_string1 = R"( +START + 1 JAN 2000 / + +RESTART + 'CASE' 5 / + +SCHEDULE + +DATES + 1 JAN 2001 / + 1 JAN 2002 / + 1 JAN 2003 / + 1 JAN 2004 / -- 4 +/ + +DATES -- Report step 5 + 1 JAN 2005 / +/ + +DATES + 1 JAN 2006 / -- 6 + 1 JAN 2007 / -- 7 + 1 JAN 2008 / -- 8 + 1 JAN 2009 / -- 9 + 1 JAN 2010 / -- 10 +/ +)"; + + std::string deck_string2 = R"( +START + 1 JAN 2000 / + +RESTART + 'CASE' 5 / + +SCHEDULE + +DATES + 1 JAN 2001 / + 1 JAN 2002 / + 1 JAN 2004 / -- 3 +/ + +DATES -- Report step 4 + 1 JAN 2005 / +/ + +TSTEP +1 / -- <- Restart from here 5 + +DATES + 1 JAN 2006 / -- 6 + 1 JAN 2007 / -- 7 + 1 JAN 2008 / -- 8 + 1 JAN 2009 / -- 9 + 1 JAN 2010 / -- 10 +/ +)"; + + std::string deck_string3 = R"( +START + 1 JAN 2000 / + +RESTART + 'CASE' 5 / + +SCHEDULE + +DATES + 1 JAN 2001 / + 1 JAN 2002 / + 1 JAN 2004 / -- 3 +/ + +DATES -- Report step 4 + 1 JAN 2005 / +/ + +TSTEP +1 / -- <- Restart from here 5 + +)"; + + std::string deck_string4 = R"( +START + 1 JAN 2000 / + +RESTART + 'CASE' 5 / + +SCHEDULE + +DATES + 1 JAN 2001 / + 1 JAN 2002 / + 1 JAN 2004 / -- 3 +/ + +DATES -- Report step 4 + 1 JAN 2005 / +/ + +TSTEP +2 / -- <- Restart from here 5 + +)"; + Opm::Parser parser; + const auto deck1 = parser.parseString(deck_string1); + const auto deck2 = parser.parseString(deck_string2); + const auto deck3 = parser.parseString(deck_string3); + const auto deck4 = parser.parseString(deck_string4); + auto restart = std::make_pair(Opm::asTimeT(Opm::TimeStampUTC(2005, 1, 2)), 5); + BOOST_CHECK_THROW( Opm::TimeMap(deck1, restart) , std::exception); + BOOST_CHECK_NO_THROW( Opm::TimeMap(deck2, restart) ); + BOOST_CHECK_NO_THROW( Opm::TimeMap(deck3, restart) ); + BOOST_CHECK_THROW( Opm::TimeMap(deck4, restart) , std::exception); +}