Add New Summary Node Category of Completion
This enables detecting the last remaining case that has a valid NUMS entry despite nominally being a well-level keyword.
This commit is contained in:
parent
c5158a1c24
commit
03a94a7288
@ -20,12 +20,11 @@
|
|||||||
#ifndef OPM_IO_SUMMARYNODE_HPP
|
#ifndef OPM_IO_SUMMARYNODE_HPP
|
||||||
#define OPM_IO_SUMMARYNODE_HPP
|
#define OPM_IO_SUMMARYNODE_HPP
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <limits>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
|
||||||
#include <array>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
namespace Opm { namespace EclIO {
|
namespace Opm { namespace EclIO {
|
||||||
|
|
||||||
@ -42,6 +41,7 @@ struct SummaryNode {
|
|||||||
Region,
|
Region,
|
||||||
Block,
|
Block,
|
||||||
Connection,
|
Connection,
|
||||||
|
Completion,
|
||||||
Segment,
|
Segment,
|
||||||
Aquifer,
|
Aquifer,
|
||||||
Node,
|
Node,
|
||||||
@ -59,7 +59,6 @@ struct SummaryNode {
|
|||||||
Undefined,
|
Undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
std::string keyword;
|
std::string keyword;
|
||||||
Category category;
|
Category category;
|
||||||
Type type;
|
Type type;
|
||||||
@ -79,6 +78,14 @@ struct SummaryNode {
|
|||||||
|
|
||||||
static Category category_from_keyword(const std::string&);
|
static Category category_from_keyword(const std::string&);
|
||||||
|
|
||||||
|
static std::string normalise_keyword(const Category category,
|
||||||
|
const std::string& keyword);
|
||||||
|
|
||||||
|
static inline std::string normalise_keyword(const std::string& keyword)
|
||||||
|
{
|
||||||
|
return normalise_keyword(category_from_keyword(keyword), keyword);
|
||||||
|
}
|
||||||
|
|
||||||
// Return true for keywords which should be Miscellaneous, although the
|
// Return true for keywords which should be Miscellaneous, although the
|
||||||
// naive first-character-based classification suggests something else.
|
// naive first-character-based classification suggests something else.
|
||||||
static bool miscellaneous_exception(const std::string& keyword);
|
static bool miscellaneous_exception(const std::string& keyword);
|
||||||
|
@ -310,10 +310,6 @@ struct SummaryConfigContext {
|
|||||||
&& is_in_set(countkw, keyword.substr(1));
|
&& is_in_set(countkw, keyword.substr(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_liquid_phase(const std::string& keyword) {
|
|
||||||
return keyword == "WPIL";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_supported_region_to_region(const std::string& keyword)
|
bool is_supported_region_to_region(const std::string& keyword)
|
||||||
{
|
{
|
||||||
static const auto supported_kw = std::regex {
|
static const auto supported_kw = std::regex {
|
||||||
@ -363,39 +359,22 @@ struct SummaryConfigContext {
|
|||||||
|
|
||||||
bool is_connection_completion(const std::string& keyword)
|
bool is_connection_completion(const std::string& keyword)
|
||||||
{
|
{
|
||||||
if (keyword[0] != 'C')
|
static const auto conn_compl_kw = std::regex {
|
||||||
return false;
|
R"(C[OGW][IP][RT]L)"
|
||||||
|
};
|
||||||
|
|
||||||
if (keyword.back() != 'L')
|
return std::regex_match(keyword, conn_compl_kw);
|
||||||
return false;
|
|
||||||
|
|
||||||
if (is_udq(keyword))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (keyword.size() != 5)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_well_completion(const std::string& keyword)
|
bool is_well_completion(const std::string& keyword)
|
||||||
{
|
{
|
||||||
if (keyword[0] != 'W')
|
static const auto well_compl_kw = std::regex {
|
||||||
return false;
|
R"(W[OGWLV][PIGOLCF][RT]L([0-9_]{2}[0-9])?)"
|
||||||
|
};
|
||||||
|
|
||||||
if (keyword.back() != 'L')
|
// True, e.g., for WOPRL, WOPRL__8, WOPRL123, but not WOPRL___ or
|
||||||
return false;
|
// WKITL.
|
||||||
|
return std::regex_match(keyword, well_compl_kw);
|
||||||
if (is_liquid_phase(keyword))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (is_udq(keyword))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (keyword == "WMCTL")
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_node_keyword(const std::string& keyword)
|
bool is_node_keyword(const std::string& keyword)
|
||||||
@ -447,6 +426,21 @@ struct SummaryConfigContext {
|
|||||||
: SummaryConfigNode::Category::Group;
|
: SummaryConfigNode::Category::Group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SummaryConfigNode::Category
|
||||||
|
distinguish_connection_from_completion(const std::string& keyword)
|
||||||
|
{
|
||||||
|
return is_connection_completion(keyword)
|
||||||
|
? SummaryConfigNode::Category::Completion
|
||||||
|
: SummaryConfigNode::Category::Connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
SummaryConfigNode::Category
|
||||||
|
distinguish_well_from_completion(const std::string& keyword)
|
||||||
|
{
|
||||||
|
return is_well_completion(keyword)
|
||||||
|
? SummaryConfigNode::Category::Completion
|
||||||
|
: SummaryConfigNode::Category::Well;
|
||||||
|
}
|
||||||
|
|
||||||
void handleMissingWell( const ParseContext& parseContext, ErrorGuard& errors, const KeywordLocation& location, const std::string& well) {
|
void handleMissingWell( const ParseContext& parseContext, ErrorGuard& errors, const KeywordLocation& location, const std::string& well) {
|
||||||
std::string msg_fmt = fmt::format("Request for missing well {} in {{keyword}}\n"
|
std::string msg_fmt = fmt::format("Request for missing well {} in {{keyword}}\n"
|
||||||
@ -561,34 +555,36 @@ inline std::array< int, 3 > getijk( const DeckRecord& record ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void keywordCL( SummaryConfig::keyword_list& list,
|
inline void keywordCL(SummaryConfig::keyword_list& list,
|
||||||
const ParseContext& parseContext,
|
const ParseContext& parseContext,
|
||||||
ErrorGuard& errors,
|
ErrorGuard& errors,
|
||||||
const DeckKeyword& keyword,
|
const DeckKeyword& keyword,
|
||||||
const Schedule& schedule ,
|
const Schedule& schedule,
|
||||||
const GridDims& dims)
|
const GridDims& dims)
|
||||||
{
|
{
|
||||||
auto node = SummaryConfigNode{keyword.name(), SummaryConfigNode::Category::Connection, keyword.location()};
|
auto node = SummaryConfigNode {
|
||||||
node.parameterType( parseKeywordType(keyword.name()) );
|
keyword.name(), SummaryConfigNode::Category::Completion, keyword.location()
|
||||||
node.isUserDefined( is_udq(keyword.name()) );
|
}
|
||||||
|
.parameterType(parseKeywordType(keyword.name()))
|
||||||
|
.isUserDefined(is_udq(keyword.name()));
|
||||||
|
|
||||||
for (const auto& record : keyword) {
|
for (const auto& record : keyword) {
|
||||||
const auto& pattern = record.getItem(0).get<std::string>(0);
|
const auto& pattern = record.getItem(0).get<std::string>(0);
|
||||||
auto well_names = schedule.wellNames( pattern, schedule.size() - 1 );
|
auto well_names = schedule.wellNames(pattern, schedule.size() - 1);
|
||||||
|
|
||||||
|
if (well_names.empty()) {
|
||||||
|
handleMissingWell(parseContext, errors, keyword.location(), pattern);
|
||||||
|
}
|
||||||
|
|
||||||
if( well_names.empty() )
|
const auto ijk_defaulted = record.getItem(1).defaultApplied(0);
|
||||||
handleMissingWell( parseContext, errors, keyword.location(), pattern );
|
|
||||||
|
|
||||||
const auto ijk_defaulted = record.getItem( 1 ).defaultApplied( 0 );
|
|
||||||
for (const auto& wname : well_names) {
|
for (const auto& wname : well_names) {
|
||||||
const auto& well = schedule.getWellatEnd(wname);
|
const auto& well = schedule.getWellatEnd(wname);
|
||||||
const auto& all_connections = well.getConnections();
|
const auto& all_connections = well.getConnections();
|
||||||
|
|
||||||
node.namedEntity( wname );
|
node.namedEntity(wname);
|
||||||
if (ijk_defaulted) {
|
if (ijk_defaulted) {
|
||||||
for (const auto& conn : all_connections)
|
for (const auto& conn : all_connections)
|
||||||
list.push_back( node.number( 1 + conn.global_index()));
|
list.push_back(node.number(1 + conn.global_index()));
|
||||||
} else {
|
} else {
|
||||||
const auto& ijk = getijk(record);
|
const auto& ijk = getijk(record);
|
||||||
auto global_index = dims.getGlobalIndex(ijk[0], ijk[1], ijk[2]);
|
auto global_index = dims.getGlobalIndex(ijk[0], ijk[1], ijk[2]);
|
||||||
@ -599,7 +595,8 @@ inline void keywordCL( SummaryConfig::keyword_list& list,
|
|||||||
} else {
|
} else {
|
||||||
std::string msg = fmt::format("Problem with keyword {{keyword}}\n"
|
std::string msg = fmt::format("Problem with keyword {{keyword}}\n"
|
||||||
"In {{file}} line {{line}}\n"
|
"In {{file}} line {{line}}\n"
|
||||||
"Connection ({},{},{}) not defined for well {} ", ijk[0], ijk[1], ijk[2], wname);
|
"Connection ({},{},{}) not defined for well {}",
|
||||||
|
ijk[0] + 1, ijk[1] + 1, ijk[2] + 1, wname);
|
||||||
parseContext.handleError( ParseContext::SUMMARY_UNHANDLED_KEYWORD, msg, keyword.location(), errors);
|
parseContext.handleError( ParseContext::SUMMARY_UNHANDLED_KEYWORD, msg, keyword.location(), errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -607,37 +604,46 @@ inline void keywordCL( SummaryConfig::keyword_list& list,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void keywordWL( SummaryConfig::keyword_list& list,
|
inline void keywordWL(SummaryConfig::keyword_list& list,
|
||||||
const ParseContext& parseContext,
|
const ParseContext& parseContext,
|
||||||
ErrorGuard& errors,
|
ErrorGuard& errors,
|
||||||
const DeckKeyword& keyword,
|
const DeckKeyword& keyword,
|
||||||
const Schedule& schedule )
|
const Schedule& schedule)
|
||||||
{
|
{
|
||||||
for (const auto& record : keyword) {
|
for (const auto& record : keyword) {
|
||||||
const auto& pattern = record.getItem(0).get<std::string>(0);
|
const auto& pattern = record.getItem(0).get<std::string>(0);
|
||||||
const int completion = record.getItem(1).get<int>(0);
|
const auto well_names = schedule.wellNames(pattern, schedule.size() - 1);
|
||||||
auto well_names = schedule.wellNames( pattern, schedule.size() - 1 );
|
|
||||||
|
|
||||||
// We add the completion number both the extra field which contains
|
if (well_names.empty()) {
|
||||||
// parsed data from the keywordname - i.e. WOPRL__8 and also to the
|
handleMissingWell(parseContext, errors, keyword.location(), pattern);
|
||||||
// numeric member which will be written to the NUMS field.
|
continue;
|
||||||
auto node = SummaryConfigNode{ fmt::format("{}{:_>3}", keyword.name(), completion), SummaryConfigNode::Category::Well, keyword.location()};
|
}
|
||||||
node.parameterType( parseKeywordType(keyword.name()) );
|
|
||||||
node.isUserDefined( is_udq(keyword.name()) );
|
|
||||||
node.number(completion);
|
|
||||||
|
|
||||||
if( well_names.empty() )
|
const auto completion = record.getItem(1).get<int>(0);
|
||||||
handleMissingWell( parseContext, errors, keyword.location(), pattern );
|
|
||||||
|
// Use an amended KEYWORDS entry incorporating the completion ID,
|
||||||
|
// e.g. "WOPRL_12", for the W*L summary vectors. This is special
|
||||||
|
// case treatment for compatibility reasons as the more common entry
|
||||||
|
// here would be to just use "keyword.name()".
|
||||||
|
auto node = SummaryConfigNode {
|
||||||
|
fmt::format("{}{:_>3}", keyword.name(), completion),
|
||||||
|
SummaryConfigNode::Category::Completion, keyword.location()
|
||||||
|
}
|
||||||
|
.parameterType(parseKeywordType(keyword.name()))
|
||||||
|
.isUserDefined(is_udq(keyword.name()))
|
||||||
|
.number(completion);
|
||||||
|
|
||||||
for (const auto& wname : well_names) {
|
for (const auto& wname : well_names) {
|
||||||
const auto& well = schedule.getWellatEnd(wname);
|
if (schedule.getWellatEnd(wname).hasCompletion(completion)) {
|
||||||
if (well.hasCompletion(completion))
|
list.push_back(node.namedEntity(wname));
|
||||||
list.push_back( node.namedEntity( wname ) );
|
}
|
||||||
else {
|
else {
|
||||||
std::string msg = fmt::format("Problem with keyword {{keyword}}\n"
|
const auto msg = fmt::format("Problem with keyword {{keyword}}\n"
|
||||||
"In {{file}} line {{line}}\n"
|
"In {{file}} line {{line}}\n"
|
||||||
"Completion number {} not defined for well {} ", completion, wname);
|
"Completion number {} not defined for well {}",
|
||||||
parseContext.handleError( ParseContext::SUMMARY_UNHANDLED_KEYWORD, msg, keyword.location(), errors);
|
completion, wname);
|
||||||
|
parseContext.handleError(ParseContext::SUMMARY_UNHANDLED_KEYWORD,
|
||||||
|
msg, keyword.location(), errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1170,7 +1176,7 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
|
|||||||
|
|
||||||
void keywordSWithRecords(const std::size_t last_timestep,
|
void keywordSWithRecords(const std::size_t last_timestep,
|
||||||
const ParseContext& parseContext,
|
const ParseContext& parseContext,
|
||||||
ErrorGuard& errors,
|
ErrorGuard& errors,
|
||||||
const DeckKeyword& keyword,
|
const DeckKeyword& keyword,
|
||||||
const Schedule& schedule,
|
const Schedule& schedule,
|
||||||
SummaryConfig::keyword_list& list)
|
SummaryConfig::keyword_list& list)
|
||||||
@ -1213,7 +1219,7 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
|
|||||||
|
|
||||||
inline void keywordS(SummaryConfig::keyword_list& list,
|
inline void keywordS(SummaryConfig::keyword_list& list,
|
||||||
const ParseContext& parseContext,
|
const ParseContext& parseContext,
|
||||||
ErrorGuard& errors,
|
ErrorGuard& errors,
|
||||||
const DeckKeyword& keyword,
|
const DeckKeyword& keyword,
|
||||||
const Schedule& schedule)
|
const Schedule& schedule)
|
||||||
{
|
{
|
||||||
@ -1253,18 +1259,20 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string to_string(const SummaryConfigNode::Category cat) {
|
std::string to_string(const SummaryConfigNode::Category cat)
|
||||||
switch( cat ) {
|
{
|
||||||
case SummaryConfigNode::Category::Aquifer: return "Aquifer";
|
switch (cat) {
|
||||||
case SummaryConfigNode::Category::Well: return "Well";
|
case SummaryConfigNode::Category::Aquifer: return "Aquifer";
|
||||||
case SummaryConfigNode::Category::Group: return "Group";
|
case SummaryConfigNode::Category::Well: return "Well";
|
||||||
case SummaryConfigNode::Category::Field: return "Field";
|
case SummaryConfigNode::Category::Group: return "Group";
|
||||||
case SummaryConfigNode::Category::Region: return "Region";
|
case SummaryConfigNode::Category::Field: return "Field";
|
||||||
case SummaryConfigNode::Category::Block: return "Block";
|
case SummaryConfigNode::Category::Region: return "Region";
|
||||||
case SummaryConfigNode::Category::Connection: return "Connection";
|
case SummaryConfigNode::Category::Block: return "Block";
|
||||||
case SummaryConfigNode::Category::Segment: return "Segment";
|
case SummaryConfigNode::Category::Connection: return "Connection";
|
||||||
case SummaryConfigNode::Category::Node: return "Node";
|
case SummaryConfigNode::Category::Completion: return "Completion";
|
||||||
case SummaryConfigNode::Category::Miscellaneous: return "Miscellaneous";
|
case SummaryConfigNode::Category::Segment: return "Segment";
|
||||||
|
case SummaryConfigNode::Category::Node: return "Node";
|
||||||
|
case SummaryConfigNode::Category::Miscellaneous: return "Miscellaneous";
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::invalid_argument {
|
throw std::invalid_argument {
|
||||||
@ -1299,40 +1307,84 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void handleKW( SummaryConfig::keyword_list& list,
|
inline void handleKW( SummaryConfig::keyword_list& list,
|
||||||
SummaryConfigContext& context,
|
SummaryConfigContext& context,
|
||||||
const std::vector<std::string>& node_names,
|
const std::vector<std::string>& node_names,
|
||||||
const std::vector<int>& analyticAquiferIDs,
|
const std::vector<int>& analyticAquiferIDs,
|
||||||
const std::vector<int>& numericAquiferIDs,
|
const std::vector<int>& numericAquiferIDs,
|
||||||
const DeckKeyword& keyword,
|
const DeckKeyword& keyword,
|
||||||
const Schedule& schedule,
|
const Schedule& schedule,
|
||||||
const FieldPropsManager& field_props,
|
const FieldPropsManager& field_props,
|
||||||
const ParseContext& parseContext,
|
const ParseContext& parseContext,
|
||||||
ErrorGuard& errors,
|
ErrorGuard& errors,
|
||||||
const GridDims& dims) {
|
const GridDims& dims)
|
||||||
|
{
|
||||||
using Cat = SummaryConfigNode::Category;
|
using Cat = SummaryConfigNode::Category;
|
||||||
|
|
||||||
const auto& name = keyword.name();
|
const auto& name = keyword.name();
|
||||||
check_udq( keyword.location(), schedule, parseContext, errors );
|
check_udq(keyword.location(), schedule, parseContext, errors);
|
||||||
|
|
||||||
const auto cat = parseKeywordCategory( name );
|
const auto cat = parseKeywordCategory(name);
|
||||||
switch( cat ) {
|
switch (cat) {
|
||||||
case Cat::Well: return keywordW( list, parseContext, errors, keyword, schedule );
|
case Cat::Well:
|
||||||
case Cat::Group: return keywordG( list, parseContext, errors, keyword, schedule );
|
keywordW(list, parseContext, errors, keyword, schedule);
|
||||||
case Cat::Field: return keywordF( list, keyword );
|
break;
|
||||||
case Cat::Block: return keywordB( list, keyword, dims );
|
|
||||||
case Cat::Region: return keywordR( list, context, keyword, schedule, field_props, parseContext, errors );
|
|
||||||
case Cat::Connection: return keywordC( list, parseContext, errors, keyword, schedule, dims);
|
|
||||||
case Cat::Segment: return keywordS( list, parseContext, errors, keyword, schedule );
|
|
||||||
case Cat::Node: return keyword_node( list, node_names, parseContext, errors, keyword );
|
|
||||||
case Cat::Aquifer: return keywordAquifer(list, analyticAquiferIDs, numericAquiferIDs, parseContext, errors, keyword);
|
|
||||||
case Cat::Miscellaneous: return keywordMISC( list, keyword );
|
|
||||||
|
|
||||||
default:
|
case Cat::Group:
|
||||||
std::string msg_fmt = fmt::format("Summary output keyword {{keyword}} of type {} is not supported\n"
|
keywordG(list, parseContext, errors, keyword, schedule);
|
||||||
"In {{file}} line {{line}}", to_string(cat));
|
break;
|
||||||
parseContext.handleError(ParseContext::SUMMARY_UNHANDLED_KEYWORD, msg_fmt, keyword.location(), errors);
|
|
||||||
return;
|
case Cat::Field:
|
||||||
|
keywordF(list, keyword);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cat::Block:
|
||||||
|
keywordB(list, keyword, dims);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cat::Region:
|
||||||
|
keywordR(list, context, keyword, schedule, field_props, parseContext, errors);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cat::Connection:
|
||||||
|
keywordC(list, parseContext, errors, keyword, schedule, dims);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cat::Completion:
|
||||||
|
if (is_well_completion(name)) {
|
||||||
|
keywordWL(list, parseContext, errors, keyword, schedule);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
keywordCL(list, parseContext, errors, keyword, schedule, dims);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cat::Segment:
|
||||||
|
keywordS(list, parseContext, errors, keyword, schedule);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cat::Node:
|
||||||
|
keyword_node(list, node_names, parseContext, errors, keyword);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cat::Aquifer:
|
||||||
|
keywordAquifer(list, analyticAquiferIDs, numericAquiferIDs, parseContext, errors, keyword);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cat::Miscellaneous:
|
||||||
|
keywordMISC(list, keyword);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
const auto msg_fmt = fmt::format("Summary output keyword {{keyword}} of "
|
||||||
|
"type {} is not supported\n"
|
||||||
|
"In {{file}} line {{line}}",
|
||||||
|
to_string(cat));
|
||||||
|
|
||||||
|
parseContext.handleError(ParseContext::SUMMARY_UNHANDLED_KEYWORD,
|
||||||
|
msg_fmt, keyword.location(), errors);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1343,24 +1395,45 @@ inline void handleKW( SummaryConfig::keyword_list& list,
|
|||||||
const KeywordLocation& location,
|
const KeywordLocation& location,
|
||||||
const Schedule& schedule,
|
const Schedule& schedule,
|
||||||
const ParseContext& /* parseContext */,
|
const ParseContext& /* parseContext */,
|
||||||
ErrorGuard& /* errors */) {
|
ErrorGuard& /* errors */)
|
||||||
|
{
|
||||||
|
if (is_udq(keyword)) {
|
||||||
if (is_udq(keyword))
|
throw std::logic_error {
|
||||||
throw std::logic_error("UDQ keywords not handleded when expanding alias list");
|
"UDQ keywords not handleded when expanding alias list"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
using Cat = SummaryConfigNode::Category;
|
using Cat = SummaryConfigNode::Category;
|
||||||
const auto cat = parseKeywordCategory( keyword );
|
const auto cat = parseKeywordCategory(keyword);
|
||||||
|
|
||||||
switch( cat ) {
|
switch (cat) {
|
||||||
case Cat::Well: return keywordW( list, keyword, location, schedule );
|
case Cat::Well:
|
||||||
case Cat::Group: return keywordG( list, keyword, location, schedule );
|
keywordW(list, keyword, location, schedule);
|
||||||
case Cat::Field: return keywordF( list, keyword, location );
|
break;
|
||||||
case Cat::Aquifer: return keywordAquifer( list, keyword, analyticAquiferIDs, numericAquiferIDs, location );
|
|
||||||
case Cat::Miscellaneous: return keywordMISC( list, keyword, location);
|
|
||||||
|
|
||||||
default:
|
case Cat::Group:
|
||||||
throw std::logic_error("Keyword type: " + to_string( cat ) + " is not supported in alias lists. Internal error handling: " + keyword);
|
keywordG(list, keyword, location, schedule);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cat::Field:
|
||||||
|
keywordF(list, keyword, location);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cat::Aquifer:
|
||||||
|
keywordAquifer(list, keyword, analyticAquiferIDs,
|
||||||
|
numericAquiferIDs, location);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cat::Miscellaneous:
|
||||||
|
keywordMISC(list, keyword, location);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::logic_error {
|
||||||
|
fmt::format("Keyword type {} is not supported in alias "
|
||||||
|
"lists. Internal error handling keyword {}",
|
||||||
|
to_string(cat), keyword)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1395,7 +1468,8 @@ inline void handleKW( SummaryConfig::keyword_list& list,
|
|||||||
|
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
|
|
||||||
SummaryConfigNode::Type parseKeywordType(std::string keyword) {
|
SummaryConfigNode::Type parseKeywordType(std::string keyword)
|
||||||
|
{
|
||||||
if (is_well_completion(keyword))
|
if (is_well_completion(keyword))
|
||||||
keyword.pop_back();
|
keyword.pop_back();
|
||||||
|
|
||||||
@ -1413,20 +1487,21 @@ SummaryConfigNode::Type parseKeywordType(std::string keyword) {
|
|||||||
return SummaryConfigNode::Type::Undefined;
|
return SummaryConfigNode::Type::Undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
SummaryConfigNode::Category parseKeywordCategory(const std::string& keyword) {
|
SummaryConfigNode::Category parseKeywordCategory(const std::string& keyword)
|
||||||
|
{
|
||||||
using Cat = SummaryConfigNode::Category;
|
using Cat = SummaryConfigNode::Category;
|
||||||
|
|
||||||
if (is_special(keyword)) { return Cat::Miscellaneous; }
|
if (is_special(keyword)) { return Cat::Miscellaneous; }
|
||||||
|
|
||||||
switch (keyword[0]) {
|
switch (keyword[0]) {
|
||||||
case 'A': if (is_aquifer(keyword)) return Cat::Aquifer; break;
|
case 'A': if (is_aquifer(keyword)) return Cat::Aquifer; break;
|
||||||
case 'W': return Cat::Well;
|
case 'W': return distinguish_well_from_completion(keyword);
|
||||||
case 'G': return distinguish_group_from_node(keyword);
|
case 'G': return distinguish_group_from_node(keyword);
|
||||||
case 'F': return Cat::Field;
|
case 'F': return Cat::Field;
|
||||||
case 'C': return Cat::Connection;
|
case 'C': return distinguish_connection_from_completion(keyword);
|
||||||
case 'R': return Cat::Region;
|
case 'R': return Cat::Region;
|
||||||
case 'B': return Cat::Block;
|
case 'B': return Cat::Block;
|
||||||
case 'S': return Cat::Segment;
|
case 'S': return Cat::Segment;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TCPU, MLINEARS, NEWTON, &c
|
// TCPU, MLINEARS, NEWTON, &c
|
||||||
@ -1434,10 +1509,10 @@ SummaryConfigNode::Category parseKeywordCategory(const std::string& keyword) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SummaryConfigNode::SummaryConfigNode(std::string keyword, const Category cat, KeywordLocation loc_arg) :
|
SummaryConfigNode::SummaryConfigNode(std::string keyword, const Category cat, KeywordLocation loc_arg)
|
||||||
keyword_(std::move(keyword)),
|
: keyword_ (std::move(keyword))
|
||||||
category_(cat),
|
, category_(cat)
|
||||||
loc(std::move(loc_arg))
|
, loc (std::move(loc_arg))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
SummaryConfigNode SummaryConfigNode::serializeObject()
|
SummaryConfigNode SummaryConfigNode::serializeObject()
|
||||||
@ -1503,6 +1578,7 @@ std::string SummaryConfigNode::uniqueNodeKey() const
|
|||||||
return this->keyword() + ':' + std::to_string(this->number());
|
return this->keyword() + ':' + std::to_string(this->number());
|
||||||
|
|
||||||
case SummaryConfigNode::Category::Connection: [[fallthrough]];
|
case SummaryConfigNode::Category::Connection: [[fallthrough]];
|
||||||
|
case SummaryConfigNode::Category::Completion: [[fallthrough]];
|
||||||
case SummaryConfigNode::Category::Segment:
|
case SummaryConfigNode::Category::Segment:
|
||||||
return this->keyword() + ':' + this->namedEntity() + ':' + std::to_string(this->number());
|
return this->keyword() + ':' + this->namedEntity() + ':' + std::to_string(this->number());
|
||||||
}
|
}
|
||||||
@ -1538,6 +1614,7 @@ bool operator==(const SummaryConfigNode& lhs, const SummaryConfigNode& rhs)
|
|||||||
return lhs.number() == rhs.number();
|
return lhs.number() == rhs.number();
|
||||||
|
|
||||||
case SummaryConfigNode::Category::Connection: [[fallthrough]];
|
case SummaryConfigNode::Category::Connection: [[fallthrough]];
|
||||||
|
case SummaryConfigNode::Category::Completion: [[fallthrough]];
|
||||||
case SummaryConfigNode::Category::Segment:
|
case SummaryConfigNode::Category::Segment:
|
||||||
// Equal if associated to same numeric
|
// Equal if associated to same numeric
|
||||||
// sub-entity of same named entity
|
// sub-entity of same named entity
|
||||||
@ -1575,6 +1652,7 @@ bool operator<(const SummaryConfigNode& lhs, const SummaryConfigNode& rhs)
|
|||||||
return lhs.number() < rhs.number();
|
return lhs.number() < rhs.number();
|
||||||
|
|
||||||
case SummaryConfigNode::Category::Connection: [[fallthrough]];
|
case SummaryConfigNode::Category::Connection: [[fallthrough]];
|
||||||
|
case SummaryConfigNode::Category::Completion: [[fallthrough]];
|
||||||
case SummaryConfigNode::Category::Segment:
|
case SummaryConfigNode::Category::Segment:
|
||||||
{
|
{
|
||||||
// Ordering determined by pair of named entity and numeric ID.
|
// Ordering determined by pair of named entity and numeric ID.
|
||||||
|
@ -18,9 +18,12 @@
|
|||||||
|
|
||||||
#include <opm/io/eclipse/ESmry.hpp>
|
#include <opm/io/eclipse/ESmry.hpp>
|
||||||
|
|
||||||
|
#include <opm/io/eclipse/SummaryNode.hpp>
|
||||||
|
|
||||||
#include <opm/common/ErrorMacros.hpp>
|
#include <opm/common/ErrorMacros.hpp>
|
||||||
#include <opm/common/utility/shmatch.hpp>
|
#include <opm/common/utility/shmatch.hpp>
|
||||||
#include <opm/common/utility/TimeService.hpp>
|
#include <opm/common/utility/TimeService.hpp>
|
||||||
|
|
||||||
#include <opm/io/eclipse/EclFile.hpp>
|
#include <opm/io/eclipse/EclFile.hpp>
|
||||||
#include <opm/io/eclipse/EclUtil.hpp>
|
#include <opm/io/eclipse/EclUtil.hpp>
|
||||||
#include <opm/io/eclipse/EclOutput.hpp>
|
#include <opm/io/eclipse/EclOutput.hpp>
|
||||||
@ -34,10 +37,13 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <regex>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
@ -46,19 +52,21 @@
|
|||||||
KEYWORDS WGNAMES NUMS | PARAM index Corresponding ERT key
|
KEYWORDS WGNAMES NUMS | PARAM index Corresponding ERT key
|
||||||
---------------------------------------------------+--------------------------------------------------
|
---------------------------------------------------+--------------------------------------------------
|
||||||
WGOR OP_1 0 | 0 WGOR:OP_1
|
WGOR OP_1 0 | 0 WGOR:OP_1
|
||||||
FOPT :+:+:+:+ 0 | 1 FOPT
|
WOPRL__1 OP_1 1 | 1 WOPRL:OP_1:1 -- KEYWORDS is strictly speaking "WOPRL__1" here.
|
||||||
WWCT OP_1 0 | 2 WWCT:OP_1
|
FOPT :+:+:+:+ 0 | 2 FOPT
|
||||||
WIR OP_1 0 | 3 WIR:OP_1
|
WWCT OP_1 0 | 3 WWCT:OP_1
|
||||||
WGOR WI_1 0 | 4 WWCT:OP_1
|
WIR OP_1 0 | 4 WIR:OP_1
|
||||||
WWCT W1_1 0 | 5 WWCT:WI_1
|
WGOR WI_1 0 | 5 WWCT:OP_1
|
||||||
BPR :+:+:+:+ 12675 | 6 BPR:12675, BPR:i,j,k
|
WWCT W1_1 0 | 6 WWCT:WI_1
|
||||||
RPR :+:+:+:+ 1 | 7 RPR:1
|
BPR :+:+:+:+ 12675 | 7 BPR:12675, BPR:i,j,k
|
||||||
FOPT :+:+:+:+ 0 | 8 FOPT
|
RPR :+:+:+:+ 1 | 8 RPR:1
|
||||||
GGPR NORTH 0 | 9 GGPR:NORTH
|
FOPT :+:+:+:+ 0 | 9 FOPT
|
||||||
COPR OP_1 5628 | 10 COPR:OP_1:56286, COPR:OP_1:i,j,k
|
GGPR NORTH 0 | 10 GGPR:NORTH
|
||||||
RXF :+:+:+:+ R1 + 32768*(R2 + 10) | 11 RXF:2-3
|
COPR OP_1 5628 | 11 COPR:OP_1:56286, COPR:OP_1:i,j,k
|
||||||
SOFX OP_1 12675 | 12 SOFX:OP_1:12675, SOFX:OP_1:i,j,jk
|
COPRL OP_1 5628 | 12 COPRL:OP_1:5628, COPRL:OP_1:i,j,k
|
||||||
AAQX :+:+:+:+ 12 | 13 AAQX:12
|
RXF :+:+:+:+ R1 + 32768*(R2 + 10) | 13 RXF:2-3
|
||||||
|
SOFX OP_1 12675 | 14 SOFX:OP_1:12675, SOFX:OP_1:i,j,jk
|
||||||
|
AAQX :+:+:+:+ 12 | 15 AAQX:12
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -85,6 +93,25 @@ Opm::time_point make_date(const std::vector<int>& datetime) {
|
|||||||
return Opm::TimeService::from_time_t( Opm::asTimeT(ts) );
|
return Opm::TimeService::from_time_t( Opm::asTimeT(ts) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_connection_completion(const std::string& keyword)
|
||||||
|
{
|
||||||
|
static const auto conn_compl_kw = std::regex {
|
||||||
|
R"(C[OGW][IP][RT]L)"
|
||||||
|
};
|
||||||
|
|
||||||
|
return std::regex_match(keyword, conn_compl_kw);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_well_completion(const std::string& keyword)
|
||||||
|
{
|
||||||
|
static const auto well_compl_kw = std::regex {
|
||||||
|
R"(W[OGWLV][PIGOLCF][RT]L([0-9_]{2}[0-9])?)"
|
||||||
|
};
|
||||||
|
|
||||||
|
// True, e.g., for WOPRL, WOPRL__8, WOPRL123, but not WOPRL___ or
|
||||||
|
// WKITL.
|
||||||
|
return std::regex_match(keyword, well_compl_kw);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,13 +213,17 @@ ESmry::ESmry(const std::string &filename, bool loadBaseRunData) :
|
|||||||
for (unsigned int i=0; i<keywords.size(); i++) {
|
for (unsigned int i=0; i<keywords.size(); i++) {
|
||||||
|
|
||||||
Opm::EclIO::lgr_info lgr { lgrs[i], {numlx[i], numly[i], numlz[i]}};
|
Opm::EclIO::lgr_info lgr { lgrs[i], {numlx[i], numly[i], numlz[i]}};
|
||||||
const std::string keyString = makeKeyString(keywords[i], wgnames[i], nums[i], lgr);
|
|
||||||
|
const auto category = SummaryNode::category_from_keyword(keywords[i]);
|
||||||
|
const auto normKw = SummaryNode::normalise_keyword(category, keywords[i]);
|
||||||
|
|
||||||
|
const std::string keyString = makeKeyString(normKw, wgnames[i], nums[i], lgr);
|
||||||
combindKeyList.push_back(keyString);
|
combindKeyList.push_back(keyString);
|
||||||
|
|
||||||
if (! keyString.empty()) {
|
if (! keyString.empty()) {
|
||||||
summaryNodes.push_back( {
|
summaryNodes.push_back( {
|
||||||
keywords[i],
|
normKw,
|
||||||
SummaryNode::category_from_keyword(keywords[i]),
|
category,
|
||||||
SummaryNode::Type::Undefined,
|
SummaryNode::Type::Undefined,
|
||||||
wgnames[i],
|
wgnames[i],
|
||||||
nums[i],
|
nums[i],
|
||||||
@ -207,13 +238,16 @@ ESmry::ESmry(const std::string &filename, bool loadBaseRunData) :
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
for (unsigned int i=0; i<keywords.size(); i++) {
|
for (unsigned int i=0; i<keywords.size(); i++) {
|
||||||
const std::string keyString = makeKeyString(keywords[i], wgnames[i], nums[i], {});
|
const auto category = SummaryNode::category_from_keyword(keywords[i]);
|
||||||
|
const auto normKw = SummaryNode::normalise_keyword(category, keywords[i]);
|
||||||
|
|
||||||
|
const std::string keyString = makeKeyString(normKw, wgnames[i], nums[i], {});
|
||||||
combindKeyList.push_back(keyString);
|
combindKeyList.push_back(keyString);
|
||||||
|
|
||||||
if (! keyString.empty()) {
|
if (! keyString.empty()) {
|
||||||
summaryNodes.push_back( {
|
summaryNodes.push_back( {
|
||||||
keywords[i],
|
normKw,
|
||||||
SummaryNode::category_from_keyword(keywords[i]),
|
category,
|
||||||
SummaryNode::Type::Undefined,
|
SummaryNode::Type::Undefined,
|
||||||
wgnames[i],
|
wgnames[i],
|
||||||
nums[i],
|
nums[i],
|
||||||
@ -317,14 +351,17 @@ ESmry::ESmry(const std::string &filename, bool loadBaseRunData) :
|
|||||||
if (have_lgr) {
|
if (have_lgr) {
|
||||||
for (size_t i = 0; i < keywords.size(); i++) {
|
for (size_t i = 0; i < keywords.size(); i++) {
|
||||||
Opm::EclIO::lgr_info lgr { lgrs[i], {numlx[i], numly[i], numlz[i]}};
|
Opm::EclIO::lgr_info lgr { lgrs[i], {numlx[i], numly[i], numlz[i]}};
|
||||||
const std::string keyString = makeKeyString(keywords[i], wgnames[i], nums[i], lgr);
|
|
||||||
|
|
||||||
|
const auto category = SummaryNode::category_from_keyword(keywords[i]);
|
||||||
|
const auto normKw = SummaryNode::normalise_keyword(category, keywords[i]);
|
||||||
|
|
||||||
|
const std::string keyString = makeKeyString(normKw, wgnames[i], nums[i], lgr);
|
||||||
combindKeyList.push_back(keyString);
|
combindKeyList.push_back(keyString);
|
||||||
|
|
||||||
if (! keyString.empty()) {
|
if (! keyString.empty()) {
|
||||||
summaryNodes.push_back( {
|
summaryNodes.push_back( {
|
||||||
keywords[i],
|
normKw,
|
||||||
SummaryNode::category_from_keyword(keywords[i]),
|
category,
|
||||||
SummaryNode::Type::Undefined,
|
SummaryNode::Type::Undefined,
|
||||||
wgnames[i],
|
wgnames[i],
|
||||||
nums[i],
|
nums[i],
|
||||||
@ -341,13 +378,16 @@ ESmry::ESmry(const std::string &filename, bool loadBaseRunData) :
|
|||||||
|
|
||||||
for (size_t i = 0; i < keywords.size(); i++) {
|
for (size_t i = 0; i < keywords.size(); i++) {
|
||||||
|
|
||||||
const std::string keyString = makeKeyString(keywords[i], wgnames[i], nums[i], {});
|
const auto category = SummaryNode::category_from_keyword(keywords[i]);
|
||||||
|
const auto normKw = SummaryNode::normalise_keyword(category, keywords[i]);
|
||||||
|
|
||||||
|
const std::string keyString = makeKeyString(normKw, wgnames[i], nums[i], {});
|
||||||
combindKeyList.push_back(keyString);
|
combindKeyList.push_back(keyString);
|
||||||
|
|
||||||
if (! keyString.empty()) {
|
if (! keyString.empty()) {
|
||||||
summaryNodes.push_back( {
|
summaryNodes.push_back( {
|
||||||
keywords[i],
|
normKw,
|
||||||
SummaryNode::category_from_keyword(keywords[i]),
|
category,
|
||||||
SummaryNode::Type::Undefined,
|
SummaryNode::Type::Undefined,
|
||||||
wgnames[i],
|
wgnames[i],
|
||||||
nums[i],
|
nums[i],
|
||||||
@ -425,13 +465,16 @@ ESmry::ESmry(const std::string &filename, bool loadBaseRunData) :
|
|||||||
if (have_lgr) {
|
if (have_lgr) {
|
||||||
for (size_t i=0; i < keywords.size(); i++) {
|
for (size_t i=0; i < keywords.size(); i++) {
|
||||||
Opm::EclIO::lgr_info lgr { lgrs[i], {numlx[i], numly[i], numlz[i]}};
|
Opm::EclIO::lgr_info lgr { lgrs[i], {numlx[i], numly[i], numlz[i]}};
|
||||||
const std::string keyw = makeKeyString(keywords[i], wgnames[i], nums[i], lgr);
|
|
||||||
|
const auto normKw = SummaryNode::normalise_keyword(keywords[i]);
|
||||||
|
const std::string keyw = makeKeyString(normKw, wgnames[i], nums[i], lgr);
|
||||||
if ((!keyw.empty()) && (keywList.find(keyw) != keywList.end()))
|
if ((!keyw.empty()) && (keywList.find(keyw) != keywList.end()))
|
||||||
arrayPos[specInd][keyIndex[keyw]]=i;
|
arrayPos[specInd][keyIndex[keyw]]=i;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (size_t i=0; i < keywords.size(); i++) {
|
for (size_t i=0; i < keywords.size(); i++) {
|
||||||
const std::string keyw = makeKeyString(keywords[i], wgnames[i], nums[i], {});
|
const auto normKw = SummaryNode::normalise_keyword(keywords[i]);
|
||||||
|
const std::string keyw = makeKeyString(normKw, wgnames[i], nums[i], {});
|
||||||
if ((!keyw.empty()) && (keywList.find(keyw) != keywList.end()))
|
if ((!keyw.empty()) && (keywList.find(keyw) != keywList.end()))
|
||||||
arrayPos[specInd][keyIndex[keyw]]=i;
|
arrayPos[specInd][keyIndex[keyw]]=i;
|
||||||
}
|
}
|
||||||
@ -1251,6 +1294,10 @@ std::string ESmry::makeKeyString(const std::string& keywordArg, const std::strin
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_well_completion(keywordArg)) {
|
||||||
|
return fmt::format("{}:{}:{}", keywordArg, wgname, num);
|
||||||
|
}
|
||||||
|
|
||||||
return fmt::format("{}:{}", keywordArg, wgname);
|
return fmt::format("{}:{}", keywordArg, wgname);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1259,16 +1306,20 @@ std::string ESmry::makeKeyString(const std::string& keywordArg, const std::strin
|
|||||||
|
|
||||||
std::string ESmry::unpackNumber(const SummaryNode& node) const
|
std::string ESmry::unpackNumber(const SummaryNode& node) const
|
||||||
{
|
{
|
||||||
if (node.category == SummaryNode::Category::Block ||
|
if ((node.category == SummaryNode::Category::Block) ||
|
||||||
node.category == SummaryNode::Category::Connection) {
|
(node.category == SummaryNode::Category::Connection) ||
|
||||||
|
((node.category == SummaryNode::Category::Completion) &&
|
||||||
|
is_connection_completion(node.keyword)))
|
||||||
|
{
|
||||||
int _i,_j,_k;
|
int _i,_j,_k;
|
||||||
ijk_from_global_index(node.number, _i, _j, _k);
|
ijk_from_global_index(node.number, _i, _j, _k);
|
||||||
|
|
||||||
return fmt::format("{},{},{}", _i, _j, _k);
|
return fmt::format("{},{},{}", _i, _j, _k);
|
||||||
}
|
}
|
||||||
else if (node.category == SummaryNode::Category::Region && node.keyword[2] == 'F') {
|
else if ((node.category == SummaryNode::Category::Region) &&
|
||||||
const auto r1 = node.number % (1 << 15);
|
(node.keyword[2] == 'F'))
|
||||||
const auto r2 = (node.number / (1 << 15)) - 10;
|
{
|
||||||
|
const auto& [r1, r2] = splitSummaryNumber(node.number);
|
||||||
|
|
||||||
return fmt::format("{}-{}", r1, r2);
|
return fmt::format("{}-{}", r1, r2);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ constexpr bool use_number(Opm::EclIO::SummaryNode::Category category) {
|
|||||||
case Opm::EclIO::SummaryNode::Category::Aquifer: [[fallthrough]];
|
case Opm::EclIO::SummaryNode::Category::Aquifer: [[fallthrough]];
|
||||||
case Opm::EclIO::SummaryNode::Category::Block: [[fallthrough]];
|
case Opm::EclIO::SummaryNode::Category::Block: [[fallthrough]];
|
||||||
case Opm::EclIO::SummaryNode::Category::Connection: [[fallthrough]];
|
case Opm::EclIO::SummaryNode::Category::Connection: [[fallthrough]];
|
||||||
|
case Opm::EclIO::SummaryNode::Category::Completion: [[fallthrough]];
|
||||||
case Opm::EclIO::SummaryNode::Category::Region: [[fallthrough]];
|
case Opm::EclIO::SummaryNode::Category::Region: [[fallthrough]];
|
||||||
case Opm::EclIO::SummaryNode::Category::Segment:
|
case Opm::EclIO::SummaryNode::Category::Segment:
|
||||||
return true;
|
return true;
|
||||||
@ -50,6 +51,7 @@ constexpr bool use_number(Opm::EclIO::SummaryNode::Category category) {
|
|||||||
constexpr bool use_name(Opm::EclIO::SummaryNode::Category category) {
|
constexpr bool use_name(Opm::EclIO::SummaryNode::Category category) {
|
||||||
switch (category) {
|
switch (category) {
|
||||||
case Opm::EclIO::SummaryNode::Category::Connection: [[fallthrough]];
|
case Opm::EclIO::SummaryNode::Category::Connection: [[fallthrough]];
|
||||||
|
case Opm::EclIO::SummaryNode::Category::Completion: [[fallthrough]];
|
||||||
case Opm::EclIO::SummaryNode::Category::Group: [[fallthrough]];
|
case Opm::EclIO::SummaryNode::Category::Group: [[fallthrough]];
|
||||||
case Opm::EclIO::SummaryNode::Category::Segment: [[fallthrough]];
|
case Opm::EclIO::SummaryNode::Category::Segment: [[fallthrough]];
|
||||||
case Opm::EclIO::SummaryNode::Category::Node: [[fallthrough]];
|
case Opm::EclIO::SummaryNode::Category::Node: [[fallthrough]];
|
||||||
@ -80,6 +82,25 @@ bool is_node_keyword(const std::string& keyword)
|
|||||||
return node_kw.find(keyword) != node_kw.end();
|
return node_kw.find(keyword) != node_kw.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_connection_completion(const std::string& keyword)
|
||||||
|
{
|
||||||
|
static const auto conn_compl_kw = std::regex {
|
||||||
|
R"(C[OGW][IP][RT]L)"
|
||||||
|
};
|
||||||
|
|
||||||
|
return std::regex_match(keyword, conn_compl_kw);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_well_completion(const std::string& keyword)
|
||||||
|
{
|
||||||
|
static const auto well_compl_kw = std::regex {
|
||||||
|
R"(W[OGWLV][PIGOLCF][RT]L([0-9_]{2}[0-9])?)"
|
||||||
|
};
|
||||||
|
|
||||||
|
// True, e.g., for WOPRL, WOPRL__8, WOPRL123, but not WOPRL___ or WKITL.
|
||||||
|
return std::regex_match(keyword, well_compl_kw);
|
||||||
|
}
|
||||||
|
|
||||||
Opm::EclIO::SummaryNode::Category
|
Opm::EclIO::SummaryNode::Category
|
||||||
distinguish_group_from_node(const std::string& keyword)
|
distinguish_group_from_node(const std::string& keyword)
|
||||||
{
|
{
|
||||||
@ -87,10 +108,42 @@ distinguish_group_from_node(const std::string& keyword)
|
|||||||
? Opm::EclIO::SummaryNode::Category::Node
|
? Opm::EclIO::SummaryNode::Category::Node
|
||||||
: Opm::EclIO::SummaryNode::Category::Group;
|
: Opm::EclIO::SummaryNode::Category::Group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Opm::EclIO::SummaryNode::Category
|
||||||
|
distinguish_connection_from_completion(const std::string& keyword)
|
||||||
|
{
|
||||||
|
return is_connection_completion(keyword)
|
||||||
|
? Opm::EclIO::SummaryNode::Category::Completion
|
||||||
|
: Opm::EclIO::SummaryNode::Category::Connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Opm::EclIO::SummaryNode::unique_key(number_renderer render_number) const {
|
Opm::EclIO::SummaryNode::Category
|
||||||
std::vector<std::string> key_parts { keyword } ;
|
distinguish_well_from_completion(const std::string& keyword)
|
||||||
|
{
|
||||||
|
return is_well_completion(keyword)
|
||||||
|
? Opm::EclIO::SummaryNode::Category::Completion
|
||||||
|
: Opm::EclIO::SummaryNode::Category::Well;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string normalise_well_completion_keyword(const std::string& keyword)
|
||||||
|
{
|
||||||
|
static const auto well_compl_kw = std::regex {
|
||||||
|
R"((W[OGWLV][PIGOLCF][RT]L)([0-9_]{2}[0-9])?)"
|
||||||
|
};
|
||||||
|
|
||||||
|
auto keywordPieces = std::smatch {};
|
||||||
|
if (std::regex_match(keyword, keywordPieces, well_compl_kw)) {
|
||||||
|
return keywordPieces[1].str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
std::string Opm::EclIO::SummaryNode::unique_key(number_renderer render_number) const
|
||||||
|
{
|
||||||
|
auto key_parts = std::vector<std::string> { normalise_keyword(this->category, this->keyword) };
|
||||||
|
|
||||||
if (auto opt = display_name())
|
if (auto opt = display_name())
|
||||||
key_parts.emplace_back(opt.value());
|
key_parts.emplace_back(opt.value());
|
||||||
@ -146,43 +199,50 @@ bool Opm::EclIO::SummaryNode::is_user_defined() const {
|
|||||||
return matched && !blacklisted;
|
return matched && !blacklisted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Observe that this function started out as a slight generalisation of the
|
Observe that this function started out as a slight generalisation of the
|
||||||
special case handling of segment variables; i.e. variables starting with 'S'.
|
special case handling of segment variables; i.e. variables starting with 'S'.
|
||||||
In general there are many other expecptions e.g. 'NEWTON' is an Miscellaneous
|
In general there are many other expecptions e.g. 'NEWTON' is an Miscellaneous
|
||||||
variable and not a network variable - but they will be added when/if required.
|
variable and not a network variable - but they will be added when/if required.
|
||||||
*/
|
*/
|
||||||
bool Opm::EclIO::SummaryNode::miscellaneous_exception(const std::string& keyword) {
|
bool Opm::EclIO::SummaryNode::miscellaneous_exception(const std::string& keyword)
|
||||||
|
{
|
||||||
static const std::unordered_set<std::string> miscellaneous_keywords = {"SEPARATE", "STEPTYPE", "SUMTHIN"};
|
static const std::unordered_set<std::string> miscellaneous_keywords = {"SEPARATE", "STEPTYPE", "SUMTHIN"};
|
||||||
return miscellaneous_keywords.count(keyword) == 1;
|
return miscellaneous_keywords.count(keyword) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Opm::EclIO::SummaryNode::Category
|
||||||
Opm::EclIO::SummaryNode::Category Opm::EclIO::SummaryNode::category_from_keyword(
|
Opm::EclIO::SummaryNode::category_from_keyword(const std::string& keyword)
|
||||||
const std::string& keyword
|
{
|
||||||
) {
|
if (keyword.empty() ||
|
||||||
static const std::unordered_set<std::string> miscellaneous_keywords = {"SEPARATE", "STEPTYPE", "SUMTHIN"};
|
Opm::EclIO::SummaryNode::miscellaneous_exception(keyword))
|
||||||
if (keyword.length() == 0) {
|
{
|
||||||
return Category::Miscellaneous;
|
return Category::Miscellaneous;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Opm::EclIO::SummaryNode::miscellaneous_exception(keyword))
|
|
||||||
return Category::Miscellaneous;
|
|
||||||
|
|
||||||
switch (keyword[0]) {
|
switch (keyword[0]) {
|
||||||
case 'A': return Category::Aquifer;
|
case 'A': return Category::Aquifer;
|
||||||
case 'B': return Category::Block;
|
case 'B': return Category::Block;
|
||||||
case 'C': return Category::Connection;
|
case 'C': return distinguish_connection_from_completion(keyword);
|
||||||
case 'F': return Category::Field;
|
case 'F': return Category::Field;
|
||||||
case 'G': return distinguish_group_from_node(keyword);
|
case 'G': return distinguish_group_from_node(keyword);
|
||||||
case 'R': return Category::Region;
|
case 'R': return Category::Region;
|
||||||
case 'S': return Category::Segment;
|
case 'S': return Category::Segment;
|
||||||
case 'W': return Category::Well;
|
case 'W': return distinguish_well_from_completion(keyword);
|
||||||
default: return Category::Miscellaneous;
|
default: return Category::Miscellaneous;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Opm::EclIO::SummaryNode::normalise_keyword(const Opm::EclIO::SummaryNode::Category category,
|
||||||
|
const std::string& keyword)
|
||||||
|
{
|
||||||
|
return ((category == Opm::EclIO::SummaryNode::Category::Completion) &&
|
||||||
|
is_well_completion(keyword))
|
||||||
|
? normalise_well_completion_keyword(keyword)
|
||||||
|
: keyword;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::string> Opm::EclIO::SummaryNode::display_name() const {
|
std::optional<std::string> Opm::EclIO::SummaryNode::display_name() const {
|
||||||
if (use_name(category)) {
|
if (use_name(category)) {
|
||||||
return wgname;
|
return wgname;
|
||||||
|
@ -99,6 +99,7 @@ template <> struct fmt::formatter<Opm::EclIO::SummaryNode::Category>: fmt::forma
|
|||||||
case Category::Region: name = "Region"; break;
|
case Category::Region: name = "Region"; break;
|
||||||
case Category::Block: name = "Block"; break;
|
case Category::Block: name = "Block"; break;
|
||||||
case Category::Connection: name = "Connection"; break;
|
case Category::Connection: name = "Connection"; break;
|
||||||
|
case Category::Completion: name = "Completion"; break;
|
||||||
case Category::Segment: name = "Segment"; break;
|
case Category::Segment: name = "Segment"; break;
|
||||||
case Category::Aquifer: name = "Aquifer"; break;
|
case Category::Aquifer: name = "Aquifer"; break;
|
||||||
case Category::Node: name = "Node"; break;
|
case Category::Node: name = "Node"; break;
|
||||||
@ -817,7 +818,7 @@ inline quantity cpr( const fn_args& args ) {
|
|||||||
// The args.num value is the literal value which will go to the
|
// The args.num value is the literal value which will go to the
|
||||||
// NUMS array in the eclipse SMSPEC file; the values in this array
|
// NUMS array in the eclipse SMSPEC file; the values in this array
|
||||||
// are offset 1 - whereas we need to use this index here to look
|
// are offset 1 - whereas we need to use this index here to look
|
||||||
// up a completion with offset 0.
|
// up a connection with offset 0.
|
||||||
const size_t global_index = args.num - 1;
|
const size_t global_index = args.num - 1;
|
||||||
if (args.schedule_wells.empty())
|
if (args.schedule_wells.empty())
|
||||||
return zero;
|
return zero;
|
||||||
@ -920,7 +921,7 @@ inline quantity crate( const fn_args& args ) {
|
|||||||
// The args.num value is the literal value which will go to the
|
// The args.num value is the literal value which will go to the
|
||||||
// NUMS array in the eclipse SMSPEC file; the values in this array
|
// NUMS array in the eclipse SMSPEC file; the values in this array
|
||||||
// are offset 1 - whereas we need to use this index here to look
|
// are offset 1 - whereas we need to use this index here to look
|
||||||
// up a completion with offset 0.
|
// up a connection with offset 0.
|
||||||
const size_t global_index = args.num - 1;
|
const size_t global_index = args.num - 1;
|
||||||
if (args.schedule_wells.empty())
|
if (args.schedule_wells.empty())
|
||||||
return zero;
|
return zero;
|
||||||
@ -975,7 +976,7 @@ quantity crate_resv( const fn_args& args ) {
|
|||||||
// The args.num value is the literal value which will go to the
|
// The args.num value is the literal value which will go to the
|
||||||
// NUMS array in the eclipse SMSPEC file; the values in this array
|
// NUMS array in the eclipse SMSPEC file; the values in this array
|
||||||
// are offset 1 - whereas we need to use this index here to look
|
// are offset 1 - whereas we need to use this index here to look
|
||||||
// up a completion with offset 0.
|
// up a connection with offset 0.
|
||||||
const auto global_index = static_cast<std::size_t>(args.num - 1);
|
const auto global_index = static_cast<std::size_t>(args.num - 1);
|
||||||
|
|
||||||
const auto& well_data = xwPos->second;
|
const auto& well_data = xwPos->second;
|
||||||
@ -1004,7 +1005,7 @@ inline quantity srate( const fn_args& args ) {
|
|||||||
// The args.num value is the literal value which will go to the
|
// The args.num value is the literal value which will go to the
|
||||||
// NUMS array in the eclispe SMSPEC file; the values in this array
|
// NUMS array in the eclispe SMSPEC file; the values in this array
|
||||||
// are offset 1 - whereas we need to use this index here to look
|
// are offset 1 - whereas we need to use this index here to look
|
||||||
// up a completion with offset 0.
|
// up a connection with offset 0.
|
||||||
if (args.schedule_wells.empty()) {
|
if (args.schedule_wells.empty()) {
|
||||||
return zero;
|
return zero;
|
||||||
}
|
}
|
||||||
@ -1049,7 +1050,7 @@ inline quantity trans_factors ( const fn_args& args ) {
|
|||||||
// No dynamic results for this well. Not open?
|
// No dynamic results for this well. Not open?
|
||||||
return zero;
|
return zero;
|
||||||
|
|
||||||
// Like completion rate we need to look up a connection with offset 0.
|
// Like connection rate we need to look up a connection with offset 0.
|
||||||
const size_t global_index = args.num - 1;
|
const size_t global_index = args.num - 1;
|
||||||
const auto& connections = xwPos->second.connections;
|
const auto& connections = xwPos->second.connections;
|
||||||
auto connPos = std::find_if(connections.begin(), connections.end(),
|
auto connPos = std::find_if(connections.begin(), connections.end(),
|
||||||
@ -1082,7 +1083,7 @@ inline quantity segpress ( const fn_args& args )
|
|||||||
return zero;
|
return zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Like completion rate we need to look up a connection with offset 0.
|
// Like connection rate we need to look up a connection with offset 0.
|
||||||
const size_t segNumber = args.num;
|
const size_t segNumber = args.num;
|
||||||
|
|
||||||
const auto& well_data = xwPos->second;
|
const auto& well_data = xwPos->second;
|
||||||
@ -1410,7 +1411,7 @@ inline quantity connection_productivity_index(const fn_args& args)
|
|||||||
// The args.num value is the literal value which will go to the
|
// The args.num value is the literal value which will go to the
|
||||||
// NUMS array in the eclipse SMSPEC file; the values in this array
|
// NUMS array in the eclipse SMSPEC file; the values in this array
|
||||||
// are offset 1 - whereas we need to use this index here to look
|
// are offset 1 - whereas we need to use this index here to look
|
||||||
// up a completion with offset 0.
|
// up a connection with offset 0.
|
||||||
const auto global_index = static_cast<std::size_t>(args.num) - 1;
|
const auto global_index = static_cast<std::size_t>(args.num) - 1;
|
||||||
|
|
||||||
const auto& xcon = xwPos->second.connections;
|
const auto& xcon = xwPos->second.connections;
|
||||||
@ -2429,6 +2430,7 @@ find_wells(const Opm::Schedule& schedule,
|
|||||||
switch (node.category) {
|
switch (node.category) {
|
||||||
case Opm::EclIO::SummaryNode::Category::Well:
|
case Opm::EclIO::SummaryNode::Category::Well:
|
||||||
case Opm::EclIO::SummaryNode::Category::Connection:
|
case Opm::EclIO::SummaryNode::Category::Connection:
|
||||||
|
case Opm::EclIO::SummaryNode::Category::Completion:
|
||||||
case Opm::EclIO::SummaryNode::Category::Segment:
|
case Opm::EclIO::SummaryNode::Category::Segment:
|
||||||
return find_single_well(schedule, node.wgname, sim_step);
|
return find_single_well(schedule, node.wgname, sim_step);
|
||||||
|
|
||||||
@ -2450,7 +2452,7 @@ find_wells(const Opm::Schedule& schedule,
|
|||||||
|
|
||||||
throw std::runtime_error {
|
throw std::runtime_error {
|
||||||
fmt::format("Unhandled summary node category \"{}\" in find_wells()",
|
fmt::format("Unhandled summary node category \"{}\" in find_wells()",
|
||||||
node.category)
|
static_cast<int>(node.category))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2463,6 +2465,7 @@ bool need_wells(const Opm::EclIO::SummaryNode& node)
|
|||||||
|
|
||||||
switch (node.category) {
|
switch (node.category) {
|
||||||
case Cat::Connection: [[fallthrough]];
|
case Cat::Connection: [[fallthrough]];
|
||||||
|
case Cat::Completion: [[fallthrough]];
|
||||||
case Cat::Field: [[fallthrough]];
|
case Cat::Field: [[fallthrough]];
|
||||||
case Cat::Group: [[fallthrough]];
|
case Cat::Group: [[fallthrough]];
|
||||||
case Cat::Segment: [[fallthrough]];
|
case Cat::Segment: [[fallthrough]];
|
||||||
@ -2484,7 +2487,10 @@ bool need_wells(const Opm::EclIO::SummaryNode& node)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("Unhandled summary node category in need_wells");
|
throw std::runtime_error {
|
||||||
|
fmt::format("Unhandled summary node category \"{}\" in need_wells()",
|
||||||
|
static_cast<int>(node.category))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateValue(const Opm::EclIO::SummaryNode& node, const double value, Opm::SummaryState& st)
|
void updateValue(const Opm::EclIO::SummaryNode& node, const double value, Opm::SummaryState& st)
|
||||||
@ -3396,7 +3402,10 @@ namespace Evaluator {
|
|||||||
|
|
||||||
bool Factory::isFunctionRelation()
|
bool Factory::isFunctionRelation()
|
||||||
{
|
{
|
||||||
auto pos = funs.find(this->node_->keyword);
|
const auto normKw = Opm::EclIO::SummaryNode::
|
||||||
|
normalise_keyword(this->node_->category, this->node_->keyword);
|
||||||
|
|
||||||
|
auto pos = funs.find(normKw);
|
||||||
if (pos != funs.end()) {
|
if (pos != funs.end()) {
|
||||||
// 'node_' represents a functional relation.
|
// 'node_' represents a functional relation.
|
||||||
// Capture evaluation function and return true.
|
// Capture evaluation function and return true.
|
||||||
@ -3404,41 +3413,45 @@ namespace Evaluator {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto keyword = this->node_->keyword;
|
if (normKw.length() <= std::string::size_type{4}) {
|
||||||
auto dash_pos = keyword.find("_");
|
return false;
|
||||||
if (dash_pos != std::string::npos)
|
|
||||||
keyword = keyword.substr(0, dash_pos);
|
|
||||||
|
|
||||||
pos = funs.find(keyword);
|
|
||||||
if (pos != funs.end()) {
|
|
||||||
// 'node_' represents a functional relation.
|
|
||||||
// Capture evaluation function and return true.
|
|
||||||
this->paramFunction_ = pos->second;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyword.length() > 4 ) {
|
const auto tracer_name = normKw.substr(4);
|
||||||
std::string tracer_tag = keyword.substr(0, 4);
|
|
||||||
std::string tracer_name = keyword.substr(4);
|
|
||||||
const auto& tracers = es_.tracer();
|
|
||||||
for (const auto& tracer : tracers) {
|
|
||||||
if (tracer.name == tracer_name) {
|
|
||||||
if (tracer.phase == Opm::Phase::WATER)
|
|
||||||
tracer_tag += "#W";
|
|
||||||
else if (tracer.phase == Opm::Phase::OIL)
|
|
||||||
tracer_tag += "#O";
|
|
||||||
else if (tracer.phase == Opm::Phase::GAS)
|
|
||||||
tracer_tag += "#G";
|
|
||||||
|
|
||||||
pos = funs.find(tracer_tag);
|
const auto& tracers = this->es_.tracer();
|
||||||
if (pos != funs.end()) {
|
auto trPos = std::find_if(tracers.begin(), tracers.end(),
|
||||||
this->paramFunction_ = pos->second;
|
[&tracer_name](const auto& tracer)
|
||||||
return true;
|
{
|
||||||
}
|
return tracer.name == tracer_name;
|
||||||
|
});
|
||||||
|
|
||||||
break;
|
if (trPos == tracers.end()) {
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto tracer_tag = normKw.substr(0, 4);
|
||||||
|
switch (trPos->phase) {
|
||||||
|
case Opm::Phase::WATER:
|
||||||
|
tracer_tag += "#W";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Opm::Phase::OIL:
|
||||||
|
tracer_tag += "#O";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Opm::Phase::GAS:
|
||||||
|
tracer_tag += "#G";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = funs.find(tracer_tag);
|
||||||
|
if (pos != funs.end()) {
|
||||||
|
this->paramFunction_ = pos->second;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -445,21 +445,12 @@ double ecl_sum_get_general_var(const EclIO::ESmry* smry,
|
|||||||
return smry->get(var)[timeIdx];
|
return smry->get(var)[timeIdx];
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
bool ecl_sum_has_well_var( const EclIO::ESmry* smry,
|
|
||||||
const std::string& wellname,
|
|
||||||
const std::string& variable )
|
|
||||||
{
|
|
||||||
return smry->hasKey(variable + ':' + wellname);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
double ecl_sum_get_well_var( const EclIO::ESmry* smry,
|
double ecl_sum_get_well_var( const EclIO::ESmry* smry,
|
||||||
const int timeIdx,
|
const int timeIdx,
|
||||||
const std::string& wellname,
|
const std::string& wellname,
|
||||||
const std::string& variable )
|
const std::string& variable )
|
||||||
{
|
{
|
||||||
return smry->get(variable + ':' + wellname)[timeIdx];
|
return smry->get(fmt::format("{}:{}", variable, wellname))[timeIdx];
|
||||||
}
|
}
|
||||||
|
|
||||||
double ecl_sum_get_group_var( const EclIO::ESmry* smry,
|
double ecl_sum_get_group_var( const EclIO::ESmry* smry,
|
||||||
@ -467,7 +458,16 @@ double ecl_sum_get_group_var( const EclIO::ESmry* smry,
|
|||||||
const std::string& groupname,
|
const std::string& groupname,
|
||||||
const std::string& variable )
|
const std::string& variable )
|
||||||
{
|
{
|
||||||
return smry->get(variable + ':' + groupname)[timeIdx];
|
return smry->get(fmt::format("{}:{}", variable, groupname))[timeIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
double ecl_sum_get_well_completion_var( const EclIO::ESmry* smry,
|
||||||
|
const int timeIdx,
|
||||||
|
const std::string& wellname,
|
||||||
|
const std::string& variable,
|
||||||
|
const int completion)
|
||||||
|
{
|
||||||
|
return smry->get(fmt::format("{}:{}:{}", variable, wellname, completion))[timeIdx];
|
||||||
}
|
}
|
||||||
|
|
||||||
double ecl_sum_get_well_connection_var( const EclIO::ESmry* smry,
|
double ecl_sum_get_well_connection_var( const EclIO::ESmry* smry,
|
||||||
@ -478,8 +478,7 @@ double ecl_sum_get_well_connection_var( const EclIO::ESmry* smry,
|
|||||||
const int j,
|
const int j,
|
||||||
const int k)
|
const int k)
|
||||||
{
|
{
|
||||||
const auto ijk = std::to_string(i) + ',' + std::to_string(j) + ',' + std::to_string(k);
|
return smry->get(fmt::format("{}:{}:{},{},{}", variable, wellname, i, j, k))[timeIdx];
|
||||||
return smry->get(variable + ':' + wellname + ':' + ijk)[timeIdx];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ecl_sum_has_well_connection_var( const EclIO::ESmry* smry,
|
bool ecl_sum_has_well_connection_var( const EclIO::ESmry* smry,
|
||||||
@ -489,7 +488,7 @@ bool ecl_sum_has_well_connection_var( const EclIO::ESmry* smry,
|
|||||||
const int j,
|
const int j,
|
||||||
const int k)
|
const int k)
|
||||||
{
|
{
|
||||||
const auto key = fmt::format("{}:{}:{},{},{}", wellname, variable, i, j, k);
|
const auto key = fmt::format("{}:{}:{},{},{}", variable, wellname, i, j, k);
|
||||||
return ecl_sum_has_key(smry, key);
|
return ecl_sum_has_key(smry, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,7 +496,6 @@ struct setup {
|
|||||||
Deck deck;
|
Deck deck;
|
||||||
EclipseState es;
|
EclipseState es;
|
||||||
const EclipseGrid& grid;
|
const EclipseGrid& grid;
|
||||||
std::shared_ptr<Python> python;
|
|
||||||
Schedule schedule;
|
Schedule schedule;
|
||||||
SummaryConfig config;
|
SummaryConfig config;
|
||||||
data::Wells wells;
|
data::Wells wells;
|
||||||
@ -511,8 +509,7 @@ struct setup {
|
|||||||
deck( Parser().parseFile( path) ),
|
deck( Parser().parseFile( path) ),
|
||||||
es( deck ),
|
es( deck ),
|
||||||
grid( es.getInputGrid() ),
|
grid( es.getInputGrid() ),
|
||||||
python( std::make_shared<Python>() ),
|
schedule( deck, es, std::make_shared<Python>()),
|
||||||
schedule( deck, es, python),
|
|
||||||
config( deck, schedule, es.fieldProps(), es.aquifer()),
|
config( deck, schedule, es.fieldProps(), es.aquifer()),
|
||||||
wells( result_wells(w3_injector) ),
|
wells( result_wells(w3_injector) ),
|
||||||
grp_nwrk( result_group_nwrk() ),
|
grp_nwrk( result_group_nwrk() ),
|
||||||
@ -520,6 +517,7 @@ struct setup {
|
|||||||
ta( "summary_test" )
|
ta( "summary_test" )
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE(Summary)
|
BOOST_AUTO_TEST_SUITE(Summary)
|
||||||
@ -1265,13 +1263,13 @@ BOOST_AUTO_TEST_CASE(connection_kewords) {
|
|||||||
BOOST_CHECK_CLOSE( 234.5, ecl_sum_get_well_connection_var( resp, 2, "W_2", "CVPR", 2, 1, 2 ), 1e-5 );
|
BOOST_CHECK_CLOSE( 234.5, ecl_sum_get_well_connection_var( resp, 2, "W_2", "CVPR", 2, 1, 2 ), 1e-5 );
|
||||||
BOOST_CHECK_CLOSE( 0.0, ecl_sum_get_well_connection_var( resp, 1, "W_3", "CVPR", 3, 1, 1 ), 1e-5 );
|
BOOST_CHECK_CLOSE( 0.0, ecl_sum_get_well_connection_var( resp, 1, "W_3", "CVPR", 3, 1, 1 ), 1e-5 );
|
||||||
|
|
||||||
BOOST_CHECK_CLOSE(ecl_sum_get_well_var(resp, 1, "W_1", "WOPRL__1"), ecl_sum_get_well_connection_var(resp, 1, "W_1", "COPR", 1,1,1), 1e-5);
|
BOOST_CHECK_CLOSE(ecl_sum_get_well_completion_var(resp, 1, "W_1", "WOPRL", 1), ecl_sum_get_well_connection_var(resp, 1, "W_1", "COPR", 1,1,1), 1e-5);
|
||||||
BOOST_CHECK_CLOSE(ecl_sum_get_well_var(resp, 1, "W_2", "WOPRL__2"), ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPR", 2,1,1) +
|
BOOST_CHECK_CLOSE(ecl_sum_get_well_completion_var(resp, 1, "W_2", "WOPRL", 2), ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPR", 2,1,1) +
|
||||||
ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPR", 2,1,2), 1e-5);
|
ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPR", 2,1,2), 1e-5);
|
||||||
BOOST_CHECK_CLOSE(ecl_sum_get_well_var(resp, 1, "W_3", "WOPRL__3"), ecl_sum_get_well_connection_var(resp, 1, "W_3", "COPR", 3,1,1), 1e-5);
|
BOOST_CHECK_CLOSE(ecl_sum_get_well_completion_var(resp, 1, "W_3", "WOPRL", 3), ecl_sum_get_well_connection_var(resp, 1, "W_3", "COPR", 3,1,1), 1e-5);
|
||||||
BOOST_CHECK_EQUAL(ecl_sum_get_well_var(resp, 1, "W_2", "WOPRL__2"), ecl_sum_get_well_var(resp, 1, "W_2", "WOFRL__2"));
|
BOOST_CHECK_EQUAL(ecl_sum_get_well_completion_var(resp, 1, "W_2", "WOPRL", 2), ecl_sum_get_well_completion_var(resp, 1, "W_2", "WOFRL", 2));
|
||||||
|
|
||||||
BOOST_CHECK_CLOSE(ecl_sum_get_well_var(resp, 1, "W_1", "WOPRL__1"), ecl_sum_get_well_connection_var(resp, 1, "W_1", "COPRL", 1,1,1), 1e-5);
|
BOOST_CHECK_CLOSE(ecl_sum_get_well_completion_var(resp, 1, "W_1", "WOPRL", 1), ecl_sum_get_well_connection_var(resp, 1, "W_1", "COPRL", 1,1,1), 1e-5);
|
||||||
BOOST_CHECK_CLOSE(ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPRL", 2, 1, 1), ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPR", 2,1,1) +
|
BOOST_CHECK_CLOSE(ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPRL", 2, 1, 1), ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPR", 2,1,1) +
|
||||||
ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPR", 2,1,2), 1e-5);
|
ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPR", 2,1,2), 1e-5);
|
||||||
BOOST_CHECK_CLOSE(ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPRL", 2, 1, 2), ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPR", 2,1,1) +
|
BOOST_CHECK_CLOSE(ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPRL", 2, 1, 2), ecl_sum_get_well_connection_var(resp, 1, "W_2", "COPR", 2,1,1) +
|
||||||
|
@ -23,11 +23,16 @@
|
|||||||
|
|
||||||
#include <opm/io/eclipse/SummaryNode.hpp>
|
#include <opm/io/eclipse/SummaryNode.hpp>
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void expect_key(const Opm::EclIO::SummaryNode& node, const std::string& unique_key) {
|
void expect_key(const Opm::EclIO::SummaryNode& node, const std::string& unique_key) {
|
||||||
BOOST_CHECK_EQUAL(node.unique_key(), unique_key);
|
BOOST_CHECK_EQUAL(node.unique_key(), unique_key);
|
||||||
}
|
}
|
||||||
}
|
} // Anonymous namespace
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE(UniqueKey)
|
BOOST_AUTO_TEST_SUITE(UniqueKey)
|
||||||
|
|
||||||
@ -86,3 +91,100 @@ BOOST_AUTO_TEST_CASE(user_defined) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END() // UniqueKey
|
BOOST_AUTO_TEST_SUITE_END() // UniqueKey
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(Category)
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::string to_string(const Opm::EclIO::SummaryNode::Category cat)
|
||||||
|
{
|
||||||
|
using Cat = Opm::EclIO::SummaryNode::Category;
|
||||||
|
|
||||||
|
switch (cat) {
|
||||||
|
case Cat::Aquifer: return "Aquifer";
|
||||||
|
case Cat::Well: return "Well";
|
||||||
|
case Cat::Group: return "Group";
|
||||||
|
case Cat::Field: return "Field";
|
||||||
|
case Cat::Region: return "Region";
|
||||||
|
case Cat::Block: return "Block";
|
||||||
|
case Cat::Connection: return "Connection";
|
||||||
|
case Cat::Completion: return "Completion";
|
||||||
|
case Cat::Segment: return "Segment";
|
||||||
|
case Cat::Node: return "Node";
|
||||||
|
case Cat::Miscellaneous: return "Miscellaneous";
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::invalid_argument {
|
||||||
|
"Unhandled Summary Parameter Category '"
|
||||||
|
+ std::to_string(static_cast<int>(cat)) + '\''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Opm::EclIO::SummaryNode::Category category(const std::string& kw)
|
||||||
|
{
|
||||||
|
return Opm::EclIO::SummaryNode::category_from_keyword(kw);
|
||||||
|
}
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(Well)
|
||||||
|
{
|
||||||
|
const auto well_kw = std::vector<std::string> {
|
||||||
|
"WOPR", "WOPT", "WGIR", "WWIR",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& kw : well_kw) {
|
||||||
|
BOOST_CHECK_MESSAGE(category(kw) == Opm::EclIO::SummaryNode::Category::Well,
|
||||||
|
"Keyword '" << kw << "' must be category 'Well'. Got '" <<
|
||||||
|
to_string(category(kw)) << "' instead");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_CHECK_MESSAGE(category("WOPRL") != Opm::EclIO::SummaryNode::Category::Well,
|
||||||
|
"Keyword 'WOPRL' must NOT be category 'Well'");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(Connection)
|
||||||
|
{
|
||||||
|
const auto connection_kw = std::vector<std::string> {
|
||||||
|
"COPR", "COPT", "CGIR", "CWIR",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& kw : connection_kw) {
|
||||||
|
BOOST_CHECK_MESSAGE(category(kw) == Opm::EclIO::SummaryNode::Category::Connection,
|
||||||
|
"Keyword '" << kw << "' must be category 'Connection'. Got '" <<
|
||||||
|
to_string(category(kw)) << "' instead");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_CHECK_MESSAGE(category("COPRL") != Opm::EclIO::SummaryNode::Category::Connection,
|
||||||
|
"Keyword 'COPRL' must NOT be category 'Connection'");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(Completion)
|
||||||
|
{
|
||||||
|
const auto compl_kw = std::vector<std::string> {
|
||||||
|
"OPRL", "OPTL", "GIRL", "WIRL",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& kw_base : compl_kw) {
|
||||||
|
const auto kw = 'C' + kw_base;
|
||||||
|
BOOST_CHECK_MESSAGE(category(kw) == Opm::EclIO::SummaryNode::Category::Completion,
|
||||||
|
"Keyword '" << kw << "' must be category 'Completion'. Got '" <<
|
||||||
|
to_string(category(kw)) << "' instead");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto* suffix : { "", "__1", "_12", "123" }) {
|
||||||
|
for (const auto& kw_base : compl_kw) {
|
||||||
|
const auto kw = 'W' + kw_base + suffix;
|
||||||
|
BOOST_CHECK_MESSAGE(category(kw) == Opm::EclIO::SummaryNode::Category::Completion,
|
||||||
|
"Keyword '" << kw << "' must be category 'Completion'. Got '" <<
|
||||||
|
to_string(category(kw)) << "' instead");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto* kw : { "WOPRLK", "CGIR", "WKITL__8", "WOOOOPRL", "WHIRL" }) {
|
||||||
|
BOOST_CHECK_MESSAGE(category(kw) != Opm::EclIO::SummaryNode::Category::Completion,
|
||||||
|
"Keyword '" << kw << "' must NOT be category 'Completion'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END() // Category
|
||||||
|
Loading…
Reference in New Issue
Block a user