diff --git a/opm/io/eclipse/ESmry.hpp b/opm/io/eclipse/ESmry.hpp index 6942db8e7..57449e870 100644 --- a/opm/io/eclipse/ESmry.hpp +++ b/opm/io/eclipse/ESmry.hpp @@ -22,7 +22,9 @@ #include #include #include + #include +#include namespace Opm { namespace EclIO { @@ -38,18 +40,22 @@ public: bool hasKey(const std::string& key) const; const std::vector& get(const std::string& name) const; + const std::vector& get(const SummaryNode& node) const; std::vector get_at_rstep(const std::string& name) const; + std::vector get_at_rstep(const SummaryNode& node) const; const std::vector& get_startdat() const { return startdat; } - const std::vector& keywordList() const { return keyword; } + const std::vector& keywordList() const; + const std::vector& summaryNodeList() const; int timestepIdxAtReportstepStart(const int reportStep) const; size_t numberOfTimeSteps() const { return param[0].size(); } const std::string& get_unit(const std::string& name) const; + const std::string& get_unit(const SummaryNode& node) const; private: int nVect, nI, nJ, nK; @@ -57,6 +63,7 @@ private: void ijk_from_global_index(int glob, int &i, int &j, int &k) const; std::vector> param; std::vector keyword; + std::vector summaryNodes; std::unordered_map kwunits; std::vector seqIndex; @@ -74,6 +81,9 @@ private: void updatePathAndRootName(Opm::filesystem::path& dir, Opm::filesystem::path& rootN) const; std::string makeKeyString(const std::string& keyword, const std::string& wgname, int num) const; + + std::string unpackNumber(const SummaryNode&) const; + std::string lookupKey(const SummaryNode&) const; }; }} // namespace Opm::EclIO diff --git a/opm/io/eclipse/SummaryNode.hpp b/opm/io/eclipse/SummaryNode.hpp index 748c4852d..0590d49a8 100644 --- a/opm/io/eclipse/SummaryNode.hpp +++ b/opm/io/eclipse/SummaryNode.hpp @@ -17,7 +17,12 @@ along with OPM. If not, see . */ +#ifndef OPM_IO_SUMMARYNODE_HPP +#define OPM_IO_SUMMARYNODE_HPP + +#include #include +#include namespace Opm::EclIO { @@ -53,7 +58,15 @@ struct SummaryNode { constexpr static int default_number { std::numeric_limits::min() }; std::string unique_key() const; + + using number_renderer = std::function; + std::string unique_key(number_renderer) const; + bool is_user_defined() const; + + static Category category_from_keyword(const std::string&, const std::unordered_set &miscellaneous_keywords = {}); }; } // namespace Opm::EclIO + +#endif // OPM_IO_SUMMARYNODE_HPP diff --git a/src/opm/io/eclipse/ESmry.cpp b/src/opm/io/eclipse/ESmry.cpp index 6d4152f5b..75cc40794 100644 --- a/src/opm/io/eclipse/ESmry.cpp +++ b/src/opm/io/eclipse/ESmry.cpp @@ -51,7 +51,8 @@ namespace Opm { namespace EclIO { -ESmry::ESmry(const std::string &filename, bool loadBaseRunData) +ESmry::ESmry(const std::string &filename, bool loadBaseRunData) : + summaryNodes { } { Opm::filesystem::path inputFileName(filename); @@ -84,6 +85,12 @@ ESmry::ESmry(const std::string &filename, bool loadBaseRunData) std::set keywList; std::vector> smryArray; + const std::unordered_set segmentExceptions { + "SEPARATE", + "STEPTYPE", + "SUMTHIN", + } ; + // Read data from the summary into local data members. { EclFile smspec(smspec_file.string()); @@ -107,6 +114,14 @@ ESmry::ESmry(const std::string &filename, bool loadBaseRunData) for (unsigned int i=0; i 0) { + summaryNodes.push_back({ + keywords[i], + SummaryNode::category_from_keyword(keywords[i], segmentExceptions), + SummaryNode::Type::Undefined, + wgnames[i], + nums[i] + }); + keywList.insert(keyString); kwunits[keyString] = units[i]; } @@ -150,6 +165,14 @@ 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]); if (keyString.length() > 0) { + summaryNodes.push_back({ + keywords[i], + SummaryNode::category_from_keyword(keywords[i], segmentExceptions), + SummaryNode::Type::Undefined, + wgnames[i], + nums[i] + }); + keywList.insert(keyString); kwunits[keyString] = units[i]; } @@ -496,6 +519,38 @@ std::string ESmry::makeKeyString(const std::string& keywordArg, const std::strin return keyStr; } +std::string ESmry::unpackNumber(const SummaryNode& node) const { + if (node.category == SummaryNode::Category::Block || + node.category == SummaryNode::Category::Connection) { + int _i,_j,_k; + ijk_from_global_index(node.number, _i, _j, _k); + + return std::to_string(_i) + "," + std::to_string(_j) + "," + std::to_string(_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; + + return std::to_string(r1) + "-" + std::to_string(r2); + } else { + return std::to_string(node.number); + } +} + +std::string ESmry::lookupKey(const SummaryNode& node) const { + return node.unique_key(std::bind( &ESmry::unpackNumber, this, std::placeholders::_1 )); +} + +const std::vector& ESmry::get(const SummaryNode& node) const { + return get(lookupKey(node)); +} + +std::vector ESmry::get_at_rstep(const SummaryNode& node) const { + return get_at_rstep(lookupKey(node)); +} + +const std::string& ESmry::get_unit(const SummaryNode& node) const { + return get_unit(lookupKey(node)); +} const std::vector& ESmry::get(const std::string& name) const { @@ -543,4 +598,12 @@ const std::string& ESmry::get_unit(const std::string& name) const { return kwunits.at(name); } +const std::vector& ESmry::keywordList() const { + return keyword; +} + +const std::vector& ESmry::summaryNodeList() const { + return summaryNodes; +} + }} // namespace Opm::ecl diff --git a/src/opm/io/eclipse/SummaryNode.cpp b/src/opm/io/eclipse/SummaryNode.cpp index d3f0821f5..e151a3606 100644 --- a/src/opm/io/eclipse/SummaryNode.cpp +++ b/src/opm/io/eclipse/SummaryNode.cpp @@ -65,16 +65,20 @@ constexpr bool use_name(Opm::EclIO::SummaryNode::Category category) { return false; // Never reached, but quells compiler warning } +std::string default_number_renderer(const Opm::EclIO::SummaryNode& node) { + return std::to_string(node.number); +} + }; -std::string Opm::EclIO::SummaryNode::unique_key() const { +std::string Opm::EclIO::SummaryNode::unique_key(number_renderer render_number) 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)); + key_parts.emplace_back(render_number(*this)); auto compose_key = [](std::string& key, const std::string& key_part) -> std::string { constexpr auto delimiter { ':' } ; @@ -84,6 +88,10 @@ std::string Opm::EclIO::SummaryNode::unique_key() const { return std::accumulate(std::begin(key_parts), std::end(key_parts), std::string(), compose_key); } +std::string Opm::EclIO::SummaryNode::unique_key() const { + return unique_key(default_number_renderer); +} + bool Opm::EclIO::SummaryNode::is_user_defined() const { static const std::unordered_set udq_blacklist { "AUTOCOAR", @@ -119,3 +127,28 @@ bool Opm::EclIO::SummaryNode::is_user_defined() const { return matched && !blacklisted; } + +Opm::EclIO::SummaryNode::Category Opm::EclIO::SummaryNode::category_from_keyword( + const std::string& keyword, + const std::unordered_set& miscellaneous_keywords +) { + if (keyword.length() == 0) { + return Category::Miscellaneous; + } + + if (miscellaneous_keywords.find(keyword) != miscellaneous_keywords.end()) { + return Category::Miscellaneous; + } + + switch (keyword[0]) { + case 'A': return Category::Aquifer; + case 'B': return Category::Block; + case 'C': return Category::Connection; + case 'F': return Category::Field; + case 'G': return Category::Group; + case 'R': return Category::Region; + case 'S': return Category::Segment; + case 'W': return Category::Well; + default: return Category::Miscellaneous; + } +} diff --git a/tests/test_SummaryNode.cpp b/tests/test_SummaryNode.cpp index 5af6c0718..1ea499ed6 100644 --- a/tests/test_SummaryNode.cpp +++ b/tests/test_SummaryNode.cpp @@ -45,4 +45,32 @@ BOOST_AUTO_TEST_CASE(UniqueKey) { expect_key( { "KEYW", Category::Miscellaneous, Type::Rate, "NORA", 8 }, "KEYW" ); } +BOOST_AUTO_TEST_CASE(InjectedNumberRenderer) { + using Category = Opm::EclIO::SummaryNode::Category; + using Type = Opm::EclIO::SummaryNode::Type; + + Opm::EclIO::SummaryNode positiveNode { + "SIGN", + Category::Region, + Type::Undefined, + "-", + 2 + }; + + Opm::EclIO::SummaryNode negativeNode { + "SIGN", + Category::Region, + Type::Undefined, + "-", + -2 + }; + + auto chooseSign = [](const Opm::EclIO::SummaryNode& node) -> std::string { + return node.number > 0 ? "+" : "-"; + }; + + BOOST_CHECK_EQUAL(positiveNode.unique_key(chooseSign), "SIGN:+"); + BOOST_CHECK_EQUAL(negativeNode.unique_key(chooseSign), "SIGN:-"); +} + BOOST_AUTO_TEST_SUITE_END() // UniqueKey