From def4dd2c6864ea6d1b6516ce3d7c094cac809bd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Wed, 3 Nov 2021 22:46:12 +0100 Subject: [PATCH 1/4] Return Restart State Object by Move() Useful since we don't get in-place construction when returning a composite object. Also useful if we decide to make RstState a move-only type. --- tests/parser/ScheduleRestartTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/parser/ScheduleRestartTests.cpp b/tests/parser/ScheduleRestartTests.cpp index 1bea132ce..5cc6afbfd 100644 --- a/tests/parser/ScheduleRestartTests.cpp +++ b/tests/parser/ScheduleRestartTests.cpp @@ -123,7 +123,7 @@ std::tuple load_schedule_pair(const std EclipseState ecl_state_restart(restart_deck); Schedule restart_sched(restart_deck, ecl_state_restart, python, {}, &rst_state); - return {sched, restart_sched, rst_state}; + return {sched, restart_sched, std::move(rst_state)}; } From c3b3ce48321562d8161b25e0deabf253f1970a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Wed, 3 Nov 2021 22:44:47 +0100 Subject: [PATCH 2/4] Sort Headers in Restart State Object Implementation In preparation of adding restart support for the extended network model. --- opm/io/eclipse/rst/state.hpp | 15 +++++----- src/opm/io/eclipse/rst/state.cpp | 49 ++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/opm/io/eclipse/rst/state.hpp b/opm/io/eclipse/rst/state.hpp index bb1528872..1593adcfd 100644 --- a/opm/io/eclipse/rst/state.hpp +++ b/opm/io/eclipse/rst/state.hpp @@ -24,22 +24,21 @@ #include #include -#include #include -#include -#include -#include #include +#include +#include +#include +#include + +#include +#include #include -#include -#include - namespace Opm { class EclipseGrid; class Parser; - class Parser; } // namespace Opm namespace Opm { namespace EclIO { diff --git a/src/opm/io/eclipse/rst/state.cpp b/src/opm/io/eclipse/rst/state.cpp index 15d28c4d8..ca4966f1b 100644 --- a/src/opm/io/eclipse/rst/state.cpp +++ b/src/opm/io/eclipse/rst/state.cpp @@ -16,33 +16,40 @@ along with OPM. If not, see . */ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + #include #include #include #include #include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - namespace { std::string udq_define(const std::vector& zudl, From 19ec3b34f9e286bac7cf88c3655eeba8c0d3c571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Wed, 3 Nov 2021 22:50:05 +0100 Subject: [PATCH 3/4] Support Loading Extended Network Model From Restart This commit adds a new helper class, RstNetwork, that loads the raw data representing the extended network model from restart vectors IBRAN, INODE, RNODE, and ZNODE. We support loading the information that is currently output to those restart vectors, but those do not include every option supported by the input keywords BRANPROP and NODEPROP. The RstNetwork type supports querying whether or not the extended network model is active and, if so, to get iterable collections of branches and nodes that are geared towards constructing ExtNetwork objects. --- CMakeLists_files.cmake | 2 + opm/io/eclipse/rst/network.hpp | 95 +++++++++++ src/opm/io/eclipse/rst/network.cpp | 250 +++++++++++++++++++++++++++++ 3 files changed, 347 insertions(+) create mode 100644 opm/io/eclipse/rst/network.hpp create mode 100644 src/opm/io/eclipse/rst/network.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index f66858321..e51b422a4 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -298,6 +298,7 @@ if(ENABLE_ECL_OUTPUT) src/opm/io/eclipse/rst/connection.cpp src/opm/io/eclipse/rst/group.cpp src/opm/io/eclipse/rst/header.cpp + src/opm/io/eclipse/rst/network.cpp src/opm/io/eclipse/rst/udq.cpp src/opm/io/eclipse/rst/segment.cpp src/opm/io/eclipse/rst/state.cpp @@ -910,6 +911,7 @@ if(ENABLE_ECL_OUTPUT) opm/io/eclipse/rst/connection.hpp opm/io/eclipse/rst/group.hpp opm/io/eclipse/rst/header.hpp + opm/io/eclipse/rst/network.hpp opm/io/eclipse/rst/segment.hpp opm/io/eclipse/rst/state.hpp opm/io/eclipse/rst/udq.hpp diff --git a/opm/io/eclipse/rst/network.hpp b/opm/io/eclipse/rst/network.hpp new file mode 100644 index 000000000..ef961bf2d --- /dev/null +++ b/opm/io/eclipse/rst/network.hpp @@ -0,0 +1,95 @@ +/* + Copyright 2021 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 RST_NETWORK_HPP +#define RST_NETWORK_HPP + +#include +#include +#include +#include + +namespace Opm { + class UnitSystem; +} // namespace Opm + +namespace Opm { namespace EclIO { + class RestartFileView; +}} // namespace Opm::EclIO + +namespace Opm { namespace RestartIO { + + class RstNetwork + { + public: + /// Single branch in extended network model. + struct Branch + { + /// Downtree node. Index into 'nodes' array. + int down{-1}; + + /// Uptree node. Index into 'nodes' array. + int up{-1}; + + /// One-based VFP table ID. + int vfp{-1}; + }; + + /// Single node in extended network model. + struct Node + { + /// Name of network node. + std::string name{}; + + /// Fixed pressure for terminal node. Nullopt if not terminal. + std::optional terminal_pressure{}; + + /// Group whose rate target the choking mechanism attempts to + /// match. Nullopt if this node does not act as a choke or if + /// choking is disabled. + std::optional as_choke{}; + + /// Whether or not to include lift gas of subordinate wells as + /// part of the produced gas entering the network at this node. + bool add_lift_gas{false}; + }; + + explicit RstNetwork(std::shared_ptr rstView, + const UnitSystem& usys); + + bool isActive() const; + + const std::vector& branches() const + { + return this->branches_; + } + + const std::vector& nodes() const + { + return this->nodes_; + } + + private: + std::vector branches_{}; + std::vector nodes_{}; + }; + +}} // namespace Opm::RestartIO + +#endif // RST_NETWORK_HPP diff --git a/src/opm/io/eclipse/rst/network.cpp b/src/opm/io/eclipse/rst/network.cpp new file mode 100644 index 000000000..1af1b0e59 --- /dev/null +++ b/src/opm/io/eclipse/rst/network.cpp @@ -0,0 +1,250 @@ +/* + Copyright 2021 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 +#include +#include +#include +#include + +#include + +namespace VI = ::Opm::RestartIO::Helpers::VectorItems; + +namespace { + template + boost::iterator_range::const_iterator> + getDataWindow(const std::vector& arr, + const std::size_t windowSize, + const std::size_t entity) + { + const auto off = windowSize * entity; + + auto begin = arr.begin() + off; + auto end = begin + windowSize; + + return { begin, end }; + } +} + +// --------------------------------------------------------------------------- + +class BranchVectors +{ +public: + template + using Window = boost::iterator_range< + typename std::vector::const_iterator + >; + + explicit BranchVectors(const std::vector& intehead, + std::shared_ptr rst_view); + + std::size_t numActiveBranches() const + { + return this->numActiveBranches_; + } + + Window ibran(const std::size_t branchID) const; + +private: + std::size_t numActiveBranches_; + std::size_t numIBranElem_; + + std::shared_ptr rstView_; +}; + +BranchVectors::BranchVectors(const std::vector& intehead, + std::shared_ptr rst_view) + : numActiveBranches_(intehead[VI::intehead::NOACTBR]) + , numIBranElem_ (intehead[VI::intehead::NIBRAN]) + , rstView_ (std::move(rst_view)) +{} + +BranchVectors::Window +BranchVectors::ibran(const std::size_t branchID) const +{ + return getDataWindow(this->rstView_->getKeyword("IBRAN"), + this->numIBranElem_, branchID); +} + +// --------------------------------------------------------------------------- + +class NodeVectors +{ +public: + template + using Window = boost::iterator_range< + typename std::vector::const_iterator + >; + + explicit NodeVectors(const std::vector& intehead, + std::shared_ptr rst_view); + + std::size_t numActiveNodes() const + { + return this->numActiveNodes_; + } + + Window inode(const std::size_t nodeID) const; + Window rnode(const std::size_t nodeID) const; + Window znode(const std::size_t nodeID) const; + +private: + std::size_t numActiveNodes_; + std::size_t numINodeElem_; + std::size_t numRNodeElem_; + std::size_t numZNodeElem_; + + std::shared_ptr rstView_; +}; + +NodeVectors::NodeVectors(const std::vector& intehead, + std::shared_ptr rst_view) + : numActiveNodes_(intehead[VI::intehead::NOACTNOD]) + , numINodeElem_ (intehead[VI::intehead::NINODE]) + , numRNodeElem_ (intehead[VI::intehead::NRNODE]) + , numZNodeElem_ (intehead[VI::intehead::NZNODE]) + , rstView_ (std::move(rst_view)) +{} + +NodeVectors::Window +NodeVectors::inode(const std::size_t nodeID) const +{ + return getDataWindow(this->rstView_->getKeyword("INODE"), + this->numINodeElem_, nodeID); +} + +NodeVectors::Window +NodeVectors::rnode(const std::size_t nodeID) const +{ + return getDataWindow(this->rstView_->getKeyword("RNODE"), + this->numRNodeElem_, nodeID); +} + +NodeVectors::Window +NodeVectors::znode(const std::size_t nodeID) const +{ + return getDataWindow(this->rstView_->getKeyword("ZNODE"), + this->numZNodeElem_, nodeID); +} + +// --------------------------------------------------------------------------- + +namespace { + int branch_vfp_no_pressure_loss() + { + return 9999; + } + + template + Opm::RestartIO::RstNetwork::Branch make_branch(const IBranArray ibran) + { + using Ix = VI::IBran::index; + + auto branch = Opm::RestartIO::RstNetwork::Branch{}; + + branch.down = ibran[Ix::DownTreeNode] - 1; // Index into ZNODE + branch.up = ibran[Ix::UpTreeNode] - 1; // Index into ZNODE + branch.vfp = (ibran[Ix::VfpTableNo] > 0) + ? ibran[Ix::VfpTableNo] // One-based VFP table ID + : branch_vfp_no_pressure_loss(); + + return branch; + } + + template + Opm::RestartIO::RstNetwork::Node + make_node(const std::string& name, + const Opm::UnitSystem& usys, + const INodeArray inode, + const RNodeArray rnode) + { + auto node = Opm::RestartIO::RstNetwork::Node{}; + node.name = name; + + if (inode[VI::INode::index::FixedPresNode] != 0) { + node.terminal_pressure = + usys.to_si(Opm::UnitSystem::measure::pressure, + rnode[VI::RNode::index::PressureLimit]); + } + + return node; + } + + std::vector + create_branches(std::shared_ptr rstView) + { + auto branches = std::vector{}; + + const auto branchData = BranchVectors { rstView->intehead(), rstView }; + const auto numBranches = branchData.numActiveBranches(); + + branches.reserve(numBranches); + + for (auto branchID = 0*numBranches; branchID < numBranches; ++branchID) { + branches.push_back(make_branch(branchData.ibran(branchID))); + } + + return branches; + } + + std::vector + create_nodes(std::shared_ptr rstView, + const Opm::UnitSystem& usys) + { + auto nodes = std::vector{}; + + const auto nodeData = NodeVectors { rstView->intehead(), rstView }; + const auto numNodes = nodeData.numActiveNodes(); + + nodes.reserve(numNodes); + + for (auto nodeID = 0*numNodes; nodeID < numNodes; ++nodeID) { + const auto znode = nodeData.znode(nodeID); + + nodes.push_back(make_node(znode[VI::ZNode::index::NodeName], usys, + nodeData.inode(nodeID), + nodeData.rnode(nodeID))); + } + + return nodes; + } +} + +Opm::RestartIO::RstNetwork::RstNetwork(std::shared_ptr rstView, + const UnitSystem& usys) + : branches_{ create_branches(rstView) } + , nodes_ { create_nodes (rstView, usys) } +{} + +bool Opm::RestartIO::RstNetwork::isActive() const +{ + return ! (this->branches_.empty() || + this->nodes_.empty()); +} From 459fd672036ddf1ef2d6be10db55362614e229cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Wed, 3 Nov 2021 22:56:34 +0100 Subject: [PATCH 4/4] Incorporate Extended Network Objects From Restart into Schedule This commit hooks the new RstNetwork object into the restart feature of the main Schedule object. We form ExtNetwork Schedule subobjects if there are active branches and nodes in the restart file. --- opm/io/eclipse/rst/state.hpp | 2 + src/opm/io/eclipse/rst/state.cpp | 2 + .../EclipseState/Schedule/Schedule.cpp | 40 +++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/opm/io/eclipse/rst/state.hpp b/opm/io/eclipse/rst/state.hpp index 1593adcfd..0a0d6fe10 100644 --- a/opm/io/eclipse/rst/state.hpp +++ b/opm/io/eclipse/rst/state.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,7 @@ struct RstState { ::Opm::UnitSystem unit_system; RstHeader header; RstAquifer aquifers; + RstNetwork network; std::vector wells; std::vector groups; std::vector udqs; diff --git a/src/opm/io/eclipse/rst/state.cpp b/src/opm/io/eclipse/rst/state.cpp index ca4966f1b..1ce511cfe 100644 --- a/src/opm/io/eclipse/rst/state.cpp +++ b/src/opm/io/eclipse/rst/state.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -85,6 +86,7 @@ RstState::RstState(std::shared_ptr rstView, : unit_system(rstView->intehead()[VI::intehead::UNIT]) , header(unit_system, rstView->intehead(), rstView->logihead(), rstView->doubhead()) , aquifers(rstView, grid, unit_system) + , network(rstView, unit_system) { this->load_tuning(rstView->intehead(), rstView->doubhead()); } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index 3edb3f4ac..7e5650d39 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -1561,6 +1562,45 @@ namespace { if (!rst_state.wlists.empty()) this->snapshots.back().wlist_manager.update( WListManager(rst_state) ); + + if (rst_state.network.isActive()) { + auto network = this->snapshots.back().network(); + + // Note: We presently support only the default value of BRANPROP(4). + const auto alq_value = + ParserKeywords::BRANPROP::ALQ::defaultValue; + + const auto& rst_nodes = rst_state.network.nodes(); + for (const auto& rst_branch : rst_state.network.branches()) { + if ((rst_branch.down < 0) || (rst_branch.up < 0)) { + // Prune branches to non-existent nodes. + continue; + } + + const auto& downtree_node = rst_nodes[rst_branch.down].name; + const auto& uptree_node = rst_nodes[rst_branch.up].name; + + network.add_branch({ downtree_node, uptree_node, rst_branch.vfp, alq_value }); + } + + for (const auto& rst_node : rst_nodes) { + auto node = Network::Node { rst_node.name }; + + if (rst_node.terminal_pressure.has_value()) { + node.terminal_pressure(rst_node.terminal_pressure.value()); + } + + if (rst_node.as_choke.has_value()) { + node.as_choke(rst_node.as_choke.value()); + } + + node.add_gas_lift_gas(rst_node.add_lift_gas); + + network.add_node(std::move(node)); + } + + this->snapshots.back().network.update(std::move(network)); + } } std::shared_ptr Schedule::python() const