From fd773423b061befe97f5475423344ff4a91478fa Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 20 Apr 2020 09:56:05 +0200 Subject: [PATCH] adding the following aquifer summary keywords AAQR, AAQT, AAQP --- examples/msim.cpp | 3 +- examples/opmi.cpp | 3 +- opm/output/data/Aquifer.hpp | 17 +++++ opm/output/eclipse/Summary.hpp | 6 +- opm/parser/eclipse/EclipseState/AquiferCT.hpp | 2 + .../eclipse/EclipseState/AquiferConfig.hpp | 1 + opm/parser/eclipse/EclipseState/Aquifetp.hpp | 2 + .../SummaryConfig/SummaryConfig.hpp | 7 +- src/opm/output/eclipse/Summary.cpp | 75 ++++++++++++++++++- .../parser/eclipse/EclipseState/AquiferCT.cpp | 7 ++ .../eclipse/EclipseState/AquiferConfig.cpp | 4 + .../parser/eclipse/EclipseState/Aquifetp.cpp | 7 ++ .../SummaryConfig/SummaryConfig.cpp | 65 ++++++++++++++-- tests/parser/SummaryConfigTests.cpp | 6 +- 14 files changed, 187 insertions(+), 18 deletions(-) diff --git a/examples/msim.cpp b/examples/msim.cpp index 846d3e5de..62e1f2fc7 100644 --- a/examples/msim.cpp +++ b/examples/msim.cpp @@ -43,7 +43,8 @@ int main(int /* argc */, char** argv) { Opm::Deck deck = parser.parseFile(deck_file, parse_context, error_guard); Opm::EclipseState state(deck); Opm::Schedule schedule(deck, state, parse_context, error_guard, python); - Opm::SummaryConfig summary_config(deck, schedule, state.getTableManager(), parse_context, error_guard); + Opm::SummaryConfig summary_config(deck, schedule, state.getTableManager(), state.aquifer(), + parse_context, error_guard); if (error_guard) { error_guard.dump(); diff --git a/examples/opmi.cpp b/examples/opmi.cpp index 2ff185f5c..97d730fa5 100644 --- a/examples/opmi.cpp +++ b/examples/opmi.cpp @@ -70,7 +70,8 @@ inline void loadDeck( const char * deck_file) { std::cout << "creating SummaryConfig .... "; std::cout.flush(); start = std::chrono::system_clock::now(); - Opm::SummaryConfig summary( deck, schedule, state.getTableManager( ), parseContext, errors ); + Opm::SummaryConfig summary( deck, schedule, state.getTableManager( ), state.aquifer(), + parseContext, errors ); auto summary_time = std::chrono::system_clock::now() - start; std::cout << "complete." << std::endl << std::endl; diff --git a/opm/output/data/Aquifer.hpp b/opm/output/data/Aquifer.hpp index 0985b27d8..5f8301db1 100644 --- a/opm/output/data/Aquifer.hpp +++ b/opm/output/data/Aquifer.hpp @@ -46,14 +46,31 @@ namespace Opm { namespace data { struct AquiferData { int aquiferID; //< One-based ID, range 1..NANAQ double pressure; //< Aquifer pressure + double fluxRate; //< Aquifer influx rate (liquid aquifer) + // TODO: volume should have a better name, since meaning not clear double volume; //< Produced liquid volume double initPressure; //< Aquifer's initial pressure double datumDepth; //< Aquifer's pressure reference depth AquiferType type; std::shared_ptr aquFet; + + double get(const std::string& key) const + { + if ( key == "AAQR" ) { + return this->fluxRate; + } else if ( key == "AAQT" ) { + return this->volume; + } else if ( key == "AAQP" ) { + return this->pressure; + } + return 0.; + } }; + // TODO: not sure what extension we will need + using Aquifers = std::map; + }} // Opm::data #endif // OPM_OUTPUT_AQUIFER_HPP diff --git a/opm/output/eclipse/Summary.hpp b/opm/output/eclipse/Summary.hpp index a6698800d..78e14d1c5 100644 --- a/opm/output/eclipse/Summary.hpp +++ b/opm/output/eclipse/Summary.hpp @@ -22,6 +22,9 @@ #include +// TODO: following the convention here, it should be removed. +#include + #include #include #include @@ -68,7 +71,8 @@ public: const data::GroupAndNetworkValues& group_and_nwrk_solution, GlobalProcessParameters single_values, const RegionParameters& region_values = {}, - const BlockValues& block_values = {}) const; + const BlockValues& block_values = {}, + const data::Aquifers& aquifers_values = {}) const; void write() const; diff --git a/opm/parser/eclipse/EclipseState/AquiferCT.hpp b/opm/parser/eclipse/EclipseState/AquiferCT.hpp index 91e342a68..aabef7bef 100755 --- a/opm/parser/eclipse/EclipseState/AquiferCT.hpp +++ b/opm/parser/eclipse/EclipseState/AquiferCT.hpp @@ -118,6 +118,8 @@ namespace Opm { const std::vector& data() const; bool operator==(const AquiferCT& other) const; + bool hasAquifer(const int aquID) const; + template void serializeOp(Serializer& serializer) { diff --git a/opm/parser/eclipse/EclipseState/AquiferConfig.hpp b/opm/parser/eclipse/EclipseState/AquiferConfig.hpp index 91c220356..4ede92e9a 100644 --- a/opm/parser/eclipse/EclipseState/AquiferConfig.hpp +++ b/opm/parser/eclipse/EclipseState/AquiferConfig.hpp @@ -43,6 +43,7 @@ public: const Aquifetp& fetp() const; const Aquancon& connections() const; bool operator==(const AquiferConfig& other); + bool hasAquifer(const int aquID) const; template void serializeOp(Serializer& serializer) diff --git a/opm/parser/eclipse/EclipseState/Aquifetp.hpp b/opm/parser/eclipse/EclipseState/Aquifetp.hpp index bcd62ca2d..fa2e1846c 100755 --- a/opm/parser/eclipse/EclipseState/Aquifetp.hpp +++ b/opm/parser/eclipse/EclipseState/Aquifetp.hpp @@ -76,6 +76,8 @@ class Aquifetp { std::vector::const_iterator end() const; bool operator==(const Aquifetp& other) const; + bool hasAquifer(const int aquID) const; + template void serializeOp(Serializer& serializer) { diff --git a/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp b/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp index 35de9a726..f0a3eb7ea 100644 --- a/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp +++ b/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp @@ -123,6 +123,7 @@ namespace Opm { class ParseContext; class Schedule; class TableManager; + class AquiferConfig; class SummaryConfig { public: @@ -134,6 +135,7 @@ namespace Opm { SummaryConfig( const Deck&, const Schedule&, const TableManager&, + const AquiferConfig&, const ParseContext&, ErrorGuard&); @@ -141,12 +143,14 @@ namespace Opm { SummaryConfig( const Deck&, const Schedule&, const TableManager&, + const AquiferConfig&, const ParseContext&, T&&); SummaryConfig( const Deck&, const Schedule&, - const TableManager&); + const TableManager&, + const AquiferConfig&); SummaryConfig(const keyword_list& kwds, const std::set& shortKwds, @@ -209,6 +213,7 @@ namespace Opm { SummaryConfig( const Deck& deck, const Schedule& schedule, const TableManager& tables, + const AquiferConfig& aquiferConfig, const ParseContext& parseContext, ErrorGuard& errors, const GridDims& dims); diff --git a/src/opm/output/eclipse/Summary.cpp b/src/opm/output/eclipse/Summary.cpp index 05858b18c..8e6ff65f8 100644 --- a/src/opm/output/eclipse/Summary.cpp +++ b/src/opm/output/eclipse/Summary.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include @@ -1447,6 +1448,12 @@ static const std::unordered_map< std::string, Opm::UnitSystem::measure> block_un {"BOVIS" , Opm::UnitSystem::measure::viscosity}, }; +static const std::unordered_map< std::string, Opm::UnitSystem::measure> aquifer_units = { + {"AAQT", Opm::UnitSystem::measure::liquid_surface_volume}, + {"AAQR", Opm::UnitSystem::measure::liquid_surface_rate}, + {"AAQP", Opm::UnitSystem::measure::pressure}, +}; + inline std::vector find_wells( const Opm::Schedule& schedule, const Opm::EclIO::SummaryNode& node, const int sim_step, @@ -1639,6 +1646,7 @@ namespace Evaluator { const std::map& single; const std::map>& region; const std::map, double>& block; + const Opm::data::Aquifers& aquifers; }; class Base @@ -1747,6 +1755,35 @@ namespace Evaluator { } }; + + class AquiferValue: public Base + { + public: + explicit AquiferValue(Opm::EclIO::SummaryNode node, + const Opm::UnitSystem::measure m) + : node_(std::move(node)) + , m_ (m) + {} + + void update(const std::size_t /* sim_step */, + const double /* stepSize */, + const InputData& input, + const SimulatorResults& simRes, + Opm::SummaryState& st) const override + { + auto xPos = simRes.aquifers.find(this->node_.number); + if (xPos == simRes.aquifers.end()) { + return; + } + + const auto& usys = input.es.getUnits(); + updateValue(this->node_, usys.from_si(this->m_, xPos->second.get(this->node_.keyword)), st); + } + private: + Opm::EclIO::SummaryNode node_; + Opm::UnitSystem::measure m_; + }; + class RegionValue : public Base { public: @@ -1983,12 +2020,14 @@ namespace Evaluator { Descriptor functionRelation(); Descriptor blockValue(); + Descriptor aquiferValue(); Descriptor regionValue(); Descriptor globalProcessValue(); Descriptor userDefinedValue(); Descriptor unknownParameter(); bool isBlockValue(); + bool isAquiferValue(); bool isRegionValue(); bool isGlobalProcessValue(); bool isFunctionRelation(); @@ -2009,6 +2048,9 @@ namespace Evaluator { if (this->isBlockValue()) return this->blockValue(); + if (this->isAquiferValue()) + return this->aquiferValue(); + if (this->isRegionValue()) return this->regionValue(); @@ -2045,6 +2087,18 @@ namespace Evaluator { return desc; } + Factory::Descriptor Factory::aquiferValue() + { + auto desc = this->unknownParameter(); + + desc.unit = this->directUnitString(); + desc.evaluator.reset(new AquiferValue { + *this->node_, this->paramUnit_ + }); + + return desc; + } + Factory::Descriptor Factory::regionValue() { auto desc = this->unknownParameter(); @@ -2105,6 +2159,18 @@ namespace Evaluator { return true; } + bool Factory::isAquiferValue() + { + auto pos = aquifer_units.find(this->node_->keyword); + if (pos == aquifer_units.end()) return false; + + // if the aquifer does not exist, should we warn? + if ( !this->es_.aquifer().hasAquifer(this->node_->number) ) return false; + + this->paramUnit_ = pos->second; + return true; + } + bool Factory::isRegionValue() { auto keyword = this->node_->keyword; @@ -2392,6 +2458,7 @@ public: const GlobalProcessParameters& single_values, const RegionParameters& region_values, const BlockValues& block_values, + const data::Aquifers& aquifer_values, SummaryState& st) const; void internal_store(const SummaryState& st, const int report_step); @@ -2494,6 +2561,7 @@ eval(const EclipseState& es, const GlobalProcessParameters& single_values, const RegionParameters& region_values, const BlockValues& block_values, + const data::Aquifers& aquifer_values, Opm::SummaryState& st) const { const Evaluator::InputData input { @@ -2501,7 +2569,7 @@ eval(const EclipseState& es, }; const Evaluator::SimulatorResults simRes { - well_solution, grp_nwrk_solution, single_values, region_values, block_values + well_solution, grp_nwrk_solution, single_values, region_values, block_values, aquifer_values }; for (auto& evalPtr : this->outputParameters_.getEvaluators()) { @@ -2794,7 +2862,8 @@ void Summary::eval(SummaryState& st, const data::GroupAndNetworkValues& grp_nwrk_solution, GlobalProcessParameters single_values, const RegionParameters& region_values, - const BlockValues& block_values) const + const BlockValues& block_values, + const Opm::data::Aquifers& aquifer_values) const { validateElapsedTime(secs_elapsed, es, st); @@ -2816,7 +2885,7 @@ void Summary::eval(SummaryState& st, this->pImpl_->eval(es, schedule, sim_step, duration, well_solution, grp_nwrk_solution, single_values, - region_values, block_values, st); + region_values, block_values, aquifer_values, st); st.update_elapsed(duration); } diff --git a/src/opm/parser/eclipse/EclipseState/AquiferCT.cpp b/src/opm/parser/eclipse/EclipseState/AquiferCT.cpp index 000ad9da5..d044cfc2d 100755 --- a/src/opm/parser/eclipse/EclipseState/AquiferCT.cpp +++ b/src/opm/parser/eclipse/EclipseState/AquiferCT.cpp @@ -175,4 +175,11 @@ const std::vector& AquiferCT::data() const { return this->m_aquct; } +bool AquiferCT::hasAquifer(const int aquID) const { + const auto it = std::find_if(this->m_aquct.begin(), this->m_aquct.end(), + [&aquID](const auto& aqu){ return aqu.aquiferID == aquID; }); + + return ( it != this->m_aquct.end() ); +} + } diff --git a/src/opm/parser/eclipse/EclipseState/AquiferConfig.cpp b/src/opm/parser/eclipse/EclipseState/AquiferConfig.cpp index 48bf2a53b..9ef44c6cd 100644 --- a/src/opm/parser/eclipse/EclipseState/AquiferConfig.cpp +++ b/src/opm/parser/eclipse/EclipseState/AquiferConfig.cpp @@ -70,4 +70,8 @@ const Aquancon& AquiferConfig::connections() const { return this->aqconn; } +bool AquiferConfig::hasAquifer(const int aquID) const { + return aquifetp.hasAquifer(aquID) || aquiferct.hasAquifer(aquID); +} + } diff --git a/src/opm/parser/eclipse/EclipseState/Aquifetp.cpp b/src/opm/parser/eclipse/EclipseState/Aquifetp.cpp index dad295eca..dcaa119c3 100755 --- a/src/opm/parser/eclipse/EclipseState/Aquifetp.cpp +++ b/src/opm/parser/eclipse/EclipseState/Aquifetp.cpp @@ -113,4 +113,11 @@ std::vector::const_iterator Aquifetp::end() const { return this->m_aqufetp.end(); } +bool Aquifetp::hasAquifer(const int aquID) const { + const auto it = std::find_if(this->m_aqufetp.begin(), this->m_aqufetp.end(), + [&aquID](const auto& aqu){ return aqu.aquiferID == aquID; }); + + return ( it != this->m_aqufetp.end() ); +} + } diff --git a/src/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.cpp b/src/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.cpp index 6a37c7efa..c8d16d0cd 100644 --- a/src/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.cpp +++ b/src/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.cpp @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -71,7 +72,8 @@ namespace { "WVPR", "WVPT", "WWCT", "WWGR", "WWIR", "WWIT", "WWPR", "WWPT", // ALL will not expand to these keywords yet - "AAQR", "AAQRG", "AAQT", "AAQTG" + // Analytical aquifer keywords + "AAQR", "AAQRG", "AAQT", "AAQTG", "AAQP" }; const std::vector GMWSET_keywords = { @@ -354,6 +356,33 @@ inline void keywordW( SummaryConfig::keyword_list& list, for (const auto& wname : well_names) list.push_back( baseWellParam.namedEntity(wname) ); } +// 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, + const Schedule& schedule) { + auto param = SummaryConfigNode { + keyword.name(), SummaryConfigNode::Category::Aquifer, keyword.location() + } + .parameterType( parseKeywordType(keyword.name()) ) + .isUserDefined( is_udq(keyword.name()) ); + + if (keyword.size() && keyword.getDataRecord().getDataItem().hasValue(0)) { + for( const int id: keyword.getIntData()) { + list.push_back(param.number(id)); + } + } else { + for (const auto& aq : aquiferConfig.ct()) { + list.push_back(param.number(aq.aquiferID)); + } + for (const auto& aq : aquiferConfig.fetp()) { + list.push_back(param.number(aq.aquiferID)); + } + } +} inline void keywordW( SummaryConfig::keyword_list& list, const std::string& keyword, @@ -518,6 +547,18 @@ inline void keywordF( SummaryConfig::keyword_list& list, list.push_back( std::move(param) ); } +inline void keywordAquifer( SummaryConfig::keyword_list& list, + const std::string& keyword, + KeywordLocation loc) { + auto param = SummaryConfigNode { + keyword, SummaryConfigNode::Category::Aquifer, std::move(loc) + } + .parameterType( parseKeywordType(keyword) ) + .isUserDefined( is_udq(keyword) ); + + list.push_back( std::move(param) ); +} + inline void keywordF( SummaryConfig::keyword_list& list, const DeckKeyword& keyword ) { if( keyword.name() == "FMWSET" ) return; @@ -879,6 +920,7 @@ inline void keywordMISC( SummaryConfig::keyword_list& list, const DeckKeyword& keyword, const Schedule& schedule, const TableManager& tables, + const AquiferConfig& aquiferConfig, const ParseContext& parseContext, ErrorGuard& errors, const GridDims& dims) { @@ -897,6 +939,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, schedule); case Cat::Miscellaneous: return keywordMISC( list, keyword ); default: @@ -919,12 +962,12 @@ inline void handleKW( SummaryConfig::keyword_list& list, if (is_udq(keyword)) throw std::logic_error("UDQ keywords not handleded when expanding alias list"); - if (is_aquifer(keyword)) { +/* if (is_aquifer(keyword)) { std::string msg = "Summary output keyword {keyword} of type AQUIFER is not supported\n" "In {{file}} line {{line}}"; parseContext.handleError(ParseContext::SUMMARY_UNHANDLED_KEYWORD, msg, location, errors); return; - } + } */ using Cat = SummaryConfigNode::Category; const auto cat = parseKeywordCategory( keyword ); @@ -933,6 +976,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, location ); case Cat::Miscellaneous: return keywordMISC( list, keyword, location); default: @@ -956,6 +1000,7 @@ SummaryConfigNode::Category parseKeywordCategory(const std::string& keyword) { if (is_special(keyword)) { return Cat::Miscellaneous; } switch (keyword[0]) { + // TODO: maybe A and N? case 'A': return Cat::Aquifer; case 'W': return Cat::Well; case 'G': return distinguish_group_from_node(keyword); @@ -1136,6 +1181,7 @@ bool operator<(const SummaryConfigNode& lhs, const SummaryConfigNode& rhs) SummaryConfig::SummaryConfig( const Deck& deck, const Schedule& schedule, const TableManager& tables, + const AquiferConfig& aquiferConfig, const ParseContext& parseContext, ErrorGuard& errors, const GridDims& dims) { @@ -1148,7 +1194,7 @@ SummaryConfig::SummaryConfig( const Deck& deck, if (is_processing_instruction(kw.name())) { handleProcessingInstruction(kw.name()); } else { - handleKW(this->m_keywords, node_names, kw, schedule, tables, parseContext, errors, dims); + handleKW(this->m_keywords, node_names, kw, schedule, tables, aquiferConfig, parseContext, errors, dims); } } @@ -1182,9 +1228,10 @@ SummaryConfig::SummaryConfig( const Deck& deck, SummaryConfig::SummaryConfig( const Deck& deck, const Schedule& schedule, const TableManager& tables, + const AquiferConfig& aquiferConfig, const ParseContext& parseContext, ErrorGuard& errors) : - SummaryConfig( deck , schedule, tables, parseContext, errors, GridDims( deck )) + SummaryConfig( deck , schedule, tables, aquiferConfig, parseContext, errors, GridDims( deck )) { } @@ -1192,16 +1239,18 @@ template SummaryConfig::SummaryConfig( const Deck& deck, const Schedule& schedule, const TableManager& tables, + const AquiferConfig& aquiferConfig, const ParseContext& parseContext, T&& errors) : - SummaryConfig(deck, schedule, tables, parseContext, errors) + SummaryConfig(deck, schedule, tables, aquiferConfig, parseContext, errors) {} SummaryConfig::SummaryConfig( const Deck& deck, const Schedule& schedule, - const TableManager& tables) : - SummaryConfig(deck, schedule, tables, ParseContext(), ErrorGuard()) + const TableManager& tables, + const AquiferConfig& aquiferConfig) : + SummaryConfig(deck, schedule, tables, aquiferConfig, ParseContext(), ErrorGuard()) {} diff --git a/tests/parser/SummaryConfigTests.cpp b/tests/parser/SummaryConfigTests.cpp index d119ec436..e6408fbb5 100644 --- a/tests/parser/SummaryConfigTests.cpp +++ b/tests/parser/SummaryConfigTests.cpp @@ -156,7 +156,7 @@ static SummaryConfig createSummary( std::string input , const ParseContext& pars auto python = std::make_shared(); EclipseState state( deck ); Schedule schedule(deck, state, parseContext, errors, python); - return SummaryConfig( deck, schedule, state.getTableManager( ), parseContext, errors ); + return SummaryConfig(deck, schedule, state.getTableManager(), <#initializer#>, parseContext, errors); } BOOST_AUTO_TEST_CASE(wells_all) { @@ -189,7 +189,7 @@ BOOST_AUTO_TEST_CASE(wells_missingI) { parseContext.update(ParseContext::SUMMARY_UNKNOWN_WELL, InputError::THROW_EXCEPTION); EclipseState state( deck ); Schedule schedule(deck, state, parseContext, errors, python ); - BOOST_CHECK_NO_THROW( SummaryConfig( deck, schedule, state.getTableManager( ), parseContext, errors )); + BOOST_CHECK_NO_THROW(SummaryConfig(deck, schedule, state.getTableManager(), <#initializer#>, parseContext, errors)); } @@ -1077,7 +1077,7 @@ END const auto parseContext = ParseContext{}; const auto state = EclipseState (deck); const auto schedule = Schedule (deck, state, parseContext, errors, std::make_shared()); - const auto smry = SummaryConfig(deck, schedule, state.getTableManager(), parseContext, errors ); + const auto smry = SummaryConfig(deck, schedule, state.getTableManager(), <#initializer#>, parseContext, errors); BOOST_CHECK_MESSAGE(deck.hasKeyword("GPR"), R"(Deck must have "GPR" keyword)"); BOOST_CHECK_MESSAGE(smry.hasKeyword("GPR"), R"(SummaryConfig must have "GPR" keyword)");