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/Network/ExtNetwork.hpp>
26#include <opm/input/eclipse/Schedule/Schedule.hpp>
27#include <opm/input/eclipse/Schedule/VFPProdTable.hpp>
28
32
33#include <algorithm>
34#include <cassert>
35#include <map>
36#include <ranges>
37#include <set>
38#include <stack>
39#include <string>
40#include <vector>
41
42namespace Opm {
43
46template<typename Scalar, typename IndexTraits, typename VfpProperties>
48
49// Production specialization.
50template<typename Scalar, typename IndexTraits>
51struct NetworkVfpPressureCalculator<Scalar, IndexTraits, VFPProdProperties<Scalar>>
52{
53 static void prepareRates(std::vector<Scalar>& rates)
54 {
55 // Network rates are positive, while production VFP expects negative rates.
56 std::ranges::transform(rates, rates.begin(), [](const auto r) { return -r; });
57 }
58
59 template <class GroupState>
60 static const std::vector<Scalar>
61 leafNodeRate(const GroupState& group_state, const std::string& node)
62 {
63 return group_state.network_leaf_node_production_rates(node);
64 }
65
66 template<typename Branch>
67 static Scalar compute(const VFPProdProperties<Scalar>& vfp_props,
68 const int table_id,
69 const std::vector<Scalar>& rates,
70 const Scalar up_press,
71 const Branch& upbranch,
72 const UnitSystem& unit_system)
73 {
74 // NB! ALQ in extended network is never implicitly the gas lift rate (GRAT), i.e., the
75 // gas lift rates only enters the network pressure calculations through the rates
76 // (e.g., in GOR calculations) unless a branch ALQ is set in BRANPROP.
77 const auto alq_type = vfp_props.getTable(table_id).getALQType();
78 const auto dimension = VFPProdTable::ALQDimension(alq_type, unit_system);
79 const Scalar alq = upbranch.alq_value(dimension).value_or(0.0);
80
81 return vfp_props.bhp(table_id,
82 rates[IndexTraits::waterPhaseIdx],
83 rates[IndexTraits::oilPhaseIdx],
84 rates[IndexTraits::gasPhaseIdx],
85 up_press,
86 alq,
87 0.0, // explicit_wfr
88 0.0, // explicit_gfr
89 false); // use_expvfp we dont support explicit lookup
90 }
91};
92
93// Injection specialization.
94template<typename Scalar, typename IndexTraits>
95struct NetworkVfpPressureCalculator<Scalar, IndexTraits, VFPInjProperties<Scalar>>
96{
97 static void prepareRates(std::vector<Scalar>&)
98 {
99 }
100
101 template <class GroupState>
102 static const std::vector<Scalar>
103 leafNodeRate(const GroupState& group_state, const std::string& node)
104 {
105 return group_state.network_leaf_node_injection_rates(node);
106 }
107
108 template<typename Branch>
109 static Scalar compute(const VFPInjProperties<Scalar>& vfp_props,
110 const int table_id,
111 const std::vector<Scalar>& rates,
112 const Scalar up_press,
113 const Branch&,
114 const UnitSystem&)
115 {
116 return vfp_props.bhp(table_id,
117 rates[IndexTraits::waterPhaseIdx],
118 rates[IndexTraits::oilPhaseIdx],
119 rates[IndexTraits::gasPhaseIdx],
120 up_press);
121 }
122};
123
127template<typename GenericWellModel, typename VfpProperties, typename Communication = Parallel::Communication>
129{
130public:
131 NetworkPressureComputation(const GenericWellModel& well_model,
132 const Network::ExtNetwork& network,
133 const VfpProperties& vfp_props,
134 const UnitSystem& unit_system,
135 const int report_step_idx,
136 const Communication& comm)
137 : well_model_(well_model)
138 , network_(network)
139 , vfp_props_(vfp_props)
140 , unit_system_(unit_system)
141 , report_step_idx_(report_step_idx)
142 , comm_(comm)
143 {
144 }
145
146 using Scalar = typename GenericWellModel::Scalar;
147 using IndexTraits = GenericWellModel::IndexTraits;
148
149 std::map<std::string, Scalar> run()
150 {
151 const auto roots = network_.roots();
152 for (const auto& root : roots) {
153 // Fixed pressure nodes of the network are the roots of trees.
154 // Leaf nodes must correspond to groups in the group structure.
155 // Let us first find all leaf nodes of the network. We also
156 // create a vector of all nodes, ordered so that a child is
157 // always after its parent.
158 const auto [root_to_child_nodes, leaf_nodes] = collectTreeNodes(root.get().name());
159
160 // Starting with the leaf nodes of the network, get the flow rates
161 // from the corresponding groups.
162 auto node_inflows = initializeLeafInflows(leaf_nodes);
163
164 // Accumulate flow rates in the network, towards the roots.
165 // Note that a root (i.e. fixed pressure node) can still be
166 // contributing flow towards other nodes in the network, i.e.
167 // a node can be the root of a subtree.
168 accumulateInflows(root_to_child_nodes, node_inflows);
169
170 // Going the other way (from roots to leafs), calculate the pressure
171 // at each node using VFP tables and rates.
172 computeNodePressures(root_to_child_nodes, node_inflows);
173 }
174
175 return node_pressures_;
176 }
177
178private:
179 std::pair<std::vector<std::string>, std::set<std::string>>
180 collectTreeNodes(const std::string& root) const
181 {
182 std::stack<std::string> children;
183 std::set<std::string> leaf_nodes;
184 std::vector<std::string> root_to_child_nodes;
185 children.push(root);
186 while (!children.empty()) {
187 const auto node = children.top();
188 children.pop();
189 root_to_child_nodes.push_back(node);
190 auto branches = network_.downtree_branches(node);
191 if (branches.empty()) {
192 leaf_nodes.insert(node);
193 }
194 for (const auto& branch : branches) {
195 children.push(branch.downtree_node());
196 }
197 }
198
199 assert(children.empty());
200 return {root_to_child_nodes, leaf_nodes};
201 }
202
203 std::map<std::string, std::vector<Scalar>>
204 initializeLeafInflows(const std::set<std::string>& leaf_nodes) const
205 {
206 std::map<std::string, std::vector<Scalar>> node_inflows;
207 const std::vector<Scalar> zero_rates(3, 0.0);
208
209 for (const auto& node : leaf_nodes) {
210 // Guard against empty leaf nodes (may not be present in GRUPTREE)
211 if (!well_model_.groupStateHelper().groupState().has_production_rates(node)) {
212 node_inflows[node] = zero_rates;
213 continue;
214 }
215
216 using Calc = NetworkVfpPressureCalculator<Scalar, IndexTraits, VfpProperties>;
217 node_inflows[node] = Calc::leafNodeRate(well_model_.groupStateHelper().groupState(), node);
218 if (network_.node(node).add_gas_lift_gas()) {
219 addGasLiftGas(node, node_inflows[node]);
220 }
221 }
222
223 return node_inflows;
224 }
225
226 void addGasLiftGas(const std::string& node,
227 std::vector<Scalar>& rates) const
228 {
229 const auto& group = well_model_.schedule().getGroup(node, report_step_idx_);
230 const auto& well_state = well_model_.groupStateHelper().wellState();
231 Scalar alq = 0.0;
232 // Add gas lift from all wells on this process
233 for (const std::string& wellname : group.wells()) {
234 const Well& well = well_model_.schedule().getWell(wellname, report_step_idx_);
235 if (well.isInjector() || !well_state.isOpen(wellname)) {
236 continue;
237 }
238
239 const Scalar efficiency = well.getEfficiencyFactor(/*network*/ true)
240 * well_state.getGlobalEfficiencyScalingFactor(wellname);
241 const auto& well_index = well_state.index(wellname);
242 if (well_index.has_value() &&
243 well_state.wellIsOwned(well_index.value(), wellname))
244 {
245 alq += well_state.well(wellname).alq_state.get() * efficiency;
246 }
247 }
248 // Sum ALQ across all processes to get total ALQ for the node.
249 // Note that communication is required here since each
250 // process has different wells, and the loop above therefore
251 // only considers local wells.
252 // However, all processes have all groups and their rates available,
253 // so we do not need to communicate those.
254 alq = comm_.sum(alq);
255 // Only add satellite production once for parallel runs
256 // (i.e. add after communication)
257 if (group.hasSatelliteProduction()) {
258 const auto& gsat_prod = well_model_.schedule()[report_step_idx_].gsatprod().get(node, well_model_.summaryState());
259 alq += gsat_prod.rate[GSatProd::GSatProdGroupProp::Rate::GLift];
260 }
261
262 rates[IndexTraits::gasPhaseIdx] += alq;
263 }
264
265 void accumulateInflows(const std::vector<std::string>& root_to_child_nodes,
266 std::map<std::string, std::vector<Scalar>>& node_inflows) const
267 {
268 const auto child_to_root_nodes = std::ranges::reverse_view(root_to_child_nodes);
269
270 for (const auto& node : child_to_root_nodes) {
271 const auto upbranch = network_.uptree_branch(node);
272 if (!upbranch) {
273 continue;
274 }
275 std::vector<Scalar>& up = node_inflows[(*upbranch).uptree_node()];
276 const std::vector<Scalar>& down = node_inflows[node];
277 // NEFAC support
278 const Scalar efficiency = network_.node(node).efficiency();
279 if (up.empty()) {
280 up = std::vector<Scalar>(down.size(), 0.0);
281 }
282 assert(up.size() == down.size());
283 for (std::size_t ii = 0; ii < up.size(); ++ii) {
284 up[ii] += efficiency * down[ii];
285 }
286 }
287 }
288
289 void computeNodePressures(const std::vector<std::string>& root_to_child_nodes,
290 const std::map<std::string, std::vector<Scalar>>& node_inflows)
291 {
292 for (const auto& node : root_to_child_nodes) {
293 const auto terminal_pressure = network_.node(node).terminal_pressure();
294 if (terminal_pressure) {
295 node_pressures_[node] = *terminal_pressure;
296 continue;
297 }
298
299 const auto upbranch = network_.uptree_branch(node);
300 assert(upbranch);
301 const Scalar up_press = node_pressures_[(*upbranch).uptree_node()];
302 const auto vfp_table = (*upbranch).vfp_table();
303 if (!vfp_table) {
304 // Table number specified as 9999 in the deck, no pressure loss.
305 if (network_.node(node).as_choke()) {
306 // Node pressure is set to the group THP.
307 node_pressures_[node] = well_model_.groupStateHelper().groupState().well_group_thp(node);
308 } else {
309 node_pressures_[node] = up_press;
310 }
311 continue;
312 }
313
314 OPM_TIMEBLOCK(NetworkVfpCalculations);
315 auto rates = node_inflows.at(node);
316 assert(rates.size() == 3);
317 using Calc = NetworkVfpPressureCalculator<Scalar, IndexTraits, VfpProperties>;
318 Calc::prepareRates(rates);
319 node_pressures_[node]
320 = Calc::compute(vfp_props_, *vfp_table, rates, up_press, *upbranch, unit_system_);
321 }
322 }
323
324 const GenericWellModel& well_model_;
325 const Network::ExtNetwork& network_;
326 const VfpProperties& vfp_props_;
327 const UnitSystem& unit_system_;
328 const int report_step_idx_;
329 const Communication& comm_;
330 std::map<std::string, Scalar> node_pressures_;
331};
332
333} // namespace Opm
334
335#endif // OPM_BLACKOIL_WELL_MODEL_NETWORK_PRESSURE_COMPUTATION_HPP
Definition: GroupState.hpp:41
const std::vector< Scalar > & 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:129
typename GenericWellModel::Scalar Scalar
Definition: BlackoilWellModelNetworkPressureComputation.hpp:146
GenericWellModel::IndexTraits IndexTraits
Definition: BlackoilWellModelNetworkPressureComputation.hpp:147
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)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:131
std::map< std::string, Scalar > run()
Definition: BlackoilWellModelNetworkPressureComputation.hpp:149
Definition: VFPInjProperties.hpp:34
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
Definition: blackoilbioeffectsmodules.hh:45
static Scalar compute(const VFPInjProperties< Scalar > &vfp_props, const int table_id, const std::vector< Scalar > &rates, const Scalar up_press, const Branch &, const UnitSystem &)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:109
static void prepareRates(std::vector< Scalar > &)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:97
static const std::vector< Scalar > leafNodeRate(const GroupState &group_state, const std::string &node)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:103
static const std::vector< Scalar > leafNodeRate(const GroupState &group_state, const std::string &node)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:61
static Scalar compute(const VFPProdProperties< Scalar > &vfp_props, const int table_id, const std::vector< Scalar > &rates, const Scalar up_press, const Branch &upbranch, const UnitSystem &unit_system)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:67
static void prepareRates(std::vector< Scalar > &rates)
Definition: BlackoilWellModelNetworkPressureComputation.hpp:53
Helper class to insulate the NetworkPressureComputation class from the differences between production...
Definition: BlackoilWellModelNetworkPressureComputation.hpp:47