Merge pull request #2347 from joakim-hove/ched-state-rst-config2
Internalize restart configuration in new schedule structure
This commit is contained in:
commit
c7200aa1af
@ -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
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include <getopt.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <iostream>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
|
@ -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
|
@ -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
|
||||
|
235
opm/parser/eclipse/EclipseState/Schedule/RSTConfig.hpp
Normal file
235
opm/parser/eclipse/EclipseState/Schedule/RSTConfig.hpp
Normal 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
|
@ -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&);
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 },
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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 ));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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() );
|
||||
|
@ -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() );
|
||||
}
|
||||
|
||||
|
@ -199,6 +199,9 @@ DATES
|
||||
DATES
|
||||
21 'AUG' 2001 /
|
||||
/
|
||||
|
||||
SAVE
|
||||
|
||||
DATES
|
||||
24 'AUG' 2001 /
|
||||
/
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user