2016-03-04 14:56:18 +01:00
/*
Copyright 2016 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/>.
*/
2019-03-08 07:12:07 +01:00
# include <iostream>
# include <algorithm>
# include <array>
2016-03-04 14:56:18 +01:00
2019-03-08 07:12:07 +01:00
# include <ert/ecl/ecl_smspec.hpp>
2016-03-04 14:56:18 +01:00
2016-06-29 16:38:09 +02:00
# include <opm/parser/eclipse/Parser/ParseContext.hpp>
2019-03-08 07:12:07 +01:00
2016-01-29 11:32:13 +01:00
# include <opm/parser/eclipse/Deck/Deck.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>
2019-03-08 07:12:07 +01:00
2016-01-29 11:32:13 +01:00
# include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
2019-03-08 07:12:07 +01:00
# include <opm/parser/eclipse/EclipseState/Tables/TableManager.hpp>
2016-01-29 11:32:13 +01:00
# include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
2017-09-25 10:05:09 +02:00
# include <opm/parser/eclipse/EclipseState/Grid/GridDims.hpp>
2019-03-09 10:19:11 +01:00
# include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.hpp>
2016-01-29 11:32:13 +01:00
# include <opm/parser/eclipse/EclipseState/Schedule/Group.hpp>
# include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
2016-03-29 14:15:03 +02:00
# include <opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp>
2019-03-08 07:12:07 +01:00
# include <opm/parser/eclipse/EclipseState/Schedule/Well/Connection.hpp>
# include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
2016-04-25 11:45:39 +02:00
# include <opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
2016-01-29 11:32:13 +01:00
2016-02-29 10:10:14 +01:00
2016-01-29 11:32:13 +01:00
namespace Opm {
2016-07-04 11:37:01 +02:00
namespace {
2017-05-30 07:22:08 +02:00
/*
Small dummy decks which contain a list of keywords ; observe that
these dummy decks will be used as proper decks and MUST START
WITH SUMMARY .
*/
2016-07-04 13:10:57 +02:00
const Deck ALL_keywords = {
" SUMMARY " ,
2016-07-04 11:37:01 +02:00
" FAQR " , " FAQRG " , " FAQT " , " FAQTG " , " FGIP " , " FGIPG " , " FGIPL " ,
" FGIR " , " FGIT " , " FGOR " , " FGPR " , " FGPT " , " FOIP " , " FOIPG " ,
" FOIPL " , " FOIR " , " FOIT " , " FOPR " , " FOPT " , " FPR " , " FVIR " ,
" FVIT " , " FVPR " , " FVPT " , " FWCT " , " FWGR " , " FWIP " , " FWIR " ,
2018-12-05 14:29:32 +01:00
" FWIT " , " FWPR " , " FWPT " , " FWPP " , " FOPP " , " FGPP " , " FWPI " ,
2019-05-20 12:34:37 +02:00
" FOPI " , " FGPI " ,
2016-07-04 11:37:01 +02:00
" GGIR " , " GGIT " , " GGOR " , " GGPR " , " GGPT " , " GOIR " , " GOIT " ,
" GOPR " , " GOPT " , " GVIR " , " GVIT " , " GVPR " , " GVPT " , " GWCT " ,
2018-12-05 14:29:32 +01:00
" GWGR " , " GWIR " , " GWIT " , " GWPR " , " GWPT " , " GWPP " , " GOPP " ,
2019-05-20 12:34:37 +02:00
" GGPP " , " GWPI " , " GOPI " , " GGPI " ,
2016-07-04 11:37:01 +02:00
" WBHP " , " WGIR " , " WGIT " , " WGOR " , " WGPR " , " WGPT " , " WOIR " ,
" WOIT " , " WOPR " , " WOPT " , " WPI " , " WTHP " , " WVIR " , " WVIT " ,
" WVPR " , " WVPT " , " WWCT " , " WWGR " , " WWIR " , " WWIT " , " WWPR " ,
2018-12-05 14:29:32 +01:00
" WWPT " , " WWPP " , " WOPP " , " WGPP " , " WWPI " , " WGPI " , " WOPI " ,
2016-06-29 10:11:06 +02:00
// ALL will not expand to these keywords yet
2016-07-04 11:37:01 +02:00
" AAQR " , " AAQRG " , " AAQT " , " AAQTG "
} ;
2016-06-29 16:38:09 +02:00
2016-12-13 12:55:09 +01:00
const Deck GMWSET_keywords = {
" SUMMARY " ,
" GMCTG " , " GMWPT " , " GMWPR " , " GMWPA " , " GMWPU " , " GMWPG " , " GMWPO " , " GMWPS " ,
" GMWPV " , " GMWPP " , " GMWPL " , " GMWIT " , " GMWIN " , " GMWIA " , " GMWIU " , " GMWIG " ,
" GMWIS " , " GMWIV " , " GMWIP " , " GMWDR " , " GMWDT " , " GMWWO " , " GMWWT "
} ;
2016-12-13 13:11:46 +01:00
const Deck FMWSET_keywords = {
" SUMMARY " ,
" FMCTF " , " FMWPT " , " FMWPR " , " FMWPA " , " FMWPU " , " FMWPF " , " FMWPO " , " FMWPS " ,
" FMWPV " , " FMWPP " , " FMWPL " , " FMWIT " , " FMWIN " , " FMWIA " , " FMWIU " , " FMWIF " ,
" FMWIS " , " FMWIV " , " FMWIP " , " FMWDR " , " FMWDT " , " FMWWO " , " FMWWT "
} ;
2017-02-06 23:52:39 +01:00
2017-05-30 07:22:08 +02:00
const Deck PERFORMA_keywords = {
" SUMMARY " ,
" TCPU " , " ELAPSED " , " NEWTON " , " NLINERS " , " NLINSMIN " , " NLINSMAX " , " MLINEARS " ,
" MSUMLINS " , " MSUMNEWT " , " TIMESTEP " , " TCPUTS " , " TCPUDAY " , " STEPTYPE " , " TELAPLIN "
} ;
/*
The variable type ' ECL_SMSPEC_MISC_TYPE ' is a catch - all variable
type , and will by default internalize keywords like ' ALL ' and
' PERFORMA ' , where only the keywords in the expanded list should
be included .
*/
const std : : set < std : : string > meta_keywords = { " PERFORMA " , " ALL " , " FMWSET " , " GMWSET " } ;
2017-02-06 23:52:39 +01:00
/*
This is a hardcoded mapping between 3 D field keywords ,
e . g . ' PRESSURE ' and ' SWAT ' and summary keywords like ' RPR ' and
' BPR ' . The purpose of this mapping is to maintain an overview of
which 3 D field keywords are needed by the Summary calculation
machinery , based on which summary keywords are requested . The
Summary calculations are implemented in the opm - output
repository .
*/
const std : : map < std : : string , std : : set < std : : string > > required_fields = {
{ " PRESSURE " , { " FPR " , " RPR " , " BPR " } } ,
{ " OIP " , { " ROIP " , " FOIP " , " FOE " } } ,
2018-01-16 10:38:32 +01:00
{ " OIPL " , { " ROIPL " , " FOIPL " } } ,
{ " OIPG " , { " ROIPG " , " FOIPG " } } ,
2017-02-06 23:52:39 +01:00
{ " GIP " , { " RGIP " , " FGIP " } } ,
2018-01-16 10:38:32 +01:00
{ " GIPL " , { " RGIPL " , " FGIPL " } } ,
{ " GIPG " , { " RGIPG " , " FGIPG " } } ,
2017-02-07 14:35:37 +01:00
{ " WIP " , { " RWIP " , " FWIP " } } ,
2017-02-06 23:52:39 +01:00
{ " SWAT " , { " BSWAT " } } ,
{ " SGAS " , { " BSGAS " } }
} ;
2019-03-09 10:19:11 +01:00
bool is_udq ( const std : : string & keyword ) {
2019-06-27 16:08:08 +02:00
return ( keyword . size ( ) > 1 & & keyword [ 1 ] = = ' U ' & & keyword ! = " SUMTHIN " ) ;
2019-03-09 10:19:11 +01:00
}
2017-02-06 23:52:39 +01:00
2019-01-03 11:53:32 +01:00
void handleMissingWell ( const ParseContext & parseContext , ErrorGuard & errors , const std : : string & keyword , const std : : string & well ) {
2016-07-04 16:33:54 +02:00
std : : string msg = std : : string ( " Error in keyword: " ) + keyword + std : : string ( " No such well: " ) + well ;
if ( parseContext . get ( ParseContext : : SUMMARY_UNKNOWN_WELL ) = = InputError : : WARN )
std : : cerr < < " ERROR: " < < msg < < std : : endl ;
2019-01-03 11:53:32 +01:00
parseContext . handleError ( ParseContext : : SUMMARY_UNKNOWN_WELL , msg , errors ) ;
2016-07-04 16:33:54 +02:00
}
2019-01-03 11:53:32 +01:00
void handleMissingGroup ( const ParseContext & parseContext , ErrorGuard & errors , const std : : string & keyword , const std : : string & group ) {
2016-07-04 16:33:54 +02:00
std : : string msg = std : : string ( " Error in keyword: " ) + keyword + std : : string ( " No such group: " ) + group ;
if ( parseContext . get ( ParseContext : : SUMMARY_UNKNOWN_GROUP ) = = InputError : : WARN )
std : : cerr < < " ERROR: " < < msg < < std : : endl ;
2019-01-03 11:53:32 +01:00
parseContext . handleError ( ParseContext : : SUMMARY_UNKNOWN_GROUP , msg , errors ) ;
2016-07-04 16:33:54 +02:00
}
2016-01-29 11:32:13 +01:00
2019-01-03 11:53:32 +01:00
inline void keywordW ( SummaryConfig : : keyword_list & list ,
2016-07-04 15:54:05 +02:00
const ParseContext & parseContext ,
2019-01-03 11:53:32 +01:00
ErrorGuard & errors ,
2016-07-04 15:54:05 +02:00
const DeckKeyword & keyword ,
const Schedule & schedule ) {
2016-01-29 11:32:13 +01:00
2019-06-28 12:11:06 +02:00
/*
Here is a two step check whether this keyword should be discarded as not
supported :
1. Well keywords ending with ' L ' represent completions , they are not
supported .
2016-06-28 15:37:05 +02:00
2019-06-28 12:11:06 +02:00
2. If the keyword is a UDQ keyword there is no convention enforced to
the last character , and in that case it is treated as a normal well
keyword anyways .
*/
2019-05-20 12:52:05 +02:00
if ( keyword . name ( ) . back ( ) = = ' L ' ) {
2019-06-28 12:11:06 +02:00
if ( ! is_udq ( keyword . name ( ) ) ) {
std : : string msg = std : : string ( " The completion keywords like: " + keyword . name ( ) + " are not supported " ) ;
parseContext . handleError ( ParseContext : : SUMMARY_UNHANDLED_KEYWORD , msg , errors ) ;
return ;
}
2019-05-20 12:52:05 +02:00
}
2019-06-28 12:11:06 +02:00
if ( keyword . size ( ) & & keyword . getDataRecord ( ) . getDataItem ( ) . hasValue ( 0 ) ) {
2018-02-19 15:30:42 +01:00
for ( const std : : string & pattern : keyword . getStringData ( ) ) {
2019-03-23 17:18:27 +01:00
auto well_names = schedule . wellNames ( pattern , schedule . size ( ) - 1 ) ;
2016-06-28 15:37:05 +02:00
2019-03-23 17:18:27 +01:00
if ( well_names . empty ( ) )
2019-01-03 11:53:32 +01:00
handleMissingWell ( parseContext , errors , keyword . name ( ) , pattern ) ;
2016-05-23 10:32:41 +02:00
2019-03-23 17:18:27 +01:00
for ( const auto & well_name : well_names )
list . push_back ( SummaryConfig : : keyword_type ( keyword . name ( ) , well_name ) ) ;
2018-02-19 15:30:42 +01:00
}
} else
2019-04-19 07:24:10 +02:00
for ( const auto & wname : schedule . wellNames ( ) )
list . push_back ( SummaryConfig : : keyword_type ( keyword . name ( ) , wname ) ) ;
2018-09-07 10:23:21 +02:00
}
2016-05-23 10:32:41 +02:00
2018-09-07 10:23:21 +02:00
inline void keywordG ( SummaryConfig : : keyword_list & list ,
2016-07-04 15:54:05 +02:00
const ParseContext & parseContext ,
2019-01-03 11:53:32 +01:00
ErrorGuard & errors ,
2016-07-04 15:54:05 +02:00
const DeckKeyword & keyword ,
const Schedule & schedule ) {
2016-05-23 10:32:41 +02:00
2016-12-13 12:55:09 +01:00
if ( keyword . name ( ) = = " GMWSET " ) return ;
2016-07-04 15:54:05 +02:00
if ( keyword . size ( ) = = 0 | |
! keyword . getDataRecord ( ) . getDataItem ( ) . hasValue ( 0 ) ) {
2016-06-28 15:37:05 +02:00
2016-11-02 10:19:15 +01:00
for ( const auto & group : schedule . getGroups ( ) ) {
if ( group - > name ( ) = = " FIELD " ) continue ;
2018-11-25 11:35:41 +01:00
list . push_back ( SummaryConfig : : keyword_type ( keyword . name ( ) , group - > name ( ) ) ) ;
2016-11-02 10:19:15 +01:00
}
2016-07-04 15:54:05 +02:00
return ;
2016-06-28 15:37:05 +02:00
}
2016-07-04 15:54:05 +02:00
const auto & item = keyword . getDataRecord ( ) . getDataItem ( ) ;
2016-07-04 13:15:25 +02:00
2016-07-04 15:54:05 +02:00
for ( const std : : string & group : item . getData < std : : string > ( ) ) {
if ( schedule . hasGroup ( group ) )
2018-11-25 11:35:41 +01:00
list . push_back ( SummaryConfig : : keyword_type ( keyword . name ( ) , group ) ) ;
2016-07-04 15:54:05 +02:00
else
2019-01-03 11:53:32 +01:00
handleMissingGroup ( parseContext , errors , keyword . name ( ) , group ) ;
2016-06-29 16:38:09 +02:00
}
2016-07-04 15:54:05 +02:00
}
2016-06-29 16:38:09 +02:00
2018-11-25 11:35:41 +01:00
inline void keywordF ( SummaryConfig : : keyword_list & list ,
const DeckKeyword & keyword ) {
if ( keyword . name ( ) = = " FMWSET " ) return ;
list . push_back ( SummaryConfig : : keyword_type ( keyword . name ( ) ) ) ;
}
2016-06-29 16:38:09 +02:00
2016-07-04 15:54:05 +02:00
inline std : : array < int , 3 > getijk ( const DeckRecord & record ,
int offset = 0 ) {
return { {
record . getItem ( offset + 0 ) . get < int > ( 0 ) - 1 ,
record . getItem ( offset + 1 ) . get < int > ( 0 ) - 1 ,
record . getItem ( offset + 2 ) . get < int > ( 0 ) - 1
} } ;
}
2016-06-28 15:37:05 +02:00
2018-06-08 16:23:03 +02:00
inline std : : array < int , 3 > getijk ( const Connection & completion ) {
2018-09-07 10:23:21 +02:00
return { { completion . getI ( ) , completion . getJ ( ) , completion . getK ( ) } } ;
2016-07-04 15:54:05 +02:00
}
2016-06-28 15:37:05 +02:00
2018-09-07 10:23:21 +02:00
2018-11-25 11:35:41 +01:00
inline void keywordB ( SummaryConfig : : keyword_list & list ,
const DeckKeyword & keyword ,
const GridDims & dims ) {
for ( const auto & record : keyword ) {
auto ijk = getijk ( record ) ;
int global_index = 1 + dims . getGlobalIndex ( ijk [ 0 ] , ijk [ 1 ] , ijk [ 2 ] ) ;
list . push_back ( SummaryConfig : : keyword_type ( keyword . name ( ) , global_index , dims . getNXYZ ( ) . data ( ) ) ) ;
}
2016-07-04 15:54:05 +02:00
}
2016-03-29 12:02:42 +02:00
2019-01-08 14:51:20 +01:00
inline void keywordR2R ( SummaryConfig : : keyword_list & /* list */ ,
2018-12-19 10:09:02 +01:00
const ParseContext & parseContext ,
2019-01-03 11:53:32 +01:00
ErrorGuard & errors ,
2018-12-19 10:09:02 +01:00
const DeckKeyword & keyword )
{
std : : string msg = " OPM/flow does not support region to region summary keywords - " + keyword . name ( ) + " is ignored. " ;
2019-01-03 11:53:32 +01:00
parseContext . handleError ( ParseContext : : SUMMARY_UNHANDLED_KEYWORD , msg , errors ) ;
2018-12-19 10:09:02 +01:00
}
2018-09-07 10:23:21 +02:00
inline void keywordR ( SummaryConfig : : keyword_list & list ,
2016-07-04 15:54:05 +02:00
const DeckKeyword & keyword ,
2018-12-10 16:08:44 +01:00
const TableManager & tables ) {
2016-03-29 12:11:54 +02:00
2016-07-04 15:54:05 +02:00
/* RUNSUM is not a region keyword but a directive for how to format and
* print output . Unfortunately its * recognised * as a region keyword
* because of its structure and position . Hence the special handling of ignoring it .
*/
if ( keyword . name ( ) = = " RUNSUM " ) return ;
if ( keyword . name ( ) = = " RPTONLY " ) return ;
2016-03-29 14:15:03 +02:00
2017-05-25 23:39:45 +02:00
const size_t numfip = tables . numFIPRegions ( ) ;
2016-07-04 15:54:05 +02:00
const auto & item = keyword . getDataRecord ( ) . getDataItem ( ) ;
2017-05-25 23:39:45 +02:00
std : : vector < int > regions ;
2016-01-29 11:32:13 +01:00
2017-05-25 23:39:45 +02:00
if ( item . size ( ) > 0 )
regions = item . getData < int > ( ) ;
else {
for ( size_t region = 1 ; region < = numfip ; region + + )
regions . push_back ( region ) ;
}
for ( const int region : regions ) {
2017-06-09 15:08:09 +02:00
if ( region > = 1 & & region < = static_cast < int > ( numfip ) )
2018-11-25 11:35:41 +01:00
list . push_back ( SummaryConfig : : keyword_type ( keyword . name ( ) , region ) ) ;
2017-05-25 23:39:45 +02:00
else
throw std : : invalid_argument ( " Illegal region value: " + std : : to_string ( region ) ) ;
}
2016-07-04 15:54:05 +02:00
}
2016-01-29 11:32:13 +01:00
2017-05-29 17:09:12 +02:00
2018-11-25 11:35:41 +01:00
inline void keywordMISC ( SummaryConfig : : keyword_list & list ,
2018-09-07 10:23:21 +02:00
const DeckKeyword & keyword )
2017-05-29 17:09:12 +02:00
{
2017-05-30 07:22:08 +02:00
if ( meta_keywords . count ( keyword . name ( ) ) = = 0 )
2018-11-25 11:35:41 +01:00
list . push_back ( SummaryConfig : : keyword_type ( keyword . name ( ) ) ) ;
2017-05-29 17:09:12 +02:00
}
2018-09-07 10:23:21 +02:00
inline void keywordC ( SummaryConfig : : keyword_list & list ,
const ParseContext & parseContext ,
2019-01-03 11:53:32 +01:00
ErrorGuard & errors ,
2018-09-07 10:23:21 +02:00
const DeckKeyword & keyword ,
const Schedule & schedule ,
const GridDims & dims ) {
2016-01-29 11:32:13 +01:00
2016-07-04 15:54:05 +02:00
const auto & keywordstring = keyword . name ( ) ;
for ( const auto & record : keyword ) {
2016-05-02 11:49:04 +02:00
2016-07-15 13:24:58 +02:00
const auto & wellitem = record . getItem ( 0 ) ;
2016-03-18 13:55:48 +01:00
2019-03-23 17:18:27 +01:00
const auto well_names = wellitem . defaultApplied ( 0 )
? schedule . wellNames ( )
: schedule . wellNames ( wellitem . getTrimmedString ( 0 ) ) ;
2016-03-18 13:55:48 +01:00
2019-03-23 17:18:27 +01:00
if ( well_names . empty ( ) )
2019-01-03 11:53:32 +01:00
handleMissingWell ( parseContext , errors , keyword . name ( ) , wellitem . getTrimmedString ( 0 ) ) ;
2016-03-18 13:55:48 +01:00
2019-03-23 17:18:27 +01:00
for ( const auto & name : well_names ) {
2019-05-04 12:00:32 +02:00
const auto & well = schedule . getWell2atEnd ( name ) ;
2016-07-15 13:24:58 +02:00
/*
* we don ' t want to add completions that don ' t exist , so we iterate
* over a well ' s completions regardless of the desired block is
* defaulted or not
*/
2019-05-04 12:00:32 +02:00
for ( const auto & connection : well . getConnections ( ) ) {
2016-07-15 13:24:58 +02:00
/* block coordinates defaulted */
2018-06-10 20:54:34 +02:00
auto cijk = getijk ( connection ) ;
2016-07-04 15:54:05 +02:00
2016-07-15 13:24:58 +02:00
if ( record . getItem ( 1 ) . defaultApplied ( 0 ) ) {
2018-09-07 10:23:21 +02:00
int global_index = 1 + dims . getGlobalIndex ( cijk [ 0 ] , cijk [ 1 ] , cijk [ 2 ] ) ;
2018-11-25 11:35:41 +01:00
list . push_back ( SummaryConfig : : keyword_type ( keywordstring , name . c_str ( ) , global_index , dims . getNXYZ ( ) . data ( ) ) ) ;
2018-09-07 10:23:21 +02:00
} else {
2016-07-15 13:24:58 +02:00
/* block coordinates specified */
auto recijk = getijk ( record , 1 ) ;
2018-09-07 10:23:21 +02:00
if ( std : : equal ( recijk . begin ( ) , recijk . end ( ) , cijk . begin ( ) ) ) {
int global_index = 1 + dims . getGlobalIndex ( recijk [ 0 ] , recijk [ 1 ] , recijk [ 2 ] ) ;
2018-11-25 11:35:41 +01:00
list . push_back ( SummaryConfig : : keyword_type ( keywordstring , name . c_str ( ) , global_index , dims . getNXYZ ( ) . data ( ) ) ) ;
2018-09-07 10:23:21 +02:00
}
2016-07-04 15:54:05 +02:00
}
}
2016-01-29 11:32:13 +01:00
}
}
2016-07-04 15:54:05 +02:00
}
2016-01-29 11:32:13 +01:00
2018-10-18 23:20:19 +02:00
bool isKnownSegmentKeyword ( const DeckKeyword & keyword )
{
const auto & kw = keyword . name ( ) ;
if ( kw . size ( ) > 4 ) {
// Easy check first--handles SUMMARY and SUMTHIN &c.
return false ;
}
const auto kw_whitelist = std : : vector < const char * > {
" SOFR " , " SGFR " , " SWFR " , " SPR " ,
} ;
return std : : any_of ( kw_whitelist . begin ( ) , kw_whitelist . end ( ) ,
[ & kw ] ( const char * known ) - > bool
{
return kw = = known ;
} ) ;
}
2018-10-11 14:12:14 +02:00
int maxNumWellSegments ( const std : : size_t last_timestep ,
2019-05-04 12:00:32 +02:00
const Well2 & well )
2018-10-11 14:12:14 +02:00
{
auto numSeg = 0 ;
for ( auto step = 0 * last_timestep ;
step < = last_timestep ; + + step )
{
2019-05-04 12:00:32 +02:00
if ( ! well . isMultiSegment ( ) )
2018-10-11 14:12:14 +02:00
continue ;
const auto nseg =
2019-05-04 12:00:32 +02:00
well . getSegments ( ) . size ( ) ;
2018-10-11 14:12:14 +02:00
if ( nseg > numSeg ) {
numSeg = nseg ;
}
}
return numSeg ;
}
2018-10-09 21:10:28 +02:00
void makeSegmentNodes ( const std : : size_t last_timestep ,
const int segID ,
const DeckKeyword & keyword ,
2019-05-04 12:00:32 +02:00
const Well2 & well ,
2018-10-09 21:10:28 +02:00
SummaryConfig : : keyword_list & list )
2018-10-04 21:31:05 +02:00
{
// Modifies 'list' in place.
2018-10-09 21:10:28 +02:00
auto makeNode = [ & keyword , & list ]
2019-06-23 00:02:23 +02:00
( const std : : string & well_name , const int segNumber )
2018-10-04 21:31:05 +02:00
{
2019-06-23 00:02:23 +02:00
list . push_back ( SummaryConfig : : keyword_type ( keyword . name ( ) , well_name , segNumber ) ) ;
2018-10-04 21:31:05 +02:00
} ;
2019-05-04 12:00:32 +02:00
if ( ! well . isMultiSegment ( ) )
2019-04-19 08:07:33 +02:00
// Not an MSW. Don't create summary vectors for segments.
return ;
2018-10-09 21:10:28 +02:00
2019-05-04 12:00:32 +02:00
const auto & wname = well . name ( ) ;
2019-04-19 08:07:33 +02:00
if ( segID < 1 ) {
// Segment number defaulted. Allocate a summary
// vector for each segment.
const auto nSeg = maxNumWellSegments ( last_timestep , well ) ;
2018-10-09 21:10:28 +02:00
2019-04-19 08:07:33 +02:00
for ( auto segNumber = 0 * nSeg ;
segNumber < nSeg ; + + segNumber )
2018-10-09 21:10:28 +02:00
{
// One-based segment number.
makeNode ( wname , segNumber + 1 ) ;
}
2019-04-19 08:07:33 +02:00
}
else {
// Segment number specified. Allocate single
// summary vector for that segment number.
makeNode ( wname , segID ) ;
2018-10-09 21:10:28 +02:00
}
}
void keywordSNoRecords ( const std : : size_t last_timestep ,
const DeckKeyword & keyword ,
const Schedule & schedule ,
SummaryConfig : : keyword_list & list )
{
// No keyword records. Allocate summary vectors for all
// segments in all wells at all times.
//
// Expected format:
//
// SGFR
// / -- All segments in all MS wells at all times.
const auto segID = - 1 ;
2019-05-04 12:00:32 +02:00
for ( const auto & well : schedule . getWells2atEnd ( ) )
2019-04-19 08:07:33 +02:00
makeSegmentNodes ( last_timestep , segID , keyword ,
well , list ) ;
2018-10-09 21:10:28 +02:00
}
void keywordSWithRecords ( const std : : size_t last_timestep ,
const ParseContext & parseContext ,
2019-01-03 11:53:32 +01:00
ErrorGuard & errors ,
2018-10-09 21:10:28 +02:00
const DeckKeyword & keyword ,
const Schedule & schedule ,
SummaryConfig : : keyword_list & list )
{
// Keyword has explicit records. Process those and create
// segment-related summary vectors for those wells/segments
// that match the description.
//
// Expected formats:
//
// SOFR
// 'W1' 1 /
// 'W1' 10 /
// 'W3' / -- All segments
// /
//
// SPR
// 1* 2 / -- Segment 2 in all multi-segmented wells
// /
2018-10-04 21:31:05 +02:00
for ( const auto & record : keyword ) {
const auto & wellitem = record . getItem ( 0 ) ;
2019-03-23 17:18:27 +01:00
const auto & well_names = wellitem . defaultApplied ( 0 )
? schedule . wellNames ( )
: schedule . wellNames ( wellitem . getTrimmedString ( 0 ) ) ;
2018-10-04 21:31:05 +02:00
2019-03-23 17:18:27 +01:00
if ( well_names . empty ( ) )
2019-01-03 11:53:32 +01:00
handleMissingWell ( parseContext , errors , keyword . name ( ) ,
2018-10-04 21:31:05 +02:00
wellitem . getTrimmedString ( 0 ) ) ;
2018-10-09 21:10:28 +02:00
// Negative 1 (< 0) if segment ID defaulted. Defaulted
// segment number in record implies all segments.
const auto segID = record . getItem ( 1 ) . defaultApplied ( 0 )
? - 1 : record . getItem ( 1 ) . get < int > ( 0 ) ;
2019-04-19 08:07:33 +02:00
for ( const auto & well_name : well_names )
2019-05-04 12:00:32 +02:00
makeSegmentNodes ( last_timestep , segID , keyword , schedule . getWell2atEnd ( well_name ) , list ) ;
2018-10-09 21:10:28 +02:00
}
}
inline void keywordS ( SummaryConfig : : keyword_list & list ,
const ParseContext & parseContext ,
2019-01-03 11:53:32 +01:00
ErrorGuard & errors ,
2018-10-09 21:10:28 +02:00
const DeckKeyword & keyword ,
const Schedule & schedule )
{
// Generate SMSPEC nodes for SUMMARY keywords of the form
//
// SOFR
// 'W1' 1 /
// 'W1' 10 /
// 'W3' / -- All segments
// /
//
// SPR
// 1* 2 / -- Segment 2 in all multi-segmented wells
// /
//
// SGFR
// / -- All segments in all MS wells at all times.
2018-10-04 21:31:05 +02:00
2018-10-18 23:20:19 +02:00
if ( ! isKnownSegmentKeyword ( keyword ) ) {
// Ignore keywords that have not been explicitly white-listed
// for treatment as segment summary vectors.
2018-10-09 21:10:28 +02:00
return ;
}
2018-10-04 21:31:05 +02:00
2018-10-09 21:10:28 +02:00
const auto last_timestep = schedule . getTimeMap ( ) . last ( ) ;
2018-10-04 21:31:05 +02:00
2018-10-09 21:10:28 +02:00
if ( keyword . size ( ) > 0 ) {
// Keyword with explicit records.
// Handle as alternatives SOFR and SPR above
2019-01-03 11:53:32 +01:00
keywordSWithRecords ( last_timestep , parseContext , errors ,
2018-10-09 21:10:28 +02:00
keyword , schedule , list ) ;
}
else {
// Keyword with no explicit records.
// Handle as alternative SGFR above.
keywordSNoRecords ( last_timestep , keyword , schedule , list ) ;
2018-10-04 21:31:05 +02:00
}
}
2018-09-07 10:23:21 +02:00
inline void handleKW ( SummaryConfig : : keyword_list & list ,
const DeckKeyword & keyword ,
const Schedule & schedule ,
const TableManager & tables ,
const ParseContext & parseContext ,
2019-01-03 11:53:32 +01:00
ErrorGuard & errors ,
2018-09-07 10:23:21 +02:00
const GridDims & dims ) {
2016-07-04 15:54:05 +02:00
const auto var_type = ecl_smspec_identify_var_type ( keyword . name ( ) . c_str ( ) ) ;
2019-03-09 10:19:11 +01:00
const auto & name = keyword . name ( ) ;
if ( is_udq ( name ) ) {
const auto & udq = schedule . getUDQConfig ( schedule . size ( ) - 1 ) ;
if ( ! udq . has_keyword ( name ) ) {
std : : string msg { " Summary output has been requested for UDQ keyword: " + name + " but it has not been configured " } ;
parseContext . handleError ( ParseContext : : SUMMARY_UNDEFINED_UDQ , msg , errors ) ;
return ;
}
if ( ! udq . has_unit ( name ) ) {
std : : string msg { " Summary output has been requested for UDQ keyword: " + name + " but no unit has not been configured " } ;
parseContext . handleError ( ParseContext : : SUMMARY_UDQ_MISSING_UNIT , msg , errors ) ;
}
}
2016-07-04 15:54:05 +02:00
switch ( var_type ) {
2019-01-03 11:53:32 +01:00
case ECL_SMSPEC_WELL_VAR : return keywordW ( list , parseContext , errors , keyword , schedule ) ;
case ECL_SMSPEC_GROUP_VAR : return keywordG ( list , parseContext , errors , keyword , schedule ) ;
2016-07-04 15:54:05 +02:00
case ECL_SMSPEC_FIELD_VAR : return keywordF ( list , keyword ) ;
2017-09-25 10:05:09 +02:00
case ECL_SMSPEC_BLOCK_VAR : return keywordB ( list , keyword , dims ) ;
2018-12-10 16:08:44 +01:00
case ECL_SMSPEC_REGION_VAR : return keywordR ( list , keyword , tables ) ;
2019-01-03 11:53:32 +01:00
case ECL_SMSPEC_REGION_2_REGION_VAR : return keywordR2R ( list , parseContext , errors , keyword ) ;
case ECL_SMSPEC_COMPLETION_VAR : return keywordC ( list , parseContext , errors , keyword , schedule , dims ) ;
case ECL_SMSPEC_SEGMENT_VAR : return keywordS ( list , parseContext , errors , keyword , schedule ) ;
2017-05-29 17:09:12 +02:00
case ECL_SMSPEC_MISC_VAR : return keywordMISC ( list , keyword ) ;
2016-07-04 15:54:05 +02:00
2018-12-19 10:09:02 +01:00
default :
std : : string msg = " Summary keywords of type: " + std : : string ( ecl_smspec_get_var_type_name ( var_type ) ) + " is not supported. Keyword: " + keyword . name ( ) + " is ignored " ;
2019-01-03 11:53:32 +01:00
parseContext . handleError ( ParseContext : : SUMMARY_UNHANDLED_KEYWORD , msg , errors ) ;
2018-12-19 10:09:02 +01:00
return ;
2016-07-04 15:54:05 +02:00
}
2016-07-04 11:37:01 +02:00
}
2016-06-28 15:37:05 +02:00
2018-09-07 10:23:21 +02:00
inline void uniq ( SummaryConfig : : keyword_list & vec ) {
const auto lt = [ ] ( const SummaryConfig : : keyword_type & lhs ,
const SummaryConfig : : keyword_type & rhs ) {
2018-11-25 11:35:41 +01:00
return lhs . cmp ( rhs ) < 0 ;
2016-07-04 16:44:00 +02:00
} ;
2017-07-31 11:52:15 +02:00
2018-09-07 10:23:21 +02:00
const auto eq = [ ] ( const SummaryConfig : : keyword_type & lhs ,
const SummaryConfig : : keyword_type & rhs ) {
2018-11-25 11:35:41 +01:00
return lhs . cmp ( rhs ) = = 0 ;
2016-07-04 16:44:00 +02:00
} ;
std : : sort ( vec . begin ( ) , vec . end ( ) , lt ) ;
auto logical_end = std : : unique ( vec . begin ( ) , vec . end ( ) , eq ) ;
vec . erase ( logical_end , vec . end ( ) ) ;
2018-09-07 10:23:21 +02:00
}
2016-07-04 16:44:00 +02:00
}
2016-07-04 15:54:05 +02:00
SummaryConfig : : SummaryConfig ( const Deck & deck ,
2016-09-19 20:18:51 +02:00
const Schedule & schedule ,
2017-05-25 23:39:45 +02:00
const TableManager & tables ,
2016-09-19 20:18:51 +02:00
const ParseContext & parseContext ,
2019-01-03 11:53:32 +01:00
ErrorGuard & errors ,
2017-09-25 10:05:09 +02:00
const GridDims & dims ) {
2016-07-04 15:54:05 +02:00
SUMMARYSection section ( deck ) ;
2019-03-29 13:14:43 +01:00
// The kw_iter++ hoops is to skip the initial 'SUMMARY' keyword.
auto kw_iter = section . begin ( ) ;
if ( kw_iter ! = section . end ( ) )
kw_iter + + ;
for ( ; kw_iter ! = section . end ( ) ; + + kw_iter ) {
const auto & kw = * kw_iter ;
handleKW ( this - > keywords , kw , schedule , tables , parseContext , errors , dims ) ;
}
2016-07-04 15:54:05 +02:00
if ( section . hasKeyword ( " ALL " ) )
2019-01-03 11:53:32 +01:00
this - > merge ( { ALL_keywords , schedule , tables , parseContext , errors , dims } ) ;
2016-07-04 16:44:00 +02:00
2016-12-13 12:55:09 +01:00
if ( section . hasKeyword ( " GMWSET " ) )
2019-01-03 11:53:32 +01:00
this - > merge ( { GMWSET_keywords , schedule , tables , parseContext , errors , dims } ) ;
2016-12-13 12:55:09 +01:00
2016-12-13 13:11:46 +01:00
if ( section . hasKeyword ( " FMWSET " ) )
2019-01-03 11:53:32 +01:00
this - > merge ( { FMWSET_keywords , schedule , tables , parseContext , errors , dims } ) ;
2016-12-13 13:11:46 +01:00
2017-05-30 07:22:08 +02:00
if ( section . hasKeyword ( " PERFORMA " ) )
2019-01-03 11:53:32 +01:00
this - > merge ( { PERFORMA_keywords , schedule , tables , parseContext , errors , dims } ) ;
2017-05-30 07:22:08 +02:00
2016-07-04 16:44:00 +02:00
uniq ( this - > keywords ) ;
2017-05-25 23:39:45 +02:00
for ( const auto & kw : this - > keywords ) {
2018-11-25 11:35:41 +01:00
this - > short_keywords . insert ( kw . keyword ( ) ) ;
this - > summary_keywords . insert ( kw . gen_key ( ) ) ;
2017-05-25 23:39:45 +02:00
}
2017-09-25 10:05:09 +02:00
}
SummaryConfig : : SummaryConfig ( const Deck & deck ,
const Schedule & schedule ,
const TableManager & tables ,
2019-01-03 11:53:32 +01:00
const ParseContext & parseContext ,
ErrorGuard & errors ) :
SummaryConfig ( deck , schedule , tables , parseContext , errors , GridDims ( deck ) )
2019-01-03 18:05:19 +01:00
{ }
template < typename T >
SummaryConfig : : SummaryConfig ( const Deck & deck ,
const Schedule & schedule ,
const TableManager & tables ,
const ParseContext & parseContext ,
T & & errors ) :
SummaryConfig ( deck , schedule , tables , parseContext , errors )
{ }
SummaryConfig : : SummaryConfig ( const Deck & deck ,
const Schedule & schedule ,
const TableManager & tables ) :
SummaryConfig ( deck , schedule , tables , ParseContext ( ) , ErrorGuard ( ) )
{ }
2017-09-25 10:05:09 +02:00
2016-01-29 11:32:13 +01:00
2016-07-04 16:33:54 +02:00
SummaryConfig : : const_iterator SummaryConfig : : begin ( ) const {
return this - > keywords . cbegin ( ) ;
}
2016-01-29 11:32:13 +01:00
2016-07-04 16:33:54 +02:00
SummaryConfig : : const_iterator SummaryConfig : : end ( ) const {
return this - > keywords . cend ( ) ;
}
2016-01-29 11:32:13 +01:00
2016-07-04 16:33:54 +02:00
SummaryConfig & SummaryConfig : : merge ( const SummaryConfig & other ) {
this - > keywords . insert ( this - > keywords . end ( ) ,
other . keywords . begin ( ) ,
other . keywords . end ( ) ) ;
2016-07-04 16:44:00 +02:00
uniq ( this - > keywords ) ;
2016-07-04 16:33:54 +02:00
return * this ;
}
2016-07-04 12:02:43 +02:00
2016-07-04 16:33:54 +02:00
SummaryConfig & SummaryConfig : : merge ( SummaryConfig & & other ) {
2016-07-13 10:04:53 +02:00
auto fst = std : : make_move_iterator ( other . keywords . begin ( ) ) ;
auto lst = std : : make_move_iterator ( other . keywords . end ( ) ) ;
this - > keywords . insert ( this - > keywords . end ( ) , fst , lst ) ;
2016-07-04 16:33:54 +02:00
other . keywords . clear ( ) ;
2016-07-04 12:02:43 +02:00
2016-07-04 16:44:00 +02:00
uniq ( this - > keywords ) ;
2016-07-04 16:33:54 +02:00
return * this ;
}
2016-07-04 12:02:43 +02:00
2017-05-25 23:39:45 +02:00
2016-09-19 20:18:51 +02:00
bool SummaryConfig : : hasKeyword ( const std : : string & keyword ) const {
return ( this - > short_keywords . count ( keyword ) = = 1 ) ;
}
2017-02-06 23:52:39 +01:00
2017-05-25 23:39:45 +02:00
bool SummaryConfig : : hasSummaryKey ( const std : : string & keyword ) const {
return ( this - > summary_keywords . count ( keyword ) = = 1 ) ;
}
2019-03-29 15:39:50 +01:00
size_t SummaryConfig : : size ( ) const {
return this - > keywords . size ( ) ;
}
2017-02-06 23:52:39 +01:00
/*
Can be used to query if a certain 3 D field , e . g . PRESSURE , is
required to calculate the summary variables .
The implementation is based on the hardcoded datastructure
required_fields defined in a anonymous namespaces at the top of this
file ; the content of this datastructure again is based on the
implementation of the Summary calculations in the opm - output
repository : opm / output / eclipse / Summary . cpp .
*/
bool SummaryConfig : : require3DField ( const std : : string & keyword ) const {
const auto iter = required_fields . find ( keyword ) ;
if ( iter = = required_fields . end ( ) )
return false ;
for ( const auto & kw : iter - > second ) {
if ( this - > hasKeyword ( kw ) )
return true ;
}
return false ;
}
bool SummaryConfig : : requireFIPNUM ( ) const {
return this - > hasKeyword ( " ROIP " ) | |
this - > hasKeyword ( " ROIPL " ) | |
this - > hasKeyword ( " RGIP " ) | |
this - > hasKeyword ( " RGIPL " ) | |
this - > hasKeyword ( " RGIPG " ) | |
this - > hasKeyword ( " RWIP " ) | |
this - > hasKeyword ( " RPR " ) ;
}
2016-01-29 11:32:13 +01:00
}