From bb3be75dc5fb4cd3c6e81fbf3e3fe804b947374a Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Tue, 17 Nov 2020 07:55:15 +0100 Subject: [PATCH] Add container class Inplace to manage aggregated region properties --- CMakeLists_files.cmake | 3 + opm/output/eclipse/Inplace.hpp | 86 +++++++++++++ src/opm/output/eclipse/Inplace.cpp | 189 +++++++++++++++++++++++++++++ tests/test_Inplace.cpp | 83 +++++++++++++ 4 files changed, 361 insertions(+) create mode 100644 opm/output/eclipse/Inplace.hpp create mode 100644 src/opm/output/eclipse/Inplace.cpp create mode 100644 tests/test_Inplace.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index cea6e0e83..926127cc9 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -286,6 +286,7 @@ if(ENABLE_ECL_OUTPUT) src/opm/output/eclipse/LoadRestart.cpp src/opm/output/eclipse/LogiHEAD.cpp src/opm/output/eclipse/RestartIO.cpp + src/opm/output/eclipse/Inplace.cpp src/opm/output/eclipse/Summary.cpp src/opm/output/eclipse/Tables.cpp src/opm/output/eclipse/RegionCache.cpp @@ -411,6 +412,7 @@ if(ENABLE_ECL_OUTPUT) tests/test_rst.cpp tests/test_Solution.cpp tests/test_Serializer.cpp + tests/test_Inplace.cpp tests/test_Summary.cpp tests/test_Summary_Group.cpp tests/test_Tables.cpp @@ -832,6 +834,7 @@ if(ENABLE_ECL_OUTPUT) opm/output/eclipse/RegionCache.hpp opm/output/eclipse/RestartIO.hpp opm/output/eclipse/RestartValue.hpp + opm/output/eclipse/Inplace.hpp opm/output/eclipse/Summary.hpp opm/output/eclipse/Tables.hpp opm/output/eclipse/WindowedArray.hpp diff --git a/opm/output/eclipse/Inplace.hpp b/opm/output/eclipse/Inplace.hpp new file mode 100644 index 000000000..e840e48b0 --- /dev/null +++ b/opm/output/eclipse/Inplace.hpp @@ -0,0 +1,86 @@ +/* + 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 . +*/ + +#ifndef ORIGINAL_OIP +#define ORIGINAL_OIP + +#include +#include +#include + +namespace Opm { + + +class Inplace { +public: + + enum class Phase { + WATER = 0, + OIL = 1, + GAS = 2, + OilInLiquidPhase = 3, + OilInGasPhase = 4, + GasInLiquidPhase = 5, + GasInGasPhase = 6, + PoreVolume = 7 + }; + + /* + The purpose of this class is to transport inplace values from the + simulator code to the summary output code. The code is written very much + to fit in with the current implementation in the simulator, in particular + that the add/get functions exist in two varieties is a result of that. + + The functions which don't accept region_name & region_number arguments + should be called for totals, i.e. field properties. + */ + + + void add(const std::string& region, const std::string& tag, std::size_t region_number, double value); + void add(const std::string& region, Phase phase, std::size_t region_number, double value); + void add(Phase phase, double value); + void add(const std::string& tag, double value); + + double get(const std::string& region, const std::string& tag, std::size_t region_number) const; + double get(const std::string& region, Phase phase, std::size_t region_number) const; + double get(Phase phase) const; + double get(const std::string& tag) const; + + std::size_t max_region() const; + std::size_t max_region(const std::string& region_name) const; + + /* + The get_vector functions return a vector length max_region() which + contains the values added with the add() function and indexed with + (region_number - 1). This is an incarnation of id <-> index confusion and + should be replaced with a std::map instead. + */ + std::vector get_vector(const std::string& region, const std::string& tag) const; + std::vector get_vector(const std::string& region, Phase phase) const; + + static const std::vector& phases(); +private: + std::unordered_map>> phase_values; + std::unordered_map>> tag_values; +}; + + +} + +#endif diff --git a/src/opm/output/eclipse/Inplace.cpp b/src/opm/output/eclipse/Inplace.cpp new file mode 100644 index 000000000..da182005a --- /dev/null +++ b/src/opm/output/eclipse/Inplace.cpp @@ -0,0 +1,189 @@ +/* + 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 + +namespace Opm { + +namespace { +static const std::string FIELD_NAME = std::string{"FIELD"}; +static const std::size_t FIELD_ID = 0; +} + +void Inplace::add(const std::string& region, const std::string& tag, std::size_t region_id, double value) { + this->tag_values[region][tag][region_id] = value; +} + +void Inplace::add(const std::string& region, Inplace::Phase phase, std::size_t region_id, double value) { + this->phase_values[region][phase][region_id] = value; +} + +void Inplace::add(Inplace::Phase phase, double value) { + this->add( FIELD_NAME, phase, FIELD_ID, value ); +} + +void Inplace::add(const std::string& tag, double value) { + this->add( FIELD_NAME, tag, FIELD_ID, value ); +} + +double Inplace::get(const std::string& region, const std::string& tag, std::size_t region_id) const { + auto region_iter = this->tag_values.find(region); + if (region_iter == this->tag_values.end()) + throw std::logic_error(fmt::format("No such region: {}", region)); + + auto tag_iter = region_iter->second.find(tag); + if (tag_iter == region_iter->second.end()) + throw std::logic_error(fmt::format("No such tag: {}:{}", region, tag)); + + auto value_iter = tag_iter->second.find(region_id); + if (value_iter == tag_iter->second.end()) + throw std::logic_error(fmt::format("No such region id: {}:{}:{}", region, tag, region_id)); + + return value_iter->second; +} + +double Inplace::get(const std::string& region, Inplace::Phase phase, std::size_t region_id) const { + auto region_iter = this->phase_values.find(region); + if (region_iter == this->phase_values.end()) + throw std::logic_error(fmt::format("No such region: {}", region)); + + auto phase_iter = region_iter->second.find(phase); + if (phase_iter == region_iter->second.end()) + throw std::logic_error(fmt::format("No such phase: {}:{}", region, static_cast(phase))); + + auto value_iter = phase_iter->second.find(region_id); + if (value_iter == phase_iter->second.end()) + throw std::logic_error(fmt::format("No such region id: {}:{}:{}", region, static_cast(phase), region_id)); + + return value_iter->second; +} + +double Inplace::get(Inplace::Phase phase) const { + return this->get(FIELD_NAME, phase, FIELD_ID); +} + +double Inplace::get(const std::string& tag) const { + return this->get(FIELD_NAME, tag, FIELD_ID); +} + +namespace { +std::size_t region_max(const std::unordered_map& region_map) { + std::size_t max_value = 0; + for (const auto& [region_id, _] : region_map) { + (void)_; + max_value = std::max(max_value, region_id); + } + return max_value; +} +} + +std::size_t Inplace::max_region() const { + std::size_t max_value = 0; + for (const auto& [_, phase_map] : this->phase_values) { + (void)_; + for (const auto& [__, region_map] : phase_map) { + (void)__; + max_value = std::max(max_value, region_max(region_map)); + } + } + + for (const auto& [_, string_map] : this->tag_values) { + (void)_; + for (const auto& [__, region_map] : string_map) { + (void)__; + max_value = std::max(max_value, region_max(region_map)); + } + } + return max_value; +} + +std::size_t Inplace::max_region(const std::string& region_name) const { + std::optional max_value; + { + const auto& region_iter = this->phase_values.find(region_name); + if (region_iter != this->phase_values.end()) { + max_value = 0; + for (const auto& [_, region_map] : region_iter->second) { + (void)_; + max_value = std::max(*max_value, region_max(region_map)); + } + } + } + + { + const auto& region_iter = this->tag_values.find(region_name); + if (region_iter != this->tag_values.end()) { + if (!max_value.has_value()) + max_value = 0; + + for (const auto& [_, region_map] : region_iter->second) { + (void)_; + max_value = std::max(*max_value, region_max(region_map)); + } + } + } + if (!max_value.has_value()) + throw std::logic_error(fmt::format("No such region: {}", region_name)); + + return max_value.value(); +} + + +// This should probably die - temporarily added for porting of ecloutputblackoilmodule +std::vector Inplace::get_vector(const std::string& region, Phase phase) const { + std::vector v(this->max_region(region), 0); + const auto& region_map = this->phase_values.at(region).at(phase); + for (const auto& [region_id, value] : region_map) + v[region_id - 1] = value; + + return v; +} + +std::vector Inplace::get_vector(const std::string& region, const std::string& tag) const { + std::vector v(this->max_region(region), 0); + const auto& region_map = this->tag_values.at(region).at(tag); + for (const auto& [region_id, value] : region_map) + v[region_id - 1] = value; + + return v; +} + + +const std::vector& Inplace::phases() { + static const std::vector phases_ = { + Inplace::Phase::WATER, + Inplace::Phase::OIL, + Inplace::Phase::GAS, + Inplace::Phase::OilInLiquidPhase, + Inplace::Phase::OilInGasPhase, + Inplace::Phase::GasInLiquidPhase, + Inplace::Phase::GasInGasPhase, + Inplace::Phase::PoreVolume + }; + + return phases_; +} + + +} diff --git a/tests/test_Inplace.cpp b/tests/test_Inplace.cpp new file mode 100644 index 000000000..827278854 --- /dev/null +++ b/tests/test_Inplace.cpp @@ -0,0 +1,83 @@ +/* + Copyright 2020 Statoil 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 . +*/ + +#define BOOST_TEST_MODULE Inplace +#include + + + +#include + +using namespace Opm; + + +bool contains(const std::vector& phases, Inplace::Phase phase) { + auto find_iter = std::find(phases.begin(), phases.end(), phase); + return find_iter != phases.end(); +} + + +BOOST_AUTO_TEST_CASE(TESTInplace) { + Inplace oip; + + oip.add("FIPNUM", Inplace::Phase::OIL, 3, 100); + oip.add("FIPNUM", Inplace::Phase::OIL, 6, 50); + oip.add("FIPNUM", "StringID", 5, 200); + + + BOOST_CHECK_EQUAL( oip.get("FIPNUM", Inplace::Phase::OIL, 3) , 100); + BOOST_CHECK_EQUAL( oip.get("FIPNUM", Inplace::Phase::OIL, 6) , 50); + BOOST_CHECK_EQUAL( oip.get("FIPNUM", "StringID", 5) , 200); + + BOOST_CHECK_THROW( oip.get("FIPNUM", Inplace::Phase::OIL, 4), std::exception); + BOOST_CHECK_THROW( oip.get("FIPNUM", Inplace::Phase::GAS, 3), std::exception); + BOOST_CHECK_THROW( oip.get("FIPX", Inplace::Phase::OIL, 3) , std::exception); + + BOOST_CHECK_THROW( oip.get("FIPNUM", "StringID", 4), std::exception); + BOOST_CHECK_THROW( oip.get("FIPNUM", "XXX", 3), std::exception); + BOOST_CHECK_THROW( oip.get("FIPX", "StringID", 3) , std::exception); + + + BOOST_CHECK_EQUAL( oip.max_region(), 6); + BOOST_CHECK_EQUAL( oip.max_region("FIPNUM"), 6); + BOOST_CHECK_THROW( oip.max_region("FIPX"), std::exception); + + oip.add(Inplace::Phase::GAS, 100); + BOOST_CHECK_EQUAL( oip.get(Inplace::Phase::GAS) , 100); + BOOST_CHECK_THROW( oip.get(Inplace::Phase::OIL), std::exception); + + const auto& phases = Inplace::phases(); + BOOST_CHECK(contains(phases, Inplace::Phase::WATER)); + BOOST_CHECK(contains(phases, Inplace::Phase::OIL)); + BOOST_CHECK(contains(phases, Inplace::Phase::GAS)); + BOOST_CHECK(contains(phases, Inplace::Phase::OilInLiquidPhase)); + BOOST_CHECK(contains(phases, Inplace::Phase::OilInGasPhase)); + BOOST_CHECK(contains(phases, Inplace::Phase::GasInLiquidPhase)); + BOOST_CHECK(contains(phases, Inplace::Phase::GasInGasPhase)); + BOOST_CHECK(contains(phases, Inplace::Phase::PoreVolume)); + + + auto v1 = oip.get_vector("FIPNUM", Inplace::Phase::OIL); + std::vector e1 = {0,0,100,0,0,50}; + BOOST_CHECK( v1 == e1 ); + + auto v2 = oip.get_vector("FIPNUM", "StringID"); + std::vector e2 = {0,0,0,0,200,0}; + BOOST_CHECK( v2 == e2 ); +}