Merge pull request #2473 from bska/fix-smry-anx-all

Output Summary for all Numeric Aquifers When ID Defaulted
This commit is contained in:
Joakim Hove 2021-05-17 07:11:08 +02:00 committed by GitHub
commit 147359c564
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 330 additions and 76 deletions

View File

@ -71,6 +71,8 @@ private:
Aquancon aqconn;
};
std::vector<int> analyticAquiferIDs(const AquiferConfig& cfg);
std::vector<int> numericAquiferIDs(const AquiferConfig& cfg);
}
#endif

View File

@ -3604,15 +3604,7 @@ configureRequiredRestartParameters(const SummaryConfig& sumcfg,
makeEvaluator(node);
if (aqConfig.hasAnalyticalAquifer()) {
auto aquiferIDs = std::vector<int>{};
for (const auto& aquifer : aqConfig.ct())
aquiferIDs.push_back(aquifer.aquiferID);
for (const auto& aquifer : aqConfig.fetp())
aquiferIDs.push_back(aquifer.aquiferID);
std::sort(aquiferIDs.begin(), aquiferIDs.end());
const auto aquiferIDs = analyticAquiferIDs(aqConfig);
for (const auto& node : requiredAquiferVectors(aquiferIDs))
makeEvaluator(node);

View File

@ -22,6 +22,8 @@
#include <opm/parser/eclipse/EclipseState/Tables/TableManager.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <algorithm>
namespace Opm {
AquiferConfig::AquiferConfig(const TableManager& tables, const EclipseGrid& grid,
@ -100,3 +102,38 @@ bool AquiferConfig::hasAnalyticalAquifer() const {
}
}
std::vector<int> Opm::analyticAquiferIDs(const AquiferConfig& cfg)
{
auto aquiferIDs = std::vector<int>{};
if (! cfg.hasAnalyticalAquifer())
return aquiferIDs;
for (const auto& aquifer : cfg.ct())
aquiferIDs.push_back(aquifer.aquiferID);
for (const auto& aquifer : cfg.fetp())
aquiferIDs.push_back(aquifer.aquiferID);
std::sort(aquiferIDs.begin(), aquiferIDs.end());
return aquiferIDs;
}
std::vector<int> Opm::numericAquiferIDs(const AquiferConfig& cfg)
{
auto aquiferIDs = std::vector<int>{};
if (! cfg.hasNumericalAquifer())
return aquiferIDs;
const auto& aqunum = cfg.numericalAquifers();
for (const auto& aq : aqunum.aquifers())
aquiferIDs.push_back(static_cast<int>(aq.first));
std::sort(aquiferIDs.begin(), aquiferIDs.end());
return aquiferIDs;
}

View File

@ -196,7 +196,9 @@ struct SummaryConfigContext {
bool is_pressure(const std::string& keyword) {
static const keyword_set presskw {
"BHP", "BHPH", "THP", "THPH", "PR", "PRD", "PRDH", "PRDF", "PRDA",
"BHP", "BHPH", "THP", "THPH", "PR",
"PRD", "PRDH", "PRDF", "PRDA",
"AQP", "NQP",
};
return is_in_set(presskw, keyword.substr(1));
@ -216,6 +218,8 @@ struct SummaryConfigContext {
"OVIR", "GVIR", "WVIR",
"OPI", "OPP", "GPI", "GPP", "WPI", "WPP",
"AQR", "AQRG", "NQR",
};
return is_in_set(ratekw, keyword.substr(1));
@ -239,6 +243,8 @@ struct SummaryConfigContext {
"WIT", "OIT", "GIT", "LIT", "NIT", "CIT", "VIT",
"WITH", "OITH", "GITH", "WVIT", "OVIT", "GVIT",
"AQT", "AQTG", "NQT",
};
return is_in_set(totalkw, keyword.substr(1));
@ -288,7 +294,22 @@ struct SummaryConfigContext {
bool is_aquifer(const std::string& keyword)
{
return (keyword[0] == 'A') && (keyword != "ALL");
static const auto aqukw = keyword_set {
"AQP", "AQR", "AQRG", "AQT", "AQTG",
"LQR", "LQT", "LQRG", "LQTG",
"NQP", "NQR", "NQT",
"AQTD", "AQPD",
};
return (keyword.size() >= std::string::size_type{4})
&& (keyword[0] == 'A')
&& is_in_set(aqukw, keyword.substr(1));
}
bool is_numeric_aquifer(const std::string& keyword)
{
// ANQP, ANQR, ANQT
return is_aquifer(keyword) && (keyword[1] == 'N');
}
bool is_connection_completion(const std::string& keyword)
@ -398,9 +419,15 @@ void handleMissingNode( const ParseContext& parseContext, ErrorGuard& errors, co
parseContext.handleError( ParseContext::SUMMARY_UNKNOWN_NODE, msg_fmt, location, errors );
}
void handleMissingAquifer( const ParseContext& parseContext, ErrorGuard& errors, const KeywordLocation& location, const int id) {
std::string msg_fmt = fmt::format("Request for missing aquifer {} in {{keyword}}\n"
"In {{file}} line {{line}}", id);
void handleMissingAquifer( const ParseContext& parseContext,
ErrorGuard& errors,
const KeywordLocation& location,
const int id,
const bool is_numeric)
{
std::string msg_fmt = fmt::format("Request for missing {} aquifer {} in {{keyword}}\n"
"In {{file}} line {{line}}",
is_numeric ? "numeric" : "anlytic", id);
parseContext.handleError(ParseContext::SUMMARY_UNKNOWN_AQUIFER, msg_fmt, location, errors);
}
@ -412,30 +439,28 @@ inline void keywordW( SummaryConfig::keyword_list& list,
}
inline void keywordAquifer( SummaryConfig::keyword_list& list,
const AquiferConfig& aquiferConfig,
SummaryConfigNode baseAquiferParam) {
if ( !aquiferConfig.active() ) return;
for (const auto& aq : aquiferConfig.ct()) {
list.push_back(baseAquiferParam.number(aq.aquiferID));
}
for (const auto& aq : aquiferConfig.fetp()) {
list.push_back(baseAquiferParam.number(aq.aquiferID));
const std::vector<int>& aquiferIDs,
SummaryConfigNode baseAquiferParam)
{
for (const auto& id : aquiferIDs) {
list.push_back(baseAquiferParam.number(id));
}
}
// later check whether parseContext and errors are required
// maybe loc will be needed
inline void keywordAquifer( SummaryConfig::keyword_list& list,
const AquiferConfig& aquiferConfig,
const ParseContext& parseContext,
ErrorGuard& errors,
const DeckKeyword& keyword) {
void keywordAquifer( SummaryConfig::keyword_list& list,
const std::vector<int>& analyticAquiferIDs,
const std::vector<int>& numericAquiferIDs,
const ParseContext& parseContext,
ErrorGuard& errors,
const DeckKeyword& keyword)
{
/*
The keywords starting with AL take as arguments a list of Aquiferlists -
this is not supported at all.
*/
if (keyword.name().rfind("AL", 0) == 0) {
if (keyword.name().find("AL") == std::string::size_type{0}) {
Opm::OpmLog::warning(Opm::OpmInputError::format("Unhandled summary keyword {keyword}\n"
"In {file} line {line}", keyword.location()));
return;
@ -447,16 +472,33 @@ inline void keywordAquifer( SummaryConfig::keyword_list& list,
.parameterType( parseKeywordType(keyword.name()) )
.isUserDefined( is_udq(keyword.name()) );
if (!keyword.empty() && keyword.getDataRecord().getDataItem().hasValue(0)) {
for( const int id: keyword.getIntData()) {
if (aquiferConfig.hasAquifer(id)) {
list.push_back(param.number(id));
} else {
handleMissingAquifer(parseContext, errors, keyword.location(), id);
const auto is_numeric = is_numeric_aquifer(keyword.name());
const auto& pertinentIDs = is_numeric
? numericAquiferIDs : analyticAquiferIDs;
if (keyword.empty() ||
! keyword.getDataRecord().getDataItem().hasValue(0))
{
keywordAquifer(list, pertinentIDs, param);
}
else {
auto ids = std::vector<int>{};
auto end = pertinentIDs.end();
for (const int id : keyword.getIntData()) {
// Note: std::find() could be std::lower_bound() here, but we
// typically expect the number of pertinent aquifer IDs to be
// small (< 10) so there's no big gain from a log(N) algorithm
// in the common case.
if (std::find(pertinentIDs.begin(), end, id) == end) {
handleMissingAquifer(parseContext, errors, keyword.location(), id, is_numeric);
continue;
}
ids.push_back(id);
}
} else {
keywordAquifer(list, aquiferConfig, param);
keywordAquifer(list, ids, param);
}
}
@ -700,15 +742,21 @@ inline void keywordF( SummaryConfig::keyword_list& list,
inline void keywordAquifer( SummaryConfig::keyword_list& list,
const std::string& keyword,
const AquiferConfig& aquiferConfig,
KeywordLocation loc) {
const std::vector<int>& analyticAquiferIDs,
const std::vector<int>& numericAquiferIDs,
KeywordLocation loc)
{
auto param = SummaryConfigNode {
keyword, SummaryConfigNode::Category::Aquifer, std::move(loc)
keyword, SummaryConfigNode::Category::Aquifer, std::move(loc)
}
.parameterType( parseKeywordType(keyword) )
.isUserDefined( is_udq(keyword) );
.parameterType( parseKeywordType(keyword) )
.isUserDefined( is_udq(keyword) );
keywordAquifer(list, aquiferConfig, param);
const auto& pertinentIDs = is_numeric_aquifer(keyword)
? numericAquiferIDs
: analyticAquiferIDs;
keywordAquifer(list, pertinentIDs, param);
}
inline void keywordF( SummaryConfig::keyword_list& list,
@ -1127,10 +1175,11 @@ 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 AquiferConfig& aquiferConfig,
const ParseContext& parseContext,
ErrorGuard& errors,
const GridDims& dims) {
@ -1149,7 +1198,7 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
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, aquiferConfig, parseContext, errors, keyword);
case Cat::Aquifer: return keywordAquifer(list, analyticAquiferIDs, numericAquiferIDs, parseContext, errors, keyword);
case Cat::Miscellaneous: return keywordMISC( list, keyword );
default:
@ -1162,9 +1211,10 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
inline void handleKW( SummaryConfig::keyword_list& list,
const std::string& keyword,
const std::vector<int>& analyticAquiferIDs,
const std::vector<int>& numericAquiferIDs,
const KeywordLocation& location,
const Schedule& schedule,
const AquiferConfig& aquiferConfig,
const ParseContext& /* parseContext */,
ErrorGuard& /* errors */) {
@ -1179,7 +1229,7 @@ inline void handleKW( SummaryConfig::keyword_list& list,
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, aquiferConfig, location );
case Cat::Aquifer: return keywordAquifer( list, keyword, analyticAquiferIDs, numericAquiferIDs, location );
case Cat::Miscellaneous: return keywordMISC( list, keyword, location);
default:
@ -1430,13 +1480,21 @@ SummaryConfig::SummaryConfig( const Deck& deck,
try {
SUMMARYSection section( deck );
SummaryConfigContext context;
const auto node_names = need_node_names(section) ? collect_node_names(schedule) : std::vector<std::string> {};
const auto node_names = need_node_names(section)
? collect_node_names(schedule)
: std::vector<std::string> {};
const auto analyticAquifers = analyticAquiferIDs(aquiferConfig);
const auto numericAquifers = numericAquiferIDs(aquiferConfig);
for (const auto& kw : section) {
if (is_processing_instruction(kw.name())) {
handleProcessingInstruction(kw.name());
} else {
handleKW(this->m_keywords, context, node_names, kw, schedule, field_props, aquiferConfig, parseContext, errors, dims);
handleKW(this->m_keywords, context,
node_names, analyticAquifers, numericAquifers,
kw, schedule, field_props, parseContext, errors, dims);
}
}
@ -1448,7 +1506,9 @@ SummaryConfig::SummaryConfig( const Deck& deck,
KeywordLocation location = deck_keyword.location();
location.keyword = fmt::format("{}/{}", meta_pair.first, kw);
handleKW(this->m_keywords, kw, location, schedule, aquiferConfig, parseContext, errors);
handleKW(this->m_keywords, kw,
analyticAquifers, numericAquifers,
location, schedule, parseContext, errors);
}
}
}

View File

@ -566,6 +566,104 @@ PORO
BOOST_CHECK( conf == conf2 );
}
BOOST_AUTO_TEST_CASE(Test_Aquifer_Config_Active)
{
const auto deck = Parser{}.parseString(R"(
START -- 0
10 MAY 2007 /
RUNSPEC
DIMENS
10 10 10 /
REGDIMS
3/
AQUDIMS
4 4 1* 1* 3 200 1* 1* /
GRID
DXV
10*400 /
DYV
10*400 /
DZV
10*400 /
TOPS
100*2202 /
PERMX
1000*0.25 /
COPY
PERMX PERMY /
PERMX PERMZ /
/
PORO
1000*0.15 /
AQUNUM
4 1 1 1 15000 5000 0.3 30 2700 / aq cell
5 2 1 1 150000 9000 0.3 30 2700 / aq cell
6 3 1 1 150000 9000 0.3 30 2700 / aq cell
7 4 1 1 150000 9000 0.3 30 2700 / aq cell
/
AQUCON
-- # I1 I2 J1 J2 K1 K2 Face
4 1 1 16 18 19 20 'I-' / connecting cells
5 2 2 16 18 19 20 'I-' / connecting cells
6 3 3 16 18 19 20 'I-' / connecting cells
7 4 4 16 18 19 20 'I-' / connecting cells
/
REGIONS
FIPNUM
200*1 300*2 500*3 /
FIPREG
200*10 300*20 500*30 /
SOLUTION
AQUCT
1 2040 1* 1000 .3 3.0e-5 1330 10 360.0 1 1* /
2 2040 1* 1000 .3 3.0e-5 1330 10 360.0 1 1* /
3 2040 1* 1000 .3 3.0e-5 1330 10 360.0 1 1* /
/
AQUANCON
1 1 10 10 2 10 10 'I-' 0.88 1 /
2 9 10 10 10 10 10 'I+' 0.88 1 /
3 9 9 8 10 9 8 'I+' 0.88 1 /
/
END
)");
const auto es = EclipseState { deck };
const auto& aquConfig = es.aquifer();
BOOST_CHECK_MESSAGE(aquConfig.hasAnalyticalAquifer(),
"Aquifer configuration object must have analytic aquifers");
BOOST_CHECK_MESSAGE(aquConfig.hasNumericalAquifer(),
"Aquifer configuration object must have numeric aquifers");
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(1), "Configuration object must have Aquifer ID 1");
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(2), "Configuration object must have Aquifer ID 2");
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(3), "Configuration object must have Aquifer ID 3");
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(4), "Configuration object must have Aquifer ID 4");
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(5), "Configuration object must have Aquifer ID 5");
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(6), "Configuration object must have Aquifer ID 6");
BOOST_CHECK_MESSAGE( aquConfig.hasAquifer(7), "Configuration object must have Aquifer ID 7");
BOOST_CHECK_MESSAGE(! aquConfig.hasAquifer(8), "Configuration object must NOT have Aquifer ID 8");
{
const auto expect = std::vector<int>{ 1, 2, 3 };
const auto analytic = analyticAquiferIDs(aquConfig);
BOOST_CHECK_EQUAL_COLLECTIONS(analytic.begin(), analytic.end(),
expect .begin(), expect .end());
}
{
const auto expect = std::vector<int>{ 4, 5, 6, 7 };
const auto numeric = numericAquiferIDs(aquConfig);
BOOST_CHECK_EQUAL_COLLECTIONS(numeric.begin(), numeric.end(),
expect .begin(), expect .end());
}
}
inline Deck createNumericalAquiferDeck() {
const char *deckData = R"(

View File

@ -83,7 +83,7 @@ DIMENS
REGDIMS
3/
AQUDIMS
1* 1* 1* 1* 3 200 1* 1* /
4 4 1* 1* 3 200 1* 1* /
GRID
DXV
10*400 /
@ -101,6 +101,19 @@ COPY
/
PORO
1000*0.15 /
AQUNUM
4 1 1 1 15000 5000 0.3 30 2700 / aq cell
5 2 1 1 150000 9000 0.3 30 2700 / aq cell
6 3 1 1 150000 9000 0.3 30 2700 / aq cell
7 4 1 1 150000 9000 0.3 30 2700 / aq cell
/
AQUCON
-- # I1 I2 J1 J2 K1 K2 Face
4 1 1 16 18 19 20 'I-' / connecting cells
5 2 2 16 18 19 20 'I-' / connecting cells
6 3 3 16 18 19 20 'I-' / connecting cells
7 4 4 16 18 19 20 'I-' / connecting cells
/
REGIONS
FIPNUM
200*1 300*2 500*3 /
@ -175,7 +188,7 @@ static std::vector< std::string > sorted_key_names( const SummaryConfig& summary
return ret;
}
static SummaryConfig createSummary( std::string input , const ParseContext& parseContext = ParseContext()) {
static SummaryConfig createSummary(const std::string& input , const ParseContext& parseContext = ParseContext()) {
ErrorGuard errors;
auto deck = createDeck( input );
auto python = std::make_shared<Python>();
@ -563,22 +576,44 @@ BOOST_AUTO_TEST_CASE( REMOVE_DUPLICATED_ENTRIES ) {
}
BOOST_AUTO_TEST_CASE( ANALYTICAL_AQUIFERS ) {
{
const auto faulty_input = std::string {R"(
AAQT
-- Neither of these are analytic aquifers => input error
4 5 6 7 /
)" };
BOOST_CHECK_THROW(const auto summary = createSummary(faulty_input),
OpmInputError);
}
{
const auto faulty_input = std::string {R"(
AAQP
-- Aquifer ID out of range => input error
1729 /
)" };
BOOST_CHECK_THROW(const auto summary = createSummary(faulty_input),
OpmInputError);
}
const std::string input = R"(
AAQR
1 2 /
AAQP
2 1 /
AAQT
/
AAQRG
/
AAQTG
/
AAQTD
/
AAQPD
/
)";
AAQR
1 2 /
AAQP
2 1 /
AAQT
/
AAQRG
/
AAQTG
/
AAQTD
/
AAQPD
/
)";
const auto summary = createSummary( input );
const auto keywords = { "AAQP", "AAQP", "AAQPD", "AAQPD", "AAQPD",
@ -593,16 +628,46 @@ BOOST_AUTO_TEST_CASE( ANALYTICAL_AQUIFERS ) {
names.begin(), names.end() );
}
BOOST_AUTO_TEST_CASE( NUMERICAL_AQUIFERS ) {
BOOST_AUTO_TEST_CASE( NUMERICAL_AQUIFERS )
{
{
const auto faulty_input = std::string {R"(
ANQR
-- Neither of these are numeric aquifers => input error
1 2 3 /
)" };
BOOST_CHECK_THROW(const auto summary = createSummary(faulty_input),
OpmInputError);
}
{
const auto faulty_input = std::string {R"(
ANQP
-- Aquifer ID out of range => input error
42 /
)" };
BOOST_CHECK_THROW(const auto summary = createSummary(faulty_input),
OpmInputError);
}
const std::string input = R"(
ANQR
1 2 /
ANQP
2 1 /
ANQT
/
)";
ANQR
5 /
ANQP
4 7 /
ANQT
/
)";
const auto summary = createSummary( input );
const auto keywords = { "ANQP", "ANQP", "ANQR", "ANQT", "ANQT", "ANQT", "ANQT" };
const auto names = sorted_keywords( summary );
BOOST_CHECK_EQUAL_COLLECTIONS(
keywords.begin(), keywords.end(),
names.begin(), names.end() );
}
static const auto GMWSET_keywords = {