diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 5fdb279ae3f..d1aa1f9028a 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -41,6 +41,7 @@ list (APPEND MAIN_SOURCE_FILES ) if(ENABLE_ECL_INPUT) list(APPEND MAIN_SOURCE_FILES + src/opm/io/eclipse/SummaryNode.cpp src/opm/json/JsonObject.cpp src/opm/parser/eclipse/Deck/Deck.cpp src/opm/parser/eclipse/Deck/DeckItem.cpp @@ -239,6 +240,7 @@ if(ENABLE_ECL_OUTPUT) src/opm/io/eclipse/ERst.cpp src/opm/io/eclipse/ESmry.cpp src/opm/io/eclipse/OutputStream.cpp + src/opm/io/eclipse/SummaryNode.cpp src/opm/io/eclipse/rst/connection.cpp src/opm/io/eclipse/rst/group.cpp src/opm/io/eclipse/rst/header.cpp @@ -500,6 +502,7 @@ list( APPEND PUBLIC_HEADER_FILES ) if(ENABLE_ECL_INPUT) list(APPEND PUBLIC_HEADER_FILES + opm/io/eclipse/SummaryNode.hpp opm/json/JsonObject.hpp opm/parser/eclipse/Utility/Stringview.hpp opm/parser/eclipse/Utility/Functional.hpp @@ -719,6 +722,7 @@ if(ENABLE_ECL_OUTPUT) opm/io/eclipse/ESmry.hpp opm/io/eclipse/PaddedOutputString.hpp opm/io/eclipse/OutputStream.hpp + opm/io/eclipse/SummaryNode.hpp opm/io/eclipse/rst/connection.hpp opm/io/eclipse/rst/group.hpp opm/io/eclipse/rst/header.hpp @@ -763,3 +767,9 @@ if(ENABLE_ECL_OUTPUT) opm/output/OutputWriter.hpp ) endif() + +if(ENABLE_ECL_INPUT OR ENABLE_ECL_OUTPUT) + list(APPEND TEST_SOURCE_FILES + tests/test_SummaryNode.cpp +) +endif() diff --git a/opm/io/eclipse/SummaryNode.hpp b/opm/io/eclipse/SummaryNode.hpp new file mode 100644 index 00000000000..9339ca13185 --- /dev/null +++ b/opm/io/eclipse/SummaryNode.hpp @@ -0,0 +1,58 @@ +/* + Copyright 2020 Equinor ASA. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . + */ + +#include + +namespace Opm::EclIO { + +struct SummaryNode { + enum class Category { + Well, + Group, + Field, + Region, + Block, + Connection, + Segment, + Miscellaneous, + }; + + enum class Type { + Rate, + Total, + Ratio, + Pressure, + Count, + Mode, + Undefined, + }; + + std::string keyword; + Category category; + Type type; + std::string wgname; + int number; + + constexpr static int default_number { std::numeric_limits::min() }; + + std::string unique_key() const; + bool is_user_defined() const; +}; + +} // namespace Opm::EclIO diff --git a/opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp b/opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp index 20e9c4b08f1..3907061f7fc 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp @@ -21,8 +21,9 @@ #define GROUP2_HPP -#include #include +#include +#include #include #include @@ -221,8 +222,8 @@ struct ProductionControls { // [[deprecated("use Group::control_group() or Group::flow_group()")]] const std::string& parent() const; - const std::string& control_group() const; - const std::string& flow_group() const; + std::optional control_group() const; + std::optional flow_group() const; bool updateParent(const std::string& parent); bool updateInjection(const GroupInjectionProperties& injection); diff --git a/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp b/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp index 07a2c29f265..6f6ffbc6d44 100644 --- a/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp +++ b/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp @@ -26,6 +26,7 @@ #include #include +#include #include namespace Opm { @@ -38,17 +39,8 @@ namespace Opm { class SummaryConfigNode { public: - enum class Category { - Well, Group, Field, - Region, Block, - Connection, Segment, - Miscellaneous, - }; - - enum class Type { - Rate, Total, Ratio, Pressure, Count, Mode, - Undefined, - }; + using Category = Opm::EclIO::SummaryNode::Category; + using Type = Opm::EclIO::SummaryNode::Type; SummaryConfigNode() = default; explicit SummaryConfigNode(std::string keyword, const Category cat, Location loc_arg); @@ -68,6 +60,10 @@ namespace Opm { std::string uniqueNodeKey() const; const Location& location( ) const { return this->loc; } + operator Opm::EclIO::SummaryNode() const { + return { keyword_, category_, type_, name_, number_ }; + } + template void serializeOp(Serializer& serializer) { diff --git a/src/opm/io/eclipse/SummaryNode.cpp b/src/opm/io/eclipse/SummaryNode.cpp new file mode 100644 index 00000000000..ad9ed54f089 --- /dev/null +++ b/src/opm/io/eclipse/SummaryNode.cpp @@ -0,0 +1,113 @@ +/* + Copyright 2020 Equinor ASA. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . + */ + +#include +#include +#include +#include +#include + +#include + +namespace { + +constexpr bool use_number(Opm::EclIO::SummaryNode::Category category) { + switch (category) { + case Opm::EclIO::SummaryNode::Category::Block: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Connection: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Region: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Segment: + return true; + case Opm::EclIO::SummaryNode::Category::Field: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Group: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Miscellaneous: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Well: + return false; + } +} + +constexpr bool use_name(Opm::EclIO::SummaryNode::Category category) { + switch (category) { + case Opm::EclIO::SummaryNode::Category::Connection: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Group: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Segment: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Well: + return true; + case Opm::EclIO::SummaryNode::Category::Block: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Field: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Miscellaneous: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Region: + return false; + } +} + +}; + +std::string Opm::EclIO::SummaryNode::unique_key() const { + std::vector key_parts { keyword } ; + + if (use_name(category)) + key_parts.emplace_back(wgname); + + if (use_number(category)) + key_parts.emplace_back(std::to_string(number)); + + auto compose_key = [](std::string& key, const std::string& key_part) -> std::string { + constexpr auto delimiter { ':' } ; + return key.empty() ? key_part : key + delimiter + key_part; + }; + + return std::accumulate(std::begin(key_parts), std::end(key_parts), std::string(), compose_key); +} + +bool Opm::EclIO::SummaryNode::is_user_defined() const { + static const std::unordered_set udq_blacklist { + "AUTOCOAR", + "AUTOREF", + "FULLIMP", + "GUIDECAL", + "GUIDERAT", + "GUPFREQ", + "RUNSPEC", + "RUNSUM", + "SUMMARY", + "SUMTHIN", + "SURF", + "SURFACT", + "SURFACTW", + "SURFADDW", + "SURFADS", + "SURFCAPD", + "SURFESAL", + "SURFNUM", + "SURFOPTS", + "SURFROCK", + "SURFST", + "SURFSTES", + "SURFVISC", + "SURFWNUM", + } ; + + static const std::regex user_defined_regex { "[ABCFGRSW]U[A-Z]+" } ; + + const bool matched { std::regex_match(keyword, user_defined_regex) } ; + const bool blacklisted { udq_blacklist.find(keyword) != udq_blacklist.end() } ; + + return matched && !blacklisted; +} diff --git a/src/opm/output/eclipse/Summary.cpp b/src/opm/output/eclipse/Summary.cpp index a88f84a0c73..66917be38a2 100644 --- a/src/opm/output/eclipse/Summary.cpp +++ b/src/opm/output/eclipse/Summary.cpp @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -68,7 +69,7 @@ namespace { struct ParamCTorArgs { std::string kw; - Opm::SummaryConfigNode::Type type; + Opm::EclIO::SummaryNode::Type type; }; using p_cmode = Opm::Group::ProductionCMode; @@ -98,7 +99,7 @@ namespace { std::vector requiredRestartVectors() { - using Type = ::Opm::SummaryConfigNode::Type; + using Type = ::Opm::EclIO::SummaryNode::Type; return { // Production @@ -134,105 +135,86 @@ namespace { }; } - std::vector + std::vector requiredRestartVectors(const ::Opm::Schedule& sched) { - auto entities = std::vector{}; - - using SN = ::Opm::SummaryConfigNode; - const auto& vectors = requiredRestartVectors(); + const std::vector extra_well_vectors { + { "WBHP", Opm::EclIO::SummaryNode::Type::Pressure }, + { "WGVIR", Opm::EclIO::SummaryNode::Type::Rate }, + { "WWVIR", Opm::EclIO::SummaryNode::Type::Rate }, + }; + const std::vector extra_group_vectors { + { "GMCTG", Opm::EclIO::SummaryNode::Type::Mode }, + { "GMCTP", Opm::EclIO::SummaryNode::Type::Mode }, + { "GMCTW", Opm::EclIO::SummaryNode::Type::Mode }, + }; + const std::vector extra_field_vectors { + { "FMCTG", Opm::EclIO::SummaryNode::Type::Mode }, + { "FMCTP", Opm::EclIO::SummaryNode::Type::Mode }, + { "FMCTW", Opm::EclIO::SummaryNode::Type::Mode }, + }; + + std::vector entities {} ; auto makeEntities = [&vectors, &entities] (const char kwpref, - const SN::Category cat, + const Opm::EclIO::SummaryNode::Category cat, const std::string& name) -> void { for (const auto& vector : vectors) { - entities.emplace_back(kwpref + vector.kw, cat, ::Opm::Location()); + entities.push_back({kwpref + vector.kw, cat, vector.type, name, Opm::EclIO::SummaryNode::default_number }); + } + }; - entities.back().namedEntity(name) - .parameterType(vector.type); + auto makeExtraEntities = [&entities] + (const std::vector& extra_vectors, + const Opm::EclIO::SummaryNode::Category category, + const std::string& wgname) -> void + { + for (const auto &extra_vector : extra_vectors) { + entities.push_back({ extra_vector.kw, category, extra_vector.type, wgname, Opm::EclIO::SummaryNode::default_number }); } }; for (const auto& well_name : sched.wellNames()) { - makeEntities('W', SN::Category::Well, well_name); - - entities.emplace_back("WBHP", SN::Category::Well, ::Opm::Location()); - entities.back().namedEntity(well_name) - .parameterType(SN::Type::Pressure); - - entities.emplace_back("WGVIR", SN::Category::Well, ::Opm::Location()); - entities.back().namedEntity(well_name) - .parameterType(SN::Type::Rate); - - entities.emplace_back("WWVIR", SN::Category::Well, ::Opm::Location()); - entities.back().namedEntity(well_name) - .parameterType(SN::Type::Rate); + makeEntities('W', Opm::EclIO::SummaryNode::Category::Well, well_name); + makeExtraEntities(extra_well_vectors, Opm::EclIO::SummaryNode::Category::Well, well_name); } for (const auto& grp_name : sched.groupNames()) { if (grp_name != "FIELD") { - makeEntities('G', SN::Category::Group, grp_name); - - entities.emplace_back("GMCTP", SN::Category::Group, ::Opm::Location()); - entities.back().namedEntity(grp_name) - .parameterType(SN::Type::Mode); - - entities.emplace_back("GMCTW", SN::Category::Group, ::Opm::Location()); - entities.back().namedEntity(grp_name) - .parameterType(SN::Type::Mode); - - entities.emplace_back("GMCTG", SN::Category::Group, ::Opm::Location()); - entities.back().namedEntity(grp_name) - .parameterType(SN::Type::Mode); + makeEntities('G', Opm::EclIO::SummaryNode::Category::Group, grp_name); + makeExtraEntities(extra_group_vectors, Opm::EclIO::SummaryNode::Category::Group, grp_name); } } - makeEntities('F', SN::Category::Field, "FIELD"); - - entities.emplace_back("FMCTP", SN::Category::Field, ::Opm::Location()); - entities.back().namedEntity("FIELD") - .parameterType(SN::Type::Mode); - - entities.emplace_back("FMCTW", SN::Category::Field, ::Opm::Location()); - entities.back().namedEntity("FIELD") - .parameterType(SN::Type::Mode); - - entities.emplace_back("FMCTG", SN::Category::Field, ::Opm::Location()); - entities.back().namedEntity("FIELD") - .parameterType(SN::Type::Mode); + makeEntities('F', Opm::EclIO::SummaryNode::Category::Field, "FIELD"); + makeExtraEntities(extra_field_vectors, Opm::EclIO::SummaryNode::Category::Field, "FIELD"); return entities; } - std::vector + std::vector requiredSegmentVectors(const ::Opm::Schedule& sched) { - using SN = Opm::SummaryConfigNode; - auto ret = std::vector{}; - - auto sofr = SN{ "SOFR", SN::Category::Segment, ::Opm::Location() } - .parameterType(SN::Type::Rate); - - auto sgfr = SN{ "SGFR", SN::Category::Segment, ::Opm::Location() } - .parameterType(SN::Type::Rate); - - auto swfr = SN{ "SWFR", SN::Category::Segment, ::Opm::Location() } - .parameterType(SN::Type::Rate); - - auto spr = SN{ "SPR", SN::Category::Segment, ::Opm::Location() } - .parameterType(SN::Type::Pressure); + std::vector ret {}; + + constexpr Opm::EclIO::SummaryNode::Category category { Opm::EclIO::SummaryNode::Category::Segment }; + const std::vector> requiredVectors { + { "SOFR", Opm::EclIO::SummaryNode::Type::Rate }, + { "SGFR", Opm::EclIO::SummaryNode::Type::Rate }, + { "SWFR", Opm::EclIO::SummaryNode::Type::Rate }, + { "SPR", Opm::EclIO::SummaryNode::Type::Pressure }, + }; auto makeVectors = [&](const std::string& well, const int segNumber) -> void { - ret.push_back(sofr.namedEntity(well).number(segNumber)); - ret.push_back(sgfr.namedEntity(well).number(segNumber)); - ret.push_back(swfr.namedEntity(well).number(segNumber)); - ret.push_back(spr .namedEntity(well).number(segNumber)); + for (const auto &requiredVector : requiredVectors) { + ret.push_back({requiredVector.first, category, requiredVector.second, well, segNumber}); + } }; for (const auto& wname : sched.wellNames()) { @@ -1247,40 +1229,39 @@ static const std::unordered_map< std::string, Opm::UnitSystem::measure> block_un }; inline std::vector find_wells( const Opm::Schedule& schedule, - const Opm::SummaryConfigNode& node, + const Opm::EclIO::SummaryNode& node, const int sim_step, const Opm::out::RegionCache& regionCache ) { + const auto cat = node.category; - const auto cat = node.category(); - - if ((cat == Opm::SummaryConfigNode::Category::Well) || - (cat == Opm::SummaryConfigNode::Category::Connection) || - (cat == Opm::SummaryConfigNode::Category::Segment)) - { - const auto& name = node.namedEntity(); + switch (cat) { + case Opm::EclIO::SummaryNode::Category::Well: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Connection: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Segment: { + const auto& name = node.wgname; - if (schedule.hasWell(name, sim_step)) { - const auto& well = schedule.getWell( name, sim_step ); - return { well }; - } else + if (schedule.hasWell(node.wgname, sim_step)) { + return { schedule.getWell( name, sim_step ) }; + } else { return {}; + } } - if( cat == Opm::SummaryConfigNode::Category::Group ) { - const auto& name = node.namedEntity(); + case Opm::EclIO::SummaryNode::Category::Group: { + const auto& name = node.wgname; if( !schedule.hasGroup( name ) ) return {}; return schedule.getChildWells2( name, sim_step); } - if( cat == Opm::SummaryConfigNode::Category::Field ) + case Opm::EclIO::SummaryNode::Category::Field: return schedule.getWells(sim_step); - if( cat == Opm::SummaryConfigNode::Category::Region ) { + case Opm::EclIO::SummaryNode::Category::Region: { std::vector wells; - const auto region = node.number(); + const auto region = node.number; for ( const auto& connection : regionCache.connections( region ) ){ const auto& w_name = connection.first; @@ -1298,39 +1279,28 @@ inline std::vector find_wells( const Opm::Schedule& schedule, return wells; } - return {}; + case Opm::EclIO::SummaryNode::Category::Block: + case Opm::EclIO::SummaryNode::Category::Miscellaneous: + return {}; + } } - -bool need_wells(Opm::SummaryConfigNode::Category cat, const std::string& keyword) { - static const std::set region_keywords{"ROIR", "RGIR", "RWIR", "ROPR", "RGPR", "RWPR", "ROIT", "RWIT", "RGIT", "ROPT", "RGPT", "RWPT"}; - if (cat == Opm::SummaryConfigNode::Category::Well) - return true; - - if (cat == Opm::SummaryConfigNode::Category::Group) - return true; - - if (cat == Opm::SummaryConfigNode::Category::Field) - return true; - - if (cat == Opm::SummaryConfigNode::Category::Connection) - return true; - - if (cat == Opm::SummaryConfigNode::Category::Segment) - return true; - - /* - Some of the region keywords are based on summing over all the connections - which fall in the region; i.e. RGIR is the total gas injection rate in the - region and consequently the list of defined wells is required, other - region keywords like 'ROIP' do not require well information. - */ - if (cat == Opm::SummaryConfigNode::Category::Region) { - if (region_keywords.count(keyword) > 0) - return true; +bool need_wells(const Opm::EclIO::SummaryNode& node) { + static const std::regex region_keyword_regex { "R[OGW][IP][RT]" }; + + switch (node.category) { + case Opm::EclIO::SummaryNode::Category::Connection: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Field: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Group: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Segment: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Well: + return true; + case Opm::EclIO::SummaryNode::Category::Region: + return std::regex_match(node.keyword, region_keyword_regex); + case Opm::EclIO::SummaryNode::Category::Miscellaneous: [[fallthrough]]; + case Opm::EclIO::SummaryNode::Category::Block: + return false; } - - return false; } void eval_udq(const Opm::Schedule& schedule, std::size_t sim_step, Opm::SummaryState& st) @@ -1391,16 +1361,16 @@ void eval_udq(const Opm::Schedule& schedule, std::size_t sim_step, Opm::SummaryS } } -void updateValue(const Opm::SummaryConfigNode& node, const double value, Opm::SummaryState& st) +void updateValue(const Opm::EclIO::SummaryNode& node, const double value, Opm::SummaryState& st) { - if (node.category() == Opm::SummaryConfigNode::Category::Well) - st.update_well_var(node.namedEntity(), node.keyword(), value); + if (node.category == Opm::EclIO::SummaryNode::Category::Well) + st.update_well_var(node.wgname, node.keyword, value); - else if (node.category() == Opm::SummaryConfigNode::Category::Group) - st.update_group_var(node.namedEntity(), node.keyword(), value); + else if (node.category == Opm::EclIO::SummaryNode::Category::Group) + st.update_group_var(node.wgname, node.keyword, value); else - st.update(node.uniqueNodeKey(), value); + st.update(node.unique_key(), value); } /* @@ -1426,31 +1396,27 @@ struct EfficiencyFactor FacColl factors{}; - void setFactors(const Opm::SummaryConfigNode& node, + void setFactors(const Opm::EclIO::SummaryNode& node, const Opm::Schedule& schedule, const std::vector& schedule_wells, const int sim_step); }; -void EfficiencyFactor::setFactors(const Opm::SummaryConfigNode& node, +void EfficiencyFactor::setFactors(const Opm::EclIO::SummaryNode& node, const Opm::Schedule& schedule, - const std::vector& schedule_wells, + const std::vector& schedule_wells, const int sim_step) { this->factors.clear(); - if (schedule_wells.empty()) { return; } + const bool is_field { node.category == Opm::EclIO::SummaryNode::Category::Field } ; + const bool is_group { node.category == Opm::EclIO::SummaryNode::Category::Group } ; + const bool is_region { node.category == Opm::EclIO::SummaryNode::Category::Region } ; + const bool is_rate { node.type != Opm::EclIO::SummaryNode::Type::Total } ; - const auto cat = node.category(); - if( cat != Opm::SummaryConfigNode::Category::Group - && cat != Opm::SummaryConfigNode::Category::Field - && cat != Opm::SummaryConfigNode::Category::Region - && (node.type() != Opm::SummaryConfigNode::Type::Total)) + if (!is_field && !is_group && !is_region && is_rate) return; - const bool is_group = (cat == Opm::SummaryConfigNode::Category::Group); - const bool is_rate = (node.type() != Opm::SummaryConfigNode::Type::Total); - for( const auto& well : schedule_wells ) { if (!well.hasBeenDefined(sim_step)) continue; @@ -1458,16 +1424,18 @@ void EfficiencyFactor::setFactors(const Opm::SummaryConfigNode& node, double eff_factor = well.getEfficiencyFactor(); const auto* group_ptr = std::addressof(schedule.getGroup(well.groupName(), sim_step)); - while(true){ - if(( is_group - && is_rate - && group_ptr->name() == node.namedEntity() )) + while (group_ptr) { + if (is_group && is_rate && group_ptr->name() == node.wgname ) break; + eff_factor *= group_ptr->getGroupEfficiencyFactor(); - if (group_ptr->name() == "FIELD") - break; - group_ptr = std::addressof( schedule.getGroup( group_ptr->parent(), sim_step ) ); + const auto parent_group = group_ptr->flow_group(); + + if (parent_group) + group_ptr = std::addressof(schedule.getGroup( parent_group.value(), sim_step )); + else + group_ptr = nullptr; } this->factors.emplace_back( well.name(), eff_factor ); @@ -1507,7 +1475,7 @@ namespace Evaluator { class FunctionRelation : public Base { public: - explicit FunctionRelation(Opm::SummaryConfigNode node, ofun fcn) + explicit FunctionRelation(Opm::EclIO::SummaryNode node, ofun fcn) : node_(std::move(node)) , fcn_ (std::move(fcn)) {} @@ -1519,7 +1487,7 @@ namespace Evaluator { Opm::SummaryState& st) const override { const auto get_wells = - need_wells(this->node_.category(), this->node_.keyword()); + need_wells(node_); const auto wells = get_wells ? find_wells(input.sched, this->node_, @@ -1531,14 +1499,14 @@ namespace Evaluator { // wells apply at this sim_step. Nothing to do. return; - std::string group_name = this->node_.category() == Opm::SummaryConfigNode::Category::Group ? this->node_.namedEntity() : ""; + std::string group_name = this->node_.category == Opm::EclIO::SummaryNode::Category::Group ? this->node_.wgname : ""; EfficiencyFactor efac{}; efac.setFactors(this->node_, input.sched, wells, sim_step); const fn_args args { wells, group_name, stepSize, static_cast(sim_step), - std::max(0, this->node_.number()), + std::max(0, this->node_.number), st, simRes.wellSol, simRes.groupSol, input.reg, input.grid, std::move(efac.factors) }; @@ -1550,14 +1518,14 @@ namespace Evaluator { } private: - Opm::SummaryConfigNode node_; + Opm::EclIO::SummaryNode node_; ofun fcn_; }; class BlockValue : public Base { public: - explicit BlockValue(Opm::SummaryConfigNode node, + explicit BlockValue(Opm::EclIO::SummaryNode node, const Opm::UnitSystem::measure m) : node_(std::move(node)) , m_ (m) @@ -1579,19 +1547,19 @@ namespace Evaluator { } private: - Opm::SummaryConfigNode node_; + Opm::EclIO::SummaryNode node_; Opm::UnitSystem::measure m_; Opm::out::Summary::BlockValues::key_type lookupKey() const { - return { this->node_.keyword(), this->node_.number() }; + return { this->node_.keyword, this->node_.number }; } }; class RegionValue : public Base { public: - explicit RegionValue(Opm::SummaryConfigNode node, + explicit RegionValue(Opm::EclIO::SummaryNode node, const Opm::UnitSystem::measure m) : node_(std::move(node)) , m_ (m) @@ -1603,10 +1571,10 @@ namespace Evaluator { const SimulatorResults& simRes, Opm::SummaryState& st) const override { - if (this->node_.number() < 0) + if (this->node_.number < 0) return; - auto xPos = simRes.region.find(this->node_.keyword()); + auto xPos = simRes.region.find(this->node_.keyword); if (xPos == simRes.region.end()) return; @@ -1621,19 +1589,19 @@ namespace Evaluator { } private: - Opm::SummaryConfigNode node_; + Opm::EclIO::SummaryNode node_; Opm::UnitSystem::measure m_; std::vector::size_type index() const { - return this->node_.number() - 1; + return this->node_.number - 1; } }; class GlobalProcessValue : public Base { public: - explicit GlobalProcessValue(Opm::SummaryConfigNode node, + explicit GlobalProcessValue(Opm::EclIO::SummaryNode node, const Opm::UnitSystem::measure m) : node_(std::move(node)) , m_ (m) @@ -1645,7 +1613,7 @@ namespace Evaluator { const SimulatorResults& simRes, Opm::SummaryState& st) const override { - auto xPos = simRes.single.find(this->node_.keyword()); + auto xPos = simRes.single.find(this->node_.keyword); if (xPos == simRes.single.end()) return; @@ -1656,7 +1624,7 @@ namespace Evaluator { } private: - Opm::SummaryConfigNode node_; + Opm::EclIO::SummaryNode node_; Opm::UnitSystem::measure m_; }; @@ -1746,7 +1714,7 @@ namespace Evaluator { Factory& operator=(const Factory&) = delete; Factory& operator=(Factory&&) = delete; - Descriptor create(const Opm::SummaryConfigNode&); + Descriptor create(const Opm::EclIO::SummaryNode&); private: const Opm::EclipseState& es_; @@ -1754,7 +1722,7 @@ namespace Evaluator { const Opm::SummaryState& st_; const Opm::UDQConfig& udq_; - const Opm::SummaryConfigNode* node_; + const Opm::EclIO::SummaryNode* node_; Opm::UnitSystem::measure paramUnit_; ofun paramFunction_; @@ -1777,7 +1745,7 @@ namespace Evaluator { std::string userDefinedUnit() const; }; - Factory::Descriptor Factory::create(const Opm::SummaryConfigNode& node) + Factory::Descriptor Factory::create(const Opm::EclIO::SummaryNode& node) { this->node_ = &node; @@ -1861,18 +1829,18 @@ namespace Evaluator { { auto desc = Descriptor{}; - desc.uniquekey = this->node_->uniqueNodeKey(); + desc.uniquekey = this->node_->unique_key(); return desc; } bool Factory::isBlockValue() { - auto pos = block_units.find(this->node_->keyword()); + auto pos = block_units.find(this->node_->keyword); if (pos == block_units.end()) return false; - if (! this->grid_.cellActive(this->node_->number() - 1)) + if (! this->grid_.cellActive(this->node_->number - 1)) // 'node_' is a block value, but it is configured in a // deactivated cell. Don't create an evaluation function. return false; @@ -1885,7 +1853,7 @@ namespace Evaluator { bool Factory::isRegionValue() { - auto pos = region_units.find(this->node_->keyword()); + auto pos = region_units.find(this->node_->keyword); if (pos == region_units.end()) return false; @@ -1897,7 +1865,7 @@ namespace Evaluator { bool Factory::isGlobalProcessValue() { - auto pos = single_values_units.find(this->node_->keyword()); + auto pos = single_values_units.find(this->node_->keyword); if (pos == single_values_units.end()) return false; @@ -1909,7 +1877,7 @@ namespace Evaluator { bool Factory::isFunctionRelation() { - auto pos = funs.find(this->node_->keyword()); + auto pos = funs.find(this->node_->keyword); if (pos == funs.end()) return false; @@ -1921,7 +1889,7 @@ namespace Evaluator { bool Factory::isUserDefined() { - return this->node_->isUserDefined(); + return this->node_->is_user_defined(); } std::string Factory::functionUnitString() const @@ -1929,7 +1897,7 @@ namespace Evaluator { const auto reg = Opm::out::RegionCache{}; const fn_args args { - {}, "", 0.0, 0, std::max(0, this->node_->number()), + {}, "", 0.0, 0, std::max(0, this->node_->number), this->st_, {}, {}, reg, this->grid_, {} }; @@ -1946,7 +1914,7 @@ namespace Evaluator { std::string Factory::userDefinedUnit() const { - const auto& kw = this->node_->keyword(); + const auto& kw = this->node_->keyword; return this->udq_.has_unit(kw) ? this->udq_.unit(kw) : "?????"; @@ -2410,13 +2378,13 @@ Opm::out::Summary::SummaryImplementation:: configureRequiredRestartParameters(const SummaryConfig& sumcfg, const Schedule& sched) { - auto makeEvaluator = [&sumcfg, this](const SummaryConfigNode& node) -> void + auto makeEvaluator = [&sumcfg, this](const Opm::EclIO::SummaryNode& node) -> void { - if (sumcfg.hasSummaryKey(node.uniqueNodeKey())) + if (sumcfg.hasSummaryKey(node.unique_key())) // Handler already exists. Don't add second evaluation. return; - auto fcnPos = funs.find(node.keyword()); + auto fcnPos = funs.find(node.keyword); assert ((fcnPos != funs.end()) && "Internal error creating required restart vectors"); diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Group/Group.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Group/Group.cpp index 61f3462d6d3..b4711128a20 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Group/Group.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Group/Group.cpp @@ -336,12 +336,18 @@ const std::string& Group::parent() const { return this->parent_group; } -const std::string& Group::control_group() const { - return this->parent(); +std::optional Group::control_group() const { + if (m_name == "FIELD") + return std::nullopt; + else + return this->parent(); } -const std::string& Group::flow_group() const { - return this->parent(); +std::optional Group::flow_group() const { + if (m_name == "FIELD") + return std::nullopt; + else + return this->parent(); } const Phase& Group::topup_phase() const { diff --git a/tests/parser/integration/ScheduleCreateFromDeck.cpp b/tests/parser/integration/ScheduleCreateFromDeck.cpp index f3ac4c8b17b..221fda931a6 100644 --- a/tests/parser/integration/ScheduleCreateFromDeck.cpp +++ b/tests/parser/integration/ScheduleCreateFromDeck.cpp @@ -357,11 +357,11 @@ BOOST_AUTO_TEST_CASE(GroupTreeTest_GRUPTREE_WITH_REPARENT_correct_tree) { BOOST_CHECK_EQUAL(field_group.groups().size(), 2); BOOST_CHECK( field_group.hasGroup("GROUP_NEW")); BOOST_CHECK( field_group.hasGroup("GROUP_BJARNE")); - BOOST_CHECK_EQUAL( new_group.control_group(), "FIELD"); - BOOST_CHECK_EQUAL( new_group.flow_group(), "FIELD"); + BOOST_CHECK_EQUAL( new_group.control_group().value_or("ERROR"), "FIELD"); + BOOST_CHECK_EQUAL( new_group.flow_group().value_or("ERROR"), "FIELD"); BOOST_CHECK( new_group.hasGroup("GROUP_NILS")); - BOOST_CHECK_EQUAL( nils_group.control_group(), "GROUP_NEW"); - BOOST_CHECK_EQUAL( nils_group.flow_group(), "GROUP_NEW"); + BOOST_CHECK_EQUAL( nils_group.control_group().value_or("ERROR"), "GROUP_NEW"); + BOOST_CHECK_EQUAL( nils_group.flow_group().value_or("ERROR"), "GROUP_NEW"); } BOOST_AUTO_TEST_CASE( WellTestGroups ) { diff --git a/tests/test_SummaryNode.cpp b/tests/test_SummaryNode.cpp new file mode 100644 index 00000000000..5af6c0718da --- /dev/null +++ b/tests/test_SummaryNode.cpp @@ -0,0 +1,48 @@ +/* + Copyright 2019 Equinor + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#define BOOST_TEST_MODULE SummaryNode + +#include + +#include + +namespace { + void expect_key(const Opm::EclIO::SummaryNode& node, const std::string& unique_key) { + BOOST_CHECK_EQUAL(node.unique_key(), unique_key); + } +} + +BOOST_AUTO_TEST_SUITE(UniqueKey) + +BOOST_AUTO_TEST_CASE(UniqueKey) { + using Category = Opm::EclIO::SummaryNode::Category; + using Type = Opm::EclIO::SummaryNode::Type; + + expect_key( { "KEYW", Category::Well, Type::Rate, "NORA", 1 }, "KEYW:NORA" ); + expect_key( { "KEYW", Category::Group, Type::Rate, "NORA", 2 }, "KEYW:NORA" ); + expect_key( { "KEYW", Category::Field, Type::Rate, "NORA", 3 }, "KEYW" ); + expect_key( { "KEYW", Category::Region, Type::Rate, "NORA", 4 }, "KEYW:4" ); + expect_key( { "KEYW", Category::Block, Type::Rate, "NORA", 5 }, "KEYW:5" ); + expect_key( { "KEYW", Category::Connection, Type::Rate, "NORA", 6 }, "KEYW:NORA:6" ); + expect_key( { "KEYW", Category::Segment, Type::Rate, "NORA", 7 }, "KEYW:NORA:7" ); + expect_key( { "KEYW", Category::Miscellaneous, Type::Rate, "NORA", 8 }, "KEYW" ); +} + +BOOST_AUTO_TEST_SUITE_END() // UniqueKey