|
29 | 29 |
|
30 | 30 | #include <opm/input/eclipse/Schedule/Schedule.hpp> |
31 | 31 | #include <opm/input/eclipse/Schedule/Network/Balance.hpp> |
32 | | -#include <opm/input/eclipse/Schedule/Network/ExtNetwork.hpp> |
33 | 32 |
|
34 | 33 | #include <opm/simulators/wells/BlackoilWellModelGeneric.hpp> |
35 | 34 | #include <opm/simulators/wells/GroupStateHelper.hpp> |
@@ -144,10 +143,10 @@ updatePressures(const int reportStepIdx, |
144 | 143 |
|
145 | 144 | const auto previous_node_pressures = node_pressures_; |
146 | 145 |
|
147 | | - node_pressures_ = well_model_.groupStateHelper(). |
148 | | - computeNetworkPressures(network, |
149 | | - *well_model_.getVFPProperties().getProd(), |
150 | | - well_model_.comm()); |
| 146 | + node_pressures_ = this->computePressures(network, |
| 147 | + *well_model_.getVFPProperties().getProd(), |
| 148 | + reportStepIdx, |
| 149 | + well_model_.comm()); |
151 | 150 |
|
152 | 151 | // here, the network imbalance is the difference between the previous nodal pressure and the new nodal pressure |
153 | 152 | Scalar network_imbalance = 0.; |
@@ -233,11 +232,10 @@ assignNodeValues(std::map<std::string, data::NodeData>& nodevalues, |
233 | 232 | return; |
234 | 233 | } |
235 | 234 |
|
236 | | - auto converged_pressures = |
237 | | - well_model_.groupStateHelper(). |
238 | | - computeNetworkPressures(network, |
239 | | - *well_model_.getVFPProperties().getProd(), |
240 | | - well_model_.comm()); |
| 235 | + auto converged_pressures = this->computePressures(network, |
| 236 | + *well_model_.getVFPProperties().getProd(), |
| 237 | + reportStepIdx, |
| 238 | + well_model_.comm()); |
241 | 239 | for (const auto& [node, converged_pressure] : converged_pressures) { |
242 | 240 | auto it = nodevalues.find(node); |
243 | 241 | assert(it != nodevalues.end() ); |
@@ -285,6 +283,172 @@ initializeWell(WellInterfaceGeneric<Scalar,IndexTraits>& well) |
285 | 283 | } |
286 | 284 | } |
287 | 285 |
|
| 286 | +template <typename Scalar, typename IndexTraits> |
| 287 | +std::map<std::string, Scalar> |
| 288 | +BlackoilWellModelNetworkGeneric<Scalar, IndexTraits>:: |
| 289 | +computePressures(const Network::ExtNetwork& network, |
| 290 | + const VFPProdProperties<Scalar>& vfp_prod_props, |
| 291 | + const int reportStepIdx, |
| 292 | + const Parallel::Communication& comm) const |
| 293 | +{ |
| 294 | + // TODO: Only dealing with production networks for now. |
| 295 | + OPM_TIMEFUNCTION(); |
| 296 | + if (!network.active()) { |
| 297 | + return {}; |
| 298 | + } |
| 299 | + |
| 300 | + const auto& group_state = well_model_.groupStateHelper().groupState(); |
| 301 | + const auto& well_state = well_model_.groupStateHelper().wellState(); |
| 302 | + |
| 303 | + std::map<std::string, Scalar> node_pressures; |
| 304 | + auto roots = network.roots(); |
| 305 | + for (const auto& root : roots) { |
| 306 | + // Fixed pressure nodes of the network are the roots of trees. |
| 307 | + // Leaf nodes must correspond to groups in the group structure. |
| 308 | + // Let us first find all leaf nodes of the network. We also |
| 309 | + // create a vector of all nodes, ordered so that a child is |
| 310 | + // always after its parent. |
| 311 | + std::stack<std::string> children; |
| 312 | + std::set<std::string> leaf_nodes; |
| 313 | + std::vector<std::string> root_to_child_nodes; |
| 314 | + // children.push(network.root().name()); |
| 315 | + children.push(root.get().name()); |
| 316 | + while (!children.empty()) { |
| 317 | + const auto node = children.top(); |
| 318 | + children.pop(); |
| 319 | + root_to_child_nodes.push_back(node); |
| 320 | + auto branches = network.downtree_branches(node); |
| 321 | + if (branches.empty()) { |
| 322 | + leaf_nodes.insert(node); |
| 323 | + } |
| 324 | + for (const auto& branch : branches) { |
| 325 | + children.push(branch.downtree_node()); |
| 326 | + } |
| 327 | + } |
| 328 | + assert(children.empty()); |
| 329 | + |
| 330 | + // Starting with the leaf nodes of the network, get the flow rates |
| 331 | + // from the corresponding groups. |
| 332 | + std::map<std::string, std::vector<Scalar>> node_inflows; |
| 333 | + const std::vector<Scalar> zero_rates(3, 0.0); |
| 334 | + for (const auto& node : leaf_nodes) { |
| 335 | + // Guard against empty leaf nodes (may not be present in GRUPTREE) |
| 336 | + if (!well_model_.groupStateHelper().groupState().has_production_rates(node)) { |
| 337 | + node_inflows[node] = zero_rates; |
| 338 | + continue; |
| 339 | + } |
| 340 | + |
| 341 | + node_inflows[node] = group_state.network_leaf_node_production_rates(node); |
| 342 | + // Add the ALQ amounts to the gas rates if requested. |
| 343 | + if (network.node(node).add_gas_lift_gas()) { |
| 344 | + const auto& group = well_model_.schedule().getGroup(node, reportStepIdx); |
| 345 | + Scalar alq = 0.0; |
| 346 | + for (const std::string& wellname : group.wells()) { |
| 347 | + const Well& well = well_model_.schedule().getWell(wellname, reportStepIdx); |
| 348 | + if (well.isInjector() || !well_state.isOpen(wellname)) { |
| 349 | + continue; |
| 350 | + } |
| 351 | + const Scalar efficiency = well.getEfficiencyFactor(/*network*/ true) |
| 352 | + * well_state.getGlobalEfficiencyScalingFactor(wellname); |
| 353 | + if (const auto& well_index = well_state.index(wellname); |
| 354 | + well_index.has_value() && well_state.wellIsOwned(well_index.value(), wellname)) |
| 355 | + { |
| 356 | + alq += well_state.well(wellname).alq_state.get() * efficiency; |
| 357 | + } |
| 358 | + } |
| 359 | + alq = comm.sum(alq); |
| 360 | + node_inflows[node][IndexTraits::gasPhaseIdx] += alq; |
| 361 | + } |
| 362 | + } |
| 363 | + |
| 364 | + // Accumulate in the network, towards the roots. Note that a |
| 365 | + // root (i.e. fixed pressure node) can still be contributing |
| 366 | + // flow towards other nodes in the network, i.e. a node is |
| 367 | + // the root of a subtree. |
| 368 | + auto child_to_root_nodes = root_to_child_nodes; |
| 369 | + std::reverse(child_to_root_nodes.begin(), child_to_root_nodes.end()); |
| 370 | + for (const auto& node : child_to_root_nodes) { |
| 371 | + const auto upbranch = network.uptree_branch(node); |
| 372 | + if (upbranch) { |
| 373 | + // Add downbranch rates to upbranch. |
| 374 | + std::vector<Scalar>& up = node_inflows[(*upbranch).uptree_node()]; |
| 375 | + const std::vector<Scalar>& down = node_inflows[node]; |
| 376 | + // We now also support NEFAC |
| 377 | + const Scalar efficiency = network.node(node).efficiency(); |
| 378 | + if (up.empty()) { |
| 379 | + up = std::vector<Scalar>(down.size(), 0.0); |
| 380 | + } |
| 381 | + assert(up.size() == down.size()); |
| 382 | + for (std::size_t ii = 0; ii < up.size(); ++ii) { |
| 383 | + up[ii] += efficiency * down[ii]; |
| 384 | + } |
| 385 | + } |
| 386 | + } |
| 387 | + |
| 388 | + // Going the other way (from roots to leafs), calculate the pressure |
| 389 | + // at each node using VFP tables and rates. |
| 390 | + // std::map<std::string, double> node_pressures; |
| 391 | + for (const auto& node : root_to_child_nodes) { |
| 392 | + auto press = network.node(node).terminal_pressure(); |
| 393 | + if (press) { |
| 394 | + node_pressures[node] = *press; |
| 395 | + } |
| 396 | + else { |
| 397 | + const auto upbranch = network.uptree_branch(node); |
| 398 | + assert(upbranch); |
| 399 | + const Scalar up_press = node_pressures[(*upbranch).uptree_node()]; |
| 400 | + const auto vfp_table = (*upbranch).vfp_table(); |
| 401 | + if (vfp_table) { |
| 402 | + OPM_TIMEBLOCK(NetworkVfpCalculations); |
| 403 | + // The rates are here positive, but the VFP code expects the |
| 404 | + // convention that production rates are negative, so we must |
| 405 | + // take a copy and flip signs. |
| 406 | + auto rates = node_inflows[node]; |
| 407 | + std::transform( |
| 408 | + rates.begin(), rates.end(), rates.begin(), [](const auto r) { return -r; }); |
| 409 | + assert(rates.size() == 3); |
| 410 | + // NB! ALQ in extended network is never implicitly the gas lift rate (GRAT), i.e., the |
| 411 | + // gas lift rates only enters the network pressure calculations through the rates |
| 412 | + // (e.g., in GOR calculations) unless a branch ALQ is set in BRANPROP. |
| 413 | + // |
| 414 | + // @TODO: Standard network |
| 415 | + Scalar alq = (*upbranch).alq_value().value_or(0.0); |
| 416 | + node_pressures[node] |
| 417 | + = vfp_prod_props.bhp(*vfp_table, |
| 418 | + rates[IndexTraits::waterPhaseIdx], |
| 419 | + rates[IndexTraits::oilPhaseIdx], |
| 420 | + rates[IndexTraits::gasPhaseIdx], |
| 421 | + up_press, |
| 422 | + alq, |
| 423 | + 0.0, // explicit_wfr |
| 424 | + 0.0, // explicit_gfr |
| 425 | + false); // use_expvfp we dont support explicit lookup |
| 426 | +#define EXTRA_DEBUG_NETWORK 0 |
| 427 | +#if EXTRA_DEBUG_NETWORK |
| 428 | + std::ostringstream oss; |
| 429 | + oss << "parent: " << (*upbranch).uptree_node() << " child: " << node << " rates = [ " |
| 430 | + << rates[0] * 86400 << ", " << rates[1] * 86400 << ", " << rates[2] * 86400 << " ]" |
| 431 | + << " p(parent) = " << up_press / 1e5 << " p(child) = " << node_pressures[node] / 1e5 |
| 432 | + << std::endl; |
| 433 | + OpmLog::debug(oss.str()); |
| 434 | +#endif |
| 435 | + } |
| 436 | + else { |
| 437 | + // Table number specified as 9999 in the deck, no pressure loss. |
| 438 | + if (network.node(node).as_choke()) { |
| 439 | + // Node pressure is set to the group THP. |
| 440 | + node_pressures[node] = well_model_.groupStateHelper().groupState().well_group_thp(node); |
| 441 | + } |
| 442 | + else { |
| 443 | + node_pressures[node] = up_press; |
| 444 | + } |
| 445 | + } |
| 446 | + } |
| 447 | + } |
| 448 | + } |
| 449 | + return node_pressures; |
| 450 | +} |
| 451 | + |
288 | 452 | template<typename Scalar, typename IndexTraits> |
289 | 453 | bool BlackoilWellModelNetworkGeneric<Scalar, IndexTraits>:: |
290 | 454 | operator==(const BlackoilWellModelNetworkGeneric<Scalar,IndexTraits>& rhs) const |
|
0 commit comments