Files
opm-common/opm/parser/eclipse/EclipseState/IOConfig/IOConfig.cpp
2016-06-27 11:55:58 +02:00

587 lines
20 KiB
C++

/*
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/>.
*/
#include <stdio.h>
#include <iostream>
#include <iterator>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.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/Section.hpp>
#include <opm/parser/eclipse/Deck/SCHEDULESection.hpp>
#include <opm/parser/eclipse/EclipseState/IOConfig/IOConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/DynamicState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well.hpp>
#include <ert/ecl/ecl_util.h>
namespace Opm {
namespace {
const char* default_dir = ".";
inline std::string basename( const std::string& path ) {
return boost::filesystem::path( path ).stem().string();
}
inline std::string outputdir( const std::string& path ) {
auto dir = boost::filesystem::path( path ).parent_path().string();
if( dir.empty() ) return default_dir;
return dir;
}
inline bool is_int( const std::string& x ) {
auto is_digit = []( char c ) { return std::isdigit( c ); };
return !x.empty()
&& ( x.front() == '-' || is_digit( x.front() ) )
&& std::all_of( x.begin() + 1, x.end(), is_digit );
}
}
IOConfig::restartConfig IOConfig::rptrst( const DeckKeyword& kw, size_t step ) {
const auto& items = kw.getStringData();
/* if any of the values are pure integers we assume this is meant to be
* the slash-terminated list of integers way of configuring. If
* integers and non-integers are mixed, this is an error.
*/
const auto ints = std::any_of( items.begin(), items.end(), is_int );
const auto strs = !std::all_of( items.begin(), items.end(), is_int );
if( ints && strs ) throw std::runtime_error(
"RPTRST does not support mixed mnemonics and integer list."
);
size_t basic = 1;
size_t freq = 0;
bool found_basic = false;
for( const auto& mnemonic : items ) {
const auto freq_pos = mnemonic.find( "FREQ=" );
if( freq_pos != std::string::npos ) {
freq = std::stoul( mnemonic.substr( freq_pos + 5 ) );
}
const auto basic_pos = mnemonic.find( "BASIC=" );
if( basic_pos != std::string::npos ) {
basic = std::stoul( mnemonic.substr( basic_pos + 6 ) );
found_basic = true;
}
}
if( found_basic ) return restartConfig( step, basic, freq );
/* If no BASIC mnemonic is found, either it is not present or we might
* have an old data set containing integer controls instead of mnemonics.
* BASIC integer switch is integer control nr 1, FREQUENCY is integer
* control nr 6.
*/
/* mnemonics, but without basic and freq. Effectively ignored */
if( !ints ) return {};
const int BASIC_index = 0;
const int FREQ_index = 5;
if( items.size() > BASIC_index )
basic = std::stoul( items[ BASIC_index ] );
// Peculiar special case in eclipse, - not documented
// This ignore of basic = 0 for the integer mnemonics case
// is done to make flow write restart file at the same intervals
// as eclipse for the Norne data set. There might be some rules
// we are missing here.
if( 0 == basic ) return {};
if( items.size() > FREQ_index ) // if frequency is set
freq = std::stoul( items[ FREQ_index ] );
return restartConfig( step, basic, freq );
}
IOConfig::restartConfig IOConfig::rptsched( const DeckKeyword& keyword ) {
size_t restart = 0;
bool restart_found = false;
const auto& items = keyword.getStringData();
const auto ints = std::any_of( items.begin(), items.end(), is_int );
const auto strs = !std::all_of( items.begin(), items.end(), is_int );
if( ints && strs ) throw std::runtime_error(
"RPTSCHED does not support mixed mnemonics and integer list."
);
for( const auto& mnemonic : items ) {
const auto restart_pos = mnemonic.find( "RESTART=" );
if( restart_pos != std::string::npos ) {
restart = std::stoul( mnemonic.substr( restart_pos + 8 ) );
restart_found = true;
}
const auto nothing_pos = mnemonic.find( "NOTHING" );
if( nothing_pos != std::string::npos ) {
restart = 0;
restart_found = true;
}
}
if( restart_found ) return restartConfig( restart );
/* No RESTART or NOTHING found, but it is not an integer list */
if( strs ) return {};
/* We might have an old data set containing integer controls instead of
* mnemonics. Restart integer switch is integer control nr 7
*/
const int RESTART_index = 6;
if( items.size() <= RESTART_index ) return {};
return restartConfig( std::stoul( items[ RESTART_index ] ) );
}
DynamicState< IOConfig::restartConfig > IOConfig::rstconf(
const SCHEDULESection& schedule,
std::shared_ptr< const TimeMap > timemap ) {
size_t current_step = 1;
bool ignore_RPTSCHED_restart = false;
restartConfig unset;
DynamicState< IOConfig::restartConfig >
restart_config( timemap, restartConfig( 0, 0, 1 ) );
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 ).size();
continue;
}
if( !( name == "RPTRST" || name == "RPTSCHED" ) ) continue;
if( timemap->size() <= current_step ) continue;
const bool is_RPTRST = name == "RPTRST";
if( !is_RPTRST && ignore_RPTSCHED_restart ) continue;
const auto rs = is_RPTRST
? rptrst( keyword, current_step - 1 )
: rptsched( keyword );
if( is_RPTRST ) ignore_RPTSCHED_restart = rs.basic > 2;
/* we're using the default state of restartConfig to signal "no
* update". The default state is non-sensical
*/
if( rs == unset ) 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)"
);
restart_config.update( current_step, rs );
}
return restart_config;
}
IOConfig::IOConfig( const Deck& deck ) :
IOConfig( GRIDSection( deck ),
RUNSPECSection( deck ),
SCHEDULESection( deck ),
std::make_shared< const TimeMap >( deck ),
deck.getDataFile() )
{}
IOConfig::IOConfig( const std::string& input_path ) :
m_deck_filename( input_path ),
m_output_dir( outputdir( input_path ) ),
m_base_name( basename( input_path ) )
{}
static inline bool write_egrid_file( const GRIDSection& grid ) {
if( grid.hasKeyword( "NOGGF" ) ) return false;
if( !grid.hasKeyword( "GRIDFILE" ) ) return true;
const auto& keyword = grid.getKeyword( "GRIDFILE" );
if( keyword.size() == 0 ) return false;
const auto& rec = keyword.getRecord( 0 );
const auto& item1 = rec.getItem( 0 );
if( item1.hasValue( 0 ) && item1.get< int >( 0 ) != 0 ) {
std::cerr << "IOConfig: Reading GRIDFILE keyword from GRID section: "
<< "Output of GRID file is not supported. "
<< "Supported format: EGRID"
<< std::endl;
return true;
}
if( rec.size() < 1 ) return true;
const auto& item2 = rec.getItem( 1 );
return !item2.hasValue( 0 ) || item2.get< int >( 0 ) != 0;
}
IOConfig::IOConfig( const GRIDSection& grid,
const RUNSPECSection& runspec,
const SCHEDULESection& schedule,
std::shared_ptr< const TimeMap > timemap,
const std::string& input_path ) :
m_timemap( timemap ),
m_write_INIT_file( grid.hasKeyword( "INIT" ) ),
m_write_EGRID_file( write_egrid_file( grid ) ),
m_UNIFIN( runspec.hasKeyword( "UNIFIN" ) ),
m_UNIFOUT( runspec.hasKeyword( "UNIFOUT" ) ),
m_FMTIN( runspec.hasKeyword( "FMTIN" ) ),
m_FMTOUT( runspec.hasKeyword( "FMTOUT" ) ),
m_deck_filename( input_path ),
m_output_dir( outputdir( input_path ) ),
m_base_name( basename( input_path ) ),
m_restart_output_config( std::make_shared< DynamicState< restartConfig > >(
rstconf( schedule, timemap ) ) )
{}
bool IOConfig::getWriteEGRIDFile() const {
return m_write_EGRID_file;
}
bool IOConfig::getWriteINITFile() const {
return m_write_INIT_file;
}
bool IOConfig::getWriteRestartFile(size_t timestep) const {
bool write_restart_ts = false;
if (0 == timestep) {
write_restart_ts = m_write_initial_RST_file;
} else if (m_restart_output_config) {
restartConfig ts_restart_config = m_restart_output_config->get(timestep);
//Look at rptsched restart setting
if (ts_restart_config.rptsched_restart_set) {
if (ts_restart_config.rptsched_restart > 0) {
write_restart_ts = true;
}
} else { //Look at rptrst basic setting
switch (ts_restart_config.basic) {
case 0: //Do not write restart files
write_restart_ts = false;
break;
case 1: //Write restart file every report time
write_restart_ts = true;
break;
case 2: //Write restart file every report time
write_restart_ts = true;
break;
case 3: //Every n'th report time
write_restart_ts = getWriteRestartFileFrequency(timestep, ts_restart_config.timestep, ts_restart_config.frequency);
break;
case 4: //First reportstep of every year, or if n > 1, n'th years
write_restart_ts = getWriteRestartFileFrequency(timestep, ts_restart_config.timestep, ts_restart_config.frequency, true);
break;
case 5: //First reportstep of every month, or if n > 1, n'th months
write_restart_ts = getWriteRestartFileFrequency(timestep, ts_restart_config.timestep, ts_restart_config.frequency, false, true);
break;
default:
// do nothing
break;
}
}
}
return write_restart_ts;
}
bool IOConfig::getWriteRestartFileFrequency(size_t timestep,
size_t start_timestep,
size_t frequency,
bool years,
bool months) const {
bool write_restart_file = false;
if ((!years && !months) && (timestep >= start_timestep)) {
write_restart_file = ((timestep % frequency) == 0) ? true : false;
} else {
write_restart_file = m_timemap->isTimestepInFirstOfMonthsYearsSequence(timestep, years, start_timestep, frequency);
}
return write_restart_file;
}
void IOConfig::assertTimeMap(TimeMapConstPtr timemap) {
if (!m_timemap) {
restartConfig rs;
rs.timestep = 0;
rs.basic = 0;
rs.frequency = 1;
rs.rptsched_restart_set = false;
rs.rptsched_restart = 0;
m_timemap = timemap;
m_restart_output_config = std::make_shared<DynamicState<restartConfig>>(timemap, rs);
}
}
/*
Will initialize the two internal variables holding the first
report step when restart and rft 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 IOConfig::initFirstOutput(const Schedule& schedule) {
m_first_restart_step = -1;
m_first_rft_step = -1;
assertTimeMap( this->m_timemap );
{
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;
}
}
{
for (const auto& well : schedule.getWells( )) {
int well_output = well->firstRFTOutput();
if (well_output >= 0) {
if ((m_first_rft_step < 0) || (well_output < m_first_rft_step))
m_first_rft_step = well_output;
}
}
}
}
void IOConfig::handleSolutionSection(TimeMapConstPtr timemap, std::shared_ptr<const SOLUTIONSection> solutionSection) {
if (solutionSection->hasKeyword("RPTRST")) {
const auto& rptrstkeyword = solutionSection->getKeyword("RPTRST");
auto rs = rptrst( rptrstkeyword, 0 );
if( rs != restartConfig() )
m_restart_output_config->updateInitial( rs );
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") && (timemap->size() > 0)) {
handleRPTSOL(solutionSection->getKeyword("RPTSOL"));
} //RPTSOL
}
void IOConfig::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;
restartConfig rs( step, basic, interval );
m_restart_output_config->globalReset( rs );
setWriteInitialRestartFile( interval > 0 );
}
bool IOConfig::getUNIFIN() const {
return m_UNIFIN;
}
bool IOConfig::getUNIFOUT() const {
return m_UNIFOUT;
}
bool IOConfig::getFMTIN() const {
return m_FMTIN;
}
bool IOConfig::getFMTOUT() const {
return m_FMTOUT;
}
void IOConfig::setWriteInitialRestartFile(bool writeInitialRestartFile) {
m_write_initial_RST_file = writeInitialRestartFile;
}
void IOConfig::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.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 = boost::lexical_cast<size_t>(restart_no);
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.size() >= 7) {
const std::string& integer_control = item.get< std::string >(6);
try {
restart = boost::lexical_cast<size_t>(integer_control);
handle_RPTSOL_RESTART = true;
} catch (boost::bad_lexical_cast &) {
//do nothing
}
}
}
if (handle_RPTSOL_RESTART) {
if (restart > 1) {
setWriteInitialRestartFile(true);
} else {
setWriteInitialRestartFile(false);
}
}
}
boost::gregorian::date IOConfig::getTimestepDate(size_t reportStep) const {
auto time = (*m_timemap)[reportStep];
return time.date();
}
void IOConfig::dumpRestartConfig() const {
for (size_t reportStep = 0; reportStep < m_timemap->size(); reportStep++) {
if (getWriteRestartFile(reportStep)) {
auto time = (*m_timemap)[reportStep];
boost::gregorian::date date = time.date();
printf("%04zu : %02hu/%02hu/%hu \n" , reportStep ,
date.day().as_number() , date.month().as_number() , static_cast<unsigned short>(date.year()));
}
}
}
std::string IOConfig::getRestartFileName(const std::string& restart_base, int report_step, bool output) const {
bool unified = output ? getUNIFOUT() : getUNIFIN();
bool fmt_file = output ? getFMTOUT() : getFMTIN();
ecl_file_enum file_type = (unified) ? ECL_UNIFIED_RESTART_FILE : ECL_RESTART_FILE;
char * c_str = ecl_util_alloc_filename( NULL , restart_base.c_str() , file_type, fmt_file , report_step);
std::string restart_filename = c_str;
free( c_str );
return restart_filename;
}
int IOConfig::getFirstRestartStep() const {
return m_first_restart_step;
}
int IOConfig::getFirstRFTStep() const {
return m_first_rft_step;
}
bool IOConfig::getOutputEnabled(){
return m_output_enabled;
}
void IOConfig::setOutputEnabled(bool enabled){
m_output_enabled = enabled;
}
std::string IOConfig::getOutputDir() {
return m_output_dir;
}
void IOConfig::setOutputDir(const std::string& outputDir) {
m_output_dir = outputDir;
}
const std::string& IOConfig::getBaseName() const {
return m_base_name;
}
void IOConfig::setBaseName(std::string baseName) {
m_base_name = baseName;
}
std::string IOConfig::fullBasePath( ) const {
namespace fs = boost::filesystem;
fs::path dir( m_output_dir );
fs::path base( m_base_name );
fs::path full_path = dir / base;
return full_path.string();
}
} //namespace Opm