commit
fc69eb7b72
@ -114,6 +114,9 @@ if(ENABLE_ECL_INPUT)
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/MSW/updatingConnectionsWithSegments.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/MSW/SpiralICD.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/MSW/Valve.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/Network/Branch.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/Network/ExtNetwork.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/Network/Node.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/OilVaporizationProperties.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/RFTConfig.cpp
|
||||
src/opm/parser/eclipse/EclipseState/Schedule/RPTConfig.cpp
|
||||
@ -332,6 +335,7 @@ if(ENABLE_ECL_INPUT)
|
||||
tests/parser/MultiRegTests.cpp
|
||||
tests/parser/MultisegmentWellTests.cpp
|
||||
tests/parser/MULTREGTScannerTests.cpp
|
||||
tests/parser/NetworkTests.cpp
|
||||
tests/parser/OrderedMapTests.cpp
|
||||
tests/parser/ParseContextTests.cpp
|
||||
tests/parser/ParseContext_EXIT1.cpp
|
||||
@ -664,6 +668,9 @@ if(ENABLE_ECL_INPUT)
|
||||
opm/parser/eclipse/EclipseState/Schedule/Action/ASTNode.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/Action/PyAction.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/ArrayDimChecker.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/Network/Branch.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/Network/ExtNetwork.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/Network/Node.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/VFPInjTable.hpp
|
||||
opm/parser/eclipse/EclipseState/Schedule/VFPProdTable.hpp
|
||||
|
73
opm/parser/eclipse/EclipseState/Schedule/Network/Branch.hpp
Normal file
73
opm/parser/eclipse/EclipseState/Schedule/Network/Branch.hpp
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NETWORK_BRANCH_HPP
|
||||
#define NETWORK_BRANCH_HPP
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace Opm {
|
||||
namespace Network {
|
||||
|
||||
class Branch {
|
||||
public:
|
||||
|
||||
enum class AlqEQ {
|
||||
OIL_DENSITY,
|
||||
GAS_DENSITY,
|
||||
ALQ_INPUT
|
||||
};
|
||||
|
||||
|
||||
static AlqEQ AlqEqfromString(const std::string& input_string);
|
||||
|
||||
Branch() = default;
|
||||
Branch(const std::string& downtree_node, const std::string& uptree_node, int vfp_table, double alq);
|
||||
Branch(const std::string& downtree_node, const std::string& uptree_node, int vfp_table, AlqEQ alq_eq);
|
||||
|
||||
const std::string& downtree_node() const;
|
||||
const std::string& uptree_node() const;
|
||||
std::optional<int> vfp_table() const;
|
||||
AlqEQ alq_eq() const;
|
||||
std::optional<double> alq_value() const;
|
||||
|
||||
static Branch serializeObject();
|
||||
bool operator==(const Branch& other) const;
|
||||
|
||||
template<class Serializer>
|
||||
void serializeOp(Serializer& serializer)
|
||||
{
|
||||
serializer(m_downtree_node);
|
||||
serializer(m_uptree_node);
|
||||
serializer(m_vfp_table);
|
||||
serializer(m_alq_value);
|
||||
serializer(m_alq_eq);
|
||||
}
|
||||
private:
|
||||
std::string m_downtree_node;
|
||||
std::string m_uptree_node;
|
||||
int m_vfp_table;
|
||||
std::optional<double> m_alq_value;
|
||||
AlqEQ m_alq_eq;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef EXT_NETWORK_HPP
|
||||
#define EXT_NETWORK_HPP
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Network/Branch.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Network/Node.hpp>
|
||||
|
||||
namespace Opm {
|
||||
namespace Network {
|
||||
|
||||
|
||||
class ExtNetwork {
|
||||
public:
|
||||
ExtNetwork() = default;
|
||||
bool active() const;
|
||||
bool has_node(const std::string& name) const;
|
||||
void add_node(Node node);
|
||||
void add_branch(Branch branch);
|
||||
const Node& node(const std::string& name) const;
|
||||
const Node& root() const;
|
||||
std::vector<Branch> downtree_branches(const std::string& node) const;
|
||||
std::optional<Branch> uptree_branch(const std::string& node) const;
|
||||
|
||||
|
||||
bool operator==(const ExtNetwork& other) const;
|
||||
static ExtNetwork serializeObject();
|
||||
|
||||
template<class Serializer>
|
||||
void serializeOp(Serializer& serializer)
|
||||
{
|
||||
serializer.vector(m_branches);
|
||||
serializer.map(m_nodes);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Branch> m_branches;
|
||||
std::map<std::string, Node> m_nodes;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
64
opm/parser/eclipse/EclipseState/Schedule/Network/Node.hpp
Normal file
64
opm/parser/eclipse/EclipseState/Schedule/Network/Node.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NETWORK_NODE_HPP
|
||||
#define NETWORK_NODE_HPP
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace Opm {
|
||||
namespace Network {
|
||||
|
||||
class Node {
|
||||
public:
|
||||
Node() = default;
|
||||
Node(const std::string& name);
|
||||
|
||||
const std::string& name() const;
|
||||
const std::optional<double>& terminal_pressure() const;
|
||||
bool as_choke() const;
|
||||
bool add_gas_lift_gas() const;
|
||||
const std::optional<std::string>& target_group() const;
|
||||
|
||||
void terminal_pressure(double pressure);
|
||||
void add_gas_lift_gas(bool add_gas);
|
||||
void as_choke(const std::string& target_group);
|
||||
|
||||
static Node serializeObject();
|
||||
bool operator==(const Node& other) const;
|
||||
|
||||
template<class Serializer>
|
||||
void serializeOp(Serializer& serializer)
|
||||
{
|
||||
serializer(m_name);
|
||||
serializer(m_terminal_pressure);
|
||||
serializer(m_add_gas_lift_gas);
|
||||
serializer(m_choke_target_group);
|
||||
}
|
||||
private:
|
||||
std::string m_name;
|
||||
std::optional<double> m_terminal_pressure;
|
||||
std::optional<std::string> m_choke_target_group;
|
||||
bool m_add_gas_lift_gas = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
@ -40,6 +40,7 @@
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/RFTConfig.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/VFPInjTable.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/VFPProdTable.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Network/ExtNetwork.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellTestConfig.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Action/Actions.hpp>
|
||||
@ -263,6 +264,9 @@ namespace Opm
|
||||
void applyAction(size_t reportStep, const Action::ActionX& action, const Action::Result& result);
|
||||
int getNupcol(size_t reportStep) const;
|
||||
|
||||
|
||||
const Network::ExtNetwork& network(std::size_t report_step) const;
|
||||
|
||||
bool operator==(const Schedule& data) const;
|
||||
std::shared_ptr<const Python> python() const;
|
||||
|
||||
@ -305,6 +309,7 @@ namespace Opm
|
||||
gconsump.serializeOp(serializer);
|
||||
global_whistctl_mode.template serializeOp<Serializer, false>(serializer);
|
||||
m_actions.serializeOp(serializer);
|
||||
m_network.serializeOp(serializer);
|
||||
rft_config.serializeOp(serializer);
|
||||
m_nupcol.template serializeOp<Serializer, false>(serializer);
|
||||
restart_config.serializeOp(serializer);
|
||||
@ -340,6 +345,7 @@ namespace Opm
|
||||
DynamicState<std::shared_ptr<GConSump>> gconsump;
|
||||
DynamicState<Well::ProducerCMode> global_whistctl_mode;
|
||||
DynamicState<std::shared_ptr<Action::Actions>> m_actions;
|
||||
DynamicState<std::shared_ptr<Network::ExtNetwork>> m_network;
|
||||
RFTConfig rft_config;
|
||||
DynamicState<int> m_nupcol;
|
||||
RestartConfig restart_config;
|
||||
@ -367,6 +373,8 @@ namespace Opm
|
||||
const UnitSystem& unit_system);
|
||||
|
||||
DynamicState<std::shared_ptr<RPTConfig>> rpt_config;
|
||||
void updateNetwork(std::shared_ptr<Network::ExtNetwork> network, std::size_t report_step);
|
||||
|
||||
|
||||
GTNode groupTree(const std::string& root_node, std::size_t report_step, const GTNode * parent) const;
|
||||
void updateGroup(std::shared_ptr<Group> group, size_t reportStep);
|
||||
@ -419,6 +427,9 @@ namespace Opm
|
||||
void handleLINCOM( const DeckKeyword& keyword, size_t currentStep);
|
||||
void handleWEFAC( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors);
|
||||
|
||||
void handleBRANPROP( const DeckKeyword& keyword, size_t currentStep);
|
||||
void handleNODEPROP( const DeckKeyword& keyword, size_t currentStep);
|
||||
|
||||
void handleTUNING( const DeckKeyword& keyword, size_t currentStep);
|
||||
void handlePYACTION( std::shared_ptr<const Python> python, const std::string& input_path, const DeckKeyword& keyword, size_t currentStep);
|
||||
void handleNUPCOL( const DeckKeyword& keyword, size_t currentStep);
|
||||
|
@ -160,7 +160,7 @@ T DeckItem::get( size_t index ) const {
|
||||
throw std::out_of_range("Invalid index");
|
||||
|
||||
if (!value::has_value(this->value_status[index]))
|
||||
throw std::invalid_argument("Invalid arguemnt");
|
||||
throw std::invalid_argument("Tried to get unitialized value from DeckItem index: " + std::to_string(index));
|
||||
|
||||
return this->value_ref< T >()[index];
|
||||
}
|
||||
|
103
src/opm/parser/eclipse/EclipseState/Schedule/Network/Branch.cpp
Normal file
103
src/opm/parser/eclipse/EclipseState/Schedule/Network/Branch.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Network/Branch.hpp>
|
||||
|
||||
namespace Opm {
|
||||
namespace Network {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int invalid_vfp_table = 9999;
|
||||
|
||||
}
|
||||
|
||||
|
||||
Branch Branch::serializeObject() {
|
||||
Branch object;
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
Branch::Branch(const std::string& downtree_node, const std::string& uptree_node, int vfp_table, double alq) :
|
||||
m_downtree_node(downtree_node),
|
||||
m_uptree_node(uptree_node),
|
||||
m_vfp_table(vfp_table),
|
||||
m_alq_value(alq),
|
||||
m_alq_eq(AlqEQ::ALQ_INPUT)
|
||||
{
|
||||
}
|
||||
|
||||
Branch::Branch(const std::string& downtree_node, const std::string& uptree_node, int vfp_table, AlqEQ alq_eq):
|
||||
m_downtree_node(downtree_node),
|
||||
m_uptree_node(uptree_node),
|
||||
m_vfp_table(vfp_table),
|
||||
m_alq_eq(alq_eq)
|
||||
{
|
||||
if (alq_eq == AlqEQ::ALQ_INPUT)
|
||||
throw std::logic_error("Wrong constructor - must supply ALQ value");
|
||||
}
|
||||
|
||||
const std::string& Branch::uptree_node() const {
|
||||
return this->m_uptree_node;
|
||||
}
|
||||
|
||||
const std::string& Branch::downtree_node() const {
|
||||
return this->m_downtree_node;
|
||||
}
|
||||
|
||||
bool Branch::operator==(const Branch& other) const {
|
||||
return this->m_downtree_node == other.m_downtree_node &&
|
||||
this->m_uptree_node == other.m_uptree_node &&
|
||||
this->m_vfp_table == other.m_vfp_table &&
|
||||
this->m_alq_value == other.m_alq_value &&
|
||||
this->m_alq_eq == other.m_alq_eq;
|
||||
}
|
||||
|
||||
Branch::AlqEQ Branch::AlqEqfromString(const std::string& input_string) {
|
||||
if (input_string == "NONE")
|
||||
return AlqEQ::ALQ_INPUT;
|
||||
|
||||
if (input_string == "DENO")
|
||||
return AlqEQ::OIL_DENSITY;
|
||||
|
||||
if (input_string == "DENG")
|
||||
return AlqEQ::GAS_DENSITY;
|
||||
|
||||
throw std::invalid_argument("Invalid input for ALQ surface density eq: " + input_string);
|
||||
}
|
||||
|
||||
std::optional<int> Branch::vfp_table() const {
|
||||
if (this->m_vfp_table == invalid_vfp_table)
|
||||
return {};
|
||||
else
|
||||
return this->m_vfp_table;
|
||||
}
|
||||
|
||||
Branch::AlqEQ Branch::alq_eq() const {
|
||||
return this->m_alq_eq;
|
||||
}
|
||||
|
||||
std::optional<double> Branch::alq_value() const {
|
||||
return this->m_alq_value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Network/ExtNetwork.hpp>
|
||||
|
||||
namespace Opm {
|
||||
namespace Network {
|
||||
|
||||
ExtNetwork ExtNetwork::serializeObject() {
|
||||
ExtNetwork object;
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
bool ExtNetwork::active() const {
|
||||
return !this->m_branches.empty() && !this->m_nodes.empty();
|
||||
}
|
||||
|
||||
bool ExtNetwork::operator==(const ExtNetwork&) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ExtNetwork::has_node(const std::string& name) const {
|
||||
return (this->m_nodes.count(name) > 0);
|
||||
}
|
||||
|
||||
const Node& ExtNetwork::node(const std::string& name) const {
|
||||
const auto node_iter = this->m_nodes.find( name );
|
||||
if (node_iter == this->m_nodes.end())
|
||||
throw std::out_of_range("No such node: " + name);
|
||||
|
||||
return node_iter->second;
|
||||
}
|
||||
|
||||
|
||||
const Node& ExtNetwork::root() const {
|
||||
if (this->m_nodes.empty())
|
||||
throw std::invalid_argument("No root defined for empty network");
|
||||
|
||||
auto node_ptr = &(this->m_nodes.begin()->second);
|
||||
while (true) {
|
||||
auto next_branch = this->uptree_branch(node_ptr->name());
|
||||
if (!next_branch)
|
||||
break;
|
||||
|
||||
node_ptr = &(this->node( next_branch->uptree_node() ));
|
||||
}
|
||||
|
||||
return *node_ptr;
|
||||
}
|
||||
|
||||
void ExtNetwork::add_branch(Branch branch)
|
||||
{
|
||||
this->m_branches.push_back( std::move(branch) );
|
||||
}
|
||||
|
||||
|
||||
std::optional<Branch> ExtNetwork::uptree_branch(const std::string& node) const {
|
||||
if (!this->has_node(node))
|
||||
throw std::out_of_range("No such node: " + node);
|
||||
|
||||
std::vector<Branch> branches;
|
||||
std::copy_if(this->m_branches.begin(), this->m_branches.end(), std::back_inserter(branches), [&node](const Branch& b) { return b.downtree_node() == node; });
|
||||
if (branches.empty())
|
||||
return {};
|
||||
|
||||
if (branches.size() == 1)
|
||||
return std::move(branches[0]);
|
||||
|
||||
throw std::logic_error("Bug - more than upstree branch for node: " + node);
|
||||
}
|
||||
|
||||
|
||||
std::vector<Branch> ExtNetwork::downtree_branches(const std::string& node) const {
|
||||
if (!this->has_node(node))
|
||||
throw std::out_of_range("No such node: " + node);
|
||||
|
||||
std::vector<Branch> branches;
|
||||
std::copy_if(this->m_branches.begin(), this->m_branches.end(), std::back_inserter(branches), [&node](const Branch& b) { return b.uptree_node() == node; });
|
||||
return branches;
|
||||
}
|
||||
|
||||
/*
|
||||
The validation of the network structure is very weak. The current validation
|
||||
goes as follows:
|
||||
|
||||
1. A branch is defined with and uptree and downtree node; the node names used
|
||||
in the Branch definition is totally unchecked.
|
||||
|
||||
2. When a node is added we check that the name of the node corresponds to a
|
||||
node name referred to in one of the previous branch definitions.
|
||||
|
||||
The algorithm feels quite illogical, but from the documentation it seems to be
|
||||
the only possibility.
|
||||
*/
|
||||
|
||||
void ExtNetwork::add_node(Node node)
|
||||
{
|
||||
std::string name = node.name();
|
||||
auto branch = std::find_if(this->m_branches.begin(), this->m_branches.end(),
|
||||
[&name](const Branch& b) { return b.uptree_node() == name || b.downtree_node() == name;});
|
||||
|
||||
if (branch == this->m_branches.end())
|
||||
throw std::invalid_argument("Node: " + name + " is not referenced by any branch and would be dangling.");
|
||||
|
||||
|
||||
if (branch->downtree_node() == name) {
|
||||
if (node.as_choke() && branch->vfp_table().has_value())
|
||||
throw std::invalid_argument("Node: " + name + " should serve as a choke => upstream branch can not have VFP table");
|
||||
}
|
||||
|
||||
|
||||
this->m_nodes.insert({ name, std::move(node) });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stdexcept>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Network/Node.hpp>
|
||||
|
||||
namespace Opm {
|
||||
namespace Network {
|
||||
|
||||
Node::Node(const std::string& name) :
|
||||
m_name(name)
|
||||
{}
|
||||
|
||||
const std::string& Node::name() const {
|
||||
return this->m_name;
|
||||
}
|
||||
|
||||
const std::optional<std::string>& Node::target_group() const {
|
||||
return this->m_choke_target_group;
|
||||
}
|
||||
|
||||
const std::optional<double>& Node::terminal_pressure() const {
|
||||
return this->m_terminal_pressure;
|
||||
}
|
||||
|
||||
bool Node::add_gas_lift_gas() const {
|
||||
return this->m_add_gas_lift_gas;
|
||||
}
|
||||
|
||||
bool Node::as_choke() const {
|
||||
return this->m_choke_target_group.has_value();
|
||||
}
|
||||
|
||||
void Node::terminal_pressure(double pressure) {
|
||||
this->m_terminal_pressure = pressure;
|
||||
}
|
||||
|
||||
void Node::add_gas_lift_gas(bool add_gas) {
|
||||
this->m_add_gas_lift_gas = add_gas;
|
||||
}
|
||||
|
||||
void Node::as_choke(const std::string& target_group) {
|
||||
this->m_choke_target_group = target_group;
|
||||
}
|
||||
|
||||
|
||||
bool Node::operator==(const Node& other) const {
|
||||
return this->m_name == other.m_name &&
|
||||
this->m_terminal_pressure == other.m_terminal_pressure &&
|
||||
this->m_add_gas_lift_gas == other.m_add_gas_lift_gas &&
|
||||
this->m_choke_target_group == other.m_choke_target_group;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
@ -35,6 +36,7 @@
|
||||
#include <opm/parser/eclipse/Deck/DeckSection.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ErrorGuard.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParserKeywords/B.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParserKeywords/C.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParserKeywords/G.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParserKeywords/L.hpp>
|
||||
@ -61,6 +63,7 @@
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Tuning.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Network/Node.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WList.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WListManager.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellFoamProperties.hpp>
|
||||
@ -145,6 +148,7 @@ std::pair<std::time_t, std::size_t> restart_info(const RestartIO::RstState * rst
|
||||
gconsump(this->m_timeMap, std::make_shared<GConSump>() ),
|
||||
global_whistctl_mode(this->m_timeMap, Well::ProducerCMode::CMODE_UNDEFINED),
|
||||
m_actions(this->m_timeMap, std::make_shared<Action::Actions>()),
|
||||
m_network(this->m_timeMap, std::make_shared<Network::ExtNetwork>()),
|
||||
rft_config(this->m_timeMap),
|
||||
m_nupcol(this->m_timeMap, ParserKeywords::NUPCOL::NUM_ITER::defaultValue),
|
||||
restart_config(m_timeMap, deck, parseContext, errors),
|
||||
@ -481,6 +485,12 @@ Schedule::Schedule(const Deck& deck, const EclipseState& es, const ParseContext&
|
||||
else if (keyword.name() == "NUPCOL")
|
||||
handleNUPCOL(keyword, currentStep);
|
||||
|
||||
else if (keyword.name() == "NODEPROP")
|
||||
handleNODEPROP(keyword, currentStep);
|
||||
|
||||
else if (keyword.name() == "BRANPROP")
|
||||
handleBRANPROP(keyword, currentStep);
|
||||
|
||||
else if (keyword.name() == "PYACTION")
|
||||
handlePYACTION(python, input_path, keyword, currentStep);
|
||||
|
||||
@ -3116,6 +3126,70 @@ std::shared_ptr<const Python> Schedule::python() const
|
||||
return this->python_handle;
|
||||
}
|
||||
|
||||
|
||||
void Schedule::updateNetwork(std::shared_ptr<Network::ExtNetwork> network, std::size_t report_step) {
|
||||
this->m_network.update(report_step, std::move(network));
|
||||
}
|
||||
|
||||
const Network::ExtNetwork& Schedule::network(std::size_t report_step) const {
|
||||
return *this->m_network[report_step];
|
||||
}
|
||||
|
||||
|
||||
void Schedule::handleNODEPROP(const DeckKeyword& keyword, std::size_t report_step) {
|
||||
using NP = ParserKeywords::NODEPROP;
|
||||
auto ext_network = std::make_shared<Network::ExtNetwork>( this->network(report_step) );
|
||||
for (const auto& record : keyword) {
|
||||
const auto& name = record.getItem<NP::NAME>().get<std::string>(0);
|
||||
const auto& pressure_item = record.getItem<NP::PRESSURE>();
|
||||
bool as_choke = DeckItem::to_bool( record.getItem<NP::AS_CHOKE>().get<std::string>(0));
|
||||
bool add_gas_lift_gas = DeckItem::to_bool( record.getItem<NP::ADD_GAS_LIFT_GAS>().get<std::string>(0));
|
||||
Network::Node node{ name };
|
||||
|
||||
if (pressure_item.hasValue(0) && (pressure_item.get<double>(0) > 0))
|
||||
node.terminal_pressure( pressure_item.getSIDouble(0) );
|
||||
|
||||
if (as_choke) {
|
||||
std::string target_group = name;
|
||||
const auto& target_item = record.getItem<NP::CHOKE_GROUP>();
|
||||
if (target_item.hasValue(0))
|
||||
target_group = target_item.get<std::string>(0);
|
||||
|
||||
if (target_group != name) {
|
||||
if (this->hasGroup(name, report_step)) {
|
||||
const auto& group = this->getGroup(name, report_step);
|
||||
if (group.numWells() > 0)
|
||||
throw std::invalid_argument("A manifold group must respond to its own target");
|
||||
}
|
||||
}
|
||||
node.as_choke( target_group );
|
||||
}
|
||||
node.add_gas_lift_gas( add_gas_lift_gas );
|
||||
ext_network->add_node( node );
|
||||
}
|
||||
this->updateNetwork(ext_network, report_step);
|
||||
}
|
||||
|
||||
|
||||
void Schedule::handleBRANPROP(const DeckKeyword& keyword, std::size_t report_step) {
|
||||
using BP = ParserKeywords::BRANPROP;
|
||||
auto ext_network = std::make_shared<Network::ExtNetwork>( this->network(report_step) );
|
||||
for (const auto& record : keyword) {
|
||||
const auto& downtree_node = record.getItem<BP::DOWNTREE_NODE>().get<std::string>(0);
|
||||
const auto& uptree_node = record.getItem<BP::UPTREE_NODE>().get<std::string>(0);
|
||||
int vfp_table = record.getItem<BP::VFP_TABLE>().get<int>(0);
|
||||
auto alq_eq = Network::Branch::AlqEqfromString( record.getItem<BP::ALQ_SURFACE_DENSITY>().get<std::string>(0));
|
||||
if (alq_eq == Network::Branch::AlqEQ::ALQ_INPUT) {
|
||||
double alq_value = record.getItem<BP::ALQ>().get<double>(0);
|
||||
ext_network->add_branch( Network::Branch(downtree_node, uptree_node, vfp_table, alq_value));
|
||||
} else
|
||||
ext_network->add_branch( Network::Branch(downtree_node, uptree_node, vfp_table, alq_eq));
|
||||
}
|
||||
this->updateNetwork(ext_network, report_step);
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace {
|
||||
/*
|
||||
The insane trickery here (thank you Stackoverflow!) is to be able to provide a
|
||||
|
@ -18,6 +18,11 @@
|
||||
"value_type": "STRING",
|
||||
"default": "NO"
|
||||
},
|
||||
{
|
||||
"name": "ADD_GAS_LIFT_GAS",
|
||||
"value_type": "STRING",
|
||||
"default": "NO"
|
||||
},
|
||||
{
|
||||
"name": "CHOKE_GROUP",
|
||||
"value_type": "STRING"
|
||||
|
228
tests/parser/NetworkTests.cpp
Normal file
228
tests/parser/NetworkTests.cpp
Normal file
@ -0,0 +1,228 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define BOOST_TEST_MODULE ScheduleTests
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Network/ExtNetwork.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Network/Node.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Network/Branch.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Python/Python.hpp>
|
||||
|
||||
using namespace Opm;
|
||||
|
||||
Schedule make_schedule(const std::string& schedule_string) {
|
||||
Parser parser;
|
||||
auto python = std::make_shared<Python>();
|
||||
Deck deck = parser.parseString(schedule_string);
|
||||
EclipseGrid grid(10,10,10);
|
||||
TableManager table ( deck );
|
||||
FieldPropsManager fp( deck, Phases{true, true, true}, grid, table);
|
||||
Runspec runspec (deck);
|
||||
Schedule schedule(deck, grid , fp, runspec, python);
|
||||
return schedule;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CreateNetwork) {
|
||||
Network::ExtNetwork network;
|
||||
BOOST_CHECK( !network.active() );
|
||||
auto schedule = make_schedule("SCHEDULE\n");
|
||||
auto network2 = schedule.network(0);
|
||||
BOOST_CHECK( !network2.active() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Branch) {
|
||||
BOOST_CHECK_THROW( Network::Branch("down", "up", 100, Network::Branch::AlqEQ::ALQ_INPUT), std::logic_error);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(INVALID_DOWNTREE_NODE) {
|
||||
std::string deck_string = R"(
|
||||
SCHEDULE
|
||||
|
||||
GRUPTREE
|
||||
'PROD' 'FIELD' /
|
||||
|
||||
'M5S' 'PLAT-A' /
|
||||
'M5N' 'PLAT-A' /
|
||||
|
||||
'C1' 'M5N' /
|
||||
'F1' 'M5N' /
|
||||
'B1' 'M5S' /
|
||||
'G1' 'M5S' /
|
||||
/
|
||||
|
||||
BRANPROP
|
||||
-- Downtree Uptree #VFP ALQ
|
||||
B1X PLAT-A 5 1* /
|
||||
C1 PLAT-A 4 1* /
|
||||
/
|
||||
|
||||
NODEPROP
|
||||
-- Node_name Pr autoChock? addGasLift? Group_name
|
||||
PLAT-A 21.0 NO NO 1* /
|
||||
B1 1* NO NO 1* /
|
||||
C1 1* NO NO 1* /
|
||||
/
|
||||
)";
|
||||
|
||||
BOOST_CHECK_THROW( make_schedule(deck_string), std::invalid_argument);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(INVALID_UPTREE_NODE) {
|
||||
std::string deck_string = R"(
|
||||
SCHEDULE
|
||||
|
||||
GRUPTREE
|
||||
'PROD' 'FIELD' /
|
||||
|
||||
'M5S' 'PLAT-A' /
|
||||
'M5N' 'PLAT-A' /
|
||||
|
||||
'C1' 'M5N' /
|
||||
'F1' 'M5N' /
|
||||
'B1' 'M5S' /
|
||||
'G1' 'M5S' /
|
||||
/
|
||||
|
||||
BRANPROP
|
||||
-- Downtree Uptree #VFP ALQ
|
||||
B1 PLAT-AX 5 1* /
|
||||
C1 PLAT-AX 4 1* /
|
||||
/
|
||||
|
||||
NODEPROP
|
||||
-- Node_name Pr autoChock? addGasLift? Group_name
|
||||
PLAT-A 21.0 NO NO 1* /
|
||||
B1 1* NO NO 1* /
|
||||
C1 1* NO NO 1* /
|
||||
/
|
||||
)";
|
||||
|
||||
BOOST_CHECK_THROW( make_schedule(deck_string), std::invalid_argument);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(INVALID_VFP_NODE) {
|
||||
std::string deck_string = R"(
|
||||
SCHEDULE
|
||||
|
||||
GRUPTREE
|
||||
'PROD' 'FIELD' /
|
||||
|
||||
'M5S' 'PLAT-A' /
|
||||
'M5N' 'PLAT-A' /
|
||||
|
||||
'C1' 'M5N' /
|
||||
'F1' 'M5N' /
|
||||
'B1' 'M5S' /
|
||||
'G1' 'M5S' /
|
||||
/
|
||||
|
||||
BRANPROP
|
||||
-- Downtree Uptree #VFP ALQ
|
||||
B1 PLAT-A 5 1* /
|
||||
C1 PLAT-A 4 1* / --This is a choke branch - must have VFP=9999
|
||||
/
|
||||
|
||||
NODEPROP
|
||||
-- Node_name Pr autoChock? addGasLift? Group_name
|
||||
PLAT-A 21.0 NO NO 1* /
|
||||
B1 1* NO NO 1* /
|
||||
C1 1* YES NO 1* /
|
||||
/
|
||||
)";
|
||||
|
||||
BOOST_CHECK_THROW( make_schedule(deck_string), std::invalid_argument);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(OK) {
|
||||
std::string deck_string = R"(
|
||||
SCHEDULE
|
||||
|
||||
GRUPTREE
|
||||
'PROD' 'FIELD' /
|
||||
|
||||
'M5S' 'PLAT-A' /
|
||||
'M5N' 'PLAT-A' /
|
||||
|
||||
'C1' 'M5N' /
|
||||
'F1' 'M5N' /
|
||||
'B1' 'M5S' /
|
||||
'G1' 'M5S' /
|
||||
/
|
||||
|
||||
BRANPROP
|
||||
-- Downtree Uptree #VFP ALQ
|
||||
B1 PLAT-A 9999 1* /
|
||||
C1 PLAT-A 9999 1* /
|
||||
/
|
||||
|
||||
NODEPROP
|
||||
-- Node_name Pr autoChock? addGasLift? Group_name
|
||||
PLAT-A 21.0 NO NO 1* /
|
||||
B1 1* YES NO 1* /
|
||||
C1 1* YES NO 'GROUP' /
|
||||
/
|
||||
)";
|
||||
|
||||
auto sched = make_schedule(deck_string);
|
||||
const auto& network = sched.network(0);
|
||||
const auto& b1 = network.node("B1");
|
||||
BOOST_CHECK( b1.as_choke() );
|
||||
BOOST_CHECK(!b1.add_gas_lift_gas());
|
||||
BOOST_CHECK( b1.name() == b1.target_group());
|
||||
BOOST_CHECK(!b1.terminal_pressure());
|
||||
|
||||
const auto& p = network.node("PLAT-A");
|
||||
BOOST_CHECK( p.terminal_pressure() );
|
||||
BOOST_CHECK_EQUAL( p.terminal_pressure().value() , 21 * 100000 );
|
||||
BOOST_CHECK( p == network.root() );
|
||||
|
||||
BOOST_CHECK_THROW( network.node("NO_SUCH_NODE"), std::out_of_range);
|
||||
|
||||
|
||||
|
||||
BOOST_CHECK_EQUAL( network.downtree_branches("PLAT-A").size(), 2);
|
||||
for (const auto& b : network.downtree_branches("PLAT-A")) {
|
||||
BOOST_CHECK_EQUAL(b.uptree_node(), "PLAT-A");
|
||||
BOOST_CHECK(b.downtree_node() == "B1" || b.downtree_node() == "C1");
|
||||
}
|
||||
|
||||
|
||||
const auto& platform_uptree = network.uptree_branch("PLAT-A");
|
||||
BOOST_CHECK( !platform_uptree.has_value() );
|
||||
|
||||
const auto& B1_uptree = network.uptree_branch("B1");
|
||||
BOOST_CHECK( B1_uptree.has_value() );
|
||||
BOOST_CHECK_EQUAL( B1_uptree->downtree_node(), "B1" );
|
||||
BOOST_CHECK_EQUAL( B1_uptree->uptree_node(), "PLAT-A" );
|
||||
|
||||
BOOST_CHECK( network.active() );
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user