20#ifndef OPM_BLACKOIL_WELL_MODEL_NETWORK_PRESSURE_COMPUTATION_HPP
21#define OPM_BLACKOIL_WELL_MODEL_NETWORK_PRESSURE_COMPUTATION_HPP
23#include <opm/common/TimingMacros.hpp>
25#include <opm/input/eclipse/Schedule/Group/GConSump.hpp>
26#include <opm/input/eclipse/Schedule/Group/GSatProd.hpp>
27#include <opm/input/eclipse/Schedule/Network/ExtNetwork.hpp>
28#include <opm/input/eclipse/Schedule/Schedule.hpp>
29#include <opm/input/eclipse/Schedule/ScheduleState.hpp>
30#include <opm/input/eclipse/Schedule/SummaryState.hpp>
31#include <opm/input/eclipse/Schedule/VFPProdTable.hpp>
32#include <opm/input/eclipse/Units/Units.hpp>
34#include <opm/output/data/Groups.hpp>
41#include <fmt/format.h>
58template<
typename Scalar>
72 template<
typename Scalar,
typename IndexTraits,
typename Table>
73 bool clampToTableAxes(
const Table& table, std::vector<Scalar>& rates, Scalar& up_press)
76 const auto& thp_axis = table.getTHPAxis();
77 const Scalar thp_lo = thp_axis.front();
78 const Scalar thp_hi = thp_axis.back();
79 if (up_press < thp_lo || up_press > thp_hi) {
80 up_press = std::clamp(up_press, thp_lo, thp_hi);
83 const auto& flo_axis = table.getFloAxis();
84 const Scalar flo = std::abs(
getFlo(table,
85 rates[IndexTraits::waterPhaseIdx],
86 rates[IndexTraits::oilPhaseIdx],
87 rates[IndexTraits::gasPhaseIdx]));
88 const Scalar flo_hi = flo_axis.back();
89 if (flo > flo_hi && flo > 0.0) {
90 const Scalar s = flo_hi / flo;
91 std::ranges::transform(rates, rates.begin(), [s](
const auto r) { return s * r; });
93 }
else if (flo < flo_axis.front()) {
94 const Scalar flo_lo = flo_axis.front();
96 const Scalar s = flo_lo / flo;
97 std::ranges::transform(rates, rates.begin(), [s](
const auto r) { return s * r; });
101 switch (table.getFloType()) {
102 case VFPInjTable::FLO_TYPE::FLO_OIL:
103 rates[IndexTraits::oilPhaseIdx] = flo_lo;
105 case VFPInjTable::FLO_TYPE::FLO_WAT:
106 rates[IndexTraits::waterPhaseIdx] = flo_lo;
108 case VFPInjTable::FLO_TYPE::FLO_GAS:
109 rates[IndexTraits::gasPhaseIdx] = flo_lo;
123 const std::string& name)
125 return network.has_node(name)
126 && (network.uptree_branch(name).has_value()
127 || !network.downtree_branches(name).empty());
143template<
typename Scalar>
144std::map<std::string, Scalar>
146 const ScheduleState& sched_state,
147 const SummaryState& summary_state)
149 std::map<std::string, Scalar> node_rates;
150 if (!network.active()) {
154 const auto& gconsump = sched_state.gconsump();
155 for (
const auto& group_name : gconsump.group_names()) {
156 const auto gc = gconsump.get(group_name, summary_state);
157 const auto net_rate =
static_cast<Scalar
>(gc.consumption_rate - gc.import_rate);
158 if (net_rate == Scalar{0} || gc.network_node == GConSump::NO_NODES) {
162 if (!gc.network_node.empty()) {
164 node_rates[gc.network_node] += net_rate;
169 Scalar efficiency = 1.0;
170 std::string name = group_name;
171 while (sched_state.groups.has(name)) {
173 if (network.downtree_branches(name).empty()) {
174 node_rates[name] += efficiency * net_rate;
178 if (name ==
"FIELD") {
181 const auto& group = sched_state.groups(name);
182 efficiency *= group.getGroupEfficiencyFactor(
true);
183 name = group.parent();
192template<
typename Scalar,
typename IndexTraits,
typename VfpProperties>
196template<
typename Scalar,
typename IndexTraits>
202 std::ranges::transform(rates, rates.begin(), [](
const auto r) { return -r; });
205 template <
class GroupState>
207 const std::string& node)
212 template <
class GroupState>
213 static const std::vector<Scalar>
215 const std::string& node)
220 template<
typename Branch>
223 std::vector<Scalar> rates,
225 const Branch& upbranch,
226 const UnitSystem& unit_system)
231 const auto& table = vfp_props.
getTable(table_id);
232 const auto alq_type = table.getALQType();
233 const auto dimension = VFPProdTable::ALQDimension(alq_type, unit_system);
234 const Scalar alq = upbranch.alq_value(dimension).value_or(0.0);
242 rates[IndexTraits::waterPhaseIdx],
243 rates[IndexTraits::oilPhaseIdx],
244 rates[IndexTraits::gasPhaseIdx],
256template<
typename Scalar,
typename IndexTraits>
263 template <
class GroupState>
265 const std::string& node)
270 template <
class GroupState>
271 static const std::vector<Scalar>
273 const std::string& node)
278 template<
typename Branch>
281 std::vector<Scalar> rates,
287 result.
clamped = detail::clampToTableAxes<Scalar, IndexTraits>(vfp_props.
getTable(table_id), rates, up_press);
289 rates[IndexTraits::waterPhaseIdx],
290 rates[IndexTraits::oilPhaseIdx],
291 rates[IndexTraits::gasPhaseIdx],
303template<
typename Scalar>
306 const std::map<std::string, Scalar>& overrides)
310 if (overrides.count(node) > 0) {
313 if (network.node(node).terminal_pressure().has_value()) {
316 const auto branch = network.uptree_branch(node);
317 if (!branch.has_value()) {
320 node = branch->uptree_node();
327template<
typename GenericWellModel,
typename VfpProperties,
typename Communication = Parallel::Communication>
335 const Network::ExtNetwork& network,
336 const VfpProperties& vfp_props,
337 const UnitSystem& unit_system,
338 const int report_step_idx,
340 const std::map<std::string, typename GenericWellModel::Scalar>*
341 terminal_pressure_overrides =
nullptr,
342 std::map<std::string, typename GenericWellModel::Scalar>
343 node_gas_consumption = {})
344 : well_model_(well_model)
346 , vfp_props_(vfp_props)
347 , unit_system_(unit_system)
348 , report_step_idx_(report_step_idx)
350 , terminal_pressure_overrides_(terminal_pressure_overrides)
351 , node_gas_consumption_(std::move(node_gas_consumption))
355 using Scalar =
typename GenericWellModel::Scalar;
357 std::pair<std::map<std::string, Scalar>, std::map<std::string, data::BranchData>>
run()
359 for (
const auto& root : topRoots()) {
365 const auto [root_to_child_nodes, leaf_nodes] = collectTreeNodes(root);
369 auto node_inflows = initializeLeafInflows(leaf_nodes);
375 accumulateInflows(root_to_child_nodes, node_inflows);
379 computeNodePressures(root_to_child_nodes, node_inflows);
381#ifdef OPM_NETWORK_PRESSURE_TRACE
384 OpmLog::debug(
"Network pressure computation completed for root " + root +
". Node pressures:");
385 for (
const auto& [node, pressure] : node_pressures_) {
386 OpmLog::debug(
"Network node " + node +
" pressure: " +
std::to_string(pressure/1e5) +
" bar");
388 OpmLog::debug(
"Node inflows:");
389 for (
const auto& [node, inflows] : node_inflows) {
390 OpmLog::debug(
"Network node " + node +
" inflows: "
397 return {node_pressures_, branch_data_};
405 return invalid_nodes_;
416 std::vector<std::string> topRoots()
const
418 std::vector<std::string> top_roots;
419 for (
const auto& root : network_.roots()) {
420 auto top = root.get().name();
421 auto visited = std::set<std::string>{top};
423 while (
const auto upbranch = network_.uptree_branch(node)) {
424 node = upbranch->uptree_node();
425 if (!visited.insert(node).second) {
428 if (network_.node(node).terminal_pressure().has_value()) {
432 if (std::ranges::find(top_roots, top) == top_roots.end()) {
433 top_roots.push_back(top);
439 std::pair<std::vector<std::string>, std::set<std::string>>
440 collectTreeNodes(
const std::string& root)
const
442 std::stack<std::string> children;
443 std::set<std::string> leaf_nodes;
444 std::vector<std::string> root_to_child_nodes;
446 while (!children.empty()) {
447 const auto node = children.top();
449 root_to_child_nodes.push_back(node);
450 auto branches = network_.downtree_branches(node);
451 if (branches.empty()) {
452 leaf_nodes.insert(node);
454 for (
const auto& branch : branches) {
455 children.push(branch.downtree_node());
459 assert(children.empty());
460 return {root_to_child_nodes, leaf_nodes};
463 std::map<std::string, std::vector<Scalar>>
464 initializeLeafInflows(
const std::set<std::string>& leaf_nodes)
const
466 std::map<std::string, std::vector<Scalar>> node_inflows;
467 const std::vector<Scalar> zero_rates(3, 0.0);
469 for (
const auto& node : leaf_nodes) {
474 using Calc = NetworkVfpPressureCalculator<Scalar, IndexTraits, VfpProperties>;
475 if (!Calc::hasLeafNodeRate(well_model_.groupStateHelper().groupState(), node)) {
476 node_inflows[node] = zero_rates;
480 node_inflows[node] = Calc::leafNodeRate(well_model_.groupStateHelper().groupState(),
482 if (network_.node(node).add_gas_lift_gas()) {
483 addGasLiftGas(node, node_inflows[node]);
490 void addGasLiftGas(
const std::string& node,
491 std::vector<Scalar>& rates)
const
493 const auto& group = well_model_.schedule().getGroup(node, report_step_idx_);
494 const auto& well_state = well_model_.groupStateHelper().wellState();
497 for (
const std::string& wellname : group.wells()) {
498 const Well& well = well_model_.schedule().getWell(wellname, report_step_idx_);
499 if (well.isInjector() || !well_state.isOpen(wellname)) {
503 const Scalar efficiency = well.getEfficiencyFactor(
true)
504 * well_state.getGlobalEfficiencyScalingFactor(wellname);
505 const auto& well_index = well_state.index(wellname);
506 if (well_index.has_value() &&
507 well_state.wellIsOwned(well_index.value(), wellname))
509 alq += well_state.well(wellname).alq_state.get() * efficiency;
518 alq = comm_.sum(alq);
521 if (group.hasSatelliteProduction()) {
522 const auto gsrate = this->well_model_
523 .schedule()[this->report_step_idx_]
524 .satelliteProduction(node)
525 .getRate(GSatProd::Rate::GLift, this->well_model_.summaryState());
527 alq +=
static_cast<Scalar>(gsrate);
530 rates[IndexTraits::gasPhaseIdx] += alq;
536 void applyGasConsumption(
const std::string& node,
537 std::vector<Scalar>& rates)
const
539 const auto it = node_gas_consumption_.find(node);
540 if (it == node_gas_consumption_.end()) {
544 rates.assign(3, 0.0);
546 if (rates.size() <=
static_cast<std::size_t
>(IndexTraits::gasPhaseIdx)) {
549 auto& gas_rate = rates[IndexTraits::gasPhaseIdx];
550 gas_rate = std::max(gas_rate - it->second,
Scalar{0});
553 void accumulateInflows(
const std::vector<std::string>& root_to_child_nodes,
554 std::map<std::string, std::vector<Scalar>>& node_inflows)
const
556 const auto child_to_root_nodes = std::ranges::reverse_view(root_to_child_nodes);
560 for (
const auto& node : child_to_root_nodes) {
561 applyGasConsumption(node, node_inflows[node]);
562 const auto upbranch = network_.uptree_branch(node);
566 std::vector<Scalar>& up = node_inflows[(*upbranch).uptree_node()];
567 const std::vector<Scalar>& down = node_inflows[node];
569 const Scalar efficiency = network_.node(node).efficiency();
571 up = std::vector<Scalar>(down.size(), 0.0);
573 assert(up.size() == down.size());
574 for (std::size_t ii = 0; ii < up.size(); ++ii) {
575 up[ii] += efficiency * down[ii];
580 void computeNodePressures(
const std::vector<std::string>& root_to_child_nodes,
581 const std::map<std::string, std::vector<Scalar>>& node_inflows)
583 for (
const auto& node : root_to_child_nodes) {
585 if (node_pressures_.find(node) != node_pressures_.end()) {
589 auto terminal_pressure = network_.node(node).terminal_pressure();
590 if (terminal_pressure && terminal_pressure_overrides_) {
593 if (
const auto it = terminal_pressure_overrides_->find(node);
594 it != terminal_pressure_overrides_->end()) {
595 terminal_pressure = it->second;
598 const auto upbranch = network_.uptree_branch(node);
599 assert(upbranch || terminal_pressure);
600 using Calc = NetworkVfpPressureCalculator<Scalar, IndexTraits, VfpProperties>;
602 if (terminal_pressure) {
603 node_pressures_[node] = *terminal_pressure;
604 const auto up_it = upbranch
605 ? node_pressures_.find((*upbranch).uptree_node())
606 : node_pressures_.end();
607 if (up_it != node_pressures_.end()) {
609 const Scalar up_press = up_it->second;
610 auto rates = node_inflows.at(node);
611 branch_data_.try_emplace(node,
612 *terminal_pressure - up_press,
613 rates[IndexTraits::oilPhaseIdx],
614 rates[IndexTraits::waterPhaseIdx],
615 rates[IndexTraits::gasPhaseIdx]);
619 branch_data_.emplace(node, data::BranchData{0.0, 0.0, 0.0, 0.0});
624 const std::string& up_node = (*upbranch).uptree_node();
625 const Scalar up_press = node_pressures_[up_node];
627 if (invalid_nodes_.count(up_node) > 0) {
628 invalid_nodes_.insert(node);
630 const auto vfp_table = (*upbranch).vfp_table();
633 if (network_.node(node).as_choke()) {
635 node_pressures_[node] = well_model_.groupStateHelper().groupState().well_group_thp(node);
637 node_pressures_[node] = up_press;
639 auto rates = node_inflows.at(node);
640 branch_data_.try_emplace(node,
641 node_pressures_[node] - up_press,
642 rates[IndexTraits::oilPhaseIdx],
643 rates[IndexTraits::waterPhaseIdx],
644 rates[IndexTraits::gasPhaseIdx]);
648 OPM_TIMEBLOCK(NetworkVfpCalculations);
649 auto rates = node_inflows.at(node);
650 assert(rates.size() == 3);
651 Calc::prepareRates(rates);
652 const auto branch = Calc::compute(vfp_props_, *vfp_table, rates, up_press, *upbranch, unit_system_);
655 const Scalar node_pressure = branch.valid ? branch.pressure : up_press;
657 invalid_nodes_.insert(node);
659 if (!branch.valid || branch.clamped) {
660 OpmLog::debug(fmt::format(
"Network branch {} -> {}: VFP table {} {} at rates ({:.4g}, {:.4g}, {:.4g}) sm3/d, "
661 "upstream pressure {:.2f} bar",
662 up_node, node, *vfp_table,
663 branch.valid ?
"lookup clamped to the table axes" :
"has no solution",
664 rates[IndexTraits::waterPhaseIdx] * unit::day,
665 rates[IndexTraits::oilPhaseIdx] * unit::day,
666 rates[IndexTraits::gasPhaseIdx] * unit::day,
667 up_press / unit::barsa));
669 node_pressures_[node] = node_pressure;
671 branch_data_.try_emplace(node,
672 node_pressure - up_press,
673 -rates[IndexTraits::oilPhaseIdx],
674 -rates[IndexTraits::waterPhaseIdx],
675 -rates[IndexTraits::gasPhaseIdx]);
679 const GenericWellModel& well_model_;
680 const Network::ExtNetwork& network_;
681 const VfpProperties& vfp_props_;
682 const UnitSystem& unit_system_;
683 const int report_step_idx_;
685 const std::map<std::string, Scalar>* terminal_pressure_overrides_;
686 std::map<std::string, Scalar> node_gas_consumption_;
687 std::map<std::string, Scalar> node_pressures_;
688 std::map<std::string, data::BranchData> branch_data_;
689 std::set<std::string> invalid_nodes_;
Definition: GroupState.hpp:42
bool has_network_leaf_node_production_rates(const std::string &gname) const
const std::vector< Scalar > & network_leaf_node_injection_rates(const std::string &gname) const
bool has_network_leaf_node_injection_rates(const std::string &gname) const
const std::vector< Scalar > & network_leaf_node_production_rates(const std::string &gname) const
Class to compute network pressures using VFP tables, given flow rates for each group and fixed pressu...
Definition: BlackoilWellModelNetworkPressureComputation.hpp:329
NetworkPressureComputation(const GenericWellModel &well_model, const Network::ExtNetwork &network, const VfpProperties &vfp_props, const UnitSystem &unit_system, const int report_step_idx, const Communication &comm, const std::map< std::string, typename GenericWellModel::Scalar > *terminal_pressure_overrides=nullptr, std::map< std::string, typename GenericWellModel::Scalar > node_gas_consumption={})
Definition: BlackoilWellModelNetworkPressureComputation.hpp:334
const std::set< std::string > & invalidNodes() const
Definition: BlackoilWellModelNetworkPressureComputation.hpp:403
typename GenericWellModel::Scalar Scalar
Definition: BlackoilWellModelNetworkPressureComputation.hpp:355
std::pair< std::map< std::string, Scalar >, std::map< std::string, data::BranchData > > run()
Definition: BlackoilWellModelNetworkPressureComputation.hpp:357
GenericWellModel::IndexTraits IndexTraits
Definition: BlackoilWellModelNetworkPressureComputation.hpp:356
Definition: VFPInjProperties.hpp:34
const VFPInjTable & getTable(const int table_id) const
EvalWell bhp(const int table_id, const EvalWell &aqua, const EvalWell &liquid, const EvalWell &vapour, const Scalar thp) const
Definition: VFPProdProperties.hpp:38
EvalWell bhp(const int table_id, const EvalWell &aqua, const EvalWell &liquid, const EvalWell &vapour, const Scalar thp, const Scalar alq, const Scalar explicit_wfr, const Scalar explicit_gfr, const bool use_expvfp) const
const VFPProdTable & getTable(const int table_id) const
Dune::Communication< MPIComm > Communication
Definition: ParallelCommunication.hpp:30
bool isConnectedNetworkNode(const Network::ExtNetwork &network, const std::string &name)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:122
bool clampToTableAxes(const Table &table, std::vector< Scalar > &rates, Scalar &up_press)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:73
T getFlo(const VFPProdTable &table, const T &aqua, const T &liquid, const T &vapour)
Definition: blackoilbioeffectsmodules.hh:45
bool nodeFollowsOverriddenNode(const Network::ExtNetwork &network, std::string node, const std::map< std::string, Scalar > &overrides)
Whether the pressure of a node follows from one of the overridden nodes (see the terminal pressure ov...
Definition: BlackoilWellModelNetworkPressureComputation.hpp:304
std::map< std::string, Scalar > networkGasConsumptionRates(const Network::ExtNetwork &network, const ScheduleState &sched_state, const SummaryState &summary_state)
Net gas consumption (GCONSUMP consumption minus import, SI units) to remove from the flow at each nod...
Definition: BlackoilWellModelNetworkPressureComputation.hpp:145
std::string to_string(const ConvergenceReport::ReservoirFailure::Type t)
Result of a single network branch VFP lookup.
Definition: BlackoilWellModelNetworkPressureComputation.hpp:60
bool valid
Definition: BlackoilWellModelNetworkPressureComputation.hpp:64
Scalar pressure
Definition: BlackoilWellModelNetworkPressureComputation.hpp:61
bool clamped
Definition: BlackoilWellModelNetworkPressureComputation.hpp:66
static void prepareRates(std::vector< Scalar > &)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:259
static bool hasLeafNodeRate(const GroupState &group_state, const std::string &node)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:264
static NetworkBranchPressure< Scalar > compute(const VFPInjProperties< Scalar > &vfp_props, const int table_id, std::vector< Scalar > rates, Scalar up_press, const Branch &, const UnitSystem &)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:279
static const std::vector< Scalar > leafNodeRate(const GroupState &group_state, const std::string &node)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:272
static const std::vector< Scalar > leafNodeRate(const GroupState &group_state, const std::string &node)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:214
static void prepareRates(std::vector< Scalar > &rates)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:199
static NetworkBranchPressure< Scalar > compute(const VFPProdProperties< Scalar > &vfp_props, const int table_id, std::vector< Scalar > rates, Scalar up_press, const Branch &upbranch, const UnitSystem &unit_system)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:221
static bool hasLeafNodeRate(const GroupState &group_state, const std::string &node)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:206
Helper class to insulate the NetworkPressureComputation class from the differences between production...
Definition: BlackoilWellModelNetworkPressureComputation.hpp:193