Merge pull request #2347 from joakim-hove/ched-state-rst-config2

Internalize restart configuration in new schedule structure
This commit is contained in:
Joakim Hove 2021-03-27 11:39:23 +01:00 committed by GitHub
commit c7200aa1af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 797 additions and 1186 deletions

View File

@ -89,7 +89,6 @@ if(ENABLE_ECL_INPUT)
src/opm/parser/eclipse/EclipseState/InitConfig/FoamConfig.cpp
src/opm/parser/eclipse/EclipseState/InitConfig/InitConfig.cpp
src/opm/parser/eclipse/EclipseState/IOConfig/IOConfig.cpp
src/opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.cpp
src/opm/parser/eclipse/EclipseState/Runspec.cpp
src/opm/parser/eclipse/EclipseState/TracerConfig.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Action/ActionAST.cpp
@ -130,6 +129,7 @@ if(ENABLE_ECL_INPUT)
src/opm/parser/eclipse/EclipseState/Schedule/OilVaporizationProperties.cpp
src/opm/parser/eclipse/EclipseState/Schedule/RFTConfig.cpp
src/opm/parser/eclipse/EclipseState/Schedule/RPTConfig.cpp
src/opm/parser/eclipse/EclipseState/Schedule/RSTConfig.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp
src/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.cpp
src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp
@ -341,7 +341,6 @@ if(ENABLE_ECL_INPUT)
tests/parser/CopyRegTests.cpp
tests/parser/DeckValueTests.cpp
tests/parser/DeckTests.cpp
tests/parser/DynamicStateTests.cpp
tests/parser/EclipseGridTests.cpp
tests/parser/EmbeddedPython.cpp
tests/parser/EqualRegTests.cpp
@ -758,6 +757,7 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp
opm/parser/eclipse/EclipseState/Schedule/RFTConfig.hpp
opm/parser/eclipse/EclipseState/Schedule/RPTConfig.hpp
opm/parser/eclipse/EclipseState/Schedule/RSTConfig.hpp
opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp
opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.hpp
opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp
@ -774,7 +774,6 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/EclipseState/Schedule/MessageLimits.hpp
opm/parser/eclipse/EclipseState/Schedule/Events.hpp
opm/parser/eclipse/EclipseState/Schedule/OilVaporizationProperties.hpp
opm/parser/eclipse/EclipseState/Schedule/DynamicState.hpp
opm/parser/eclipse/EclipseState/Schedule/MSW/icd.hpp
opm/parser/eclipse/EclipseState/Schedule/MSW/Segment.hpp
opm/parser/eclipse/EclipseState/Schedule/MSW/Segment.hpp
@ -786,7 +785,6 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/EclipseState/SimulationConfig/RockConfig.hpp
opm/parser/eclipse/EclipseState/SimulationConfig/SimulationConfig.hpp
opm/parser/eclipse/EclipseState/Schedule/MSW/Valve.hpp
opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp
opm/parser/eclipse/EclipseState/IOConfig/IOConfig.hpp
opm/parser/eclipse/EclipseState/checkDeck.hpp
opm/parser/eclipse/EclipseState/Runspec.hpp

View File

@ -19,7 +19,7 @@
#include <getopt.h>
#include <vector>
#include <iostream>
#include <fmt/format.h>
#include <opm/parser/eclipse/Parser/Parser.hpp>

View File

@ -1,390 +0,0 @@
/*
Copyright 2015 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 <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_RESTART_CONFIG_HPP
#define OPM_RESTART_CONFIG_HPP
#include <optional>
#include <set>
#include <vector>
#include <opm/parser/eclipse/EclipseState/Schedule/DynamicState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp>
/*
The RestartConfig class internalizes information of when (at which
report steps) we should save restart files, and which properties
should be included in the restart files. The configuration of this
immensely complex, and this code is unfortunately also way too
complex.
The most basic question to disentangle is the "When to write restart
files" versus "What data to store write in the restart file". As
expressed in the deck keywords this completely entangled, in this
implementation we have tried to disentangle it:
Keywords involved
-----------------
RPTRST: This is the main keyword for configuring restart output; it
can be used to configure bothe when to write the files and which
properties should be included in the restart files.
RPTSCHED: The main purpose of the RPTSCHED keyword is to configure
output from the SCHEDULE section to the PRINT file. However the
mneomnic RESTART=n can be used to turn writing of restart files
on, and also for values > 2 to some configuration of what is
written to the restart file:
RESTART=1 : As RPTRST,BASIC=1
RESTART>1 : As RPTRST,BASIC=2
RESTART>2 : Flow is added to restart file
RESTART>3 : Fluid in place is added to restart file
RESTART=6 : Restart file for every timestep.
RPTSOL: The RPTSOL keyword is very similar to the RPTCHED keyword,
it configures output from the SOLUTION section to the PRINT file,
but just as the RPTSCHED keyword it accepts a RESTART=n mnenonic
which can be used similarly to the BASIC=n mnenonic of the RPTRST
keyword. In particular the writing of an initial restart files
with initial equilibrium solution is controlled by the RPTSOL
keyword. If the restart mneonic is greater than 2 that can be
used to configure FLOWS and FIP keywords in the restart file.
RESTART=1 : As RPTRST,BASIC=1
RESTART>1 : As RPTRST,BASIC=2
RESTART>2 : Flow is added to restart file
RESTART>3 : Fluid in place is added to restart file
The basic rule in ECLIPSE is generally that the 'last keyword wins',
but for the RPTRST RPTSHCED combination a BASIC setting with n >= 3
will override consecutive RESTART=n settings from RPTSCHED.
When to write restart files:
----------------------------
When to write the restart file is governed by the BASIC=n setting in
the RPTRST keyword and the RESTART=n settings in the RPTSOL and
RPTSCHED keywords. The most common setting is 'ON' - i.e. BASIC=2
which means write a restart file for every report step, that can be
turned off again with BASIC=0. For BASIC>2 there are varietes of
every n'th report step, and the first report step in every month and
every year.
Old style / new style
---------------------
All of the relevant keywords can be specified using a new style
based on string mneomnics and alternatively an old style represented
with a *strictly ordered* list of integers. For instance both of
these keywords request restart files written for every report step;
in addition to the fields required to actually restart the files
should contain the relative permeabilities KRO, KRW, KRG:
RPTRST
BASIC=2 KRG KRW KRO /
RPTRST
2 9*0 3*1 17*0
Integer controls and string mneomnics can not be mixed in the same
keyword, but they can be mixed in the same deck - and that is
actually quite common.
What is written to the restart file
-----------------------------------
The BASIC=n mneonics request the writing of a restart file which
should contain 'all properties required to restart', in addition you
can configure extra keywords to be added to the restart file. This
is configured by just adding a list as:
RPTRST
BASIC=2 KRG KRW KRO /
It is really *not clear* what is the correct persistence semantics
for these keywords, consider for insance the following series of keywords:
-- Request restart file at every report step, the restart files
-- should contain additional properties KRO, KRG and KRW.
RPTRST
BASIC=2 KRG KRW KRO /
-- Advance the simulator forward with TSTEP / DATES
TSTEP / DATES / WCONxxx
-- Turn writing of restart files OFF using integer controls.
RPTRST
0 /
-- Advance the simulator forward with TSTEP / DATES
TSTEP / DATES / WCONxxx
-- Turn writing of restart files ON using integer controls.
RPTRST
2 /
When writing of restart files is turned on again with the last
RPTRST keyword, should still the relative permeabilites KRO, KRW and
KRG be added to the restart files? The model we have implemented is:
- The list of keywords written to the restart file is persisted
independtly of the BASIC=n setting.
- Using string based mnonics you can *only add* kewyords to be
written to the files. To stop writing a keyword you must use an
integer control with value 0.
Based on this best guess heuristic the final restart files will
still contain KRO, KRW and KRG.
What is required to restart?
----------------------------
A restart capable files is requested with the 'BASIC' mneomnic, but
exactly which properties the 'BASIC' keyword is expanded to is the
responsability of the simulator; i.e. for a black oil simulation you
will at the very least need the expansion:
BASIC -> PRESSURE, SWAT, SGAS, RS, RV
But this class just carries the boolean information: Yes - restart
is requested - expanding as illustrated is the responsability of the
simulator.
What is not supported?
----------------------
The SAVE keyword is not supported in OPM at all, this implies that
the SAVE and SFREQ mneomics are not supported.
*/
namespace Opm {
template< typename > class DynamicState;
class Deck;
class DeckKeyword;
class RUNSPECSection;
class SCHEDULESection;
class SOLUTIONSection;
class Schedule;
class ParseContext;
class ErrorGuard;
/*The IOConfig class holds data about input / ouput configurations
Amongst these configuration settings, a IOConfig object knows if
a restart file should be written for a specific report step
The write of restart files is governed by several eclipse keywords.
These keywords are all described in the eclipse manual, but some
of them are rather porly described there.
To have equal sets of restart files written from Eclipse and Flow for various
configurations, we have made a qualified guess on the behaviour
for some of the keywords (by running eclipse for different configurations,
and looked at which restart files that have been written).
------ RPTSOL RESTART (solution section) ------
If RPTSOL RESTART > 1 initial restart file is written.
------ RPTRST (solution section) ------
Eclipse manual states that the initial restart file is to be written
if RPTSOL RESTART > 1. But - due to that the initial restart file
is written from Eclipse for data where RPTSOL RESTART is not set, - we
have made a guess that when RPTRST is set in SOLUTION (no basic though...),
it means that the initial restart file should be written.
Running of eclipse with different settings have proven this to be a qualified guess.
------ RPTRST BASIC=0 (solution or schedule section) ------
No restart files are written
------ RPTRST BASIC=1 or RPTRST BASIC=2 (solution or schedule section) ------
Restart files are written for every timestep, from timestep 1 to number of timesteps.
(Write of inital timestep is governed by a separate setting)
Notice! Eclipse simulator RPTRST BASIC=1 writes restart files for every
report step, but only keeps the last one written. This functionality is
not supported in Flow; so to compare Eclipse results with Flow results
for every report step, set RPTRST BASIC=2 for the eclipse run
------ RPTRST BASIC=3 FREQ=n (solution or schedule section) ------
Restart files are created every nth report time. Default frequency is 1 (every report step)
If a frequency higher than 1 is given:
start_rs = report step the setting was given.
write report step rstep if (rstep >= start_rs) && ((rstep % frequency) == 0).
------ RPTRST BASIC=4 FREQ=n or RPTRST BASIC=5 FREQ=n (solution or schedule section) ------
For the settings BASIC 4 or BASIC 5, - first report step of every new year(4) or new month(5),
the first report step is compared with report step 0 (start), and then every report step is
compared with the previous one to see if year/month has changed.
This leaves us with a set of timesteps.
All timesteps in the set that are higher or equal to the timestep the RPTRST keyword was set on is written.
If in addition FREQUENCY is given (higher than 1), every n'the value of this set are to be written.
If the setting BASIC=4 or BASIC=5 is set on a timestep that is a member of the set "first timestep of
each year" / "First timestep of each month", then the timestep that is freq-1 timesteps (within the set) from
this start timestep will be written, and then every n'the timestep (within the set) from this one will be written.
If the setting BASIC=4 or BASIC=5 is set on a timestep that is not a member of the list "first timestep of
each year" / "First timestep of each month", then the list is searched for the closest timestep that are
larger than the timestep that introduced the setting, and then; same as above - the timestep that is freq-1
timesteps from this one (within the set) will be written, and then every n'the timestep (within the set) from
this one will be written.
------ RPTRST BASIC=6 (solution or schedule section) ------
Not supported in Flow
------ Default ------
If no keywords for config of writing restart files have been handled; no restart files are written.
*/
//namespace {
class RestartSchedule {
/*
The content of this struct is logically divided in two; either the
restart behaviour is governed by { timestep , basic , frequency }, or
alternatively by { rptshec_restart_set , rptsched_restart }.
The former triplet is mainly governed by the RPTRST keyword and the
latter pair by the RPTSCHED keyword.
*/
public:
RestartSchedule() = default;
explicit RestartSchedule( size_t sched_restart);
RestartSchedule( size_t step, size_t b, size_t freq);
static RestartSchedule serializeObject();
bool writeRestartFile( size_t timestep , const TimeMap& timemap) const;
bool operator!=(const RestartSchedule& rhs) const;
bool operator==( const RestartSchedule& rhs ) const;
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(timestep);
serializer(basic);
serializer(frequency);
serializer(rptsched_restart_set);
serializer(rptsched_restart);
}
//private:
size_t timestep = 0;
size_t basic = 0;
size_t frequency = 0;
bool rptsched_restart_set = false;
size_t rptsched_restart = 0;
};
// }
class RestartConfig {
public:
RestartConfig() = default;
template<typename T>
RestartConfig( const Deck&, const std::pair<std::time_t, std::size_t>& restart, const std::optional<int>& output_interval, const ParseContext& parseContext, T&& errors );
RestartConfig( const Deck&, const std::pair<std::time_t, std::size_t>& restart, const std::optional<int>& output_interval, const ParseContext& parseContext, ErrorGuard& errors );
RestartConfig( const Deck&, const std::pair<std::time_t, std::size_t>& restart, const std::optional<int>& output_interval);
static RestartConfig serializeObject();
int getFirstRestartStep() const;
bool getWriteRestartFile(size_t timestep, bool log=true) const;
const std::map< std::string, int >& getRestartKeywords( size_t timestep ) const;
void handleSolutionSection(const SOLUTIONSection& solutionSection, const ParseContext& parseContext, ErrorGuard& errors);
void setWriteInitialRestartFile(bool writeInitialRestartFile);
bool operator==(const RestartConfig& data) const;
template<class Serializer>
void serializeOp(Serializer& serializer)
{
m_timemap.serializeOp(serializer);
serializer(m_first_restart_step);
serializer(m_write_initial_RST_file);
restart_schedule.serializeOp(serializer);
restart_keywords.serializeOp<Serializer, false>(serializer);
serializer(save_keywords);
}
private:
/// This method will internalize variables with information of
/// the first report step with restart and rft output
/// respectively. This information is important because right
/// at the first output step we must reset the files to size
/// zero, for subsequent output steps we should append.
void initFirstOutput( );
RestartSchedule getNode( size_t timestep ) const;
void overrideRestartWriteInterval(size_t interval);
bool getWriteRestartFileFrequency(size_t timestep,
size_t start_timestep,
size_t frequency,
bool years = false,
bool months = false) const;
void handleRPTSOL( const DeckKeyword& keyword);
void handleScheduleSection( const SCHEDULESection& schedule, const ParseContext& parseContext, ErrorGuard& errors);
void update( size_t step, const RestartSchedule& rs);
static RestartSchedule rptsched( const DeckKeyword& );
TimeMap m_timemap;
int m_first_restart_step = 1;
bool m_write_initial_RST_file = false;
DynamicState< RestartSchedule > restart_schedule;
DynamicState< std::map< std::string, int > > restart_keywords;
std::vector< bool > save_keywords;
};
} //namespace Opm
#endif

View File

@ -1,162 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#ifndef DYNAMICSTATE_HPP_
#define DYNAMICSTATE_HPP_
#include <algorithm>
#include <optional>
#include <stdexcept>
#include <utility>
#include <vector>
#include <opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp>
namespace Opm {
/**
The DynamicState<T> class is designed to hold information about
properties with the following semantics:
1. The property can be updated repeatedly at different
timesteps; observe that the class does not support
operator[] - only updates with weakly increasing timesteps
are supported.
2. At any point in the time the previous last set value
applies.
The class is very much tailored to support the Schedule file of
Eclipse where a control applied at time T will apply
indefinitely, or until explicitly set to a different value.
The update() method returns true if the updated value is
different from the current value, this implies that the
class<T> must support operator!=
*/
template< class T >
class DynamicState {
public:
typedef typename std::vector< T >::iterator iterator;
DynamicState() = default;
DynamicState( const TimeMap& timeMap, T initial ) :
m_data( timeMap.size(), initial ),
initial_range( timeMap.size() )
{}
DynamicState(const std::vector<T>& data,
size_t init_range) :
m_data(data), initial_range(init_range)
{}
void globalReset( T value ) {
this->m_data.assign( this->m_data.size(), value );
}
const T& back() const {
return m_data.back();
}
const T& get(size_t index) const {
return this->m_data.at( index );
}
/**
If the current value has been changed the method will
return true, otherwise it will return false.
*/
bool update( size_t index, T value ) {
if( this->initial_range == this->m_data.size() )
this->initial_range = index;
const bool change = (value != this->m_data.at( index ));
if( !change ) return false;
std::fill( this->m_data.begin() + index,
this->m_data.end(),
value );
return true;
}
bool operator==(const DynamicState<T>& data) const {
return m_data == data.m_data &&
initial_range == data.initial_range;
}
// complexType=true if contained type has a serializeOp
template<class Serializer, bool complexType = true>
void serializeOp(Serializer& serializer)
{
std::vector<T> unique;
auto indices = split(unique);
serializer.template vector<T,complexType>(unique);
serializer(indices);
if (!serializer.isSerializing())
reconstruct(unique, indices);
}
private:
std::vector< T > m_data;
size_t initial_range;
std::vector<size_t> split(std::vector<T>& unique) const {
std::vector<size_t> idxVec;
idxVec.reserve(m_data.size() + 1);
for (const auto& w : m_data) {
auto candidate = std::find(unique.begin(), unique.end(), w);
size_t idx = candidate - unique.begin();
if (candidate == unique.end()) {
unique.push_back(w);
idx = unique.size() - 1;
}
idxVec.push_back(idx);
}
idxVec.push_back(initial_range);
return idxVec;
}
void reconstruct(const std::vector<T>& unique,
const std::vector<size_t>& idxVec) {
m_data.clear();
m_data.reserve(idxVec.size() - 1);
for (size_t i = 0; i < idxVec.size() - 1; ++i)
m_data.push_back(unique[idxVec[i]]);
initial_range = idxVec.back();
}
};
}
#endif

View File

@ -0,0 +1,235 @@
/*
Copyright 2021 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 <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_RST_CONFIG_HPP
#define OPM_RST_CONFIG_HPP
/*
The RestartConfig class internalizes information of when (at which
report steps) we should save restart files, and which properties
should be included in the restart files. The configuration of this
immensely complex, and this code is unfortunately also way too
complex.
The most basic question to disentangle is the "When to write restart
files" versus "What data to store write in the restart file". As
expressed in the deck keywords this completely entangled, in this
implementation we have tried to disentangle it:
Keywords involved
-----------------
RPTRST: This is the main keyword for configuring restart output; it
can be used to configure bothe when to write the files and which
properties should be included in the restart files.
RPTSCHED: The main purpose of the RPTSCHED keyword is to configure
output from the SCHEDULE section to the PRINT file. However the
mneomnic RESTART=n can be used to turn writing of restart files
on, and also for values > 2 to some configuration of what is
written to the restart file:
RESTART=1 : As RPTRST,BASIC=1
RESTART>1 : As RPTRST,BASIC=2
RESTART>2 : Flow is added to restart file
RESTART>3 : Fluid in place is added to restart file
RESTART=6 : Restart file for every timestep.
RPTSOL: The RPTSOL keyword is very similar to the RPTCHED keyword,
it configures output from the SOLUTION section to the PRINT file,
but just as the RPTSCHED keyword it accepts a RESTART=n mnenonic
which can be used similarly to the BASIC=n mnenonic of the RPTRST
keyword. In particular the writing of an initial restart files
with initial equilibrium solution is controlled by the RPTSOL
keyword. If the restart mneonic is greater than 2 that can be
used to configure FLOWS and FIP keywords in the restart file.
RESTART=1 : As RPTRST,BASIC=1
RESTART>1 : As RPTRST,BASIC=2
RESTART>2 : Flow is added to restart file
RESTART>3 : Fluid in place is added to restart file
The basic rule in ECLIPSE is generally that the 'last keyword wins',
but for the RPTRST RPTSHCED combination a BASIC setting with n >= 3
will override consecutive RESTART=n settings from RPTSCHED.
When to write restart files:
----------------------------
When to write the restart file is governed by the BASIC=n setting in
the RPTRST keyword and the RESTART=n settings in the RPTSOL and
RPTSCHED keywords. The most common setting is 'ON' - i.e. BASIC=2
which means write a restart file for every report step, that can be
turned off again with BASIC=0. For BASIC>2 there are varietes of
every n'th report step, and the first report step in every month and
every year.
Old style / new style
---------------------
All of the relevant keywords can be specified using a new style
based on string mneomnics and alternatively an old style represented
with a *strictly ordered* list of integers. For instance both of
these keywords request restart files written for every report step;
in addition to the fields required to actually restart the files
should contain the relative permeabilities KRO, KRW, KRG:
RPTRST
BASIC=2 KRG KRW KRO /
RPTRST
2 9*0 3*1 17*0
Integer controls and string mneomnics can not be mixed in the same
keyword, but they can be mixed in the same deck - and that is
actually quite common.
What is written to the restart file
-----------------------------------
The BASIC=n mneonics request the writing of a restart file which
should contain 'all properties required to restart', in addition you
can configure extra keywords to be added to the restart file. This
is configured by just adding a list as:
RPTRST
BASIC=2 KRG KRW KRO /
It is really *not clear* what is the correct persistence semantics
for these keywords, consider for insance the following series of keywords:
-- Request restart file at every report step, the restart files
-- should contain additional properties KRO, KRG and KRW.
RPTRST
BASIC=2 KRG KRW KRO /
-- Advance the simulator forward with TSTEP / DATES
TSTEP / DATES / WCONxxx
-- Turn writing of restart files OFF using integer controls.
RPTRST
0 /
-- Advance the simulator forward with TSTEP / DATES
TSTEP / DATES / WCONxxx
-- Turn writing of restart files ON using integer controls.
RPTRST
2 /
When writing of restart files is turned on again with the last
RPTRST keyword, should still the relative permeabilites KRO, KRW and
KRG be added to the restart files? The model we have implemented is:
- The list of keywords written to the restart file is persisted
independtly of the BASIC=n setting.
- Using string based mnonics you can *only add* kewyords to be
written to the files. To stop writing a keyword you must use an
integer control with value 0.
Based on this best guess heuristic the final restart files will
still contain KRO, KRW and KRG.
What is required to restart?
----------------------------
A restart capable files is requested with the 'BASIC' mneomnic, but
exactly which properties the 'BASIC' keyword is expanded to is the
responsability of the simulator; i.e. for a black oil simulation you
will at the very least need the expansion:
BASIC -> PRESSURE, SWAT, SGAS, RS, RV
But this class just carries the boolean information: Yes - restart
is requested - expanding as illustrated is the responsability of the
simulator.
What is not supported?
----------------------
The SAVE keyword is not supported in OPM at all, this implies that
the SAVE and SFREQ mneomics are not supported.
*/
#include <unordered_map>
#include <map>
#include <optional>
#include <opm/parser/eclipse/Deck/DeckSection.hpp>
namespace Opm {
class ParseContext;
class ErrorGuard;
class RSTConfig {
public:
RSTConfig() = default;
RSTConfig(const SOLUTIONSection& solution_section, const ParseContext& parseContext, ErrorGuard& errors);
void update(const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors);
void init_next();
static RSTConfig first(const RSTConfig& src);
static RSTConfig serializeObject();
template<class Serializer>
void serializeOp(Serializer& serializer) {
serializer(write_rst_file);
serializer.template map<std::map<std::string, int>, false>(keywords);
serializer(basic);
serializer(freq);
serializer(save);
}
bool operator==(const RSTConfig& other) const;
std::optional<bool> write_rst_file;
std::map<std::string, int> keywords;
std::optional<int> basic;
std::optional<int> freq;
bool save = false;
private:
void handleRPTSOL(const DeckKeyword& keyword);
void handleRPTRST(const DeckKeyword& keyword, const ParseContext& parse_context, ErrorGuard& errors);
void handleRPTSCHED(const DeckKeyword& keyword, const ParseContext& parse_context, ErrorGuard& errors);
void update_schedule(const std::pair<std::optional<int>, std::optional<int>>& basic_freq);
};
} //namespace Opm
#endif

View File

@ -24,9 +24,9 @@
#include <optional>
#include <unordered_set>
#include <opm/parser/eclipse/Deck/DeckSection.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/Python/Python.hpp>
#include <opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GTNode.hpp>
@ -71,6 +71,8 @@ namespace Opm
MessageLimits m_deck_message_limits;
UnitSystem m_unit_system;
Runspec m_runspec;
RSTConfig rst_config;
std::optional<int> output_interval;
ScheduleStatic() = default;
@ -80,13 +82,19 @@ namespace Opm
ScheduleStatic(std::shared_ptr<const Python> python_handle,
const Deck& deck,
const Runspec& runspec) :
const Runspec& runspec,
const std::optional<int>& output_interval_,
const ParseContext& parseContext,
ErrorGuard& errors):
m_python_handle(python_handle),
m_input_path(deck.getInputPath()),
m_deck_message_limits( deck ),
m_unit_system( deck.getActiveUnitSystem() ),
m_runspec( runspec )
{}
m_runspec( runspec ),
rst_config( SOLUTIONSection(deck), parseContext, errors ),
output_interval(output_interval_)
{
}
template<class Serializer>
void serializeOp(Serializer& serializer)
@ -95,6 +103,8 @@ namespace Opm
m_runspec.serializeOp(serializer);
m_unit_system.serializeOp(serializer);
serializer(this->m_input_path);
rst_config.serializeOp(serializer);
serializer(this->output_interval);
}
@ -105,6 +115,7 @@ namespace Opm
st.m_runspec = Runspec::serializeObject();
st.m_unit_system = UnitSystem::newFIELD();
st.m_input_path = "Some/funny/path";
st.rst_config = RSTConfig::serializeObject();
return st;
}
@ -112,6 +123,7 @@ namespace Opm
return this->m_input_path == other.m_input_path &&
this->m_deck_message_limits == other.m_deck_message_limits &&
this->m_unit_system == other.m_unit_system &&
this->rst_config == other.rst_config &&
this->m_runspec == other.m_runspec;
}
};
@ -287,7 +299,6 @@ namespace Opm
void serializeOp(Serializer& serializer)
{
m_sched_deck.serializeOp(serializer);
restart_config.serializeOp(serializer);
serializer.vector(snapshots);
m_static.serializeOp(serializer);
serializer(m_restart_info);
@ -307,6 +318,7 @@ namespace Opm
pack_unpack<GuideRateConfig, Serializer>(serializer);
pack_unpack<GasLiftOpt, Serializer>(serializer);
pack_unpack<RFTConfig, Serializer>(serializer);
pack_unpack<RSTConfig, Serializer>(serializer);
pack_unpack_map<int, VFPProdTable, Serializer>(serializer);
pack_unpack_map<int, VFPInjTable, Serializer>(serializer);
@ -451,13 +463,9 @@ namespace Opm
ScheduleStatic m_static;
std::pair<std::time_t, std::size_t> m_restart_info;
ScheduleDeck m_sched_deck;
RestartConfig restart_config;
std::optional<int> exit_status;
std::vector<ScheduleState> snapshots;
RestartConfig& restart();
const RestartConfig& restart() const;
void load_rst(const RestartIO::RstState& rst,
const EclipseGrid& grid,
const FieldPropsManager& fp);
@ -599,8 +607,10 @@ namespace Opm
void handleMXUNSUPP (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleNODEPROP (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleNUPCOL (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleRPTRST (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleRPTSCHED (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleTUNING (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleSAVE (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleUDQ (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleVAPPARS (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleVFPINJ (const HandlerContext&, const ParseContext&, ErrorGuard&);

View File

@ -49,6 +49,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GuideRateConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/RFTConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/RSTConfig.hpp>
namespace {
@ -316,6 +317,10 @@ namespace Opm {
Well::ProducerCMode whistctl() const;
void update_whistctl(Well::ProducerCMode whistctl);
bool rst_file(const RSTConfig& rst_config) const;
void update_date(const time_point& prev_time);
void handleSAVE();
/*********************************************************************/
ptr_member<PAvg> pavg;
@ -333,6 +338,7 @@ namespace Opm {
ptr_member<GasLiftOpt> glo;
ptr_member<GuideRateConfig> guide_rate;
ptr_member<RFTConfig> rft_config;
ptr_member<RSTConfig> rst_config;
template <typename T> struct always_false1 : std::false_type {};
@ -368,6 +374,8 @@ namespace Opm {
return this->guide_rate;
else if constexpr ( std::is_same_v<T, RFTConfig> )
return this->rft_config;
else if constexpr ( std::is_same_v<T, RSTConfig> )
return this->rst_config;
else
static_assert(always_false1<T>::value, "Template type <T> not supported in get()");
}
@ -404,6 +412,8 @@ namespace Opm {
return this->guide_rate;
else if constexpr ( std::is_same_v<T, RFTConfig> )
return this->rft_config;
else if constexpr ( std::is_same_v<T, RSTConfig> )
return this->rst_config;
else
static_assert(always_false1<T>::value, "Template type <T> not supported in get()");
}
@ -441,6 +451,7 @@ namespace Opm {
serializer(m_year_num);
serializer(m_first_in_year);
serializer(m_first_in_month);
serializer(m_save_step);
m_tuning.serializeOp(serializer);
serializer(m_nupcol);
m_oilvap.serializeOp(serializer);
@ -460,8 +471,10 @@ namespace Opm {
std::size_t m_sim_step = 0;
std::size_t m_month_num = 0;
std::size_t m_year_num = 0;
bool m_first_in_month = true;
bool m_first_in_year = true;
bool m_first_in_month;
bool m_first_in_year;
std::optional<int> m_save_step;
Tuning m_tuning;
int m_nupcol;
OilVaporizationProperties m_oilvap;

View File

@ -23,7 +23,6 @@
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Runspec.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/ArrayDimChecker.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>

View File

@ -24,7 +24,7 @@
#include <opm/parser/eclipse/EclipseState/EclipseConfig.hpp>
#include <opm/parser/eclipse/EclipseState/InitConfig/InitConfig.hpp>
#include <opm/parser/eclipse/EclipseState/IOConfig/IOConfig.hpp>
#include <opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp>
namespace Opm {

View File

@ -790,8 +790,27 @@ namespace {
this->snapshots.back().update_nupcol(nupcol);
}
void Schedule::handleRPTSCHED(const HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) {
void Schedule::handleRPTSCHED(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) {
this->snapshots.back().rpt_config.update( RPTConfig(handlerContext.keyword ));
auto rst_config = this->snapshots.back().rst_config();
rst_config.update(handlerContext.keyword, parseContext, errors);
this->snapshots.back().rst_config.update(std::move(rst_config));
}
void Schedule::handleRPTRST(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) {
auto rst_config = this->snapshots.back().rst_config();
rst_config.update(handlerContext.keyword, parseContext, errors);
this->snapshots.back().rst_config.update(std::move(rst_config));
}
/*
We do not really handle the SAVE keyword, we just interpret it as: Write a
normal restart file at this report step.
*/
void Schedule::handleSAVE(const HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) {
auto rst_config = this->snapshots.back().rst_config();
rst_config.save = true;
this->snapshots.back().rst_config.update(std::move(rst_config));
}
void Schedule::handleTUNING(const HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) {
@ -1972,7 +1991,9 @@ namespace {
{ "MULTZ-" , &Schedule::handleMXUNSUPP },
{ "NODEPROP", &Schedule::handleNODEPROP },
{ "NUPCOL" , &Schedule::handleNUPCOL },
{ "RPTRST" , &Schedule::handleRPTRST },
{ "RPTSCHED", &Schedule::handleRPTSCHED },
{ "SAVE" , &Schedule::handleSAVE },
{ "TUNING" , &Schedule::handleTUNING },
{ "UDQ" , &Schedule::handleUDQ },
{ "VAPPARS" , &Schedule::handleVAPPARS },

View File

@ -1,5 +1,5 @@
/*
Copyright 2015 Statoil ASA.
Copyright 2021 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
@ -15,33 +15,16 @@
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <climits>
#include <iostream>
#include <iterator>
#include <iomanip>
#include <sstream>
*/
#include <optional>
#include <fmt/format.h>
#include <opm/common/utility/OpmInputError.hpp>
#include <opm/parser/eclipse/Utility/Functional.hpp>
#include <opm/parser/eclipse/Deck/DeckItem.hpp>
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
#include <opm/parser/eclipse/Deck/DeckRecord.hpp>
#include <opm/parser/eclipse/Deck/DeckSection.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/R.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/Parser/ErrorGuard.hpp>
#include <opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/RSTConfig.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/R.hpp>
#include <opm/parser/eclipse/Utility/Functional.hpp>
#include <opm/common/utility/OpmInputError.hpp>
namespace Opm {
@ -56,38 +39,6 @@ inline bool is_int( const std::string& x ) {
&& std::all_of( x.begin() + 1, x.end(), is_digit );
}
constexpr const char* RSTIntegerKeywords[] = { "BASIC", // 1
"FLOWS", // 2
"FIP", // 3
"POT", // 4
"PBPD", // 5
"FREQ", // 6
"PRES", // 7
"VISC", // 8
"DEN", // 9
"DRAIN", // 10
"KRO", // 11
"KRW", // 12
"KRG", // 13
"PORO", // 14
"NOGRAD", // 15
"NORST", // 16 NORST - not supported
"SAVE", // 17
"SFREQ", // 18 SFREQ=?? - not supported
"ALLPROPS", // 19
"ROCKC", // 20
"SGTRAP", // 21
"", // 22 - Blank - ignored.
"RSSAT", // 23
"RVSAT", // 24
"GIMULT", // 25
"SURFBLK", // 26
"", // 27 - PCOW, PCOG, special cased
"STREAM", // 28 STREAM=?? - not supported
"RK", // 29
"VELOCITY", // 30
"COMPRESS" }; // 31
constexpr const char* SCHEDIntegerKeywords[] = { "PRES", // 1
"SOIL", // 2
"SWAT", // 3
@ -168,6 +119,37 @@ constexpr const char* SCHEDIntegerKeywords[] = { "PRES", // 1
"KRN", // 78
"GRAD", // 79
};
constexpr const char* RSTIntegerKeywords[] = { "BASIC", // 1
"FLOWS", // 2
"FIP", // 3
"POT", // 4
"PBPD", // 5
"FREQ", // 6
"PRES", // 7
"VISC", // 8
"DEN", // 9
"DRAIN", // 10
"KRO", // 11
"KRW", // 12
"KRG", // 13
"PORO", // 14
"NOGRAD", // 15
"NORST", // 16 NORST - not supported
"SAVE", // 17
"SFREQ", // 18 SFREQ=?? - not supported
"ALLPROPS", // 19
"ROCKC", // 20
"SGTRAP", // 21
"", // 22 - Blank - ignored.
"RSSAT", // 23
"RVSAT", // 24
"GIMULT", // 25
"SURFBLK", // 26
"", // 27 - PCOW, PCOG, special cased
"STREAM", // 28 STREAM=?? - not supported
"RK", // 29
"VELOCITY", // 30
"COMPRESS" }; // 31
bool is_RPTRST_mnemonic( const std::string& kw ) {
/* all eclipse 100 keywords we want to not simply ignore. The list is
@ -191,6 +173,7 @@ bool is_RPTRST_mnemonic( const std::string& kw ) {
return std::binary_search( std::begin( valid ), std::end( valid ), kw );
}
bool is_RPTSCHED_mnemonic( const std::string& kw ) {
static constexpr const char* valid[] = {
"ALKALINE", "ANIONS", "AQUCT", "AQUFET", "AQUFETP", "BFORG",
@ -216,53 +199,17 @@ bool is_RPTSCHED_mnemonic( const std::string& kw ) {
return std::binary_search( std::begin( valid ), std::end( valid ), kw );
}
inline std::map< std::string, int >
RPTSCHED_integer( const std::vector< int >& ints ) {
const size_t size = std::min( ints.size(), sizeof( SCHEDIntegerKeywords ) );
std::map< std::string, int > mnemonics;
for( size_t i = 0; i < size; ++i )
mnemonics[ SCHEDIntegerKeywords[ i ] ] = ints[ i ];
return mnemonics;
}
RestartSchedule::RestartSchedule( size_t sched_restart) :
rptsched_restart_set( true ),
rptsched_restart( sched_restart )
{
}
RestartSchedule::RestartSchedule( size_t step, size_t b, size_t freq) :
timestep( step ),
basic( b ),
frequency( basic > 2 ? std::max( freq, size_t{ 1 } ) : freq )
{
/*
* if basic > 2 and freq is default (zero) we're looking at an error
* (every Nth step where N = 0). Instead of throwing we default this to
* 1 so that basic > 2 and freq unset essentially is
* write-every-timestep. It could've just as easily been an exception,
* but to be more robust handling poorly written decks we instead set a
* reasonable default and carry on.
*/
}
RestartSchedule RestartSchedule::serializeObject()
{
RestartSchedule result(1, 2, 3);
result.rptsched_restart_set = true;
result.rptsched_restart = 4;
return result;
}
bool RestartSchedule::operator!=(const RestartSchedule & rhs) const {
return !( *this == rhs );
}
bool RestartSchedule::operator==( const RestartSchedule& rhs ) const {
if( this->rptsched_restart_set ) {
return rhs.rptsched_restart_set
&& this->rptsched_restart == rhs.rptsched_restart;
}
return this->timestep == rhs.timestep &&
this->basic == rhs.basic &&
this->frequency == rhs.frequency;
}
inline std::map< std::string, int >
RPTRST_integer( const std::vector< int >& ints ) {
const size_t PCO_index = 26;
@ -271,19 +218,30 @@ RPTRST_integer( const std::vector< int >& ints ) {
std::map< std::string, int > mnemonics;
const size_t size = std::min( ints.size(), sizeof( RSTIntegerKeywords ) );
/* fun with special cases. Eclipse seems to ignore the BASIC=0,
* interpreting it as sort-of "don't modify". Handle this by *not*
* adding/updating the integer list sourced BASIC mnemonic, should it be
* zero. I'm not sure if this applies to other mnemonics, but the eclipse
* manual indicates that any zero here should disable the output.
/* fun with special cases. Eclipse seems to ignore the BASIC=0, interpreting
* it as sort-of "don't modify". Handle this by *not* adding/updating the
* integer list sourced BASIC mnemonic, should it be zero. I'm not sure if
* this applies to other mnemonics, but the eclipse manual indicates that
* any zero here should disable the output.
*
* See https://github.com/OPM/opm-parser/issues/886 for reference
*
* The current treatment of a mix on RPTRST and RPTSCHED integer keywords is
* probably not correct, but it is extremely difficult to comprehend exactly
* how it should be. Current code is a rather arbitrary hack to get through
* the tests.
*/
if( size > 0 && ints[ BASIC_index ] != 0 )
mnemonics[ RSTIntegerKeywords[ BASIC_index ] ] = ints[ BASIC_index ];
for( size_t i = 1; i < std::min( size, PCO_index ); ++i )
mnemonics[ RSTIntegerKeywords[ i ] ] = ints[ i ];
if (size >= 26) {
for( size_t i = 0; i < std::min( size, PCO_index ); ++i )
mnemonics[ RSTIntegerKeywords[ i ] ] = ints[ i ];
} else {
if( size > 0 && ints[ BASIC_index ] != 0)
mnemonics[ RSTIntegerKeywords[ BASIC_index ] ] = ints[ BASIC_index ];
for( size_t i = 1; i < std::min( size, PCO_index ); ++i )
mnemonics[ RSTIntegerKeywords[ i ] ] = ints[ i ];
}
for( size_t i = PCO_index + 1; i < size; ++i )
mnemonics[ RSTIntegerKeywords[ i ] ] = ints[ i ];
@ -297,16 +255,6 @@ RPTRST_integer( const std::vector< int >& ints ) {
return mnemonics;
}
inline std::map< std::string, int >
RPTSCHED_integer( const std::vector< int >& ints ) {
const size_t size = std::min( ints.size(), sizeof( SCHEDIntegerKeywords ) );
std::map< std::string, int > mnemonics;
for( size_t i = 0; i < size; ++i )
mnemonics[ SCHEDIntegerKeywords[ i ] ] = ints[ i ];
return mnemonics;
}
template< typename F, typename G >
inline std::map< std::string, int > RPT( const DeckKeyword& keyword,
@ -388,331 +336,198 @@ inline std::map< std::string, int > RPT( const DeckKeyword& keyword,
}
inline void expand_RPTRST_mnemonics(std::map< std::string, int >& mnemonics) {
const auto allprops = mnemonics.find( "ALLPROPS");
if (allprops != mnemonics.end()) {
const auto value = allprops->second;
mnemonics.erase( "ALLPROPS" );
const auto allprops_iter = mnemonics.find( "ALLPROPS");
if (allprops_iter != mnemonics.end()) {
const auto value = allprops_iter->second;
mnemonics.erase( allprops_iter );
for (const auto& kw : {"BG","BO","BW","KRG","KRO","KRW","VOIL","VGAS","VWAT","DEN"})
mnemonics[kw] = value;
}
}
std::optional<int> extract(std::map<std::string, int>& mnemonics, const std::string& key) {
auto iter = mnemonics.find(key);
if (iter == mnemonics.end())
return {};
inline std::pair< std::map< std::string, int >, RestartSchedule >
RPTRST( const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors, RestartSchedule prev, size_t step ) {
int value = iter->second;
mnemonics.erase(iter);
return value;
}
inline std::pair< std::map< std::string, int >, std::pair<std::optional<int>, std::optional<int>>>
RPTRST( const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors) {
auto mnemonics = RPT( keyword, parseContext, errors, is_RPTRST_mnemonic, RPTRST_integer );
const bool has_freq = mnemonics.find( "FREQ" ) != mnemonics.end();
const bool has_basic = mnemonics.find( "BASIC" ) != mnemonics.end();
std::optional<int> basic = extract(mnemonics, "BASIC");
std::optional<int> freq = extract(mnemonics, "FREQ");
expand_RPTRST_mnemonics( mnemonics );
if( !has_freq && !has_basic ) return { std::move( mnemonics ), {} };
const auto basic = has_basic ? mnemonics.at( "BASIC" ) : prev.basic;
const auto freq = has_freq ? mnemonics.at( "FREQ" ) : prev.frequency;
return { std::move( mnemonics ), { step, basic, freq } };
return {mnemonics, { basic, freq }};
}
inline std::pair< std::map< std::string, int >, RestartSchedule >
RPTSCHED( const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors ) {
template <typename T>
void update_optional(std::optional<T>& target, const std::optional<T>& src) {
if (src.has_value())
target = src;
}
}
// The handleRPTSOL() function is only invoked from the constructor which uses
// the SOLUTION section, and the only information actually extracted is whether
// to write the initial restart file.
void RSTConfig::handleRPTSOL( const DeckKeyword& keyword) {
const auto& record = keyword.getRecord(0);
const auto& item = record.getItem(0);
for (const auto& mnemonic : item.getData<std::string>()) {
auto mnemonic_RESTART_pos = mnemonic.find("RESTART=");
if (mnemonic_RESTART_pos != std::string::npos) {
std::string restart_no = mnemonic.substr(mnemonic_RESTART_pos + 8, mnemonic.size());
auto restart = std::strtoul(restart_no.c_str(), nullptr, 10);
this->write_rst_file = (restart > 1);
return;
}
}
/* If no RESTART mnemonic is found, either it is not present or we might
have an old data set containing integer controls instead of mnemonics.
Restart integer switch is integer control nr 7 */
if (item.data_size() >= 7) {
const std::string& integer_control = item.get<std::string>(6);
auto restart = std::strtoul(integer_control.c_str(), nullptr, 10);
this->write_rst_file = (restart > 1);
return;
}
}
bool RSTConfig::operator==(const RSTConfig& other) const {
return this->write_rst_file == other.write_rst_file &&
this->keywords == other.keywords &&
this->basic == other.basic &&
this->freq == other.freq &&
this->save == other.save;
}
void RSTConfig::update_schedule(const std::pair<std::optional<int>, std::optional<int>>& basic_freq) {
update_optional(this->basic, basic_freq.first);
update_optional(this->freq, basic_freq.second);
if (this->basic.has_value()) {
auto basic_value = this->basic.value();
if (basic_value == 0)
this->write_rst_file = false;
else if (basic_value == 1 || basic_value == 2)
this->write_rst_file = true;
else
this->write_rst_file = {};
}
}
void RSTConfig::handleRPTRST(const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors) {
const auto& [mnemonics, basic_freq] = RPTRST(keyword, parseContext, errors);
this->update_schedule(basic_freq);
for (const auto& [kw,num] : mnemonics)
this->keywords[kw] = num;
}
void RSTConfig::handleRPTSCHED(const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors) {
auto mnemonics = RPT( keyword, parseContext, errors, is_RPTSCHED_mnemonic, RPTSCHED_integer );
auto nothing = extract(mnemonics, "NOTHING");
if (nothing.has_value()) {
this->basic = {};
this->keywords.clear();
}
if( mnemonics.count( "NOTHING" ) )
return { std::move( mnemonics ), { RestartSchedule(0) } };
if (this->basic.value_or(2) <= 2) {
auto restart = extract(mnemonics, "RESTART");
if (restart.has_value()) {
auto basic_value = std::min(2, restart.value());
this->update_schedule({basic_value , 1});
}
}
if( mnemonics.count( "RESTART" ) )
return { std::move( mnemonics ), RestartSchedule( size_t( mnemonics.at( "RESTART" )) ) };
return { std::move( mnemonics ), {} };
for (const auto& [kw,num] : mnemonics)
this->keywords[kw] = num;
}
RSTConfig::RSTConfig(const SOLUTIONSection& solution_section, const ParseContext& parseContext, ErrorGuard& errors)
{
this->write_rst_file = false;
if (solution_section.hasKeyword<ParserKeywords::RPTRST>()) {
const auto& keyword = solution_section.getKeyword<ParserKeywords::RPTRST>();
this->handleRPTRST(keyword, parseContext, errors);
void RestartConfig::handleScheduleSection(const SCHEDULESection& schedule, const ParseContext& parseContext, ErrorGuard& errors) {
size_t current_step = 1;
RestartSchedule unset;
// Guessing on eclipse rules for write of initial RESTART file (at time 0):
// Write of initial restart file is (due to the eclipse reference manual)
// governed by RPTSOL RESTART in solution section,
// if RPTSOL RESTART > 1 initial restart file is written.
// but - due to initial restart file written from Eclipse
// for data where RPTSOL RESTART not set - guessing that
// when RPTRST is set in SOLUTION (no basic though...) -> write inital restart.
this->write_rst_file = true;
}
auto ignore_RPTSCHED_RESTART = []( decltype( restart_schedule )& x ) {
return x.back().basic > 2;
};
for( const auto& keyword : schedule ) {
const auto& name = keyword.name();
if( name == "DATES" ) {
current_step += keyword.size();
continue;
}
if( name == "TSTEP" ) {
current_step += keyword.getRecord( 0 ).getItem( 0 ).data_size();
continue;
}
if( this->m_timemap.size() <= current_step ) continue;
if (name == "SAVE") {
this->save_keywords.at(current_step) = true;
} else {
this->save_keywords.at(current_step) = false;
}
if( !( name == "RPTRST" || name == "RPTSCHED" ) ) continue;
const bool is_RPTRST = name == "RPTRST";
const auto& prev_sched = this->restart_schedule.back();
auto config = is_RPTRST ? RPTRST( keyword, parseContext, errors, prev_sched, current_step )
: RPTSCHED( keyword , parseContext, errors);
/* add the missing entries from the previous step */
{
auto& mnemonics = config.first;
const auto& prev_mnemonics = this->restart_keywords.back();
mnemonics.insert( prev_mnemonics.begin(), prev_mnemonics.end() );
if( mnemonics.find( "NOTHING" ) != mnemonics.end() )
mnemonics.clear();
this->restart_keywords.update( current_step , mnemonics );
}
const bool ignore_RESTART =
!is_RPTRST && ignore_RPTSCHED_RESTART( this->restart_schedule );
const auto& rs = config.second;
if( rs == unset || ignore_RESTART ) continue;
if( 6 == rs.rptsched_restart || 6 == rs.basic )
throw std::runtime_error(
"OPM does not support the RESTART=6 setting "
"(write restart file every timestep)"
);
this->restart_schedule.update( current_step, rs );
if (solution_section.hasKeyword<ParserKeywords::RPTSOL>()) {
const auto& keyword = solution_section.getKeyword<ParserKeywords::RPTSOL>();
this->handleRPTSOL(keyword);
}
}
bool RestartSchedule::writeRestartFile( size_t input_timestep , const TimeMap& timemap) const {
if (this->rptsched_restart_set && (this->rptsched_restart > 0))
return true;
switch (this->basic) {
//Do not write restart files
case 0: return false;
//Write restart file every report time
case 1: return true;
//Write restart file every report time
case 2: return true;
//Every n'th report time
case 3: return ((input_timestep % this->frequency) == 0) ? true : false;
//First reportstep of every year, or if n > 1, n'th years
case 4: return timemap.isTimestepInFirstOfMonthsYearsSequence(input_timestep, true , this->timestep, this->frequency);
//First reportstep of every month, or if n > 1, n'th months
case 5: return timemap.isTimestepInFirstOfMonthsYearsSequence(input_timestep, false , this->timestep, this->frequency);
default: return false;
}
}
template<typename T>
RestartConfig::RestartConfig( const Deck& deck, const std::pair<std::time_t, std::size_t>& restart, const std::optional<int>& output_interval, const ParseContext& parseContext, T&& errors ) :
RestartConfig( deck, restart, output_interval, parseContext, errors)
{}
RestartConfig::RestartConfig( const Deck& deck, const std::pair<std::time_t, std::size_t>& restart, const std::optional<int>& output_interval) :
RestartConfig( deck, restart, output_interval, ParseContext(), ErrorGuard())
{}
RestartConfig::RestartConfig( const Deck& deck, const std::pair<std::time_t, std::size_t>& restart, const std::optional<int>& output_interval, const ParseContext& parseContext, ErrorGuard& errors ) :
m_timemap( TimeMap(deck, restart) ),
m_first_restart_step( -1 ),
restart_schedule( m_timemap, {0,0,1}),
restart_keywords( m_timemap, {} ),
save_keywords( m_timemap.size(), false )
{
handleSolutionSection( SOLUTIONSection(deck), parseContext, errors );
handleScheduleSection( SCHEDULESection(deck), parseContext, errors );
initFirstOutput( );
if (output_interval.has_value())
this->overrideRestartWriteInterval(output_interval.value());
}
RestartConfig RestartConfig::serializeObject()
{
RestartConfig result;
result.m_timemap = TimeMap::serializeObject();
result.m_first_restart_step = 2;
result.m_write_initial_RST_file = true;
result.restart_schedule = {{RestartSchedule::serializeObject()}, 2};
result.restart_keywords = {{{{"test",3}}}, 3};
result.save_keywords = {false, true};
return result;
}
RestartSchedule RestartConfig::getNode( size_t timestep ) const{
return restart_schedule.get(timestep);
}
bool RestartConfig::getWriteRestartFile(size_t timestep, bool log) const {
if (0 == timestep)
return m_write_initial_RST_file;
if (save_keywords[timestep]) {
if ( log ) {
std::string logstring = "Fast restart using SAVE is not supported. Standard restart file is written instead";
Opm::OpmLog::warning("Unhandled output keyword", logstring);
}
return true;
}
{
RestartSchedule ts_restart_config = getNode( timestep );
return ts_restart_config.writeRestartFile( timestep , m_timemap );
}
}
const std::map< std::string, int >& RestartConfig::getRestartKeywords( size_t timestep ) const {
return restart_keywords.get( timestep );
}
/*
Will initialize the internal variable holding the first report
step when restart output is queried.
The reason we are interested in this report step is that when we
reach this step the output files should be opened with mode 'w'
- whereas for subsequent steps they should be opened with mode
'a'.
*/
void RestartConfig::initFirstOutput( ) {
size_t report_step = 0;
while (true) {
if (getWriteRestartFile(report_step)) {
m_first_restart_step = report_step;
break;
}
report_step++;
if (report_step == m_timemap.size())
break;
}
}
void RestartConfig::handleSolutionSection(const SOLUTIONSection& solutionSection, const ParseContext& parseContext, ErrorGuard& errors) {
using RST = ParserKeywords::RPTRST;
if (solutionSection.hasKeyword<RST>()) {
std::size_t step = 1;
const auto& rptrstkeyword = solutionSection.getKeyword<RST>();
const auto rptrst = RPTRST( rptrstkeyword, parseContext, errors, {}, step );
this->restart_keywords.update( step, rptrst.first );
this->restart_schedule.update( step, rptrst.second );
setWriteInitialRestartFile(true); // Guessing on eclipse rules for write of initial RESTART file (at time 0):
// Write of initial restart file is (due to the eclipse reference manual)
// governed by RPTSOL RESTART in solution section,
// if RPTSOL RESTART > 1 initial restart file is written.
// but - due to initial restart file written from Eclipse
// for data where RPTSOL RESTART not set - guessing that
// when RPTRST is set in SOLUTION (no basic though...) -> write inital restart.
} //RPTRST
if (solutionSection.hasKeyword("RPTSOL") && (m_timemap.size() > 0)) {
handleRPTSOL(solutionSection.getKeyword("RPTSOL"));
} //RPTSOL
}
void RestartConfig::overrideRestartWriteInterval(size_t interval) {
size_t step = 0;
/* write restart files if the interval is non-zero. The restart
* mnemonic (setting) that governs restart-on-interval is BASIC=3
*/
size_t basic = interval > 0 ? 3 : 0;
RestartSchedule rs( step, basic, interval );
restart_schedule.globalReset( rs );
setWriteInitialRestartFile( interval > 0 );
}
void RestartConfig::setWriteInitialRestartFile(bool writeInitialRestartFile) {
m_write_initial_RST_file = writeInitialRestartFile;
}
void RestartConfig::handleRPTSOL( const DeckKeyword& keyword) {
const auto& record = keyword.getRecord(0);
size_t restart = 0;
size_t found_mnemonic_RESTART = 0;
bool handle_RPTSOL_RESTART = false;
const auto& item = record.getItem(0);
for (size_t index = 0; index < item.data_size(); ++index) {
const std::string& mnemonic = item.get< std::string >(index);
found_mnemonic_RESTART = mnemonic.find("RESTART=");
if (found_mnemonic_RESTART != std::string::npos) {
std::string restart_no = mnemonic.substr(found_mnemonic_RESTART+8, mnemonic.size());
restart = std::strtoul(restart_no.c_str(), nullptr, 10);
handle_RPTSOL_RESTART = true;
}
}
/* If no RESTART mnemonic is found, either it is not present or we might
have an old data set containing integer controls instead of mnemonics.
Restart integer switch is integer control nr 7 */
if (found_mnemonic_RESTART == std::string::npos) {
if (item.data_size() >= 7) {
const std::string& integer_control = item.get< std::string >(6);
restart = std::strtoul(integer_control.c_str(), nullptr, 10);
if (restart != ULONG_MAX)
handle_RPTSOL_RESTART = true;
}
}
if (handle_RPTSOL_RESTART) {
if (restart > 1) {
setWriteInitialRestartFile(true);
} else {
setWriteInitialRestartFile(false);
}
}
}
int RestartConfig::getFirstRestartStep() const {
return m_first_restart_step;
}
bool RestartConfig::operator==(const RestartConfig& data) const {
return this->m_timemap == data.m_timemap &&
this->m_first_restart_step == data.m_first_restart_step &&
this->m_write_initial_RST_file == data.m_write_initial_RST_file &&
this->restart_schedule == data.restart_schedule &&
this->restart_keywords == data.restart_keywords &&
this->save_keywords == data.save_keywords;
}
void RSTConfig::update(const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors) {
if (keyword.name() == ParserKeywords::RPTRST::keywordName)
this->handleRPTRST(keyword, parseContext, errors);
else if (keyword.name() == ParserKeywords::RPTSCHED::keywordName) {
this->handleRPTSCHED(keyword, parseContext, errors);
} else
throw std::logic_error("The RSTConfig object can only use RPTRST and RPTSCHED keywords");
}
RSTConfig RSTConfig::serializeObject() {
RSTConfig rst_config;
rst_config.basic = 10;
rst_config.freq = {};
rst_config.write_rst_file = true;
rst_config.save = true;
rst_config.keywords = {{"S1", 1}, {"S2", 2}};
return rst_config;
}
/*
The RPTRST keyword is treated differently in the SOLUTION section and in the
SCHEDULE section. This function takes a RSTConfig object created from the
solution section and creates a transformed copy suitable as the first
RSTConfig to represent the Schedule section.
*/
RSTConfig RSTConfig::first(const RSTConfig& solution_config ) {
RSTConfig rst_config(solution_config);
auto basic = rst_config.basic;
if (!basic.has_value()) {
rst_config.write_rst_file = false;
return rst_config;
}
auto basic_value = basic.value();
if (basic_value == 0)
rst_config.write_rst_file = false;
else if (basic_value == 1 || basic_value == 2)
rst_config.write_rst_file = true;
else if (basic_value >= 3)
rst_config.write_rst_file = {};
return rst_config;
}
}

View File

@ -108,10 +108,9 @@ namespace {
const std::optional<int>& output_interval,
const RestartIO::RstState * rst)
try :
m_static( python, deck, runspec ),
m_static( python, deck, runspec, output_interval, parseContext, errors ),
m_restart_info( restart_info(rst)),
m_sched_deck(deck, m_restart_info ),
restart_config(deck, m_restart_info, output_interval, parseContext, errors)
m_sched_deck(deck, m_restart_info )
{
if (rst) {
auto restart_step = this->m_restart_info.second;
@ -211,7 +210,6 @@ Schedule::Schedule(const Deck& deck, const EclipseState& es, const std::optional
Schedule result;
result.m_static = ScheduleStatic::serializeObject();
result.restart_config = RestartConfig::serializeObject();
result.snapshots = { ScheduleState::serializeObject() };
return result;
@ -1206,28 +1204,31 @@ void Schedule::iterateScheduleSection(std::size_t load_start, std::size_t load_e
}
}
RestartConfig& Schedule::restart() {
return this->restart_config;
}
bool Schedule::write_rst_file(std::size_t report_step, bool ) const {
if (this->m_static.output_interval.has_value())
return this->m_static.output_interval.value() % report_step;
const RestartConfig& Schedule::restart() const {
return this->restart_config;
}
if (report_step == 0)
return this->m_static.rst_config.write_rst_file.value();
bool Schedule::write_rst_file(std::size_t report_step, bool log) const {
return this->restart_config.getWriteRestartFile(report_step, log);
const auto& rst_config = this->snapshots[report_step - 1].rst_config();
const auto& state = this->snapshots[report_step];
return state.rst_file(rst_config);
}
const std::map< std::string, int >& Schedule::rst_keywords( size_t timestep ) const {
return this->restart_config.getRestartKeywords(timestep);
const std::map< std::string, int >& Schedule::rst_keywords( size_t report_step ) const {
if (report_step == 0)
return this->m_static.rst_config.keywords;
const auto& keywords = this->snapshots[report_step - 1].rst_config().keywords;
return keywords;
}
bool Schedule::operator==(const Schedule& data) const {
return this->m_restart_info == data.m_restart_info &&
this->m_static == data.m_static &&
this->restart_config == data.restart_config &&
this->snapshots == data.snapshots;
}
@ -1647,6 +1648,8 @@ void Schedule::create_first(const time_point& start_time, const std::optional<ti
sched_state.glo.update( GasLiftOpt() );
sched_state.guide_rate.update( GuideRateConfig() );
sched_state.rft_config.update( RFTConfig() );
sched_state.rst_config.update( RSTConfig::first( this->m_static.rst_config ) );
//sched_state.update_date( start_time );
this->addGroup("FIELD", 0);
}

View File

@ -40,7 +40,7 @@ time_point clamp_time(time_point t) {
return TimeService::from_time_t( TimeService::to_time_t( t ) );
}
std::pair<std::size_t, std::size_t> date_diff(time_point t2, time_point t1) {
std::pair<std::size_t, std::size_t> date_diff(const time_point& t2, const time_point& t1) {
auto ts1 = TimeStampUTC(TimeService::to_time_t(t1));
auto ts2 = TimeStampUTC(TimeService::to_time_t(t2));
auto year_diff = ts2.year() - ts1.year();
@ -54,8 +54,12 @@ std::pair<std::size_t, std::size_t> date_diff(time_point t2, time_point t1) {
ScheduleState::ScheduleState(const time_point& t1):
m_start_time(clamp_time(t1))
m_start_time(clamp_time(t1)),
m_first_in_month(true),
m_first_in_year(true)
{
auto ts1 = TimeStampUTC(TimeService::to_time_t(this->m_start_time));
this->m_month_num = ts1.month() - 1;
}
ScheduleState::ScheduleState(const time_point& start_time, const time_point& end_time) :
@ -64,6 +68,19 @@ ScheduleState::ScheduleState(const time_point& start_time, const time_point& end
this->m_end_time = clamp_time(end_time);
}
void ScheduleState::update_date(const time_point& prev_time) {
auto [year_diff, month_diff] = date_diff(this->m_start_time, prev_time);
this->m_year_num += year_diff;
this->m_first_in_month = (month_diff > 0);
this->m_first_in_year = (year_diff > 0);
auto ts1 = TimeStampUTC(TimeService::to_time_t(this->m_start_time));
this->m_month_num = ts1.month() - 1;
}
ScheduleState::ScheduleState(const ScheduleState& src, const time_point& start_time) :
ScheduleState(src)
{
@ -79,14 +96,15 @@ ScheduleState::ScheduleState(const ScheduleState& src, const time_point& start_t
if (next_rft.has_value())
this->rft_config.update( std::move(*next_rft) );
auto [year_diff, month_diff] = date_diff(this->m_start_time, src.m_start_time);
this->m_year_num += year_diff;
this->m_month_num += month_diff;
this->m_first_in_month = (this->m_month_num > src.m_month_num);
this->m_first_in_year = (this->m_year_num > src.m_year_num);
this->update_date(src.m_start_time);
if (this->rst_config().save) {
auto new_rst = this->rst_config();
new_rst.save = false;
this->rst_config.update( std::move(new_rst) );
}
}
ScheduleState::ScheduleState(const ScheduleState& src, const time_point& start_time, const time_point& end_time) :
ScheduleState(src, start_time)
{
@ -244,6 +262,7 @@ ScheduleState ScheduleState::serializeObject() {
ts.guide_rate.update( GuideRateConfig::serializeObject() );
ts.glo.update( GasLiftOpt::serializeObject() );
ts.rft_config.update( RFTConfig::serializeObject() );
ts.rst_config.update( RSTConfig::serializeObject() );
return ts;
}
@ -285,4 +304,44 @@ const WellGroupEvents& ScheduleState::wellgroup_events() const {
return this->m_wellgroup_events;
}
/*
Observe that the decision to write a restart file will typically be a
combination of the RST configuration from the previous report step, and the
first_in_year++ attributes of this report step. That is the reason the
function takes a RSTConfig argument - instead of using the rst_config member.
*/
bool ScheduleState::rst_file(const RSTConfig& rst) const {
if (rst.save)
return true;
if (rst.write_rst_file.has_value())
return rst.write_rst_file.value();
auto freq = rst.freq.value_or(1);
auto basic = rst.basic.value();
if (basic == 3)
return (this->sim_step() % freq) == 0;
if (basic == 4) {
if (!this->first_in_year())
return false;
return (this->m_year_num % freq) == 0;
}
if (basic == 5) {
if (!this->first_in_month())
return false;
return (this->m_month_num % freq) == 0;
}
throw std::logic_error(fmt::format("Unsupported BASIC={} value", basic));
}
}

View File

@ -1,132 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#include <stdexcept>
#include <iostream>
#include <boost/filesystem.hpp>
#define BOOST_TEST_MODULE DynamicStateTests
#include <boost/test/unit_test.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/DynamicState.hpp>
Opm::TimeMap make_timemap(int num) {
std::vector<std::time_t> tp;
for (int i = 0; i < num; i++)
tp.push_back( Opm::asTimeT(Opm::TimeStampUTC(2010,1,i+1)));
Opm::TimeMap timeMap{ tp };
return timeMap;
}
BOOST_AUTO_TEST_CASE(CreateDynamicTest) {
const std::time_t startDate = Opm::TimeMap::mkdate(2010, 1, 1);
Opm::TimeMap timeMap({ startDate });
Opm::DynamicState<double> state(timeMap , 9.99);
}
BOOST_AUTO_TEST_CASE(DynamicStateGetOutOfRangeThrows) {
const std::time_t startDate = Opm::TimeMap::mkdate(2010, 1, 1);
Opm::TimeMap timeMap({ startDate });
Opm::DynamicState<double> state(timeMap , 9.99);
BOOST_CHECK_THROW( state.get(1) , std::out_of_range );
}
BOOST_AUTO_TEST_CASE(DynamicStateGetDefault) {
const std::time_t startDate = Opm::TimeMap::mkdate(2010, 1, 1);
Opm::TimeMap timeMap( { startDate } );
Opm::DynamicState<int> state(timeMap , 137);
BOOST_CHECK_EQUAL( 137 , state.get(0));
BOOST_CHECK_EQUAL( 137 , state.back() );
}
BOOST_AUTO_TEST_CASE(DynamicStateSetOutOfRangeThrows) {
Opm::TimeMap timeMap = make_timemap(3);
Opm::DynamicState<int> state(timeMap , 137);
BOOST_CHECK_THROW( state.update(3 , 100) , std::out_of_range );
}
BOOST_AUTO_TEST_CASE(DynamicStateSetOK) {
Opm::TimeMap timeMap = make_timemap(11);
Opm::DynamicState<int> state(timeMap , 137);
state.update(2 , 23 );
BOOST_CHECK_EQUAL( 137 , state.get(0));
BOOST_CHECK_EQUAL( 137 , state.get(1));
BOOST_CHECK_EQUAL( 23 , state.get(2));
BOOST_CHECK_EQUAL( 23 , state.get(5));
state.update(2 , 17);
BOOST_CHECK_EQUAL( 137 , state.get(0));
BOOST_CHECK_EQUAL( 137 , state.get(1));
BOOST_CHECK_EQUAL( 17 , state.get(2));
BOOST_CHECK_EQUAL( 17 , state.get(5));
state.update(6 , 60);
BOOST_CHECK_EQUAL( 17 , state.get(2));
BOOST_CHECK_EQUAL( 17 , state.get(5));
BOOST_CHECK_EQUAL( 60 , state.get(6));
BOOST_CHECK_EQUAL( 60 , state.get(8));
BOOST_CHECK_EQUAL( 60 , state.get(9));
BOOST_CHECK_EQUAL( 60 , state.back());
}
BOOST_AUTO_TEST_CASE( ResetGlobal ) {
Opm::TimeMap timeMap = make_timemap(11);
Opm::DynamicState<int> state(timeMap , 137);
state.update(5 , 100);
BOOST_CHECK_EQUAL( state.get(0) , 137 );
BOOST_CHECK_EQUAL( state.get(4) , 137 );
BOOST_CHECK_EQUAL( state.get(5) , 100 );
BOOST_CHECK_EQUAL( state.get(9) , 100 );
state.globalReset( 88 );
BOOST_CHECK_EQUAL( state.get(0) , 88 );
BOOST_CHECK_EQUAL( state.get(4) , 88 );
BOOST_CHECK_EQUAL( state.get(5) , 88 );
BOOST_CHECK_EQUAL( state.get(9) , 88 );
}
BOOST_AUTO_TEST_CASE( CheckReturn ) {
Opm::TimeMap timeMap = make_timemap(11);
Opm::DynamicState<int> state(timeMap , 137);
BOOST_CHECK_EQUAL( false , state.update( 0 , 137 ));
BOOST_CHECK_EQUAL( false , state.update( 3 , 137 ));
BOOST_CHECK_EQUAL( true , state.update( 5 , 200 ));
}

View File

@ -28,7 +28,6 @@
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/EclipseState/IOConfig/IOConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp>
#include <opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp>
using namespace Opm;
@ -196,7 +195,6 @@ DATES
auto deck = Parser().parseString( data);
IOConfig ioConfig( deck );
RestartConfig rstConfig( deck, std::make_pair(std::time_t{0}, std::size_t{0}), {});
/*If no GRIDFILE nor NOGGF keywords are specified, default output an EGRID file*/
BOOST_CHECK( ioConfig.getWriteEGRIDFile() );

View File

@ -202,14 +202,13 @@ RESTART=1
}
BOOST_AUTO_TEST_CASE(RPTRST_AND_RPTSOL_SOLUTION)
{
const auto input = std::string { R"(RUNSPEC
DIMENS
10 10 10 /
START
6 JLY 2020 /
6 JUN 2020 /
GRID
DXV
@ -259,27 +258,45 @@ END
auto sched = make_schedule(input, false);
for (const std::size_t stepID : { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 15, 16, 18 }) {
for (const std::size_t stepID : { 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 15, 16, 18 }) {
BOOST_CHECK_MESSAGE(! sched.write_rst_file(stepID),
"Must not write restart information for excluded step " << stepID);
}
for (const std::size_t stepID : { 0, 11, 14, 17 }) {
for (const std::size_t stepID : { 0, 1, 11, 14, 17 }) {
BOOST_CHECK_MESSAGE(sched.write_rst_file(stepID),
"Must write restart information for included step " << stepID);
}
std::vector<bool> first_in_month{true, false, false, false, false, true, false, true, true, true, true, true, true, true, true, true, false, true, false};
std::vector<std::size_t> month_num{ 0,0,0,0,0, 1, 1, 2, 3, 4, 5, 6, 7, 10, 12, 17, 17, 18, 18 };
std::vector<std::tuple<bool, bool, TimeStampUTC>> expected = {{true , true , TimeStampUTC(2020, 6, 6)}, // 0
{true , false, TimeStampUTC(2020, 7, 7)}, // 1
{false, false, TimeStampUTC(2020, 7, 10)}, // 2
{false, false, TimeStampUTC(2020, 7, 20)}, // 3
{false, false, TimeStampUTC(2020, 7, 30)}, // 4
{true , false, TimeStampUTC(2020, 8, 5)}, // 5
{false, false, TimeStampUTC(2020, 8, 20)}, // 6
{true , false, TimeStampUTC(2020, 9, 5)}, // 7
{true , false, TimeStampUTC(2020, 10, 1)}, // 8
{true , false, TimeStampUTC(2020, 11, 1)}, // 9
{true , false, TimeStampUTC(2020, 12, 1)}, // 10
{true , true , TimeStampUTC(2021, 1, 5)}, // 11
{true , false, TimeStampUTC(2021, 2, 1)}, // 12
{true , false, TimeStampUTC(2021, 5, 17)}, // 13
{true , false, TimeStampUTC(2021, 7, 6)}, // 14
{true , false, TimeStampUTC(2021, 12, 1)}, // 15
{false, false, TimeStampUTC(2021, 12, 31)}, // 16
{true, true , TimeStampUTC(2022, 1, 21)}, // 17
{false, false, TimeStampUTC(2022, 1, 31)}}; // 18
for (std::size_t index = 0; index < sched.size(); index++) {
const auto& state = sched[index];
BOOST_CHECK_EQUAL( state.month_num(), month_num[index] );
BOOST_CHECK_EQUAL( state.first_in_month(), first_in_month[index] );
const auto& [first_in_month, first_in_year, ts] = expected[index];
if (index == 0 || index == 11 || index == 17)
BOOST_CHECK( state.first_in_year());
else
BOOST_CHECK(!state.first_in_year());
printf("index: %ld \n", index);
BOOST_CHECK_EQUAL( state.month_num(), ts.month() - 1);
BOOST_CHECK_EQUAL( state.first_in_month(), first_in_month );
BOOST_CHECK_EQUAL( state.first_in_year(), first_in_year);
BOOST_CHECK( ts == TimeStampUTC( TimeService::to_time_t(state.start_time() )));
}
}
@ -416,23 +433,25 @@ PORO
1000*0.25 /
SOLUTION
RPTRST -- PRES,DEN,PCOW,PCOG,RK,VELOCITY,COMPRESS
6*0 1 0 1 9*0 1 7*0 1 0 3*1 /
6*0 1 0 1 9*0 1 7*0 1 0 3*1 / -- Static
SCHEDULE
-- 0
DATES -- 1
10 OKT 2008 /
/
RPTSCHED
RPTSCHED -- 1
RESTART=1
/
DATES -- 2
20 JAN 2010 /
/
RPTRST -- RK,VELOCITY,COMPRESS
RPTRST -- RK,VELOCITY,COMPRESS --2
18*0 0 8*0 /
DATES -- 3
20 FEB 2010 /
/
RPTSCHED
RPTSCHED -- 3
RESTART=0
/
)";
@ -1256,28 +1275,28 @@ RPTRST
BASIC=5 FREQ=2
/
DATES
22 MAY 1981 /
23 MAY 1981 /
24 MAY 1981 /
1 JUN 1981 /
1 JUL 1981 / -- write
1 JAN 1982 / -- write
2 JAN 1982 /
1 FEB 1982 /
1 MAR 1982 / -- write
1 APR 1983 / -- write
2 JUN 1983 / -- write
22 MAY 1981 / -- 1
23 MAY 1981 / -- 2
24 MAY 1981 / -- 3
1 JUN 1981 / -- 4
1 JUL 1981 / -- 5 Write
1 JAN 1982 / -- 6 Write
2 JAN 1982 / -- 7
1 FEB 1982 / -- 8
1 MAR 1982 / -- 9 Write
1 APR 1983 / --10
2 JUN 1983 / --11
/
)";
auto sched = make_schedule(data);
/* BASIC=5, restart file is written at the first report step of each month.
*/
for( size_t ts : { 1, 2, 3, 4, 7, 8 } )
for( size_t ts : { 1, 2, 3, 4, 7, 8, 10, 11 } )
BOOST_CHECK( !sched.write_rst_file( ts ) );
for( size_t ts : { 5, 6, 9, 10, 11 } )
BOOST_CHECK( sched.write_rst_file( ts ) );
for( size_t ts : { 5, 6, 9} )
BOOST_CHECK_MESSAGE( sched.write_rst_file( ts ) , "Restart file expected for step: " << ts);
}
BOOST_AUTO_TEST_CASE(BASIC_EQ_0) {
@ -1369,7 +1388,7 @@ BASIC=4 FREQ=2
DATES
22 MAY 1981 /
/
RPTSCHED // BASIC >2, ignore RPTSCHED RESTART
RPTSCHED -- BASIC >2, ignore RPTSCHED RESTART
RESTART=3, FREQ=1
/
DATES
@ -1456,3 +1475,77 @@ TSTEP
}
BOOST_AUTO_TEST_CASE(RPTSCHED_INTEGER2) {
const std::string deckData1 = R"(
RUNSPEC
START -- 0
19 JUN 2007 /
DIMENS
10 10 10 /
GRID
DXV
10*1 /
DYV
10*1 /
DZV
10*1 /
DEPTHZ
121*1 /
PORO
1000*0.25 /
SOLUTION
RPTRST -- PRES,DEN,PCOW,PCOG,RK,VELOCITY,COMPRESS
1 5*0 1 0 1 9*0 1 7*0 1 0 3*1 / -- Static
SCHEDULE
-- 0
DATES -- 1
10 OKT 2008 /
/
RPTSCHED
RESTART=1
/
DATES -- 2
20 JAN 2010 /
/
RPTRST -- RK,VELOCITY,COMPRESS
18*0 0 8*0 /
DATES -- 3
20 FEB 2010 /
/
RPTSCHED
RESTART=0
/
DATES -- 4
1 MAR 2010 /
/
)";
auto sched = make_schedule(deckData1, false);
BOOST_CHECK_EQUAL( sched.size(), 5);
BOOST_CHECK( sched.write_rst_file( 0 ) );
BOOST_CHECK( sched.write_rst_file( 1 ) );
BOOST_CHECK( sched.write_rst_file( 2 ) );
BOOST_CHECK( !sched.write_rst_file( 3 ) );
const auto& kw_list1 = filter_keywords(sched.rst_keywords(1));
const auto expected1 = {"BG","BO","BW","COMPRESS","DEN","KRG","KRO","KRW","PCOG","PCOW","PRES","RK","VELOCITY","VGAS","VOIL","VWAT"};
BOOST_CHECK_EQUAL_COLLECTIONS( expected1.begin(), expected1.end(),
kw_list1.begin(), kw_list1.end() );
const auto& kw_list2 = filter_keywords( sched.rst_keywords(3));
const auto expected2 = { "COMPRESS", "RK", "VELOCITY" };
BOOST_CHECK_EQUAL_COLLECTIONS( expected2.begin(), expected2.end(),
kw_list2.begin(), kw_list2.end() );
}

View File

@ -199,6 +199,9 @@ DATES
DATES
21 'AUG' 2001 /
/
SAVE
DATES
24 'AUG' 2001 /
/

View File

@ -30,7 +30,6 @@
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
@ -138,6 +137,7 @@ BOOST_AUTO_TEST_CASE( NorneRestartConfig ) {
BOOST_AUTO_TEST_CASE( RestartConfig2 ) {
std::map<int, boost::gregorian::date> rptConfig;
@ -145,6 +145,7 @@ BOOST_AUTO_TEST_CASE( RestartConfig2 ) {
rptConfig.emplace(8 , boost::gregorian::date(2000,7,1));
rptConfig.emplace(27 , boost::gregorian::date(2001,1,1));
rptConfig.emplace(45 , boost::gregorian::date(2001,7,1));
rptConfig.emplace(50 , boost::gregorian::date(2001,8,24));
rptConfig.emplace(61 , boost::gregorian::date(2002,1,1));
rptConfig.emplace(79 , boost::gregorian::date(2002,7,1));
rptConfig.emplace(89 , boost::gregorian::date(2003,1,1));
@ -177,6 +178,55 @@ BOOST_AUTO_TEST_CASE( RestartConfig2 ) {
EclipseState state( deck);
Schedule schedule(deck, state, python);
verifyRestartConfig(schedule, rptConfig);
auto keywords0 = schedule.rst_keywords(0);
std::map<std::string, int> expected0 = {{"BG", 1},
{"BO", 1},
{"BW", 1},
{"KRG", 1},
{"KRO", 1},
{"KRW", 1},
{"VOIL", 1},
{"VGAS", 1},
{"VWAT", 1},
{"DEN", 1},
{"RVSAT", 1},
{"RSSAT", 1},
{"PBPD", 1},
{"NORST", 1}};
for (const auto& [kw, num] : expected0)
BOOST_CHECK_EQUAL( keywords0.at(kw), num );
auto keywords1 = schedule.rst_keywords(1);
std::map<std::string, int> expected1 = {{"BG", 1},
{"BO", 1},
{"BW", 1},
{"KRG", 1},
{"KRO", 1},
{"KRW", 1},
{"VOIL", 1},
{"VGAS", 1},
{"VWAT", 1},
{"DEN", 1},
{"RVSAT", 1},
{"RSSAT", 1},
{"PBPD", 1},
{"NORST", 1},
{"FIP", 3},
{"WELSPECS", 1},
{"WELLS", 0},
{"NEWTON", 1},
{"SUMMARY", 1},
{"CPU", 1},
{"CONV", 10}};
for (const auto& [kw, num] : expected1)
BOOST_CHECK_EQUAL( keywords1.at(kw), num );
BOOST_CHECK_EQUAL(expected1.size(), keywords1.size());
auto keywords10 = schedule.rst_keywords(10);
BOOST_CHECK( keywords10 == keywords1 );
}

View File

@ -701,8 +701,6 @@ BOOST_AUTO_TEST_CASE(Declared_Connection_Data)
// XCONN (PROD) + (WINJ)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::XConn::index;
const auto& units = simCase.es.getUnits();
using M = ::Opm::UnitSystem::measure;
const auto& xconn = amconn.getXConn();
// PROD well