BlackoilWellModelNetworkPressureComputation.hpp
Go to the documentation of this file.
1/*
2 Copyright 2020-2026 Equinor ASA
3
4 This file is part of the Open Porous Media project (OPM).
5
6 OPM is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 OPM is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with OPM. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#ifndef OPM_BLACKOIL_WELL_MODEL_NETWORK_PRESSURE_COMPUTATION_HPP
21#define OPM_BLACKOIL_WELL_MODEL_NETWORK_PRESSURE_COMPUTATION_HPP
22
23#include <opm/common/TimingMacros.hpp>
24
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>
33
34#include <opm/output/data/Groups.hpp>
35
40
41#include <fmt/format.h>
42
43#include <algorithm>
44#include <cassert>
45#include <cmath>
46#include <cstddef>
47#include <map>
48#include <ranges>
49#include <set>
50#include <stack>
51#include <string>
52#include <utility>
53#include <vector>
54
55namespace Opm {
56
58template<typename Scalar>
60{
61 Scalar pressure{0.0};
62 // False when the table has no solution at this point (zero-filled cells give
63 // bhp <= 1 atm); the pressure must then not be used as a node pressure.
64 bool valid{true};
65 // True when the flow rate or upstream pressure had to be clamped to the table axes.
66 bool clamped{false};
67};
68
69namespace detail {
72 template<typename Scalar, typename IndexTraits, typename Table>
73 bool clampToTableAxes(const Table& table, std::vector<Scalar>& rates, Scalar& up_press)
74 {
75 bool clamped = false;
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);
81 clamped = true;
82 }
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; });
92 clamped = true;
93 } else if (flo < flo_axis.front()) {
94 const Scalar flo_lo = flo_axis.front();
95 if (flo > 0.0) {
96 const Scalar s = flo_lo / flo;
97 std::ranges::transform(rates, rates.begin(), [s](const auto r) { return s * r; });
98 } else {
99 // A zero vector cannot be scaled. Choose the phase represented
100 // by the VFPINJ flow axis for this lookup only.
101 switch (table.getFloType()) {
102 case VFPInjTable::FLO_TYPE::FLO_OIL:
103 rates[IndexTraits::oilPhaseIdx] = flo_lo;
104 break;
105 case VFPInjTable::FLO_TYPE::FLO_WAT:
106 rates[IndexTraits::waterPhaseIdx] = flo_lo;
107 break;
108 case VFPInjTable::FLO_TYPE::FLO_GAS:
109 rates[IndexTraits::gasPhaseIdx] = flo_lo;
110 break;
111 }
112 }
113 clamped = true;
114 }
115 return clamped;
116 }
117} // namespace detail
118
119namespace detail {
122 inline bool isConnectedNetworkNode(const Network::ExtNetwork& network,
123 const std::string& name)
124 {
125 return network.has_node(name)
126 && (network.uptree_branch(name).has_value()
127 || !network.downtree_branches(name).empty());
128 }
129} // namespace detail
130
143template<typename Scalar>
144std::map<std::string, Scalar>
145networkGasConsumptionRates(const Network::ExtNetwork& network,
146 const ScheduleState& sched_state,
147 const SummaryState& summary_state)
148{
149 std::map<std::string, Scalar> node_rates;
150 if (!network.active()) {
151 return node_rates;
152 }
153
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) {
159 continue;
160 }
161
162 if (!gc.network_node.empty()) {
163 if (detail::isConnectedNetworkNode(network, gc.network_node)) {
164 node_rates[gc.network_node] += net_rate;
165 }
166 continue;
167 }
168
169 Scalar efficiency = 1.0;
170 std::string name = group_name;
171 while (sched_state.groups.has(name)) {
172 if (detail::isConnectedNetworkNode(network, name)) {
173 if (network.downtree_branches(name).empty()) {
174 node_rates[name] += efficiency * net_rate;
175 }
176 break;
177 }
178 if (name == "FIELD") {
179 break;
180 }
181 const auto& group = sched_state.groups(name);
182 efficiency *= group.getGroupEfficiencyFactor(/*network=*/true);
183 name = group.parent();
184 }
185 }
186
187 return node_rates;
188}
189
192template<typename Scalar, typename IndexTraits, typename VfpProperties>
194
195// Production specialization.
196template<typename Scalar, typename IndexTraits>
197struct NetworkVfpPressureCalculator<Scalar, IndexTraits, VFPProdProperties<Scalar>>
198{
199 static void prepareRates(std::vector<Scalar>& rates)
200 {
201 // Network rates are positive, while production VFP expects negative rates.
202 std::ranges::transform(rates, rates.begin(), [](const auto r) { return -r; });
203 }
204
205 template <class GroupState>
206 static bool hasLeafNodeRate(const GroupState& group_state,
207 const std::string& node)
208 {
209 return group_state.has_network_leaf_node_production_rates(node);
210 }
211
212 template <class GroupState>
213 static const std::vector<Scalar>
214 leafNodeRate(const GroupState& group_state,
215 const std::string& node)
216 {
217 return group_state.network_leaf_node_production_rates(node);
218 }
219
220 template<typename Branch>
222 const int table_id,
223 std::vector<Scalar> rates,
224 Scalar up_press,
225 const Branch& upbranch,
226 const UnitSystem& unit_system)
227 {
228 // NB! ALQ in extended network is never implicitly the gas lift rate (GRAT), i.e., the
229 // gas lift rates only enters the network pressure calculations through the rates
230 // (e.g., in GOR calculations) unless a branch ALQ is set in BRANPROP.
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);
235
237 // Preserve the established production-network behaviour. Production VFP
238 // tables have historically been extrapolated outside their axes, and
239 // existing production cases rely on that when the result remains valid.
240 result.clamped = false;
241 result.pressure = vfp_props.bhp(table_id,
242 rates[IndexTraits::waterPhaseIdx],
243 rates[IndexTraits::oilPhaseIdx],
244 rates[IndexTraits::gasPhaseIdx],
245 up_press,
246 alq,
247 0.0, // explicit_wfr
248 0.0, // explicit_gfr
249 false); // use_expvfp we dont support explicit lookup
250 result.valid = result.pressure > unit::atm;
251 return result;
252 }
253};
254
255// Injection specialization.
256template<typename Scalar, typename IndexTraits>
257struct NetworkVfpPressureCalculator<Scalar, IndexTraits, VFPInjProperties<Scalar>>
258{
259 static void prepareRates(std::vector<Scalar>&)
260 {
261 }
262
263 template <class GroupState>
264 static bool hasLeafNodeRate(const GroupState& group_state,
265 const std::string& node)
266 {
267 return group_state.has_network_leaf_node_injection_rates(node);
268 }
269
270 template <class GroupState>
271 static const std::vector<Scalar>
272 leafNodeRate(const GroupState& group_state,
273 const std::string& node)
274 {
275 return group_state.network_leaf_node_injection_rates(node);
276 }
277
278 template<typename Branch>
280 const int table_id,
281 std::vector<Scalar> rates,
282 Scalar up_press,
283 const Branch&,
284 const UnitSystem&)
285 {
287 result.clamped = detail::clampToTableAxes<Scalar, IndexTraits>(vfp_props.getTable(table_id), rates, up_press);
288 result.pressure = vfp_props.bhp(table_id,
289 rates[IndexTraits::waterPhaseIdx],
290 rates[IndexTraits::oilPhaseIdx],
291 rates[IndexTraits::gasPhaseIdx],
292 up_press);
293 result.valid = result.pressure > unit::atm;
294 return result;
295 }
296};
297
303template<typename Scalar>
304bool nodeFollowsOverriddenNode(const Network::ExtNetwork& network,
305 std::string node,
306 const std::map<std::string, Scalar>& overrides)
307{
308 // The branches have no loops.
309 while (true) {
310 if (overrides.count(node) > 0) {
311 return true;
312 }
313 if (network.node(node).terminal_pressure().has_value()) {
314 return false;
315 }
316 const auto branch = network.uptree_branch(node);
317 if (!branch.has_value()) {
318 return false;
319 }
320 node = branch->uptree_node();
321 }
322}
323
327template<typename GenericWellModel, typename VfpProperties, typename Communication = Parallel::Communication>
329{
330public:
334 NetworkPressureComputation(const GenericWellModel& well_model,
335 const Network::ExtNetwork& network,
336 const VfpProperties& vfp_props,
337 const UnitSystem& unit_system,
338 const int report_step_idx,
339 const Communication& comm,
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)
345 , network_(network)
346 , vfp_props_(vfp_props)
347 , unit_system_(unit_system)
348 , report_step_idx_(report_step_idx)
349 , comm_(comm)
350 , terminal_pressure_overrides_(terminal_pressure_overrides)
351 , node_gas_consumption_(std::move(node_gas_consumption))
352 {
353 }
354
355 using Scalar = typename GenericWellModel::Scalar;
356 using IndexTraits = GenericWellModel::IndexTraits;
357 std::pair<std::map<std::string, Scalar>, std::map<std::string, data::BranchData>> run()
358 {
359 for (const auto& root : topRoots()) {
360 // Fixed pressure nodes of the network are the roots of trees.
361 // Leaf nodes must correspond to groups in the group structure.
362 // Let us first find all leaf nodes of the network. We also
363 // create a vector of all nodes, ordered so that a child is
364 // always after its parent.
365 const auto [root_to_child_nodes, leaf_nodes] = collectTreeNodes(root);
366
367 // Starting with the leaf nodes of the network, get the flow rates
368 // from the corresponding groups.
369 auto node_inflows = initializeLeafInflows(leaf_nodes);
370
371 // Accumulate flow rates in the network, towards the roots.
372 // Note that a root (i.e. fixed pressure node) can still be
373 // contributing flow towards other nodes in the network, i.e.
374 // a node can be the root of a subtree.
375 accumulateInflows(root_to_child_nodes, node_inflows);
376
377 // Going the other way (from roots to leafs), calculate the pressure
378 // at each node using VFP tables and rates.
379 computeNodePressures(root_to_child_nodes, node_inflows);
380
381#ifdef OPM_NETWORK_PRESSURE_TRACE
382 // Off unless the macro is defined: this builds a string per node per
383 // sub-iteration per domain, whether or not the log keeps it.
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");
387 }
388 OpmLog::debug("Node inflows:");
389 for (const auto& [node, inflows] : node_inflows) {
390 OpmLog::debug("Network node " + node + " inflows: "
391 + std::to_string(inflows[0]*86400) + ", " + std::to_string(inflows[1]*86400) + ", " + std::to_string(inflows[2]*86400));
392 }
393#endif
394
395 }
396
397 return {node_pressures_, branch_data_};
398 }
399
403 const std::set<std::string>& invalidNodes() const
404 {
405 return invalid_nodes_;
406 }
407
408private:
416 std::vector<std::string> topRoots() const
417 {
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};
422 auto node = top;
423 while (const auto upbranch = network_.uptree_branch(node)) {
424 node = upbranch->uptree_node();
425 if (!visited.insert(node).second) {
426 break; // Loop of branches, rejected at parse time.
427 }
428 if (network_.node(node).terminal_pressure().has_value()) {
429 top = node;
430 }
431 }
432 if (std::ranges::find(top_roots, top) == top_roots.end()) {
433 top_roots.push_back(top);
434 }
435 }
436 return top_roots;
437 }
438
439 std::pair<std::vector<std::string>, std::set<std::string>>
440 collectTreeNodes(const std::string& root) const
441 {
442 std::stack<std::string> children;
443 std::set<std::string> leaf_nodes;
444 std::vector<std::string> root_to_child_nodes;
445 children.push(root);
446 while (!children.empty()) {
447 const auto node = children.top();
448 children.pop();
449 root_to_child_nodes.push_back(node);
450 auto branches = network_.downtree_branches(node);
451 if (branches.empty()) {
452 leaf_nodes.insert(node);
453 }
454 for (const auto& branch : branches) {
455 children.push(branch.downtree_node());
456 }
457 }
458
459 assert(children.empty());
460 return {root_to_child_nodes, leaf_nodes};
461 }
462
463 std::map<std::string, std::vector<Scalar>>
464 initializeLeafInflows(const std::set<std::string>& leaf_nodes) const
465 {
466 std::map<std::string, std::vector<Scalar>> node_inflows;
467 const std::vector<Scalar> zero_rates(3, 0.0);
468
469 for (const auto& node : leaf_nodes) {
470 // Guard against empty leaf nodes (may not be present in GRUPTREE).
471 // Use the domain-correct check so injection networks query the injection
472 // rate map rather than the production rate map (which is always empty for
473 // pure injection groups, causing zero-rate pressure calculations).
474 using Calc = NetworkVfpPressureCalculator<Scalar, IndexTraits, VfpProperties>;
475 if (!Calc::hasLeafNodeRate(well_model_.groupStateHelper().groupState(), node)) {
476 node_inflows[node] = zero_rates;
477 continue;
478 }
479
480 node_inflows[node] = Calc::leafNodeRate(well_model_.groupStateHelper().groupState(),
481 node);
482 if (network_.node(node).add_gas_lift_gas()) {
483 addGasLiftGas(node, node_inflows[node]);
484 }
485 }
486
487 return node_inflows;
488 }
489
490 void addGasLiftGas(const std::string& node,
491 std::vector<Scalar>& rates) const
492 {
493 const auto& group = well_model_.schedule().getGroup(node, report_step_idx_);
494 const auto& well_state = well_model_.groupStateHelper().wellState();
495 Scalar alq = 0.0;
496 // Add gas lift from all wells on this process
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)) {
500 continue;
501 }
502
503 const Scalar efficiency = well.getEfficiencyFactor(/*network*/ 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))
508 {
509 alq += well_state.well(wellname).alq_state.get() * efficiency;
510 }
511 }
512 // Sum ALQ across all processes to get total ALQ for the node.
513 // Note that communication is required here since each
514 // process has different wells, and the loop above therefore
515 // only considers local wells.
516 // However, all processes have all groups and their rates available,
517 // so we do not need to communicate those.
518 alq = comm_.sum(alq);
519 // Only add satellite production once for parallel runs
520 // (i.e. add after communication)
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());
526
527 alq += static_cast<Scalar>(gsrate);
528 }
529
530 rates[IndexTraits::gasPhaseIdx] += alq;
531 }
532
536 void applyGasConsumption(const std::string& node,
537 std::vector<Scalar>& rates) const
538 {
539 const auto it = node_gas_consumption_.find(node);
540 if (it == node_gas_consumption_.end()) {
541 return;
542 }
543 if (rates.empty()) {
544 rates.assign(3, 0.0);
545 }
546 if (rates.size() <= static_cast<std::size_t>(IndexTraits::gasPhaseIdx)) {
547 return;
548 }
549 auto& gas_rate = rates[IndexTraits::gasPhaseIdx];
550 gas_rate = std::max(gas_rate - it->second, Scalar{0});
551 }
552
553 void accumulateInflows(const std::vector<std::string>& root_to_child_nodes,
554 std::map<std::string, std::vector<Scalar>>& node_inflows) const
555 {
556 const auto child_to_root_nodes = std::ranges::reverse_view(root_to_child_nodes);
557
558 // A node comes after all of its descendants in this order, so its
559 // inflow is complete when it is visited.
560 for (const auto& node : child_to_root_nodes) {
561 applyGasConsumption(node, node_inflows[node]);
562 const auto upbranch = network_.uptree_branch(node);
563 if (!upbranch) {
564 continue;
565 }
566 std::vector<Scalar>& up = node_inflows[(*upbranch).uptree_node()];
567 const std::vector<Scalar>& down = node_inflows[node];
568 // NEFAC support
569 const Scalar efficiency = network_.node(node).efficiency();
570 if (up.empty()) {
571 up = std::vector<Scalar>(down.size(), 0.0);
572 }
573 assert(up.size() == down.size());
574 for (std::size_t ii = 0; ii < up.size(); ++ii) {
575 up[ii] += efficiency * down[ii];
576 }
577 }
578 }
579
580 void computeNodePressures(const std::vector<std::string>& root_to_child_nodes,
581 const std::map<std::string, std::vector<Scalar>>& node_inflows)
582 {
583 for (const auto& node : root_to_child_nodes) {
584 // Do not traverse subtree more than once
585 if (node_pressures_.find(node) != node_pressures_.end()) {
586 continue;
587 }
588
589 auto terminal_pressure = network_.node(node).terminal_pressure();
590 if (terminal_pressure && terminal_pressure_overrides_) {
591 // The pressure of a fixed-pressure node can be imposed from outside, by a
592 // reservoir coupling master; the nodes below it follow from it.
593 if (const auto it = terminal_pressure_overrides_->find(node);
594 it != terminal_pressure_overrides_->end()) {
595 terminal_pressure = it->second;
596 }
597 }
598 const auto upbranch = network_.uptree_branch(node);
599 assert(upbranch || terminal_pressure); // If not root, must have uptree branch, and if root, must have terminal pressure.
600 using Calc = NetworkVfpPressureCalculator<Scalar, IndexTraits, VfpProperties>;
601
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()) {
608 // If terminal pressure is specified on a non-root node, we still want to calculate the branch data for the uptree branch.
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]);
616 } else {
617 // Root node with terminal pressure and either no uptree branch or an uptree
618 // node without a pressure of its own, inserting a zero-valued placeholder.
619 branch_data_.emplace(node, data::BranchData{0.0, 0.0, 0.0, 0.0});
620 }
621 continue;
622 }
623
624 const std::string& up_node = (*upbranch).uptree_node();
625 const Scalar up_press = node_pressures_[up_node];
626 // Descendants of a node without a valid pressure have none either.
627 if (invalid_nodes_.count(up_node) > 0) {
628 invalid_nodes_.insert(node);
629 }
630 const auto vfp_table = (*upbranch).vfp_table();
631 if (!vfp_table) {
632 // Table number specified as 9999 in the deck, no pressure loss.
633 if (network_.node(node).as_choke()) {
634 // Node pressure is set to the group THP.
635 node_pressures_[node] = well_model_.groupStateHelper().groupState().well_group_thp(node);
636 } else {
637 node_pressures_[node] = up_press;
638 }
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]);
645 continue;
646 }
647
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_);
653 // An invalid lookup (zero-filled table cells) gets the upstream pressure as a
654 // placeholder so downstream lookups stay in range; callers must consult invalidNodes().
655 const Scalar node_pressure = branch.valid ? branch.pressure : up_press;
656 if (!branch.valid) {
657 invalid_nodes_.insert(node);
658 }
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));
668 }
669 node_pressures_[node] = node_pressure;
670 // Prefer inserting after computing the pressure, hence negating rates
671 branch_data_.try_emplace(node,
672 node_pressure - up_press,
673 -rates[IndexTraits::oilPhaseIdx],
674 -rates[IndexTraits::waterPhaseIdx],
675 -rates[IndexTraits::gasPhaseIdx]);
676 }
677 }
678
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_;
684 const Communication& comm_;
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_;
690};
691
692} // namespace Opm
693
694#endif // OPM_BLACKOIL_WELL_MODEL_NETWORK_PRESSURE_COMPUTATION_HPP
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