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
|
||||
#define OPM_IO_SUMMARYNODE_HPP
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
|
||||
namespace Opm { namespace EclIO {
|
||||
|
||||
@ -42,6 +41,7 @@ struct SummaryNode {
|
||||
Region,
|
||||
Block,
|
||||
Connection,
|
||||
Completion,
|
||||
Segment,
|
||||
Aquifer,
|
||||
Node,
|
||||
@ -59,7 +59,6 @@ struct SummaryNode {
|
||||
Undefined,
|
||||
};
|
||||
|
||||
|
||||
std::string keyword;
|
||||
Category category;
|
||||
Type type;
|
||||
@ -79,6 +78,14 @@ struct SummaryNode {
|
||||
|
||||
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
|
||||
// naive first-character-based classification suggests something else.
|
||||
static bool miscellaneous_exception(const std::string& keyword);
|
||||
|
@ -310,10 +310,6 @@ struct SummaryConfigContext {
|
||||
&& 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)
|
||||
{
|
||||
static const auto supported_kw = std::regex {
|
||||
@ -363,39 +359,22 @@ struct SummaryConfigContext {
|
||||
|
||||
bool is_connection_completion(const std::string& keyword)
|
||||
{
|
||||
if (keyword[0] != 'C')
|
||||
return false;
|
||||
static const auto conn_compl_kw = std::regex {
|
||||
R"(C[OGW][IP][RT]L)"
|
||||
};
|
||||
|
||||
if (keyword.back() != 'L')
|
||||
return false;
|
||||
|
||||
if (is_udq(keyword))
|
||||
return false;
|
||||
|
||||
if (keyword.size() != 5)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return std::regex_match(keyword, conn_compl_kw);
|
||||
}
|
||||
|
||||
bool is_well_completion(const std::string& keyword)
|
||||
{
|
||||
if (keyword[0] != 'W')
|
||||
return false;
|
||||
static const auto well_compl_kw = std::regex {
|
||||
R"(W[OGWLV][PIGOLCF][RT]L([0-9_]{2}[0-9])?)"
|
||||
};
|
||||
|
||||
if (keyword.back() != 'L')
|
||||
return false;
|
||||
|
||||
if (is_liquid_phase(keyword))
|
||||
return false;
|
||||
|
||||
if (is_udq(keyword))
|
||||
return false;
|
||||
|
||||
if (keyword == "WMCTL")
|
||||
return false;
|
||||
|
||||
return true;
|
||||
// True, e.g., for WOPRL, WOPRL__8, WOPRL123, but not WOPRL___ or
|
||||
// WKITL.
|
||||
return std::regex_match(keyword, well_compl_kw);
|
||||
}
|
||||
|
||||
bool is_node_keyword(const std::string& keyword)
|
||||
@ -447,6 +426,21 @@ struct SummaryConfigContext {
|
||||
: 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) {
|
||||
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,
|
||||
const ParseContext& parseContext,
|
||||
ErrorGuard& errors,
|
||||
const DeckKeyword& keyword,
|
||||
const Schedule& schedule ,
|
||||
const GridDims& dims)
|
||||
inline void keywordCL(SummaryConfig::keyword_list& list,
|
||||
const ParseContext& parseContext,
|
||||
ErrorGuard& errors,
|
||||
const DeckKeyword& keyword,
|
||||
const Schedule& schedule,
|
||||
const GridDims& dims)
|
||||
{
|
||||
auto node = SummaryConfigNode{keyword.name(), SummaryConfigNode::Category::Connection, keyword.location()};
|
||||
node.parameterType( parseKeywordType(keyword.name()) );
|
||||
node.isUserDefined( is_udq(keyword.name()) );
|
||||
auto node = SummaryConfigNode {
|
||||
keyword.name(), SummaryConfigNode::Category::Completion, keyword.location()
|
||||
}
|
||||
.parameterType(parseKeywordType(keyword.name()))
|
||||
.isUserDefined(is_udq(keyword.name()));
|
||||
|
||||
for (const auto& record : keyword) {
|
||||
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() )
|
||||
handleMissingWell( parseContext, errors, keyword.location(), pattern );
|
||||
|
||||
const auto ijk_defaulted = record.getItem( 1 ).defaultApplied( 0 );
|
||||
const auto ijk_defaulted = record.getItem(1).defaultApplied(0);
|
||||
for (const auto& wname : well_names) {
|
||||
const auto& well = schedule.getWellatEnd(wname);
|
||||
const auto& all_connections = well.getConnections();
|
||||
|
||||
node.namedEntity( wname );
|
||||
node.namedEntity(wname);
|
||||
if (ijk_defaulted) {
|
||||
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 {
|
||||
const auto& ijk = getijk(record);
|
||||
auto global_index = dims.getGlobalIndex(ijk[0], ijk[1], ijk[2]);
|
||||
@ -599,7 +595,8 @@ inline void keywordCL( SummaryConfig::keyword_list& list,
|
||||
} else {
|
||||
std::string msg = fmt::format("Problem with keyword {{keyword}}\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);
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
ErrorGuard& errors,
|
||||
const DeckKeyword& keyword,
|
||||
const Schedule& schedule )
|
||||
const Schedule& schedule)
|
||||
{
|
||||
for (const auto& record : keyword) {
|
||||
const auto& pattern = record.getItem(0).get<std::string>(0);
|
||||
const int completion = record.getItem(1).get<int>(0);
|
||||
auto well_names = schedule.wellNames( pattern, schedule.size() - 1 );
|
||||
const auto well_names = schedule.wellNames(pattern, schedule.size() - 1);
|
||||
|
||||
// We add the completion number both the extra field which contains
|
||||
// parsed data from the keywordname - i.e. WOPRL__8 and also to the
|
||||
// numeric member which will be written to the NUMS field.
|
||||
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()) {
|
||||
handleMissingWell(parseContext, errors, keyword.location(), pattern);
|
||||
continue;
|
||||
}
|
||||
|
||||
if( well_names.empty() )
|
||||
handleMissingWell( parseContext, errors, keyword.location(), pattern );
|
||||
const auto completion = record.getItem(1).get<int>(0);
|
||||
|
||||
// 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) {
|
||||
const auto& well = schedule.getWellatEnd(wname);
|
||||
if (well.hasCompletion(completion))
|
||||
list.push_back( node.namedEntity( wname ) );
|
||||
if (schedule.getWellatEnd(wname).hasCompletion(completion)) {
|
||||
list.push_back(node.namedEntity(wname));
|
||||
}
|
||||
else {
|
||||
std::string msg = fmt::format("Problem with keyword {{keyword}}\n"
|
||||
"In {{file}} line {{line}}\n"
|
||||
"Completion number {} not defined for well {} ", completion, wname);
|
||||
parseContext.handleError( ParseContext::SUMMARY_UNHANDLED_KEYWORD, msg, keyword.location(), errors);
|
||||
const auto msg = fmt::format("Problem with keyword {{keyword}}\n"
|
||||
"In {{file}} line {{line}}\n"
|
||||
"Completion number {} not defined for well {}",
|
||||
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,
|
||||
const ParseContext& parseContext,
|
||||
ErrorGuard& errors,
|
||||
ErrorGuard& errors,
|
||||
const DeckKeyword& keyword,
|
||||
const Schedule& schedule,
|
||||
SummaryConfig::keyword_list& list)
|
||||
@ -1213,7 +1219,7 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
|
||||
|
||||
inline void keywordS(SummaryConfig::keyword_list& list,
|
||||
const ParseContext& parseContext,
|
||||
ErrorGuard& errors,
|
||||
ErrorGuard& errors,
|
||||
const DeckKeyword& keyword,
|
||||
const Schedule& schedule)
|
||||
{
|
||||
@ -1253,18 +1259,20 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string(const SummaryConfigNode::Category cat) {
|
||||
switch( cat ) {
|
||||
case SummaryConfigNode::Category::Aquifer: return "Aquifer";
|
||||
case SummaryConfigNode::Category::Well: return "Well";
|
||||
case SummaryConfigNode::Category::Group: return "Group";
|
||||
case SummaryConfigNode::Category::Field: return "Field";
|
||||
case SummaryConfigNode::Category::Region: return "Region";
|
||||
case SummaryConfigNode::Category::Block: return "Block";
|
||||
case SummaryConfigNode::Category::Connection: return "Connection";
|
||||
case SummaryConfigNode::Category::Segment: return "Segment";
|
||||
case SummaryConfigNode::Category::Node: return "Node";
|
||||
case SummaryConfigNode::Category::Miscellaneous: return "Miscellaneous";
|
||||
std::string to_string(const SummaryConfigNode::Category cat)
|
||||
{
|
||||
switch (cat) {
|
||||
case SummaryConfigNode::Category::Aquifer: return "Aquifer";
|
||||
case SummaryConfigNode::Category::Well: return "Well";
|
||||
case SummaryConfigNode::Category::Group: return "Group";
|
||||
case SummaryConfigNode::Category::Field: return "Field";
|
||||
case SummaryConfigNode::Category::Region: return "Region";
|
||||
case SummaryConfigNode::Category::Block: return "Block";
|
||||
case SummaryConfigNode::Category::Connection: return "Connection";
|
||||
case SummaryConfigNode::Category::Completion: return "Completion";
|
||||
case SummaryConfigNode::Category::Segment: return "Segment";
|
||||
case SummaryConfigNode::Category::Node: return "Node";
|
||||
case SummaryConfigNode::Category::Miscellaneous: return "Miscellaneous";
|
||||
}
|
||||
|
||||
throw std::invalid_argument {
|
||||
@ -1299,40 +1307,84 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
|
||||
}
|
||||
}
|
||||
|
||||
inline void handleKW( SummaryConfig::keyword_list& list,
|
||||
SummaryConfigContext& context,
|
||||
const std::vector<std::string>& node_names,
|
||||
const std::vector<int>& analyticAquiferIDs,
|
||||
const std::vector<int>& numericAquiferIDs,
|
||||
const DeckKeyword& keyword,
|
||||
const Schedule& schedule,
|
||||
const FieldPropsManager& field_props,
|
||||
const ParseContext& parseContext,
|
||||
ErrorGuard& errors,
|
||||
const GridDims& dims) {
|
||||
inline void handleKW( SummaryConfig::keyword_list& list,
|
||||
SummaryConfigContext& context,
|
||||
const std::vector<std::string>& node_names,
|
||||
const std::vector<int>& analyticAquiferIDs,
|
||||
const std::vector<int>& numericAquiferIDs,
|
||||
const DeckKeyword& keyword,
|
||||
const Schedule& schedule,
|
||||
const FieldPropsManager& field_props,
|
||||
const ParseContext& parseContext,
|
||||
ErrorGuard& errors,
|
||||
const GridDims& dims)
|
||||
{
|
||||
using Cat = SummaryConfigNode::Category;
|
||||
|
||||
const auto& name = keyword.name();
|
||||
check_udq( keyword.location(), schedule, parseContext, errors );
|
||||
check_udq(keyword.location(), schedule, parseContext, errors);
|
||||
|
||||
const auto cat = parseKeywordCategory( name );
|
||||
switch( cat ) {
|
||||
case Cat::Well: return keywordW( list, parseContext, errors, keyword, schedule );
|
||||
case Cat::Group: return keywordG( list, parseContext, errors, keyword, schedule );
|
||||
case Cat::Field: return keywordF( list, keyword );
|
||||
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 );
|
||||
const auto cat = parseKeywordCategory(name);
|
||||
switch (cat) {
|
||||
case Cat::Well:
|
||||
keywordW(list, parseContext, errors, keyword, schedule);
|
||||
break;
|
||||
|
||||
default:
|
||||
std::string 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);
|
||||
return;
|
||||
case Cat::Group:
|
||||
keywordG(list, parseContext, errors, keyword, schedule);
|
||||
break;
|
||||
|
||||
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 Schedule& schedule,
|
||||
const ParseContext& /* parseContext */,
|
||||
ErrorGuard& /* errors */) {
|
||||
|
||||
|
||||
if (is_udq(keyword))
|
||||
throw std::logic_error("UDQ keywords not handleded when expanding alias list");
|
||||
ErrorGuard& /* errors */)
|
||||
{
|
||||
if (is_udq(keyword)) {
|
||||
throw std::logic_error {
|
||||
"UDQ keywords not handleded when expanding alias list"
|
||||
};
|
||||
}
|
||||
|
||||
using Cat = SummaryConfigNode::Category;
|
||||
const auto cat = parseKeywordCategory( keyword );
|
||||
const auto cat = parseKeywordCategory(keyword);
|
||||
|
||||
switch( cat ) {
|
||||
case Cat::Well: return keywordW( list, keyword, location, schedule );
|
||||
case Cat::Group: return keywordG( list, keyword, location, schedule );
|
||||
case Cat::Field: return keywordF( list, keyword, location );
|
||||
case Cat::Aquifer: return keywordAquifer( list, keyword, analyticAquiferIDs, numericAquiferIDs, location );
|
||||
case Cat::Miscellaneous: return keywordMISC( list, keyword, location);
|
||||
switch (cat) {
|
||||
case Cat::Well:
|
||||
keywordW(list, keyword, location, schedule);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::logic_error("Keyword type: " + to_string( cat ) + " is not supported in alias lists. Internal error handling: " + keyword);
|
||||
case Cat::Group:
|
||||
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))
|
||||
keyword.pop_back();
|
||||
|
||||
@ -1413,20 +1487,21 @@ SummaryConfigNode::Type parseKeywordType(std::string keyword) {
|
||||
return SummaryConfigNode::Type::Undefined;
|
||||
}
|
||||
|
||||
SummaryConfigNode::Category parseKeywordCategory(const std::string& keyword) {
|
||||
SummaryConfigNode::Category parseKeywordCategory(const std::string& keyword)
|
||||
{
|
||||
using Cat = SummaryConfigNode::Category;
|
||||
|
||||
if (is_special(keyword)) { return Cat::Miscellaneous; }
|
||||
|
||||
switch (keyword[0]) {
|
||||
case 'A': if (is_aquifer(keyword)) return Cat::Aquifer; break;
|
||||
case 'W': return Cat::Well;
|
||||
case 'G': return distinguish_group_from_node(keyword);
|
||||
case 'F': return Cat::Field;
|
||||
case 'C': return Cat::Connection;
|
||||
case 'R': return Cat::Region;
|
||||
case 'B': return Cat::Block;
|
||||
case 'S': return Cat::Segment;
|
||||
case 'A': if (is_aquifer(keyword)) return Cat::Aquifer; break;
|
||||
case 'W': return distinguish_well_from_completion(keyword);
|
||||
case 'G': return distinguish_group_from_node(keyword);
|
||||
case 'F': return Cat::Field;
|
||||
case 'C': return distinguish_connection_from_completion(keyword);
|
||||
case 'R': return Cat::Region;
|
||||
case 'B': return Cat::Block;
|
||||
case 'S': return Cat::Segment;
|
||||
}
|
||||
|
||||
// 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) :
|
||||
keyword_(std::move(keyword)),
|
||||
category_(cat),
|
||||
loc(std::move(loc_arg))
|
||||
SummaryConfigNode::SummaryConfigNode(std::string keyword, const Category cat, KeywordLocation loc_arg)
|
||||
: keyword_ (std::move(keyword))
|
||||
, category_(cat)
|
||||
, loc (std::move(loc_arg))
|
||||
{}
|
||||
|
||||
SummaryConfigNode SummaryConfigNode::serializeObject()
|
||||
@ -1503,6 +1578,7 @@ std::string SummaryConfigNode::uniqueNodeKey() const
|
||||
return this->keyword() + ':' + std::to_string(this->number());
|
||||
|
||||
case SummaryConfigNode::Category::Connection: [[fallthrough]];
|
||||
case SummaryConfigNode::Category::Completion: [[fallthrough]];
|
||||
case SummaryConfigNode::Category::Segment:
|
||||
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();
|
||||
|
||||
case SummaryConfigNode::Category::Connection: [[fallthrough]];
|
||||
case SummaryConfigNode::Category::Completion: [[fallthrough]];
|
||||
case SummaryConfigNode::Category::Segment:
|
||||
// Equal if associated to same numeric
|
||||
// sub-entity of same named entity
|
||||
@ -1575,6 +1652,7 @@ bool operator<(const SummaryConfigNode& lhs, const SummaryConfigNode& rhs)
|
||||
return lhs.number() < rhs.number();
|
||||
|
||||
case SummaryConfigNode::Category::Connection: [[fallthrough]];
|
||||
case SummaryConfigNode::Category::Completion: [[fallthrough]];
|
||||
case SummaryConfigNode::Category::Segment:
|
||||
{
|
||||
// Ordering determined by pair of named entity and numeric ID.
|
||||
|
@ -18,9 +18,12 @@
|
||||
|
||||
#include <opm/io/eclipse/ESmry.hpp>
|
||||
|
||||
#include <opm/io/eclipse/SummaryNode.hpp>
|
||||
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/common/utility/shmatch.hpp>
|
||||
#include <opm/common/utility/TimeService.hpp>
|
||||
|
||||
#include <opm/io/eclipse/EclFile.hpp>
|
||||
#include <opm/io/eclipse/EclUtil.hpp>
|
||||
#include <opm/io/eclipse/EclOutput.hpp>
|
||||
@ -34,10 +37,13 @@
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
@ -46,19 +52,21 @@
|
||||
KEYWORDS WGNAMES NUMS | PARAM index Corresponding ERT key
|
||||
---------------------------------------------------+--------------------------------------------------
|
||||
WGOR OP_1 0 | 0 WGOR:OP_1
|
||||
FOPT :+:+:+:+ 0 | 1 FOPT
|
||||
WWCT OP_1 0 | 2 WWCT:OP_1
|
||||
WIR OP_1 0 | 3 WIR:OP_1
|
||||
WGOR WI_1 0 | 4 WWCT:OP_1
|
||||
WWCT W1_1 0 | 5 WWCT:WI_1
|
||||
BPR :+:+:+:+ 12675 | 6 BPR:12675, BPR:i,j,k
|
||||
RPR :+:+:+:+ 1 | 7 RPR:1
|
||||
FOPT :+:+:+:+ 0 | 8 FOPT
|
||||
GGPR NORTH 0 | 9 GGPR:NORTH
|
||||
COPR OP_1 5628 | 10 COPR:OP_1:56286, COPR:OP_1:i,j,k
|
||||
RXF :+:+:+:+ R1 + 32768*(R2 + 10) | 11 RXF:2-3
|
||||
SOFX OP_1 12675 | 12 SOFX:OP_1:12675, SOFX:OP_1:i,j,jk
|
||||
AAQX :+:+:+:+ 12 | 13 AAQX:12
|
||||
WOPRL__1 OP_1 1 | 1 WOPRL:OP_1:1 -- KEYWORDS is strictly speaking "WOPRL__1" here.
|
||||
FOPT :+:+:+:+ 0 | 2 FOPT
|
||||
WWCT OP_1 0 | 3 WWCT:OP_1
|
||||
WIR OP_1 0 | 4 WIR:OP_1
|
||||
WGOR WI_1 0 | 5 WWCT:OP_1
|
||||
WWCT W1_1 0 | 6 WWCT:WI_1
|
||||
BPR :+:+:+:+ 12675 | 7 BPR:12675, BPR:i,j,k
|
||||
RPR :+:+:+:+ 1 | 8 RPR:1
|
||||
FOPT :+:+:+:+ 0 | 9 FOPT
|
||||
GGPR NORTH 0 | 10 GGPR:NORTH
|
||||
COPR OP_1 5628 | 11 COPR:OP_1:56286, COPR:OP_1:i,j,k
|
||||
COPRL OP_1 5628 | 12 COPRL:OP_1:5628, COPRL:OP_1:i,j,k
|
||||
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) );
|
||||
}
|
||||
|
||||
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++) {
|
||||
|
||||
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);
|
||||
|
||||
if (! keyString.empty()) {
|
||||
summaryNodes.push_back( {
|
||||
keywords[i],
|
||||
SummaryNode::category_from_keyword(keywords[i]),
|
||||
normKw,
|
||||
category,
|
||||
SummaryNode::Type::Undefined,
|
||||
wgnames[i],
|
||||
nums[i],
|
||||
@ -207,13 +238,16 @@ ESmry::ESmry(const std::string &filename, bool loadBaseRunData) :
|
||||
} else {
|
||||
|
||||
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);
|
||||
|
||||
if (! keyString.empty()) {
|
||||
summaryNodes.push_back( {
|
||||
keywords[i],
|
||||
SummaryNode::category_from_keyword(keywords[i]),
|
||||
normKw,
|
||||
category,
|
||||
SummaryNode::Type::Undefined,
|
||||
wgnames[i],
|
||||
nums[i],
|
||||
@ -317,14 +351,17 @@ ESmry::ESmry(const std::string &filename, bool loadBaseRunData) :
|
||||
if (have_lgr) {
|
||||
for (size_t i = 0; i < keywords.size(); 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);
|
||||
|
||||
if (! keyString.empty()) {
|
||||
summaryNodes.push_back( {
|
||||
keywords[i],
|
||||
SummaryNode::category_from_keyword(keywords[i]),
|
||||
normKw,
|
||||
category,
|
||||
SummaryNode::Type::Undefined,
|
||||
wgnames[i],
|
||||
nums[i],
|
||||
@ -341,13 +378,16 @@ ESmry::ESmry(const std::string &filename, bool loadBaseRunData) :
|
||||
|
||||
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);
|
||||
|
||||
if (! keyString.empty()) {
|
||||
summaryNodes.push_back( {
|
||||
keywords[i],
|
||||
SummaryNode::category_from_keyword(keywords[i]),
|
||||
normKw,
|
||||
category,
|
||||
SummaryNode::Type::Undefined,
|
||||
wgnames[i],
|
||||
nums[i],
|
||||
@ -425,13 +465,16 @@ ESmry::ESmry(const std::string &filename, bool loadBaseRunData) :
|
||||
if (have_lgr) {
|
||||
for (size_t i=0; i < keywords.size(); 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()))
|
||||
arrayPos[specInd][keyIndex[keyw]]=i;
|
||||
}
|
||||
} else {
|
||||
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()))
|
||||
arrayPos[specInd][keyIndex[keyw]]=i;
|
||||
}
|
||||
@ -1251,6 +1294,10 @@ std::string ESmry::makeKeyString(const std::string& keywordArg, const std::strin
|
||||
return "";
|
||||
}
|
||||
|
||||
if (is_well_completion(keywordArg)) {
|
||||
return fmt::format("{}:{}:{}", keywordArg, wgname, num);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if (node.category == SummaryNode::Category::Block ||
|
||||
node.category == SummaryNode::Category::Connection) {
|
||||
if ((node.category == SummaryNode::Category::Block) ||
|
||||
(node.category == SummaryNode::Category::Connection) ||
|
||||
((node.category == SummaryNode::Category::Completion) &&
|
||||
is_connection_completion(node.keyword)))
|
||||
{
|
||||
int _i,_j,_k;
|
||||
ijk_from_global_index(node.number, _i, _j, _k);
|
||||
|
||||
return fmt::format("{},{},{}", _i, _j, _k);
|
||||
}
|
||||
else if (node.category == SummaryNode::Category::Region && node.keyword[2] == 'F') {
|
||||
const auto r1 = node.number % (1 << 15);
|
||||
const auto r2 = (node.number / (1 << 15)) - 10;
|
||||
else if ((node.category == SummaryNode::Category::Region) &&
|
||||
(node.keyword[2] == 'F'))
|
||||
{
|
||||
const auto& [r1, r2] = splitSummaryNumber(node.number);
|
||||
|
||||
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::Block: [[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::Segment:
|
||||
return true;
|
||||
@ -50,6 +51,7 @@ constexpr bool use_number(Opm::EclIO::SummaryNode::Category category) {
|
||||
constexpr bool use_name(Opm::EclIO::SummaryNode::Category category) {
|
||||
switch (category) {
|
||||
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::Segment: [[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();
|
||||
}
|
||||
|
||||
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
|
||||
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::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 {
|
||||
std::vector<std::string> key_parts { keyword } ;
|
||||
Opm::EclIO::SummaryNode::Category
|
||||
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())
|
||||
key_parts.emplace_back(opt.value());
|
||||
@ -146,43 +199,50 @@ bool Opm::EclIO::SummaryNode::is_user_defined() const {
|
||||
return matched && !blacklisted;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Observe that this function started out as a slight generalisation of the
|
||||
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
|
||||
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"};
|
||||
return miscellaneous_keywords.count(keyword) == 1;
|
||||
}
|
||||
|
||||
|
||||
Opm::EclIO::SummaryNode::Category Opm::EclIO::SummaryNode::category_from_keyword(
|
||||
const std::string& keyword
|
||||
) {
|
||||
static const std::unordered_set<std::string> miscellaneous_keywords = {"SEPARATE", "STEPTYPE", "SUMTHIN"};
|
||||
if (keyword.length() == 0) {
|
||||
Opm::EclIO::SummaryNode::Category
|
||||
Opm::EclIO::SummaryNode::category_from_keyword(const std::string& keyword)
|
||||
{
|
||||
if (keyword.empty() ||
|
||||
Opm::EclIO::SummaryNode::miscellaneous_exception(keyword))
|
||||
{
|
||||
return Category::Miscellaneous;
|
||||
}
|
||||
|
||||
if (Opm::EclIO::SummaryNode::miscellaneous_exception(keyword))
|
||||
return Category::Miscellaneous;
|
||||
|
||||
switch (keyword[0]) {
|
||||
case 'A': return Category::Aquifer;
|
||||
case 'B': return Category::Block;
|
||||
case 'C': return Category::Connection;
|
||||
case 'C': return distinguish_connection_from_completion(keyword);
|
||||
case 'F': return Category::Field;
|
||||
case 'G': return distinguish_group_from_node(keyword);
|
||||
case 'R': return Category::Region;
|
||||
case 'S': return Category::Segment;
|
||||
case 'W': return Category::Well;
|
||||
case 'W': return distinguish_well_from_completion(keyword);
|
||||
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 {
|
||||
if (use_name(category)) {
|
||||
return wgname;
|
||||
|
@ -99,6 +99,7 @@ template <> struct fmt::formatter<Opm::EclIO::SummaryNode::Category>: fmt::forma
|
||||
case Category::Region: name = "Region"; break;
|
||||
case Category::Block: name = "Block"; break;
|
||||
case Category::Connection: name = "Connection"; break;
|
||||
case Category::Completion: name = "Completion"; break;
|
||||
case Category::Segment: name = "Segment"; break;
|
||||
case Category::Aquifer: name = "Aquifer"; 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
|
||||
// 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
|
||||
// up a completion with offset 0.
|
||||
// up a connection with offset 0.
|
||||
const size_t global_index = args.num - 1;
|
||||
if (args.schedule_wells.empty())
|
||||
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
|
||||
// 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
|
||||
// up a completion with offset 0.
|
||||
// up a connection with offset 0.
|
||||
const size_t global_index = args.num - 1;
|
||||
if (args.schedule_wells.empty())
|
||||
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
|
||||
// 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
|
||||
// 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& 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
|
||||
// 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
|
||||
// up a completion with offset 0.
|
||||
// up a connection with offset 0.
|
||||
if (args.schedule_wells.empty()) {
|
||||
return zero;
|
||||
}
|
||||
@ -1049,7 +1050,7 @@ inline quantity trans_factors ( const fn_args& args ) {
|
||||
// No dynamic results for this well. Not open?
|
||||
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 auto& connections = xwPos->second.connections;
|
||||
auto connPos = std::find_if(connections.begin(), connections.end(),
|
||||
@ -1082,7 +1083,7 @@ inline quantity segpress ( const fn_args& args )
|
||||
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 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
|
||||
// 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
|
||||
// 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& xcon = xwPos->second.connections;
|
||||
@ -2429,6 +2430,7 @@ find_wells(const Opm::Schedule& schedule,
|
||||
switch (node.category) {
|
||||
case Opm::EclIO::SummaryNode::Category::Well:
|
||||
case Opm::EclIO::SummaryNode::Category::Connection:
|
||||
case Opm::EclIO::SummaryNode::Category::Completion:
|
||||
case Opm::EclIO::SummaryNode::Category::Segment:
|
||||
return find_single_well(schedule, node.wgname, sim_step);
|
||||
|
||||
@ -2450,7 +2452,7 @@ find_wells(const Opm::Schedule& schedule,
|
||||
|
||||
throw std::runtime_error {
|
||||
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) {
|
||||
case Cat::Connection: [[fallthrough]];
|
||||
case Cat::Completion: [[fallthrough]];
|
||||
case Cat::Field: [[fallthrough]];
|
||||
case Cat::Group: [[fallthrough]];
|
||||
case Cat::Segment: [[fallthrough]];
|
||||
@ -2484,7 +2487,10 @@ bool need_wells(const Opm::EclIO::SummaryNode& node)
|
||||
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)
|
||||
@ -3396,7 +3402,10 @@ namespace Evaluator {
|
||||
|
||||
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()) {
|
||||
// 'node_' represents a functional relation.
|
||||
// Capture evaluation function and return true.
|
||||
@ -3404,41 +3413,45 @@ namespace Evaluator {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto keyword = this->node_->keyword;
|
||||
auto dash_pos = keyword.find("_");
|
||||
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 (normKw.length() <= std::string::size_type{4}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyword.length() > 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";
|
||||
const auto tracer_name = normKw.substr(4);
|
||||
|
||||
pos = funs.find(tracer_tag);
|
||||
if (pos != funs.end()) {
|
||||
this->paramFunction_ = pos->second;
|
||||
return true;
|
||||
}
|
||||
const auto& tracers = this->es_.tracer();
|
||||
auto trPos = std::find_if(tracers.begin(), tracers.end(),
|
||||
[&tracer_name](const auto& tracer)
|
||||
{
|
||||
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;
|
||||
|
@ -445,21 +445,12 @@ double ecl_sum_get_general_var(const EclIO::ESmry* smry,
|
||||
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,
|
||||
const int timeIdx,
|
||||
const std::string& wellname,
|
||||
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,
|
||||
@ -467,7 +458,16 @@ double ecl_sum_get_group_var( const EclIO::ESmry* smry,
|
||||
const std::string& groupname,
|
||||
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,
|
||||
@ -478,8 +478,7 @@ double ecl_sum_get_well_connection_var( const EclIO::ESmry* smry,
|
||||
const int j,
|
||||
const int k)
|
||||
{
|
||||
const auto ijk = std::to_string(i) + ',' + std::to_string(j) + ',' + std::to_string(k);
|
||||
return smry->get(variable + ':' + wellname + ':' + ijk)[timeIdx];
|
||||
return smry->get(fmt::format("{}:{}:{},{},{}", variable, wellname, i, j, k))[timeIdx];
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
@ -497,7 +496,6 @@ struct setup {
|
||||
Deck deck;
|
||||
EclipseState es;
|
||||
const EclipseGrid& grid;
|
||||
std::shared_ptr<Python> python;
|
||||
Schedule schedule;
|
||||
SummaryConfig config;
|
||||
data::Wells wells;
|
||||
@ -511,8 +509,7 @@ struct setup {
|
||||
deck( Parser().parseFile( path) ),
|
||||
es( deck ),
|
||||
grid( es.getInputGrid() ),
|
||||
python( std::make_shared<Python>() ),
|
||||
schedule( deck, es, python),
|
||||
schedule( deck, es, std::make_shared<Python>()),
|
||||
config( deck, schedule, es.fieldProps(), es.aquifer()),
|
||||
wells( result_wells(w3_injector) ),
|
||||
grp_nwrk( result_group_nwrk() ),
|
||||
@ -520,6 +517,7 @@ struct setup {
|
||||
ta( "summary_test" )
|
||||
{}
|
||||
};
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
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( 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_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_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_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);
|
||||
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_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_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_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) +
|
||||
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) +
|
||||
|
@ -23,11 +23,16 @@
|
||||
|
||||
#include <opm/io/eclipse/SummaryNode.hpp>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
void expect_key(const Opm::EclIO::SummaryNode& node, const std::string& unique_key) {
|
||||
BOOST_CHECK_EQUAL(node.unique_key(), unique_key);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
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(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