opm-simulators
GroupStateHelper.hpp
1 /*
2  Copyright 2025 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 #ifndef OPM_GROUPSTATEHELPER_HEADER_INCLUDED
20 #define OPM_GROUPSTATEHELPER_HEADER_INCLUDED
21 
22 #include <opm/simulators/wells/rescoup/RescoupProxy.hpp>
23 
24 #include <opm/common/TimingMacros.hpp>
25 #include <opm/input/eclipse/Schedule/ResCoup/GrupSlav.hpp>
26 #include <opm/input/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
27 #include <opm/input/eclipse/Schedule/Group/GPMaint.hpp>
28 #include <opm/input/eclipse/Schedule/Group/GSatProd.hpp>
29 #include <opm/input/eclipse/Schedule/Group/GuideRate.hpp>
30 #include <opm/input/eclipse/Schedule/Schedule.hpp>
31 #include <opm/input/eclipse/Schedule/ScheduleState.hpp>
32 #include <opm/input/eclipse/Schedule/SummaryState.hpp>
33 #include <opm/material/fluidsystems/PhaseUsageInfo.hpp>
34 #include <opm/simulators/utils/DeferredLogger.hpp>
35 #include <opm/simulators/utils/gatherDeferredLogger.hpp>
36 #include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
37 #include <opm/simulators/wells/GroupState.hpp>
38 #include <opm/simulators/wells/VFPProdProperties.hpp>
39 #include <opm/simulators/wells/WellState.hpp>
40 
41 #include <opm/simulators/utils/ParallelCommunication.hpp>
42 
43 #include <algorithm>
44 #include <map>
45 #include <memory>
46 #include <optional>
47 #include <stdexcept>
48 #include <string>
49 #include <unordered_set>
50 #include <vector>
51 
52 namespace Opm
53 {
54 
55 template <typename Scalar, typename IndexTraits>
56 class GroupStateHelper
57 {
58 public:
59  // RAII guard for temporarily setting wellstate pointer
61  {
62  public:
63  WellStateGuard(GroupStateHelper& groupStateHelper, WellState<Scalar, IndexTraits>& well_state)
64  : groupStateHelper_ {groupStateHelper}
65  , previous_state_ptr_ {groupStateHelper_.well_state_}
66  {
67  // Set the new state directly
68  groupStateHelper_.well_state_ = &well_state;
69  }
70 
72  {
73  // Restore the previous state
74  groupStateHelper_.well_state_ = previous_state_ptr_;
75  }
76 
77  // Delete copy and move operations
78  WellStateGuard(const WellStateGuard&) = delete;
79  WellStateGuard& operator=(const WellStateGuard&) = delete;
80  WellStateGuard(WellStateGuard&&) = delete;
81  WellStateGuard& operator=(WellStateGuard&&) = delete;
82 
83  private:
84  GroupStateHelper& groupStateHelper_;
85  const WellState<Scalar, IndexTraits>* previous_state_ptr_;
86  };
87 
88  // RAII guard for temporarily setting groupstate pointer
90  {
91  public:
92  GroupStateGuard(GroupStateHelper& group_state_helper, GroupState<Scalar>& group_state)
93  : group_state_helper_ {group_state_helper}
94  , previous_state_ptr_ {group_state_helper.group_state_}
95  {
96  // Set the new state directly
97  group_state_helper_.group_state_ = &group_state;
98  }
99 
100  ~GroupStateGuard()
101  {
102  // Restore the previous state
103  group_state_helper_.group_state_ = previous_state_ptr_;
104  }
105 
106  // Delete copy and move operations
107  GroupStateGuard(const GroupStateGuard&) = delete;
108  GroupStateGuard& operator=(const GroupStateGuard&) = delete;
109  GroupStateGuard(GroupStateGuard&&) = delete;
110  GroupStateGuard& operator=(GroupStateGuard&&) = delete;
111 
112  private:
113  GroupStateHelper& group_state_helper_;
114  GroupState<Scalar>* previous_state_ptr_ {nullptr};
115  };
116 
130  {
131  public:
137  explicit ScopedLoggerGuard(const GroupStateHelper& helper, bool do_mpi_gather = true)
138  : helper_(&helper)
139  , previous_(helper.deferred_logger_)
140  , do_mpi_gather_(do_mpi_gather)
141  {
142  helper_->deferred_logger_ = &logger_;
143  }
144 
146  {
147  if (helper_) {
148  if (do_mpi_gather_) {
149  // 1. Gather messages across MPI ranks
150  DeferredLogger global = gatherDeferredLogger(logger_, helper_->comm());
151 
152  // 2. Log on rank 0 (if terminal_output enabled)
153  if (helper_->terminalOutput()) {
154  global.logMessages();
155  }
156  } else {
157  // Just log locally without MPI gather
158  if (helper_->terminalOutput()) {
159  logger_.logMessages();
160  }
161  }
162 
163  // 3. Restore previous logger
164  helper_->deferred_logger_ = previous_;
165  }
166  }
167 
168  // Delete copy operations and move assignment
169  ScopedLoggerGuard(const ScopedLoggerGuard&) = delete;
170  ScopedLoggerGuard& operator=(const ScopedLoggerGuard&) = delete;
171  ScopedLoggerGuard& operator=(ScopedLoggerGuard&&) = delete;
172 
173  // Move constructor required for pushLogger() return-by-value (must be
174  // available even if elided by RVO)
175  ScopedLoggerGuard(ScopedLoggerGuard&& other) noexcept
176  : helper_(other.helper_)
177  , logger_(std::move(other.logger_))
178  , previous_(other.previous_)
179  , do_mpi_gather_(other.do_mpi_gather_)
180  {
181  // Update the helper's pointer to our moved logger
182  if (helper_) {
183  helper_->deferred_logger_ = &logger_;
184  }
185  other.helper_ = nullptr;
186  other.previous_ = nullptr;
187  }
188 
189  private:
190  // Pointer (not reference) to allow nulling in move constructor
191  const GroupStateHelper* helper_{nullptr};
192  DeferredLogger logger_; // Owned logger
193  DeferredLogger* previous_{nullptr}; // For restore
194  bool do_mpi_gather_{true}; // Whether to gather messages across MPI ranks
195  };
196 
197  using GroupTarget = typename SingleWellState<Scalar, IndexTraits>::GroupTarget;
198 
199  GroupStateHelper(WellState<Scalar, IndexTraits>& well_state,
200  GroupState<Scalar>& group_state,
201  const Schedule& schedule,
202  const SummaryState& summary_state,
203  const GuideRate& guide_rate,
204  const PhaseUsageInfo<IndexTraits>& phase_usage_info,
205  const Parallel::Communication& comm,
206  bool terminal_output);
207 
208  void accumulateGroupEfficiencyFactor(const Group& group, Scalar& factor) const;
209 
210  std::pair<bool, Scalar> checkGroupConstraintsInj(const std::string& name,
211  const std::string& parent,
212  const Group& group,
213  const Scalar* rates,
214  const Phase injection_phase,
215  const Scalar efficiency_factor,
216  const std::vector<Scalar>& resv_coeff,
217  const bool check_guide_rate) const;
218 
219  std::pair<bool, Scalar> checkGroupConstraintsProd(const std::string& name,
220  const std::string& parent,
221  const Group& group,
222  const Scalar* rates,
223  const Scalar efficiency_factor,
224  const std::vector<Scalar>& resv_coeff,
225  const bool check_guide_rate) const;
226 
227  std::pair<Group::ProductionCMode, Scalar>
228  checkGroupProductionConstraints(const Group& group) const;
229 
230  const Parallel::Communication& comm() const { return this->comm_; }
231 
235  {
236  if (this->deferred_logger_ == nullptr) {
237  throw std::logic_error("DeferredLogger not set. Call pushLogger() first.");
238  }
239  return *this->deferred_logger_;
240  }
241 
242  std::vector<Scalar> getGroupRatesAvailableForHigherLevelControl(const Group& group, const bool is_injector) const;
243 
244  Scalar getInjectionGroupTarget(const Group& group,
245  const Phase& injection_phase,
246  const std::vector<Scalar>& resv_coeff) const;
247 
251  GuideRateModel::Target getInjectionGuideTargetMode(Phase injection_phase) const;
252 
253  Scalar getProductionGroupTarget(const Group& group) const;
254 
256  Scalar getProductionGroupTargetForMode(const Group& group,
257  const Group::ProductionCMode cmode) const;
258 
262  GuideRateModel::Target getProductionGuideTargetMode(const Group& group) const;
263 
264  std::pair<Scalar, Group::ProductionCMode>
265  getAutoChokeGroupProductionTargetRate(const Group& bottom_group,
266  const Group& group,
267  const std::vector<Scalar>& resv_coeff,
268  Scalar efficiencyFactor) const;
269 
270  GuideRate::RateVector getProductionGroupRateVector(const std::string& group_name) const;
271 
272  std::optional<GroupTarget> getWellGroupTargetInjector(const std::string& name,
273  const std::string& parent,
274  const Group& group,
275  const Scalar* rates,
276  const Phase injection_phase,
277  const Scalar efficiency_factor,
278  const std::vector<Scalar>& resv_coeff) const;
279 
280  std::optional<GroupTarget> getWellGroupTargetProducer(const std::string& name,
281  const std::string& parent,
282  const Group& group,
283  const Scalar* rates,
284  const Scalar efficiency_factor,
285  const std::vector<Scalar>& resv_coeff) const;
286 
287  GuideRate::RateVector getWellRateVector(const std::string& name) const;
288 
289  std::vector<std::string> groupChainTopBot(const std::string& bottom, const std::string& top) const;
290 
293  int groupControlledWells(const std::string& group_name,
294  const std::string& always_included_child,
295  const bool is_production_group,
296  const Phase injection_phase) const;
297 
298  GroupState<Scalar>& groupState() const
299  {
300  return *this->group_state_;
301  }
302 
303  const GuideRate& guideRate() const
304  {
305  return this->guide_rate_;
306  }
307 
308  bool isRank0() const
309  {
310  return this->well_state_->isRank0();
311  }
312 
313  bool isReservoirCouplingMaster() const { return rescoup_.isMaster(); }
314 
315  bool isReservoirCouplingMasterGroup(const Group& group) const { return rescoup_.isMasterGroup(group.name()); }
316 
317  bool isReservoirCouplingSlave() const { return rescoup_.isSlave(); }
318 
319  bool isReservoirCouplingSlaveGroup(const Group& group) const { return rescoup_.isSlaveGroup(group.name()); }
320 
321  constexpr int numPhases() const {
322  return this->wellState().numPhases();
323  }
324 
325  int phaseToActivePhaseIdx(const Phase phase) const;
326 
327  const PhaseUsageInfo<IndexTraits>& phaseUsage() const {
328  return this->phase_usage_info_;
329  }
330 
331  GroupStateGuard pushGroupState(GroupState<Scalar>& group_state)
332  {
333  return GroupStateGuard(*this, group_state);
334  }
335 
349  ScopedLoggerGuard pushLogger(bool do_mpi_gather = true) const
350  {
351  return ScopedLoggerGuard(*this, do_mpi_gather);
352  }
353 
354  WellStateGuard pushWellState(WellState<Scalar, IndexTraits>& well_state)
355  {
356  return WellStateGuard(*this, well_state);
357  }
358 
359  int reportStepIdx() const
360  {
361  return report_step_;
362  }
363 
364  const Schedule& schedule() const
365  {
366  return this->schedule_;
367  }
368 
369  ReservoirCoupling::Proxy<Scalar>& rescoup() {
370  return rescoup_;
371  }
372 
373  const ReservoirCoupling::Proxy<Scalar>& rescoup() const {
374  return rescoup_;
375  }
376 
377  ReservoirCouplingMaster<Scalar>& reservoirCouplingMaster() {
378  return rescoup_.master();
379  }
380 
381  const ReservoirCouplingMaster<Scalar>& reservoirCouplingMaster() const {
382  return rescoup_.master();
383  }
384 
385  ReservoirCouplingSlave<Scalar>& reservoirCouplingSlave() {
386  return rescoup_.slave();
387  }
388  const ReservoirCouplingSlave<Scalar>& reservoirCouplingSlave() const {
389  return rescoup_.slave();
390  }
391 
392 #ifdef RESERVOIR_COUPLING_ENABLED
393  void setReservoirCouplingMaster(ReservoirCouplingMaster<Scalar>* master) {
394  rescoup_.setMaster(master);
395  }
396  void setReservoirCouplingSlave(ReservoirCouplingSlave<Scalar>* slave) {
397  rescoup_.setSlave(slave);
398  }
399 #endif
400 
401  void setCmodeGroup(const Group& group);
402 
403  template <class AverageRegionalPressureType>
404  void
405  setRegionAveragePressureCalculator(const Group& group,
406  const FieldPropsManager& fp,
407  std::map<std::string, std::unique_ptr<AverageRegionalPressureType>>&
408  regional_average_pressure_calculator) const;
409 
410  void setReportStep(int report_step)
411  {
412  report_step_ = report_step;
413  }
414 
415  const SummaryState& summaryState() const
416  {
417  return this->summary_state_;
418  }
419 
420  Scalar sumSolventRates(const Group& group, const bool is_injector) const;
421 
422  Scalar sumWellResRates(const Group& group, const int phase_pos, const bool injector) const;
423 
424  Scalar sumWellSurfaceRates(const Group& group, const int phase_pos, const bool injector) const;
425 
426  Scalar sumWellPhaseRates(bool res_rates,
427  const Group& group,
428  const int phase_pos,
429  const bool injector,
430  const bool network = false) const;
431 
432  bool terminalOutput() const
433  {
434  return this->terminal_output_;
435  }
436 
437  template <class RegionalValues>
438  void updateGpMaintTargetForGroups(const Group& group,
439  const RegionalValues& regional_values,
440  const double dt);
441 
444  int updateGroupControlledWells(const bool is_production_group,
445  const Phase injection_phase);
446 
447  void updateGroupProductionRates(const Group& group);
448 
449  void updateGroupTargetReduction(const Group& group,
450  const bool is_injector);
451 
452  void updateNetworkLeafNodeProductionRates();
453 
466 
467  void updateREINForGroups(const Group& group, bool sum_rank);
468 
469  void updateReservoirRatesInjectionGroups(const Group& group);
470 
471 #ifdef RESERVOIR_COUPLING_ENABLED
472  void updateSlaveGroupCmodesFromMaster();
482 #endif
483 
484  void updateState(WellState<Scalar, IndexTraits>& well_state, GroupState<Scalar>& group_state);
485 
486  void updateSurfaceRatesInjectionGroups(const Group& group);
487 
488  void updateVREPForGroups(const Group& group);
489 
490  void updateWellRates(const Group& group,
491  const WellState<Scalar, IndexTraits>& well_state_nupcol,
492  WellState<Scalar, IndexTraits>& well_state) const;
493 
494  const WellState<Scalar, IndexTraits>& wellState() const
495  {
496  return *this->well_state_;
497  }
498 
499  void updateWellRatesFromGroupTargetScale(const Scalar scale,
500  const Group& group,
501  bool is_injector,
502  WellState<Scalar, IndexTraits>& well_state) const;
503 
505  std::pair<std::optional<std::string>, Scalar>
506  worstOffendingWell(const Group& group,
507  const Group::ProductionCMode& offended_control) const;
508 
509 private:
510 
532  template<typename ReductionLambda, typename FractionLambda>
533  Scalar applyReductionsAndFractions_(const std::vector<std::string>& chain,
534  Scalar orig_target,
535  Scalar current_rate_available,
536  std::size_t local_reduction_level,
537  bool is_production_group,
538  Phase injection_phase,
539  ReductionLambda&& local_reduction_lambda,
540  FractionLambda&& local_fraction_lambda,
541  bool do_addback) const;
542 
543  std::pair<Group::ProductionCMode, Scalar>
544  checkProductionRateConstraint_(const Group& group,
545  Group::ProductionCMode cmode,
546  Group::ProductionCMode currentControl,
547  Scalar target,
548  Scalar current_rate) const;
549 
557  std::unordered_set<std::string> collectTargetedProductionGroups_() const;
558 
569  Scalar computeAddbackEfficiency_(const std::vector<std::string>& chain,
570  std::size_t local_reduction_level) const;
571 
572  std::string controlGroup_(const Group& group) const;
573 
574  GuideRate::RateVector getGuideRateVector_(const std::vector<Scalar>& rates) const;
575 
576  Scalar getInjectionGroupTargetForMode_(const Group& group,
577  const Phase& injection_phase,
578  const std::vector<Scalar>& resv_coeff,
579  const Group::InjectionCMode cmode) const;
580 
591  std::size_t getLocalReductionLevel_(const std::vector<std::string>& chain,
592  bool is_production_group,
593  Phase injection_phase) const;
594 
595  Scalar getProductionConstraintTarget_(const Group& group,
596  Group::ProductionCMode cmode,
597  const Group::ProductionControls& controls) const;
598 
599  Scalar getProductionGroupTargetForMode_(const Group& group, const Group::ProductionCMode cmode) const;
600 
601  Scalar getSatelliteRate_(const Group& group,
602  const int phase_pos,
603  const bool res_rates,
604  const bool is_injector) const;
605 
609  bool isAutoChokeGroupUnderperforming_(const Group& group) const;
610 
612  bool isInGroupChainTopBot_(const std::string& bottom, const std::string& top) const;
613 
614  bool isSatelliteGroup_(const Group& group) const;
615 
616  Scalar satelliteInjectionRate_(const ScheduleState& sched,
617  const Group& group,
618  const int phase_pos,
619  bool res_rates) const;
620 
621  Scalar satelliteProductionRate_(const ScheduleState& sched,
622  const Group& group,
623  const GSatProd::GSatProdGroupProp::Rate rate_comp,
624  bool res_rates) const;
625 
626  std::optional<GSatProd::GSatProdGroupProp::Rate> selectRateComponent_(const int phase_pos) const;
627 
638  Scalar subtractOtherPhaseResvInjection_(
639  Phase injection_phase,
640  Scalar base_reservoir_rate,
641  const std::vector<Scalar>& group_injection_reservoir_rates) const;
642 
643  Scalar sumProductionRateForControlMode_(const Group& group, Group::ProductionCMode cmode) const;
644 
645  int updateGroupControlledWellsRecursive_(const std::string& group_name,
646  const bool is_production_group,
647  const Phase injection_phase);
648 
649  void updateGroupTargetReductionRecursive_(const Group& group,
650  const bool is_injector,
651  std::vector<Scalar>& group_target_reduction);
652 
653  // Validate that GCONINJE top-up phases (RESV/VREP) are consistent
654  // across the group hierarchy. Called once from setCmodeGroup() for FIELD.
655  void validateInjectionTopupPhases_(const Group& group) const;
656 
657  // Recursive helper for validateInjectionTopupPhases_()
658  void validateInjectionTopupPhasesRecursive_(const Group& group,
659  std::optional<Phase> inherited_topup_phase,
660  const std::string& inherited_topup_group) const;
661 
662  // --- Reservoir coupling private methods ---
663 #ifdef RESERVOIR_COUPLING_ENABLED
664  ReservoirCoupling::Phase activePhaseIdxToRescoupPhase_(int phase_pos) const;
669 
678  std::unordered_set<std::string> collectMasterGroupHierarchy_() const;
679 
693  Scalar getEffectiveProductionLimit_(const std::string& gname,
694  Group::ProductionCMode rate_type,
695  Scalar slave_local_target) const;
696 
697  ReservoirCoupling::GrupSlav::FilterFlag getInjectionFilterFlag_(const std::string& group_name,
698  const Phase injection_phase) const;
699 
700  ReservoirCoupling::GrupSlav::FilterFlag getProductionFilterFlag_(const std::string& group_name,
701  const Group::ProductionCMode cmode) const;
702 
703  Scalar getReservoirCouplingMasterGroupRate_(const Group& group,
704  const int phase_pos,
705  const ReservoirCoupling::RateKind kind) const;
706 #endif // RESERVOIR_COUPLING_ENABLED
707 
708  const WellState<Scalar, IndexTraits>* well_state_ {nullptr};
709  GroupState<Scalar>* group_state_ {nullptr};
710  const Schedule& schedule_;
711  const SummaryState& summary_state_;
712  const GuideRate& guide_rate_;
713  // NOTE: The deferred logger does not change the object "meaningful" state, so it should be ok to
714  // make it mutable and store a pointer to it here.
715  mutable DeferredLogger* deferred_logger_ {nullptr};
716  // NOTE: The phase usage info seems to be read-only throughout the simulation, so it should be safe
717  // to store a reference to it here.
718  const PhaseUsageInfo<IndexTraits>& phase_usage_info_;
719  const Parallel::Communication& comm_;
720  bool terminal_output_ {false};
721  int report_step_ {0};
722  ReservoirCoupling::Proxy<Scalar> rescoup_{};
723 };
724 
725 // -----------------------------------------------------------------------------
726 // Template member function implementations
727 // -----------------------------------------------------------------------------
728 
729 // NOTE: This template member function is defined here in the header because the
730 // AverageRegionalPressureType type parameter depends on derived class types that are
731 // not available when GroupStateHelper.cpp is compiled. Template functions
732 // must be visible at their instantiation point.
733 // See GroupStateHelper.cpp for detailed rationale.
734 template <typename Scalar, typename IndexTraits>
735 template <class AverageRegionalPressureType>
736 void
737 GroupStateHelper<Scalar, IndexTraits>::setRegionAveragePressureCalculator(
738  const Group& group,
739  const FieldPropsManager& fp,
740  std::map<std::string, std::unique_ptr<AverageRegionalPressureType>>& regional_average_pressure_calculator)
741  const
742 {
743  for (const std::string& groupName : group.groups()) {
744  this->setRegionAveragePressureCalculator(this->schedule_.getGroup(groupName, this->report_step_),
745  fp,
746  regional_average_pressure_calculator);
747  }
748  const auto& gpm = group.gpmaint();
749  if (!gpm)
750  return;
751 
752  const auto& reg = gpm->region();
753  if (!reg)
754  return;
755 
756  if (regional_average_pressure_calculator.count(reg->first) == 0) {
757  const std::string name = (reg->first.rfind("FIP", 0) == 0) ? reg->first : "FIP" + reg->first;
758  const auto& fipnum = fp.get_int(name);
759  regional_average_pressure_calculator[reg->first]
760  = std::make_unique<AverageRegionalPressureType>(fipnum);
761  }
762 }
763 
764 // NOTE: This template member function is defined in the header because the
765 // RegionalValues type parameter depends on derived class types that are
766 // not available when GroupStateHelper.cpp is compiled. Template functions
767 // must be visible at their instantiation point.
768 // See GroupStateHelper.cpp for detailed rationale.
769 template <typename Scalar, typename IndexTraits>
770 template <class RegionalValues>
771 void
772 GroupStateHelper<Scalar, IndexTraits>::updateGpMaintTargetForGroups(const Group& group,
773  const RegionalValues& regional_values,
774  const double dt)
775 {
776  OPM_TIMEFUNCTION();
777  for (const std::string& group_name : group.groups()) {
778  const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_);
779  this->updateGpMaintTargetForGroups(group_tmp, regional_values, dt);
780  }
781  const auto& gpm = group.gpmaint();
782  if (!gpm)
783  return;
784 
785  const auto& region = gpm->region();
786  if (!region)
787  return;
788  if (this->isReservoirCouplingMasterGroup(group)) {
789  // GPMAINT is not supported for reservoir coupling master groups since master groups do not have
790  // subordinate wells in the master reservoir, so the slaves cannot influence the master reservoir's
791  // average pressure.
792  // Even specifying GPMAINT on a group superior to the master group might not make sense, since if the
793  // superior target is distributed down to the master group with guide rate fractions, adjusting
794  // the master group's target (that is sent to the slave) could only indirectly influence the master
795  // reservoir's average pressure by affecting the guide rate fractions distributed to actual wells
796  // in the master reservoir.
797  OPM_DEFLOG_THROW(
798  std::runtime_error,
799  "GPMAINT is not supported for reservoir coupling master groups.",
800  this->deferredLogger()
801  );
802  return;
803  }
804  else if (this->isReservoirCouplingSlaveGroup(group)) {
805  // GPMAINT is not supported for reservoir coupling slave groups since their targets will be overridden
806  // by the corresponding master group's target anyway.
807  OPM_DEFLOG_THROW(
808  std::runtime_error,
809  "GPMAINT is not supported for reservoir coupling slave groups.",
810  this->deferredLogger()
811  );
812  return;
813  }
814  const auto [name, number] = *region;
815  const Scalar error = gpm->pressure_target() - regional_values.at(name)->pressure(number);
816  Scalar current_rate = 0.0;
817  const auto& pu = this->phase_usage_info_;
818  bool injection = true;
819  Scalar sign = 1.0;
820  switch (gpm->flow_target()) {
821  case GPMaint::FlowTarget::RESV_PROD: {
822  current_rate = -this->groupState().injection_vrep_rate(group.name());
823  injection = false;
824  sign = -1.0;
825  break;
826  }
827  case GPMaint::FlowTarget::RESV_OINJ: {
828  if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) {
829  const auto io = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx);
830  current_rate = this->groupState().injection_reservoir_rates(group.name())[io];
831  }
832  break;
833  }
834  case GPMaint::FlowTarget::RESV_WINJ: {
835  if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) {
836  const auto iw = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx);
837  current_rate = this->groupState().injection_reservoir_rates(group.name())[iw];
838  }
839  break;
840  }
841  case GPMaint::FlowTarget::RESV_GINJ: {
842  if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) {
843  const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx);
844  current_rate = this->groupState().injection_reservoir_rates(group.name())[ig];
845  }
846  break;
847  }
848  case GPMaint::FlowTarget::SURF_OINJ: {
849  if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) {
850  const auto io = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx);
851  current_rate = this->groupState().injection_surface_rates(group.name())[io];
852  }
853  break;
854  }
855  case GPMaint::FlowTarget::SURF_WINJ: {
856  if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) {
857  const auto iw = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx);
858  current_rate = this->groupState().injection_surface_rates(group.name())[iw];
859  }
860  break;
861  }
862  case GPMaint::FlowTarget::SURF_GINJ: {
863  if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) {
864  const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx);
865  current_rate = this->groupState().injection_surface_rates(group.name())[ig];
866  }
867  break;
868  }
869  default:
870  throw std::invalid_argument("Invalid Flow target type in GPMAINT");
871  }
872  auto& gpmaint_state = this->groupState().gpmaint(group.name());
873  // we only activate gpmaint if pressure is lower than the target regional pressure for injectors
874  // (i.e. error > 0) and higher for producers.
875  bool activate = (injection && error > 0) || (!injection && error < 0);
876  Scalar rate = 0.0;
877  if (activate) {
878  rate = gpm->rate(gpmaint_state, current_rate, error, dt);
879  } else {
880  gpm->resetState(gpmaint_state);
881  }
882  this->groupState().update_gpmaint_target(group.name(), std::max(Scalar {0.0}, sign * rate));
883 }
884 
885 } // namespace Opm
886 
887 #endif // OPM_GROUPSTATE_HELPER_HEADER_INCLUDED
int updateGroupControlledWells(const bool is_production_group, const Phase injection_phase)
update the number of wells that are actively under group control for a given group with name given by...
Definition: GroupStateHelper.cpp:1175
ScopedLoggerGuard(const GroupStateHelper &helper, bool do_mpi_gather=true)
Constructor for scoped logger guard.
Definition: GroupStateHelper.hpp:137
RAII guard that owns a DeferredLogger and auto-gathers on destruction.
Definition: GroupStateHelper.hpp:129
int groupControlledWells(const std::string &group_name, const std::string &always_included_child, const bool is_production_group, const Phase injection_phase) const
returns the number of wells that are actively under group control for a given group with name given b...
Definition: GroupStateHelper.cpp:906
GuideRateModel::Target getInjectionGuideTargetMode(Phase injection_phase) const
Get the guide rate target mode for an injection phase.
Definition: GroupStateHelper.cpp:562
ScopedLoggerGuard pushLogger(bool do_mpi_gather=true) const
Push a new logger onto the stack with auto-cleanup on destruction.
Definition: GroupStateHelper.hpp:349
std::pair< std::optional< std::string >, Scalar > worstOffendingWell(const Group &group, const Group::ProductionCMode &offended_control) const
Returns the name of the worst offending well and its fraction (i.e. violated_phase / preferred_phase)...
Definition: GroupStateHelper.cpp:1534
Definition: GroupStateHelper.hpp:60
Definition: BlackoilWellModelConstraints.hpp:34
This file contains a set of helper functions used by VFPProd / VFPInj.
Definition: blackoilbioeffectsmodules.hh:45
Definition: BlackoilWellModelConstraints.hpp:37
Definition: DeferredLogger.hpp:56
void updateNONEProductionGroups()
Set production control to NONE for groups not targeting any well.
Definition: GroupStateHelper.cpp:1236
DeferredLogger & deferredLogger() const
Get the deferred logger.
Definition: GroupStateHelper.hpp:234
void logMessages()
Log all messages to the OpmLog backends, and clear the message container.
Definition: DeferredLogger.cpp:92
Opm::DeferredLogger gatherDeferredLogger(const Opm::DeferredLogger &local_deferredlogger, Opm::Parallel::Communication)
Create a global log combining local logs.
Definition: gatherDeferredLogger.cpp:168
Definition: GroupStateHelper.hpp:89
Scalar getProductionGroupTargetForMode(const Group &group, const Group::ProductionCMode cmode) const
Get the production target for a specific control mode (not necessarily the active one)...
Definition: GroupStateHelper.cpp:531
GuideRateModel::Target getProductionGuideTargetMode(const Group &group) const
Get the guide rate target mode for a production group.
Definition: GroupStateHelper.cpp:540
The state of a set of wells, tailored for use by the fully implicit blackoil simulator.
Definition: TemperatureModel.hpp:61