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.
This commit is contained in:
Joakim Hove 2020-09-29 22:01:24 +02:00
parent 1ae7026a4b
commit 42f1278db3
5 changed files with 113 additions and 109 deletions

View File

@ -51,12 +51,12 @@ namespace Opm {
double getTotalTime() const; double getTotalTime() const;
double seconds(size_t timeStep) const; double seconds(size_t timeStep) const;
std::size_t restart_offset() const; std::size_t restart_offset() const;
std::time_t restart_time() const;
std::time_t operator[] (size_t index) const; std::time_t operator[] (size_t index) const;
/// Return the date and time where a given time step starts. /// Return the date and time where a given time step starts.
std::time_t getStartTime(size_t tStepIdx) const; std::time_t getStartTime(size_t tStepIdx) const;
std::time_t getEndTime() 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. /// 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; double getTimePassedUntil(size_t tLevelIdx) const;
/// Return the length of a given time step in seconds. /// Return the length of a given time step in seconds.
@ -83,7 +83,6 @@ namespace Opm {
serializer(m_timeList); serializer(m_timeList);
serializer.vector(m_first_timestep_years); serializer.vector(m_first_timestep_years);
serializer.vector(m_first_timestep_months); serializer.vector(m_first_timestep_months);
serializer(m_skiprest);
serializer(m_restart_offset); serializer(m_restart_offset);
} }
@ -117,8 +116,8 @@ namespace Opm {
std::vector<std::time_t> m_timeList; std::vector<std::time_t> m_timeList;
std::vector<StepData> m_first_timestep_years; // A list of the first timestep of every year std::vector<StepData> m_first_timestep_years; // A list of the first timestep of every year
std::vector<StepData> m_first_timestep_months; // A list of the first timestep of every month std::vector<StepData> 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::size_t m_restart_offset = 0;
std::time_t m_restart_time;
}; };
std::ostream& operator<<(std::ostream& stream, const TimeMap& tm); std::ostream& operator<<(std::ostream& stream, const TimeMap& tm);

View File

@ -311,15 +311,60 @@ namespace {
all. all.
*/ */
std::unordered_set<std::string> skiprest_whitelist = {"VFPPROD", "VFPINJ", "RPTSCHED", "RPTRST", "TUNING", "MESSAGES"}; std::unordered_set<std::string> 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()) The behavior of variable restart_skip is more lenient than the
currentStep = 0; SKIPREST keyword. If this is a restarted[1] run the loop iterating
else over keywords will skip the all keywords[2] until DATES keyword with
currentStep = this->m_timeMap.restart_offset(); 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) { while (true) {
if (keywordIdx == section.size())
break;
const auto& keyword = section.getKeyword(keywordIdx); 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") { if (keyword.name() == "ACTIONX") {
Action::ActionX action(keyword, this->m_timeMap.getStartTime(currentStep)); Action::ActionX action(keyword, this->m_timeMap.getStartTime(currentStep));
while (true) { while (true) {
@ -334,39 +379,32 @@ namespace {
if (Action::ActionX::valid_keyword(action_keyword.name())) if (Action::ActionX::valid_keyword(action_keyword.name()))
action.addKeyword(action_keyword); action.addKeyword(action_keyword);
else { 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" std::string msg_fmt = "The keyword {keyword} is not supported in the ACTIONX block\n"
"In {file} line {line}."; "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); this->addACTIONX(action, currentStep);
keywordIdx++;
continue;
} }
else if (keyword.name() == "DATES") { this->handleKeyword(python,
checkIfAllConnectionsIsShut(currentStep); input_path,
currentStep += keyword.size(); currentStep,
} section,
keywordIdx,
else if (keyword.name() == "TSTEP") { keyword,
checkIfAllConnectionsIsShut(currentStep); parseContext,
currentStep += keyword.getRecord(0).getItem(0).data_size(); errors,
} grid,
fp,
else { rftProperties);
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");
}
keywordIdx++; keywordIdx++;
if (keywordIdx == section.size())
break;
} }
checkIfAllConnectionsIsShut(currentStep); checkIfAllConnectionsIsShut(currentStep);
for (auto rftPair = rftProperties.begin(); rftPair != rftProperties.end(); ++rftPair) { for (auto rftPair = rftProperties.begin(); rftPair != rftProperties.end(); ++rftPair) {
const DeckKeyword& keyword = *rftPair->first; const DeckKeyword& keyword = *rftPair->first;
std::size_t timeStep = rftPair->second; std::size_t timeStep = rftPair->second;

View File

@ -85,41 +85,31 @@ namespace {
this->m_restart_offset += 1; this->m_restart_offset += 1;
} }
TimeMap::TimeMap( const Deck& deck, const std::pair<std::time_t, std::size_t>& restart) {
bool skiprest = deck.hasKeyword<ParserKeywords::SKIPREST>();
{
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<std::time_t, std::size_t>& 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; this->m_restart_offset = restart.second;
bool skip = false;
for (std::size_t it = 1; it < this->m_restart_offset; it++) for (std::size_t it = 1; it < this->m_restart_offset; it++)
this->m_timeList.push_back(invalid_time); this->m_timeList.push_back(invalid_time);
if (this->m_restart_offset > 0) { bool skip = (this->m_restart_offset > 0);
if (skiprest) bool restart_found = false;
skip = true;
else {
this->m_timeList.push_back(restart_time);
skip = false;
}
}
for( const auto& keyword : SCHEDULESection(deck)) { for( const auto& keyword : SCHEDULESection(deck)) {
// We're only interested in "TSTEP" and "DATES" keywords, // We're only interested in "TSTEP" and "DATES" keywords,
// so we ignore everything else here... // so we ignore everything else here...
@ -130,11 +120,13 @@ namespace {
for (size_t recordIndex = 0; recordIndex < keyword.size(); recordIndex++) { for (size_t recordIndex = 0; recordIndex < keyword.size(); recordIndex++) {
const auto &record = keyword.getRecord(recordIndex); const auto &record = keyword.getRecord(recordIndex);
const std::time_t nextTime = TimeMap::timeFromEclipse(record); const std::time_t nextTime = TimeMap::timeFromEclipse(record);
if (nextTime == restart_time) if (nextTime == this->m_restart_time) {
skip = false; skip = false;
restart_found = true;
}
if (!skip) if (!skip)
addTime(nextTime); this->addTime(nextTime);
} }
continue; continue;
@ -143,36 +135,25 @@ namespace {
if (skip) if (skip)
continue; continue;
addFromTSTEPKeyword(keyword); this->addFromTSTEPKeyword(keyword);
} }
/* /*
There is a coupling between the presence of the SKIPREST keyword and It is a hard requirement that the restart date is found as a DATES
the restart argument: The restart argument indicates whether this is keyword, although it is technically possible to create a valid
deck should be parsed as restarted deck. If m_restart_offset == 0 we do restarted case using TSTEP we do not accept that.
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".
*/ */
if (this->m_restart_offset != 0) { if (this->m_restart_offset != 0 && !restart_found) {
if (skiprest) { TimeStampUTC ts(this->m_restart_time);
const auto iter = std::find(this->m_timeList.begin(), this->m_timeList.end(), 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()));
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;
}
} }
} }
TimeMap TimeMap::serializeObject() TimeMap TimeMap::serializeObject()
{ {
TimeMap result({123}); TimeMap result({123});
result.m_skiprest = true;
result.m_restart_offset = 4; result.m_restart_offset = 4;
result.m_restart_time = 19867234;
return result; return result;
} }
@ -229,7 +210,7 @@ namespace {
} }
void TimeMap::addTStep(int64_t step) { void TimeMap::addTStep(int64_t step) {
addTime(forward(m_timeList.back(), step)); this->addTime(forward(m_timeList.back(), step));
} }
size_t TimeMap::size() const { size_t TimeMap::size() const {
@ -279,7 +260,7 @@ namespace {
for (size_t itemIndex = 0; itemIndex < item.data_size(); itemIndex++) { for (size_t itemIndex = 0; itemIndex < item.data_size(); itemIndex++) {
const int64_t seconds = static_cast<int64_t>(item.getSIDouble(itemIndex)); const int64_t seconds = static_cast<int64_t>(item.getSIDouble(itemIndex));
addTStep(seconds); this->addTStep(seconds);
} }
} }
} }
@ -444,10 +425,11 @@ namespace {
return this->m_restart_offset; return this->m_restart_offset;
} }
bool TimeMap::skiprest() const { std::time_t TimeMap::restart_time() const {
return this->m_skiprest; return this->m_restart_time;
} }
std::ostream& operator<<(std::ostream& stream, const TimeMap& tm) { std::ostream& operator<<(std::ostream& stream, const TimeMap& tm) {
std::stringstream ss; std::stringstream ss;
ss << "{"; ss << "{";

View File

@ -449,7 +449,14 @@ TSTEP
28 31 30 31 30 31 31 30 31 30 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 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 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

View File

@ -223,7 +223,6 @@ BOOST_AUTO_TEST_CASE(TimeStepsCorrect) {
BOOST_CHECK_EQUAL(tmap.getTimeStepLength(8), 6*24*60*60); BOOST_CHECK_EQUAL(tmap.getTimeStepLength(8), 6*24*60*60);
BOOST_CHECK_EQUAL(tmap.getTimeStepLength(9), 7*24*60*60); BOOST_CHECK_EQUAL(tmap.getTimeStepLength(9), 7*24*60*60);
BOOST_CHECK(!tmap.skiprest());
} }
@ -663,7 +662,7 @@ SCHEDULE
--/ --/
DATES DATES
1 JUL 2005 / 1 JAN 2005 /
/ /
DATES 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; Opm::Parser parser;
const auto deck1 = parser.parseString(deck_string1); const auto deck1 = parser.parseString(deck_string1);
const auto deck2 = parser.parseString(deck_string2); 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. // 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); auto invalid_restart = std::make_pair(Opm::asTimeT(Opm::TimeStampUTC(2005, 1, 2)), 5);
@ -706,15 +690,9 @@ TSTEP
auto start = tm1[0]; auto start = tm1[0];
BOOST_CHECK_EQUAL(start , Opm::asTimeT(Opm::TimeStampUTC(2000,1,1))); 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_EQUAL(tm1[5] , Opm::asTimeT(Opm::TimeStampUTC(2005,1,1)));
BOOST_CHECK(tm1.skiprest());
Opm::TimeMap tm2(deck2, valid_restart); Opm::TimeMap tm2(deck2, valid_restart);
BOOST_CHECK_EQUAL(tm2[5], Opm::asTimeT(Opm::TimeStampUTC(2005,1,1))); 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)));
} }