Files
opm-common/src/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.cpp

730 lines
27 KiB
C++

/*
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/>.
*/
#include <iostream>
#include <algorithm>
#include <array>
#include <ert/ecl/ecl_smspec.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#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>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/Tables/TableManager.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/GridDims.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Connection.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
#include <opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
namespace Opm {
namespace {
/*
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.
*/
const Deck ALL_keywords = {
"SUMMARY",
"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",
"FWIT", "FWPR", "FWPT", "FWPP", "FOPP", "FGPP", "FWPI",
"FOPI", "FGPI",
"GGIR", "GGIT", "GGOR", "GGPR", "GGPT", "GOIR", "GOIT",
"GOPR", "GOPT", "GVIR", "GVIT", "GVPR", "GVPT", "GWCT",
"GWGR", "GWIR", "GWIT", "GWPR", "GWPT", "GWPP", "GOPP",
"GGPP", "GWPI", "GOPI", "GGPI",
"WBHP", "WGIR", "WGIT", "WGOR", "WGPR", "WGPT", "WOIR",
"WOIT", "WOPR", "WOPT", "WPI", "WTHP", "WVIR", "WVIT",
"WVPR", "WVPT", "WWCT", "WWGR", "WWIR", "WWIT", "WWPR",
"WWPT", "WWPP", "WOPP", "WGPP", "WWPI", "WGPI", "WOPI",
// ALL will not expand to these keywords yet
"AAQR", "AAQRG", "AAQT", "AAQTG"
};
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"
};
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"
};
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"};
/*
This is a hardcoded mapping between 3D 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 3D 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"}},
{"OIPL" , {"ROIPL" ,"FOIPL" }},
{"OIPG" , {"ROIPG" ,"FOIPG"}},
{"GIP" , {"RGIP" , "FGIP"}},
{"GIPL" , {"RGIPL" , "FGIPL"}},
{"GIPG" , {"RGIPG", "FGIPG"}},
{"WIP" , {"RWIP" , "FWIP"}},
{"SWAT" , {"BSWAT"}},
{"SGAS" , {"BSGAS"}}
};
bool is_udq(const std::string& keyword) {
return (keyword.size() > 1 && keyword[1] == 'U' && keyword != "SUMTHIN");
}
void handleMissingWell( const ParseContext& parseContext, ErrorGuard& errors, const std::string& keyword, const std::string& well) {
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;
parseContext.handleError( ParseContext::SUMMARY_UNKNOWN_WELL , msg, errors );
}
void handleMissingGroup( const ParseContext& parseContext , ErrorGuard& errors, const std::string& keyword, const std::string& group) {
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;
parseContext.handleError( ParseContext::SUMMARY_UNKNOWN_GROUP , msg, errors );
}
inline void keywordW( SummaryConfig::keyword_list& list,
const ParseContext& parseContext,
ErrorGuard& errors,
const DeckKeyword& keyword,
const Schedule& schedule ) {
/*
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.
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.
*/
if (keyword.name().back() == 'L') {
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;
}
}
if (keyword.size() && keyword.getDataRecord().getDataItem().hasValue(0)) {
for( const std::string& pattern : keyword.getStringData()) {
auto well_names = schedule.wellNames( pattern, schedule.size() - 1 );
if( well_names.empty() )
handleMissingWell( parseContext, errors, keyword.name(), pattern );
for( const auto& well_name : well_names)
list.push_back( SummaryConfig::keyword_type( keyword.name(), well_name ));
}
} else
for (const auto& wname : schedule.wellNames())
list.push_back( SummaryConfig::keyword_type( keyword.name(), wname));
}
inline void keywordG( SummaryConfig::keyword_list& list,
const ParseContext& parseContext,
ErrorGuard& errors,
const DeckKeyword& keyword,
const Schedule& schedule ) {
if( keyword.name() == "GMWSET" ) return;
if( keyword.size() == 0 ||
!keyword.getDataRecord().getDataItem().hasValue( 0 ) ) {
for( const auto& group : schedule.getGroups() ) {
if( group->name() == "FIELD" ) continue;
list.push_back( SummaryConfig::keyword_type(keyword.name(), group->name() ));
}
return;
}
const auto& item = keyword.getDataRecord().getDataItem();
for( const std::string& group : item.getData< std::string >() ) {
if( schedule.hasGroup( group ) )
list.push_back( SummaryConfig::keyword_type(keyword.name(), group ));
else
handleMissingGroup( parseContext, errors, keyword.name(), group );
}
}
inline void keywordF( SummaryConfig::keyword_list& list,
const DeckKeyword& keyword ) {
if( keyword.name() == "FMWSET" ) return;
list.push_back( SummaryConfig::keyword_type( keyword.name() ));
}
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
}};
}
inline std::array< int, 3 > getijk( const Connection& completion ) {
return { { completion.getI(), completion.getJ(), completion.getK() }};
}
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() ));
}
}
inline void keywordR2R( SummaryConfig::keyword_list& /* list */,
const ParseContext& parseContext,
ErrorGuard& errors,
const DeckKeyword& keyword)
{
std::string msg = "OPM/flow does not support region to region summary keywords - " + keyword.name() + " is ignored.";
parseContext.handleError(ParseContext::SUMMARY_UNHANDLED_KEYWORD, msg, errors);
}
inline void keywordR( SummaryConfig::keyword_list& list,
const DeckKeyword& keyword,
const TableManager& tables) {
/* 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;
const size_t numfip = tables.numFIPRegions( );
const auto& item = keyword.getDataRecord().getDataItem();
std::vector<int> regions;
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 ) {
if (region >= 1 && region <= static_cast<int>(numfip))
list.push_back( SummaryConfig::keyword_type( keyword.name(), region ));
else
throw std::invalid_argument("Illegal region value: " + std::to_string( region ));
}
}
inline void keywordMISC( SummaryConfig::keyword_list& list,
const DeckKeyword& keyword)
{
if (meta_keywords.count( keyword.name() ) == 0)
list.push_back( SummaryConfig::keyword_type( keyword.name() ));
}
inline void keywordC( SummaryConfig::keyword_list& list,
const ParseContext& parseContext,
ErrorGuard& errors,
const DeckKeyword& keyword,
const Schedule& schedule,
const GridDims& dims) {
const auto& keywordstring = keyword.name();
for( const auto& record : keyword ) {
const auto& wellitem = record.getItem( 0 );
const auto well_names = wellitem.defaultApplied( 0 )
? schedule.wellNames()
: schedule.wellNames( wellitem.getTrimmedString( 0 ) );
if( well_names.empty() )
handleMissingWell( parseContext, errors, keyword.name(), wellitem.getTrimmedString( 0 ) );
for(const auto& name : well_names) {
const auto& well = schedule.getWell2atEnd(name);
/*
* 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
*/
for( const auto& connection : well.getConnections() ) {
/* block coordinates defaulted */
auto cijk = getijk( connection );
if( record.getItem( 1 ).defaultApplied( 0 ) ) {
int global_index = 1 + dims.getGlobalIndex(cijk[0], cijk[1], cijk[2]);
list.push_back( SummaryConfig::keyword_type( keywordstring, name.c_str(), global_index, dims.getNXYZ().data()));
} else {
/* block coordinates specified */
auto recijk = getijk( record, 1 );
if( std::equal( recijk.begin(), recijk.end(), cijk.begin() ) ) {
int global_index = 1 + dims.getGlobalIndex(recijk[0], recijk[1], recijk[2]);
list.push_back(SummaryConfig::keyword_type( keywordstring, name.c_str(), global_index, dims.getNXYZ().data()));
}
}
}
}
}
}
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;
});
}
int maxNumWellSegments(const std::size_t last_timestep,
const Well2& well)
{
auto numSeg = 0;
for (auto step = 0*last_timestep;
step <= last_timestep; ++step)
{
if (! well.isMultiSegment())
continue;
const auto nseg =
well.getSegments().size();
if (nseg > numSeg) {
numSeg = nseg;
}
}
return numSeg;
}
void makeSegmentNodes(const std::size_t last_timestep,
const int segID,
const DeckKeyword& keyword,
const Well2& well,
SummaryConfig::keyword_list& list)
{
// Modifies 'list' in place.
auto makeNode = [&keyword, &list]
(const std::string& well_name, const int segNumber)
{
list.push_back(SummaryConfig::keyword_type( keyword.name(), well_name, segNumber ));
};
if (!well.isMultiSegment())
// Not an MSW. Don't create summary vectors for segments.
return;
const auto& wname = well.name();
if (segID < 1) {
// Segment number defaulted. Allocate a summary
// vector for each segment.
const auto nSeg = maxNumWellSegments(last_timestep, well);
for (auto segNumber = 0*nSeg;
segNumber < nSeg; ++segNumber)
{
// One-based segment number.
makeNode(wname, segNumber + 1);
}
}
else {
// Segment number specified. Allocate single
// summary vector for that segment number.
makeNode(wname, segID);
}
}
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;
for (const auto& well : schedule.getWells2atEnd())
makeSegmentNodes(last_timestep, segID, keyword,
well, list);
}
void keywordSWithRecords(const std::size_t last_timestep,
const ParseContext& parseContext,
ErrorGuard& errors,
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
// /
for (const auto& record : keyword) {
const auto& wellitem = record.getItem(0);
const auto& well_names = wellitem.defaultApplied(0)
? schedule.wellNames()
: schedule.wellNames(wellitem.getTrimmedString(0));
if (well_names.empty())
handleMissingWell(parseContext, errors, keyword.name(),
wellitem.getTrimmedString(0));
// 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);
for (const auto& well_name : well_names)
makeSegmentNodes(last_timestep, segID, keyword, schedule.getWell2atEnd(well_name), list);
}
}
inline void keywordS(SummaryConfig::keyword_list& list,
const ParseContext& parseContext,
ErrorGuard& errors,
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.
if (! isKnownSegmentKeyword(keyword)) {
// Ignore keywords that have not been explicitly white-listed
// for treatment as segment summary vectors.
return;
}
const auto last_timestep = schedule.getTimeMap().last();
if (keyword.size() > 0) {
// Keyword with explicit records.
// Handle as alternatives SOFR and SPR above
keywordSWithRecords(last_timestep, parseContext, errors,
keyword, schedule, list);
}
else {
// Keyword with no explicit records.
// Handle as alternative SGFR above.
keywordSNoRecords(last_timestep, keyword, schedule, list);
}
}
inline void handleKW( SummaryConfig::keyword_list& list,
const DeckKeyword& keyword,
const Schedule& schedule,
const TableManager& tables,
const ParseContext& parseContext,
ErrorGuard& errors,
const GridDims& dims) {
const auto var_type = ecl_smspec_identify_var_type( keyword.name().c_str() );
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);
}
}
switch( var_type ) {
case ECL_SMSPEC_WELL_VAR: return keywordW( list, parseContext, errors, keyword, schedule );
case ECL_SMSPEC_GROUP_VAR: return keywordG( list, parseContext, errors, keyword, schedule );
case ECL_SMSPEC_FIELD_VAR: return keywordF( list, keyword );
case ECL_SMSPEC_BLOCK_VAR: return keywordB( list, keyword, dims );
case ECL_SMSPEC_REGION_VAR: return keywordR( list, keyword, tables );
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 );
case ECL_SMSPEC_MISC_VAR: return keywordMISC( list, keyword );
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";
parseContext.handleError(ParseContext::SUMMARY_UNHANDLED_KEYWORD, msg, errors);
return;
}
}
inline void uniq( SummaryConfig::keyword_list& vec ) {
const auto lt = []( const SummaryConfig::keyword_type& lhs,
const SummaryConfig::keyword_type& rhs ) {
return lhs.cmp(rhs) < 0;
};
const auto eq = []( const SummaryConfig::keyword_type& lhs,
const SummaryConfig::keyword_type& rhs ) {
return lhs.cmp(rhs) == 0;
};
std::sort( vec.begin(), vec.end(), lt );
auto logical_end = std::unique( vec.begin(), vec.end(), eq );
vec.erase( logical_end, vec.end() );
}
}
SummaryConfig::SummaryConfig( const Deck& deck,
const Schedule& schedule,
const TableManager& tables,
const ParseContext& parseContext,
ErrorGuard& errors,
const GridDims& dims) {
SUMMARYSection section( deck );
// 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);
}
if( section.hasKeyword( "ALL" ) )
this->merge( { ALL_keywords, schedule, tables, parseContext, errors, dims} );
if( section.hasKeyword( "GMWSET" ) )
this->merge( { GMWSET_keywords, schedule, tables, parseContext, errors, dims} );
if( section.hasKeyword( "FMWSET" ) )
this->merge( { FMWSET_keywords, schedule, tables, parseContext, errors, dims} );
if (section.hasKeyword( "PERFORMA" ) )
this->merge( { PERFORMA_keywords, schedule, tables, parseContext, errors, dims} );
uniq( this->keywords );
for (const auto& kw: this->keywords) {
this->short_keywords.insert( kw.keyword() );
this->summary_keywords.insert( kw.gen_key() );
}
}
SummaryConfig::SummaryConfig( const Deck& deck,
const Schedule& schedule,
const TableManager& tables,
const ParseContext& parseContext,
ErrorGuard& errors) :
SummaryConfig( deck , schedule, tables, parseContext, errors, GridDims( deck ))
{ }
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())
{}
SummaryConfig::const_iterator SummaryConfig::begin() const {
return this->keywords.cbegin();
}
SummaryConfig::const_iterator SummaryConfig::end() const {
return this->keywords.cend();
}
SummaryConfig& SummaryConfig::merge( const SummaryConfig& other ) {
this->keywords.insert( this->keywords.end(),
other.keywords.begin(),
other.keywords.end() );
uniq( this->keywords );
return *this;
}
SummaryConfig& SummaryConfig::merge( SummaryConfig&& other ) {
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 );
other.keywords.clear();
uniq( this->keywords );
return *this;
}
bool SummaryConfig::hasKeyword( const std::string& keyword ) const {
return (this->short_keywords.count( keyword ) == 1);
}
bool SummaryConfig::hasSummaryKey(const std::string& keyword ) const {
return (this->summary_keywords.count( keyword ) == 1);
}
size_t SummaryConfig::size() const {
return this->keywords.size();
}
/*
Can be used to query if a certain 3D 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");
}
}